merged master; fixed tests

feature/109
Christoph Oberhofer 9 years ago
commit e7c62221c1

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

1
.gitignore vendored

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

1299
dist/quagga.js vendored

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

@ -1,6 +1,6 @@
{ {
"name": "quagga", "name": "quagga",
"version": "0.11.0", "version": "0.11.2",
"description": "An advanced barcode-scanner written in JavaScript", "description": "An advanced barcode-scanner written in JavaScript",
"main": "lib/quagga.js", "main": "lib/quagga.js",
"browser": "dist/quagga.min.js", "browser": "dist/quagga.min.js",
@ -60,7 +60,7 @@
"phantomjs": "^1.9.18", "phantomjs": "^1.9.18",
"sinon": "^1.16.1", "sinon": "^1.16.1",
"sinon-chai": "^2.8.0", "sinon-chai": "^2.8.0",
"webpack": "^2.1.0-beta.7", "webpack": "2.1.0-beta.4",
"webpack-sources": "^0.1.1" "webpack-sources": "^0.1.1"
}, },
"directories": { "directories": {
@ -109,6 +109,6 @@
"lodash": "^4.6.1", "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.2.2" "webrtc-adapter": "^1.3.0"
} }
} }

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

@ -34,11 +34,11 @@ function waitForVideo(video) {
function initCamera(video, constraints) { function initCamera(video, constraints) {
return navigator.mediaDevices.getUserMedia(constraints) return navigator.mediaDevices.getUserMedia(constraints)
.then((stream) => { .then((stream) => {
return new Promise((resolve, reject) => { return new Promise((resolve) => {
streamRef = stream; streamRef = stream;
video.setAttribute("autoplay", 'true'); video.setAttribute("autoplay", 'true');
video.srcObject = stream; video.srcObject = stream;
video.addEventListener('loadedmetadata', (e) => { video.addEventListener('loadedmetadata', () => {
video.play(); video.play();
resolve(); resolve();
}); });
@ -51,13 +51,13 @@ 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;
@ -69,7 +69,7 @@ function applyCameraFacing(facing, constraints) {
} }
if ( typeof MediaStreamTrack !== 'undefined' && if ( typeof MediaStreamTrack !== 'undefined' &&
typeof MediaStreamTrack.getSources !== 'undefined') { typeof MediaStreamTrack.getSources !== 'undefined') {
return new Promise((resolve, reject) => { return new Promise((resolve) => {
MediaStreamTrack.getSources((sourceInfos) => { MediaStreamTrack.getSources((sourceInfos) => {
const videoSource = sourceInfos.filter((sourceInfo) => ( const videoSource = sourceInfos.filter((sourceInfo) => (
sourceInfo.kind === "video" && sourceInfo.facing === facing sourceInfo.kind === "video" && sourceInfo.facing === facing

@ -561,7 +561,7 @@ export default function createLocator(inputImageWrapper, config) {
boxes = findBoxes(topLabels, maxLabel); boxes = findBoxes(topLabels, maxLabel);
return boxes; return boxes;
} }
} };
} }
export function checkImageConstraints(inputStream, config) { export function checkImageConstraints(inputStream, config) {
var patchSize, var patchSize,

@ -7,7 +7,6 @@ import ResultCollector from './analytics/result_collector';
import Config from './config/config'; import Config from './config/config';
import {merge, pick, omitBy, isEmpty} from 'lodash'; import {merge, pick, omitBy, isEmpty} from 'lodash';
function fromImage(config, imageSrc, inputConfig = {}) { function fromImage(config, imageSrc, inputConfig = {}) {
const staticImageConfig = { const staticImageConfig = {
inputStream: { inputStream: {

@ -53,7 +53,8 @@ describe('decodeSingle', function () {
expect(result.codeResult.code).to.equal(sample.result); expect(result.codeResult.code).to.equal(sample.result);
expect(result.codeResult.format).to.equal(sample.format); expect(result.codeResult.format).to.equal(sample.format);
callback(); callback();
}); })
.start();
}, function() { }, function() {
done(); done();
}); });

@ -1,4 +1,4 @@
import BarcodeLocator from '../../src/locator/barcode_locator'; import {checkImageConstraints} from '../../src/locator/barcode_locator';
import Config from '../../src/config/config'; import Config from '../../src/config/config';
import {merge} from 'lodash'; import {merge} from 'lodash';
@ -45,7 +45,7 @@ describe('checkImageConstraints', function() {
it('should not adjust the image-size if not needed', function() { it('should not adjust the image-size if not needed', function() {
var expected = {x: imageSize.x, y: imageSize.y}; var expected = {x: imageSize.x, y: imageSize.y};
BarcodeLocator.checkImageConstraints(inputStream, config.locator); checkImageConstraints(inputStream, config.locator);
expect(inputStream.getWidth()).to.be.equal(expected.x); expect(inputStream.getWidth()).to.be.equal(expected.x);
expect(inputStream.getHeight()).to.be.equal(expected.y); expect(inputStream.getHeight()).to.be.equal(expected.y);
}); });
@ -55,7 +55,7 @@ describe('checkImageConstraints', function() {
config.locator.halfSample = true; config.locator.halfSample = true;
imageSize.y += 1; imageSize.y += 1;
BarcodeLocator.checkImageConstraints(inputStream, config.locator); checkImageConstraints(inputStream, config.locator);
expect(inputStream.getWidth()).to.be.equal(expected.x); expect(inputStream.getWidth()).to.be.equal(expected.x);
expect(inputStream.getHeight()).to.be.equal(expected.y); expect(inputStream.getHeight()).to.be.equal(expected.y);
}); });
@ -65,7 +65,7 @@ describe('checkImageConstraints', function() {
imageSize.y += 1; imageSize.y += 1;
config.locator.halfSample = false; config.locator.halfSample = false;
BarcodeLocator.checkImageConstraints(inputStream, config.locator); checkImageConstraints(inputStream, config.locator);
expect(inputStream.getHeight()).to.be.equal(expected.y); expect(inputStream.getHeight()).to.be.equal(expected.y);
expect(inputStream.getWidth()).to.be.equal(expected.x); expect(inputStream.getWidth()).to.be.equal(expected.x);
}); });
@ -92,7 +92,7 @@ describe('checkImageConstraints', function() {
}; };
config.locator.halfSample = false; config.locator.halfSample = false;
BarcodeLocator.checkImageConstraints(inputStream, config.locator); checkImageConstraints(inputStream, config.locator);
expect(inputStream.getHeight()).to.be.equal(expectedSize.y); expect(inputStream.getHeight()).to.be.equal(expectedSize.y);
expect(inputStream.getWidth()).to.be.equal(expectedSize.x); expect(inputStream.getWidth()).to.be.equal(expectedSize.x);
expect(inputStream.setTopRight.getCall(0).args[0]).to.deep.equal(expectedTopRight); expect(inputStream.setTopRight.getCall(0).args[0]).to.deep.equal(expectedTopRight);
@ -121,7 +121,7 @@ describe('checkImageConstraints', function() {
}; };
config.locator.halfSample = false; config.locator.halfSample = false;
BarcodeLocator.checkImageConstraints(inputStream, config.locator); checkImageConstraints(inputStream, config.locator);
expect(inputStream.getHeight()).to.be.equal(expectedSize.y); expect(inputStream.getHeight()).to.be.equal(expectedSize.y);
expect(inputStream.getWidth()).to.be.equal(expectedSize.x); expect(inputStream.getWidth()).to.be.equal(expectedSize.x);
expect(inputStream.setTopRight.getCall(0).args[0]).to.deep.equal(expectedTopRight); expect(inputStream.setTopRight.getCall(0).args[0]).to.deep.equal(expectedTopRight);

@ -27,9 +27,10 @@ beforeEach(function() {
sinon.spy(tracks[0], "stop"); sinon.spy(tracks[0], "stop");
video = { video = {
src: null, srcObject: null,
addEventListener: function() {}, addEventListener: function() {},
removeEventListener: function() {}, removeEventListener: function() {},
setAttribute: sinon.spy(),
play: function() {}, play: function() {},
videoWidth: 320, videoWidth: 320,
videoHeight: 480 videoHeight: 480
@ -60,12 +61,9 @@ describe('success', function() {
CameraAccess.request(video, {}) CameraAccess.request(video, {})
.then(function () { .then(function () {
expect(navigator.mediaDevices.getUserMedia.calledOnce).to.equal(true); expect(navigator.mediaDevices.getUserMedia.calledOnce).to.equal(true);
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) => { it("should allow deprecated constraints to be used", (done) => {
@ -89,9 +87,6 @@ describe('success', function() {
expect(args[0].video.maxAspectRatio).not.to.be.defined; expect(args[0].video.maxAspectRatio).not.to.be.defined;
done(); done();
}) })
window.setTimeout(() => {
video.onloadedmetadata();
}, 100);
}); });
}); });
@ -122,9 +117,6 @@ describe('success', function() {
expect(args[0].video.deviceId).to.equal("user"); expect(args[0].video.deviceId).to.equal("user");
done(); done();
}) })
window.setTimeout(() => {
video.onloadedmetadata();
}, 100);
}); });
}); });
@ -132,15 +124,12 @@ describe('success', function() {
it('should release the camera', function (done) { it('should release the camera', function (done) {
CameraAccess.request(video, {}) CameraAccess.request(video, {})
.then(function () { .then(function () {
expect(video.src).to.deep.equal(stream); expect(video.srcObject).to.deep.equal(stream);
CameraAccess.release(); CameraAccess.release();
expect(video.src.getVideoTracks()).to.have.length(1); expect(video.srcObject.getVideoTracks()).to.have.length(1);
expect(video.src.getVideoTracks()[0].stop.calledOnce).to.equal(true); expect(video.srcObject.getVideoTracks()[0].stop.calledOnce).to.equal(true);
done(); done();
}); });
window.setTimeout(() => {
video.onloadedmetadata();
}, 100);
}); });
}); });
}); });

@ -1,11 +1,10 @@
import Events from '../../src/common/events'; import createEventedElement from '../../src/common/events';
let Events;
beforeEach(function() { beforeEach(function() {
Events.unsubscribe(); Events = createEventedElement();
}); });
describe("subscribe", function() { describe("subscribe", function() {
it("should call one callback for a single event", function() { it("should call one callback for a single event", function() {
var callbackA = sinon.stub(), var callbackA = sinon.stub(),
callbackB = sinon.stub(); callbackB = sinon.stub();
@ -31,18 +30,16 @@ describe("subscribe", function() {
it("should call the callback asynchronuously", function(done) { it("should call the callback asynchronuously", function(done) {
var test = { var test = {
callback: function() { callback: function() {}
};
}
};
sinon.stub(test, "callback", function() { sinon.stub(test, "callback", function() {
expect(test.callback.calledOnce).to.be.true; expect(test.callback.calledOnce).to.equal(true);
done(); done();
}); });
Events.subscribe("test", test.callback, true); Events.subscribe("test", test.callback, true);
Events.publish("test"); Events.publish("test");
expect(test.callback.called).to.be.false; expect(test.callback.called).to.equal(false);
}); });
}); });

@ -0,0 +1,478 @@
// Type definitions for QuaggaJS v2015.05.20 Project:
// http://serratus.github.io/quaggaJS/ Definitions by: Cam Birch, Peter
// Horwood aka Madman Pierre
declare var Quagga: QuaggaJSStatic;
export default Quagga;
interface QuaggaJSStatic {
/**
* This method initializes the library for a given
* configuration config (see below) and invokes the callback when Quagga is
* ready to start. The initialization process also requests for camera
* access if real-time detection is configured.
*/
init(
config: QuaggaJSConfigObject,
callback?: (err: any) => void
): void;
init(
config: QuaggaJSConfigObject,
callback: (err: any) => void,
imageWrapper: any
): void;
/**
* When the library is initialized, the start()
* method starts the video-stream and begins locating and decoding the
* images.
*/
start(): void;
/**
* If the decoder is currently running, after calling
* stop() the decoder does not process any more images.
* Additionally, if a camera-stream was requested upon initialization,
* this operation also disconnects the camera.
*/
stop(): void;
/**
* Pauses processing, but does not release any handlers
*/
pause(): void;
/**
* This method registers a callback(data) function that is
* called for each frame after the processing is done. The data object
* contains detailed information about the success/failure of the operation.
* The output varies, depending whether the detection and/or decoding were
* successful or not.
*/
onProcessed(callback: QuaggaJSResultCallbackFunction): void;
/**
* Removes a callback that was previously registered with @see onProcessed
*/
offProcessed(callback: QuaggaJSResultCallbackFunction): void;
/**
* Registers a callback(data) function which is triggered whenever a
* barcode- pattern has been located and decoded successfully. The passed
* data object contains information about the decoding process including the
* detected code which can be obtained by calling data.codeResult.code.
*/
onDetected(callback: QuaggaJSResultCallbackFunction): void;
/**
* Removes a callback that was previously registered with @see onDetected
*/
offDetected(callback: QuaggaJSResultCallbackFunction): void;
ResultCollector: QuaggaJSResultCollector;
registerResultCollector(resultCollector: QuaggaJSResultCollector): void;
setReaders(readers: any): void;
/**
* In contrast to the calls described
* above, this method does not rely on getUserMedia and operates on a single
* image instead. The provided callback is the same as in onDetected and
* contains the result data object.
*/
decodeSingle(
config: QuaggaJSConfigObject,
resultCallback: QuaggaJSResultCallbackFunction
): void;
/**
* Constructs used for debugging purposes
*/
ImageDebug: {
drawPath: QuaggaJSDebugDrawPath;
drawRect: QuaggaJSDebugDrawRect;
};
ImageWrapper: any;
/**
* an object Quagga uses for drawing and processing, useful for calling code
* when debugging
*/
canvas: {
ctx: {
image: CanvasRenderingContext2D;
overlay: CanvasRenderingContext2D
};
dom: {
image: HTMLCanvasElement;
overlay: HTMLCanvasElement
}
};
}
/**
* Called whenever an item is detected or a process step has been completed.
*/
interface QuaggaJSResultCallbackFunction {
(
data: QuaggaJSResultObject
): void;
}
/**
* Called to draw debugging path. The path is an array of array of 2 numbers.
* The def.x specifies element in the sub array is the x, and similary the def.y
* defines the y.
* typical values 0, 1, 'x', 'y'
*/
interface QuaggaJSDebugDrawPath {
(
path: any[],
def: QuaggaJSxyDef,
ctx: CanvasRenderingContext2D,
style: QuaggaJSStyle
): void
}
/**
* Called to draw debugging Rectangle
*/
interface QuaggaJSDebugDrawRect {
(
pos: any[],
size: QuaggaJSRectSize,
ctx: CanvasRenderingContext2D,
style: QuaggaJSStyle
): void
}
/**
* an object with an x and a y value, the x and y specify which element in
* another array is the x or y value.
* typical values 0, 1, 'x', 'y'
*/
interface QuaggaJSxyDef {
x: any;
y: any;
}
/**
* an object with an x and a y value
*/
interface QuaggaJSxy {
x: number;
y: number;
}
/**
* an object with a pair of x and a y values.
* Used for giving a htiml canvas context.strokeRect function it's x, y, width
* and height values.
*/
interface QuaggaJSRectSize {
pos: QuaggaJSxy;
size: QuaggaJSxy;
}
/**
* an object with the styles, color can actually be a color, a gradient or a
* pattern (see defintions for context.strokeStyle. But is most commonly a
* colour.
*/
interface QuaggaJSStyle {
color: string;
/* http://www.w3schools.com/tags/canvas_linewidth.asp */
lineWidth: number;
}
/**
* Pass when creating a ResultCollector
*/
interface QuaggaJSResultCollector {
/**
* keep track of the image producing this result
*/
capture?: boolean;
/**
* maximum number of results to store
*/
capacity?: number;
/**
* a list of codes that should not be recorded. This is effectively a list
* of filters that return false.
*/
blacklist?: QuaggaJSCodeResult;
/**
* passed a QuaggaJSCodeResult, return true if you want this to be stored,
* false if you don't. Note: The black list is effectively a list of filters
* that return false. So if you only want to store results that are ean_13,
* you would say return codeResult.format==="ean_13"
*/
filter?: QuaggaJSResultCollectorFilterFunction;
/*
* a static function that returns you a ResultCollector
*/
create?(QuaggaJSResultCollector): QuaggaJSResultCollector;
getResults?(): QuaggaJSCodeResult[];
}
/**
* used for ResultCollector blacklists and filters
*/
interface QuaggaJSCodeResult {
code?: string;
format?: string;
}
/**
* Called to filter which Results to collect in ResultCollector
*/
interface QuaggaJSResultCollectorFilterFunction {
(
data: QuaggaJSCodeResult
): boolean;
}
/**
* The callbacks passed into onProcessed, onDetected and decodeSingle receive a
* data object upon execution. The data object contains the following
* information. Depending on the success, some fields may be undefined or just
* empty.
*/
interface QuaggaJSResultObject {
codeResult: QuaggaJSResultObject_CodeResult;
line: {
x: number;
y: number;
}[];
angle: number;
pattern: number[];
box: number[][];
boxes: number[][][];
}
interface QuaggaJSResultObject_CodeResult {
code: string;
start: number;
end: number;
codeset: number;
startInfo: {
error: number;
code: number;
start: number;
end: number;
};
decodedCodes: {
error?: number;
code: number;
start: number;
end: number;
}[];
endInfo: {
error: number;
code: number;
start: number;
end: number;
};
direction: number;
format: string;
}
interface QuaggaJSConfigObject {
/**
* The image path to load from, or a data url
* Ex: '/test/fixtures/code_128/image-001.jpg'
* or: 'data:image/jpg;base64,' + data
*/
src?: string; inputStream?: {
/**
* @default "Live"
*/
name?: string;
/**
* @default "LiveStream"
*/
type?: string;
constraints?: {
/**
* @default 640
*/
width?: number;
/**
* @default 480
*/
height?: number;
/**
* In cases where height/width does not suffice
*/
aspectRatio?: number
/**
* @default "environment"
*/
facingMode?: string;
/**
* Explicitly set the camera to the user's choice
*/
deviceId?: string
};
/**
* defines rectangle of the detection/localization area. Useful when you
* KNOW that certain parts of the image will not contain a barcode, also
* useful when you have multiple barcodes in a row and you want to make
* sure that only a code in, say the middle quarter is read not codes
* above or below
*/
area?: {
/**
* @default "0%", set this and bottom to 25% if you only want to
* read a 'line' that is in the middle quarter
*/
top?: string;
/**
* @default "0%"
*/
right?: string;
/**
* @default "0%"
*/
left?: string;
/**
* @default "0%", set this and top to 50% if you only want to read a
* 'line' that is in the middle half
*/
bottom?: string;
};
singleChannel?: boolean;
size?: number;
sequence?: boolean;
};
/**
* @default false
*/
debug?: boolean;
/**
* @default true
*/
locate?: boolean;
/**
* @default 4
*/
numOfWorkers?: number;
decoder?: {
/**
* @default [ "code_128_reader" ]
*/
readers?: string[];
debug?: {
/**
* @default false
*/
drawBoundingBox?: boolean;
/**
* @default false
*/
showFrequency?: boolean;
/**
* @default false
*/
drawScanline?: boolean;
/**
* @default false
*/
showPattern?: boolean;
}
};
locator?: {
/**
* @default true
*/
halfSample?: boolean;
/**
* @default "medium"
* Available values: x-small, small, medium, large, x-large
*/
patchSize?: string;
debug?: {
/**
* @default false
*/
showCanvas?: boolean;
/**
* @default false
*/
showPatches?: boolean;
/**
* @default false
*/
showFoundPatches?: boolean;
/**
* @default false
*/
showSkeleton?: boolean;
/**
* @default false
*/
showLabels?: boolean;
/**
* @default false
*/
showPatchLabels?: boolean;
/**
* @default false
*/
showRemainingPatchLabels?: boolean;
boxFromPatches?: {
/**
* @default false
*/
showTransformed?: boolean;
/**
* @default false
*/
showTransformedBox?: boolean;
/**
* @default false
*/
showBB?: boolean;
};
}
};
}
Loading…
Cancel
Save