UNPKG

61.3 kBPlain TextView Raw
1import { EventEmitter, ListenerFn } from "eventemitter3";
2import { Howl, Howler } from "howler/dist/howler.core.min.js";
3
4import { beepSound } from "./assets/base64assets";
5
6import { userLicenseKey } from "../index";
7import { BarcodePickerCameraManager } from "./barcodePickerCameraManager";
8import { BarcodePickerGui } from "./barcodePickerGui";
9import { BrowserCompatibility } from "./browserCompatibility";
10import { BrowserHelper } from "./browserHelper";
11import { Camera } from "./camera";
12import { CameraManager } from "./cameraManager";
13import { CameraSettings } from "./cameraSettings";
14import { CustomError } from "./customError";
15import { DummyCameraManager } from "./dummyCameraManager";
16import { ImageSettings } from "./imageSettings";
17import { Parser } from "./parser";
18import { Scanner } from "./scanner";
19import { ScanResult } from "./scanResult";
20import { ScanSettings } from "./scanSettings";
21import { SearchArea } from "./searchArea";
22import { UnsupportedBrowserError } from "./unsupportedBrowserError";
23
24/**
25 * @hidden
26 */
27type EventName = "ready" | "submitFrame" | "processFrame" | "scan" | "scanError";
28
29/**
30 * @hidden
31 */
32class BarcodePickerEventEmitter extends EventEmitter<EventName> {}
33
34/**
35 * A barcode picker element used to get and show camera input and perform scanning operations.
36 *
37 * The barcode picker will automatically fit and scale inside the given *originElement*.
38 *
39 * Each barcode picker internally contains a [[Scanner]] object with its own WebWorker thread running a
40 * separate copy of the external Scandit Engine library. To optimize loading times and performance it's
41 * recommended to reuse the same picker and to already create the picker in advance (hidden) and just
42 * display it when needed whenever possible.
43 *
44 * As the loading of the external Scandit Engine library can take some time the picker always starts inactive
45 * (but showing GUI and video) and then activates, if not paused, as soon as the library is ready to scan.
46 * The [[on]] method targeting the [[ready]] event can be used to set up a listener function to be called when the
47 * library is loaded.
48 *
49 * The picker can also operate in "single image mode", letting the user click/tap to take a single image to be scanned
50 * via the camera (mobile/tablet) or a file select dialog (desktop). This is provided automatically as fallback by
51 * default when the OS/browser only supports part of the needed features and cannot provide direct access to the camera
52 * for video streaming and continuous scanning, or can also be forced. This behaviour can be set up on creation. Note
53 * that in this mode some of the functions provided by the picker will have no effect.
54 *
55 * By default an alert is shown if an internal error during scanning is encountered which prevents the scanning
56 * procedure from continuing when running on a local IP address. As this uses the built-in [[scanError]] event
57 * functionality, if unwanted it can be disabled by calling [[removeAllListeners]] on the BarcodePicker
58 * instance (right after creation).
59 *
60 * You are not allowed to hide the Scandit logo present in the corner of the GUI.
61 */
62export class BarcodePicker {
63 private readonly cameraManager: CameraManager;
64 private readonly barcodePickerGui: BarcodePickerGui;
65 private readonly eventEmitter: BarcodePickerEventEmitter;
66 private readonly scanner: Scanner;
67 private readonly beepSound: Howl;
68 private readonly vibrateFunction: (pattern: number | number[]) => boolean;
69 private readonly scannerReadyEventListener: () => void;
70
71 private playSoundOnScan: boolean;
72 private vibrateOnScan: boolean;
73 private scanningPaused: boolean;
74 private fatalError: Error;
75 private latestVideoTimeProcessed: number;
76 private destroyed: boolean;
77 private isReadyToWork: boolean;
78 private cameraAccess: boolean;
79 private targetScanningFPS: number;
80 private averageProcessingTime: number;
81
82 private constructor(
83 originElement: HTMLElement,
84 {
85 visible,
86 singleImageMode,
87 playSoundOnScan,
88 vibrateOnScan,
89 scanningPaused,
90 guiStyle,
91 videoFit,
92 laserArea,
93 viewfinderArea,
94 scanner,
95 scanSettings,
96 targetScanningFPS,
97 hideLogo
98 }: {
99 visible: boolean;
100 singleImageMode: boolean;
101 playSoundOnScan: boolean;
102 vibrateOnScan: boolean;
103 scanningPaused: boolean;
104 guiStyle: BarcodePicker.GuiStyle;
105 videoFit: BarcodePicker.ObjectFit;
106 laserArea?: SearchArea;
107 viewfinderArea?: SearchArea;
108 scanner?: Scanner;
109 scanSettings: ScanSettings;
110 targetScanningFPS: number;
111 hideLogo: boolean;
112 }
113 ) {
114 this.isReadyToWork = false;
115 this.destroyed = false;
116 this.scanningPaused = scanningPaused;
117
118 Howler.autoSuspend = false;
119 this.beepSound = new Howl({
120 src: beepSound
121 });
122
123 // istanbul ignore else
124 if (navigator.vibrate != null) {
125 this.vibrateFunction = navigator.vibrate;
126 } else if (navigator.webkitVibrate != null) {
127 this.vibrateFunction = navigator.webkitVibrate;
128 } else if (navigator.mozVibrate != null) {
129 this.vibrateFunction = navigator.mozVibrate;
130 } else if (navigator.msVibrate != null) {
131 this.vibrateFunction = navigator.msVibrate;
132 }
133
134 this.eventEmitter = new EventEmitter();
135
136 this.setPlaySoundOnScanEnabled(playSoundOnScan);
137 this.setVibrateOnScanEnabled(vibrateOnScan);
138 this.setTargetScanningFPS(targetScanningFPS);
139
140 if (scanner == null) {
141 this.scanner = new Scanner({ scanSettings });
142 } else {
143 this.scanner = scanner;
144 this.scanner.applyScanSettings(scanSettings);
145 }
146 this.scannerReadyEventListener = this.handleScannerReady.bind(this);
147 this.scanner.on("ready", this.scannerReadyEventListener);
148
149 this.barcodePickerGui = new BarcodePickerGui({
150 scanner: this.scanner,
151 originElement,
152 singleImageMode,
153 scanningPaused,
154 visible,
155 guiStyle,
156 videoFit,
157 hideLogo,
158 laserArea,
159 viewfinderArea,
160 cameraUploadCallback: this.processVideoFrame.bind(this, true)
161 });
162
163 if (singleImageMode) {
164 this.cameraManager = new DummyCameraManager();
165 } else {
166 this.cameraManager = new BarcodePickerCameraManager(this.triggerFatalError.bind(this), this.barcodePickerGui);
167 this.scheduleVideoProcessing();
168 }
169
170 this.barcodePickerGui.setCameraManager(this.cameraManager);
171 }
172
173 /**
174 * Create a [[BarcodePicker]] instance, creating the needed HTML in the given origin element.
175 * If the *accessCamera* option is enabled (active by default) and the picker is not in "single image mode",
176 * the available cameras are accessed and camera access permission is requested to the user if needed.
177 * This object expects that at least a camera is available. The active camera is accessed and kept active during the
178 * lifetime of the picker (also when hidden or scanning is paused), and is only released when [[destroy]] is called.
179 *
180 * It is required to having configured the library via [[configure]] before this object can be created.
181 *
182 * The "single image mode" behaviour of the picker can be set up via the
183 * *singleImageMode* option, which accepts a configuration object of the form:
184 * ```
185 * {
186 * desktop: {
187 * always: false, allowFallback: true
188 * },
189 * mobile: {
190 * always: false, allowFallback: true
191 * }
192 * }
193 * ```
194 *
195 * Depending on parameters, device features and user permissions for camera access, any of the following errors
196 * could be the rejected result of the returned promise:
197 * - `LibraryNotConfiguredError`
198 * - `NoOriginElementError`
199 * - `UnsupportedBrowserError`
200 * - `PermissionDeniedError`
201 * - `NotAllowedError`
202 * - `NotFoundError`
203 * - `AbortError`
204 * - `NotReadableError`
205 * - `InternalError`
206 * - `NoCameraAvailableError`
207 *
208 * @param originElement The HTMLElement inside which all the necessary elements for the picker will be added.
209 * @param visible <div class="tsd-signature-symbol">Default =&nbsp;true</div>
210 * Whether the picker starts in a visible state.
211 * @param singleImageMode <div class="tsd-signature-symbol">Default =&nbsp;
212 * { desktop: { always: false, allowFallback: true }, mobile: { always: false, allowFallback: true } }</div>
213 * Whether to provide a UI to pick/snap a single image from the camera instead of accessing and using the persistent
214 * video stream from a camera ("force"), or to allow to provide this as a fallback ("allowFallback") in case the
215 * necessary features for direct camera access are not provided by the OS/browser.
216 * @param playSoundOnScan <div class="tsd-signature-symbol">Default =&nbsp;false</div>
217 * Whether a sound is played on barcode recognition (iOS requires user input).
218 * @param vibrateOnScan <div class="tsd-signature-symbol">Default =&nbsp;false</div>
219 * Whether the device vibrates on barcode recognition (only Chrome & Firefox, requires user input).
220 * @param scanningPaused <div class="tsd-signature-symbol">Default =&nbsp;false</div>
221 * Whether the picker starts in a paused scanning state.
222 * @param guiStyle <div class="tsd-signature-symbol">Default =&nbsp;GuiStyle.LASER</div>
223 * The GUI style for the picker.
224 * @param videoFit <div class="tsd-signature-symbol">Default =&nbsp;ObjectFit.CONTAIN</div>
225 * The fit type for the video element of the picker.
226 * @param laserArea <div class="tsd-signature-symbol">Default =&nbsp;undefined</div>
227 * The area of the laser displayed when the GUI style is set to *laser* (the laser will match the width and be
228 * vertically centered), by default the area will match the current [[ScanSettings]]'s *searchArea* option.
229 * @param viewfinderArea <div class="tsd-signature-symbol">Default =&nbsp;undefined</div>
230 * The area of the viewfinder displayed when the GUI style is set to *viewfinder*, by default the area will match
231 * the current [[ScanSettings]]'s *searchArea* option.
232 * @param enableCameraSwitcher <div class="tsd-signature-symbol">Default =&nbsp;true</div>
233 * Whether to show a GUI button to switch between different cameras (when available).
234 * @param enableTorchToggle <div class="tsd-signature-symbol">Default =&nbsp;true</div>
235 * Whether to show a GUI button to toggle device torch on/off (when available, only Chrome).
236 * @param enableTapToFocus <div class="tsd-signature-symbol">Default =&nbsp;true</div>
237 * Whether to trigger a manual focus of the camera when clicking/tapping on the video (when available, only Chrome).
238 * @param enablePinchToZoom <div class="tsd-signature-symbol">Default =&nbsp;true</div>
239 * Whether to control the zoom of the camera when doing a pinching gesture on the video (when available, only Chrome).
240 * @param accessCamera <div class="tsd-signature-symbol">Default =&nbsp;true</div>
241 * Whether to immediately access the camera (and requesting user permissions if needed) on picker creation.
242 * @param camera <div class="tsd-signature-symbol">Default =&nbsp;undefined</div>
243 * The camera to be used for video input, if not specified the back or only camera will be used.
244 * @param cameraSettings <div class="tsd-signature-symbol">Default =&nbsp;undefined</div>
245 * The camera options used when accessing the camera, by default HD resolution is used.
246 * @param scanner <div class="tsd-signature-symbol">Default =&nbsp;undefined</div>
247 * The scanner object responsible for scanning via the external Scandit Engine library
248 * (a new scanner will be created and initialized if not provided).
249 * @param scanSettings <div class="tsd-signature-symbol">Default =&nbsp;new ScanSettings()</div>
250 * The configuration object for scanning options to be applied to the scanner (all symbologies disabled by default).
251 * @param targetScanningFPS <div class="tsd-signature-symbol">Default =&nbsp;30</div>
252 * The target frames per second to be processed, the final speed is limited by the camera framerate (usually 30 FPS)
253 * and the frame processing time of the device. By setting this to lower numbers devices can save power by performing
254 * less work during scanning operations, depending on device speed (faster devices can "sleep" for longer periods).
255 * Must be a number bigger than 0.
256 * @returns A promise resolving to the created ready [[BarcodePicker]] object.
257 */
258 public static create(
259 originElement: HTMLElement,
260 {
261 visible = true,
262 singleImageMode = {
263 desktop: { always: false, allowFallback: true },
264 mobile: { always: false, allowFallback: true }
265 },
266 playSoundOnScan = false,
267 vibrateOnScan = false,
268 scanningPaused = false,
269 guiStyle = BarcodePicker.GuiStyle.LASER,
270 videoFit = BarcodePicker.ObjectFit.CONTAIN,
271 laserArea,
272 viewfinderArea,
273 scanner,
274 scanSettings = new ScanSettings(),
275 enableCameraSwitcher = true,
276 enableTorchToggle = true,
277 enableTapToFocus = true,
278 enablePinchToZoom = true,
279 accessCamera = true,
280 camera,
281 cameraSettings,
282 targetScanningFPS = 30
283 }: {
284 visible?: boolean;
285 singleImageMode?: {
286 desktop: { always: boolean; allowFallback: boolean };
287 mobile: { always: boolean; allowFallback: boolean };
288 };
289 playSoundOnScan?: boolean;
290 vibrateOnScan?: boolean;
291 scanningPaused?: boolean;
292 guiStyle?: BarcodePicker.GuiStyle;
293 videoFit?: BarcodePicker.ObjectFit;
294 laserArea?: SearchArea;
295 viewfinderArea?: SearchArea;
296 scanner?: Scanner;
297 scanSettings?: ScanSettings;
298 enableCameraSwitcher?: boolean;
299 enableTorchToggle?: boolean;
300 enableTapToFocus?: boolean;
301 enablePinchToZoom?: boolean;
302 accessCamera?: boolean;
303 camera?: Camera;
304 cameraSettings?: CameraSettings;
305 targetScanningFPS?: number;
306 } = {}
307 ): Promise<BarcodePicker> {
308 let singleImageModeForced: boolean;
309 let singleImageModeFallbackAllowed: boolean;
310 const deviceType: string | undefined = BrowserHelper.userAgentInfo.getDevice().type;
311 if (deviceType != null && ["mobile", "tablet"].includes(deviceType)) {
312 singleImageModeForced = singleImageMode.mobile.always;
313 singleImageModeFallbackAllowed = singleImageMode.mobile.allowFallback;
314 } else {
315 singleImageModeForced = singleImageMode.desktop.always;
316 singleImageModeFallbackAllowed = singleImageMode.desktop.allowFallback;
317 }
318
319 const browserCompatibility: BrowserCompatibility = BrowserHelper.checkBrowserCompatibility();
320 if (
321 !browserCompatibility.scannerSupport ||
322 (!singleImageModeForced && !singleImageModeFallbackAllowed && !browserCompatibility.fullSupport)
323 ) {
324 return Promise.reject(new UnsupportedBrowserError(browserCompatibility));
325 }
326
327 if (userLicenseKey == null) {
328 return Promise.reject(
329 new CustomError({
330 name: "LibraryNotConfiguredError",
331 message: "The library has not correctly been configured yet, please call 'configure' with valid parameters"
332 })
333 );
334 }
335 if (!BrowserHelper.isValidHTMLElement(originElement)) {
336 return Promise.reject(
337 new CustomError({
338 name: "NoOriginElementError",
339 message: "A valid origin HTML element must be given"
340 })
341 );
342 }
343
344 const barcodePicker: BarcodePicker = new BarcodePicker(originElement, {
345 visible,
346 singleImageMode: browserCompatibility.fullSupport ? singleImageModeForced : true,
347 playSoundOnScan,
348 vibrateOnScan,
349 scanningPaused,
350 guiStyle,
351 videoFit,
352 laserArea,
353 viewfinderArea,
354 scanner,
355 scanSettings,
356 targetScanningFPS,
357 // tslint:disable-next-line:use-named-parameter
358 hideLogo: arguments[1] == null ? false : arguments[1].hideLogo === true // Hidden parameter
359 });
360
361 barcodePicker.cameraManager.setInteractionOptions(
362 enableCameraSwitcher,
363 enableTorchToggle,
364 enableTapToFocus,
365 enablePinchToZoom
366 );
367 barcodePicker.cameraManager.setSelectedCamera(camera);
368 barcodePicker.cameraManager.setSelectedCameraSettings(cameraSettings);
369
370 barcodePicker.cameraAccess = accessCamera;
371
372 // Show error in alert on ScanError by default when running on local IP address for easier customer debugging
373 barcodePicker.on("scanError", error => {
374 // istanbul ignore if
375 if (["localhost", "127.0.0.1", ""].includes(window.location.hostname)) {
376 alert(error);
377 }
378 });
379
380 if (accessCamera) {
381 return barcodePicker.cameraManager.setupCameras().then(() => {
382 return barcodePicker;
383 });
384 }
385
386 return Promise.resolve(barcodePicker);
387 }
388
389 /**
390 * Stop scanning and displaying video output, remove HTML elements added to the page,
391 * destroy the internal [[Scanner]] (by default) and destroy the barcode picker itself; ensuring complete cleanup.
392 *
393 * This method should be called after you don't plan to use the picker anymore,
394 * before the object is automatically cleaned up by JavaScript.
395 * The barcode picker must not be used in any way after this call.
396 *
397 * If the [[Scanner]] is or will be in use for other purposes, the relative option can be passed to prevent
398 * its destruction.
399 *
400 * @param destroyScanner Whether to destroy the internally used [[Scanner]] or not.
401 */
402 public destroy(destroyScanner: boolean = true): void {
403 this.pauseScanning(true);
404 this.scanner.removeListener("ready", this.scannerReadyEventListener);
405 this.destroyed = true;
406 if (destroyScanner) {
407 this.scanner.destroy();
408 }
409 this.barcodePickerGui.destroy();
410 this.eventEmitter.removeAllListeners();
411 }
412
413 /**
414 * Apply a new set of scan settings to the internal scanner (replacing old settings).
415 *
416 * @param scanSettings The scan configuration object to be applied to the scanner.
417 * @returns The updated [[BarcodePicker]] object.
418 */
419 public applyScanSettings(scanSettings: ScanSettings): BarcodePicker {
420 this.scanner.applyScanSettings(scanSettings);
421
422 return this;
423 }
424
425 /**
426 * @returns Whether the scanning is currently paused.
427 */
428 public isScanningPaused(): boolean {
429 return this.scanningPaused;
430 }
431
432 /**
433 * Pause the recognition of codes in the input image.
434 *
435 * By default video from the camera is still shown, if the *pauseCamera* option is enabled the camera stream
436 * is paused (camera access is fully interrupted) and will be resumed when calling [[resumeScanning]] or
437 * [[accessCamera]], possibly requesting user permissions if needed.
438 *
439 * In "single image mode" the input for submitting a picture is disabled.
440 *
441 * @param pauseCamera Whether to also pause the camera stream.
442 * @returns The updated [[BarcodePicker]] object.
443 */
444 public pauseScanning(pauseCamera: boolean = false): BarcodePicker {
445 this.scanningPaused = true;
446
447 if (pauseCamera) {
448 this.cameraManager.stopStream();
449 }
450
451 if (this.scanner.isReady()) {
452 this.barcodePickerGui.pauseScanning();
453 }
454
455 return this;
456 }
457
458 /**
459 * Resume the recognition of codes in the input image.
460 *
461 * If the camera stream was stopped when calling [[pauseScanning]], the camera stream is also resumed and
462 * user permissions are requested if needed to resume video input.
463 *
464 * In "single image mode" the input for submitting a picture is enabled.
465 *
466 * @returns The updated [[BarcodePicker]] object.
467 */
468 public async resumeScanning(): Promise<BarcodePicker> {
469 this.scanningPaused = false;
470
471 if (this.scanner.isReady()) {
472 this.barcodePickerGui.resumeScanning();
473 }
474
475 if (this.cameraManager.activeCamera == null && this.cameraAccess) {
476 await this.cameraManager.setupCameras();
477 }
478
479 return this;
480 }
481
482 /**
483 * @returns The currently active camera.
484 */
485 public getActiveCamera(): Camera | undefined {
486 return this.cameraManager.activeCamera;
487 }
488
489 /**
490 * Select a camera to be used for video input, if no camera is passed, the default one is selected.
491 *
492 * If camera access is enabled, the camera is enabled and accessed.
493 *
494 * Depending on device features and user permissions for camera access, any of the following errors
495 * could be the rejected result of the returned promise:
496 * - `PermissionDeniedError`
497 * - `NotAllowedError`
498 * - `NotFoundError`
499 * - `AbortError`
500 * - `NotReadableError`
501 * - `InternalError`
502 * - `NoCameraAvailableError`
503 *
504 * In "single image mode" this method has no effect.
505 *
506 * @param camera The new camera to be used, by default the automatically detected back camera is used.
507 * @param cameraSettings The camera options used when accessing the camera, by default HD resolution is used.
508 * @returns A promise resolving to the updated [[BarcodePicker]] object when the camera is set
509 * (and accessed, if camera access is currently enabled).
510 */
511 public async setActiveCamera(camera?: Camera, cameraSettings?: CameraSettings): Promise<BarcodePicker> {
512 if (camera == null || !this.cameraAccess) {
513 this.cameraManager.setSelectedCamera(camera);
514 this.cameraManager.setSelectedCameraSettings(cameraSettings);
515
516 if (this.cameraAccess) {
517 await this.cameraManager.setupCameras();
518 }
519 } else {
520 await this.cameraManager.initializeCameraWithSettings(camera, cameraSettings);
521 }
522
523 return this;
524 }
525
526 /**
527 * Try to apply new settings to the currently used camera for video input,
528 * if no settings are passed the default ones are set.
529 *
530 * If camera access is enabled, the camera is updated and accessed with the new settings.
531 *
532 * Depending on device features and user permissions for camera access, any of the following errors
533 * could be the rejected result of the returned promise:
534 * - `PermissionDeniedError`
535 * - `NotAllowedError`
536 * - `NotFoundError`
537 * - `AbortError`
538 * - `NotReadableError`
539 * - `InternalError`
540 * - `NoCameraAvailableError`
541 *
542 * In "single image mode" this method has no effect.
543 *
544 * @param cameraSettings The new camera options used when accessing the camera, by default HD resolution is used.
545 * @returns A promise resolving to the updated [[BarcodePicker]] object when the camera is updated
546 * (and accessed, if camera access is currently enabled).
547 */
548 public async applyCameraSettings(cameraSettings?: CameraSettings): Promise<BarcodePicker> {
549 if (!this.cameraAccess) {
550 this.cameraManager.setSelectedCameraSettings(cameraSettings);
551 } else {
552 await this.cameraManager.applyCameraSettings(cameraSettings);
553 }
554
555 return this;
556 }
557
558 /**
559 * @returns Whether the picker is in a visible state or not.
560 */
561 public isVisible(): boolean {
562 return this.barcodePickerGui.isVisible();
563 }
564
565 /**
566 * Enable or disable picker visibility.
567 *
568 * Note that this does not affect camera access, frame processing or any other picker logic.
569 *
570 * @param visible Whether the picker is in a visible state or not.
571 * @returns The updated [[BarcodePicker]] object.
572 */
573 public setVisible(visible: boolean): BarcodePicker {
574 this.barcodePickerGui.setVisible(visible);
575
576 return this;
577 }
578
579 /**
580 * @returns Whether the currently selected camera's video is mirrored along the vertical axis.
581 */
582 public isMirrorImageEnabled(): boolean {
583 return this.barcodePickerGui.isMirrorImageEnabled();
584 }
585
586 /**
587 * Enable or disable camera video mirroring along the vertical axis.
588 * By default front cameras are automatically mirrored.
589 * This setting is applied per camera and the method has no effect if no camera is currently selected.
590 *
591 * In "single image mode" this method has no effect.
592 *
593 * @param enabled Whether the camera video is mirrored along the vertical axis.
594 * @returns The updated [[BarcodePicker]] object.
595 */
596 public setMirrorImageEnabled(enabled: boolean): BarcodePicker {
597 this.barcodePickerGui.setMirrorImageEnabled(enabled, true);
598
599 return this;
600 }
601
602 /**
603 * @returns Whether a sound should be played on barcode recognition (iOS requires user input).
604 * Note that the sound is played if there's at least a barcode not rejected via [[ScanResult.rejectCode]].
605 */
606 public isPlaySoundOnScanEnabled(): boolean {
607 return this.playSoundOnScan;
608 }
609
610 /**
611 * Enable or disable playing a sound on barcode recognition (iOS requires user input).
612 *
613 * The sound is played if there's at least a barcode not rejected via [[ScanResult.rejectCode]].
614 *
615 * @param enabled Whether a sound should be played on barcode recognition.
616 * @returns The updated [[BarcodePicker]] object.
617 */
618 public setPlaySoundOnScanEnabled(enabled: boolean): BarcodePicker {
619 this.playSoundOnScan = enabled;
620
621 return this;
622 }
623
624 /**
625 * @returns Whether the device should vibrate on barcode recognition (only Chrome & Firefox, requires user input).
626 * Note that the vibration is triggered if there's at least a barcode not rejected via [[ScanResult.rejectCode]].
627 */
628 public isVibrateOnScanEnabled(): boolean {
629 return this.vibrateOnScan;
630 }
631
632 /**
633 * Enable or disable vibrating the device on barcode recognition (only Chrome & Firefox, requires user input).
634 *
635 * The vibration is triggered if there's at least a barcode not rejected via [[ScanResult.rejectCode]].
636 *
637 * @param enabled Whether the device should vibrate on barcode recognition.
638 * @returns The updated [[BarcodePicker]] object.
639 */
640 public setVibrateOnScanEnabled(enabled: boolean): BarcodePicker {
641 this.vibrateOnScan = enabled;
642
643 return this;
644 }
645
646 /**
647 * @returns Whether a GUI button to switch between different cameras is shown (when available).
648 */
649 public isCameraSwitcherEnabled(): boolean {
650 return this.cameraManager.isCameraSwitcherEnabled();
651 }
652
653 /**
654 * Show or hide a GUI button to switch between different cameras (when available).
655 *
656 * In "single image mode" this method has no effect.
657 *
658 * @param enabled Whether to show a GUI button to switch between different cameras.
659 * @returns The updated [[BarcodePicker]] object.
660 */
661 public setCameraSwitcherEnabled(enabled: boolean): BarcodePicker {
662 this.cameraManager.setCameraSwitcherEnabled(enabled).catch(
663 /* istanbul ignore next */ () => {
664 // Ignored
665 }
666 );
667
668 return this;
669 }
670
671 /**
672 * @returns Whether a GUI button to toggle device torch on/off is shown (when available, only Chrome).
673 */
674 public isTorchToggleEnabled(): boolean {
675 return this.cameraManager.isTorchToggleEnabled();
676 }
677
678 /**
679 * Show or hide a GUI button to toggle device torch on/off (when available, only Chrome).
680 *
681 * In "single image mode" this method has no effect.
682 *
683 * @param enabled Whether to show a GUI button to toggle device torch on/off.
684 * @returns The updated [[BarcodePicker]] object.
685 */
686 public setTorchToggleEnabled(enabled: boolean): BarcodePicker {
687 this.cameraManager.setTorchToggleEnabled(enabled);
688
689 return this;
690 }
691
692 /**
693 * @returns Whether manual camera focus when clicking/tapping on the video is enabled (when available, only Chrome).
694 */
695 public isTapToFocusEnabled(): boolean {
696 return this.cameraManager.isTapToFocusEnabled();
697 }
698
699 /**
700 * Enable or disable manual camera focus when clicking/tapping on the video (when available, only Chrome).
701 *
702 * In "single image mode" this method has no effect.
703 *
704 * @param enabled Whether to enable manual camera focus when clicking/tapping on the video.
705 * @returns The updated [[BarcodePicker]] object.
706 */
707 public setTapToFocusEnabled(enabled: boolean): BarcodePicker {
708 this.cameraManager.setTapToFocusEnabled(enabled);
709
710 return this;
711 }
712
713 /**
714 * @returns Whether camera zoom control via pinching gesture on the video is enabled (when available, only Chrome).
715 */
716 public isPinchToZoomEnabled(): boolean {
717 return this.cameraManager.isPinchToZoomEnabled();
718 }
719
720 /**
721 * Enable or disable camera zoom control via pinching gesture on the video (when available, only Chrome).
722 *
723 * In "single image mode" this method has no effect.
724 *
725 * @param enabled Whether to enable camera zoom control via pinching gesture on the video.
726 * @returns The updated [[BarcodePicker]] object.
727 */
728 public setPinchToZoomEnabled(enabled: boolean): BarcodePicker {
729 this.cameraManager.setPinchToZoomEnabled(enabled);
730
731 return this;
732 }
733
734 /**
735 * Enable or disable the torch/flashlight of the device (when available, only Chrome).
736 * Changing active camera or camera settings will cause the torch to become disabled.
737 *
738 * A button on the [[BarcodePicker]] GUI to let the user toggle this functionality can also be set
739 * on creation via the *enableTorchToggle* option (enabled by default, when available).
740 *
741 * In "single image mode" this method has no effect.
742 *
743 * @param enabled Whether the torch should be enabled or disabled.
744 * @returns The updated [[BarcodePicker]] object.
745 */
746 public setTorchEnabled(enabled: boolean): BarcodePicker {
747 this.cameraManager.setTorchEnabled(enabled);
748
749 return this;
750 }
751
752 /**
753 * Set the zoom level of the device (when available, only Chrome).
754 * Changing active camera or camera settings will cause the zoom to be reset.
755 *
756 * In "single image mode" this method has no effect.
757 *
758 * @param zoomPercentage The percentage of the max zoom (between 0 and 1).
759 * @returns The updated [[BarcodePicker]] object.
760 */
761 public setZoom(zoomPercentage: number): BarcodePicker {
762 this.cameraManager.setZoom(zoomPercentage);
763
764 return this;
765 }
766
767 /**
768 * @returns Whether the barcode picker has loaded the external Scandit Engine library and is ready to scan.
769 */
770 public isReady(): boolean {
771 return this.isReadyToWork;
772 }
773
774 /**
775 * Add the listener function to the listeners array for an event.
776 *
777 * No checks are made to see if the listener has already been added.
778 * Multiple calls passing the same listener will result in the listener being added, and called, multiple times.
779 *
780 * @param eventName The name of the event to listen to.
781 * @param listener The listener function.
782 * @param once <div class="tsd-signature-symbol">Default =&nbsp;false</div>
783 * Whether the listener should just be triggered only once and then discarded.
784 * @returns The updated [[BarcodePicker]] object.
785 */
786 // tslint:disable-next-line:bool-param-default
787 public on(eventName: EventName, listener: ListenerFn, once?: boolean): BarcodePicker;
788 /**
789 * Add the listener function to the listeners array for the [[ready]] event, fired when the external
790 * Scandit Engine library has been loaded and the barcode picker can thus start to scan barcodes.
791 * If the library has already been loaded the listener is called immediately.
792 *
793 * No checks are made to see if the listener has already been added.
794 * Multiple calls passing the same listener will result in the listener being added, and called, multiple times.
795 *
796 * @param eventName The name of the event to listen to.
797 * @param listener The listener function.
798 * @returns The updated [[BarcodePicker]] object.
799 */
800 public on(eventName: "ready", listener: () => void): BarcodePicker;
801 /**
802 * Add the listener function to the listeners array for the [[submitFrame]] event, fired when a new frame is submitted
803 * to the engine to be processed. As the frame is not processed yet, the [[ScanResult.barcodes]] property will
804 * always be empty (no results yet).
805 *
806 * No checks are made to see if the listener has already been added.
807 * Multiple calls passing the same listener will result in the listener being added, and called, multiple times.
808 *
809 * @param eventName The name of the event to listen to.
810 * @param listener The listener function, which will be invoked with a [[ScanResult]] object.
811 * @param once <div class="tsd-signature-symbol">Default =&nbsp;false</div>
812 * Whether the listener should just be triggered only once and then discarded.
813 * @returns The updated [[BarcodePicker]] object.
814 */
815 public on(
816 eventName: "submitFrame",
817 listener: (scanResult: ScanResult) => void,
818 // tslint:disable-next-line:bool-param-default
819 once?: boolean
820 ): BarcodePicker;
821 /**
822 * Add the listener function to the listeners array for the [[processFrame]] event, fired when a new frame is
823 * processed. This event is fired on every frame, independently from the number of recognized barcodes (can be none).
824 * The returned barcodes are affected by [[ScanSettings]]'s *codeDuplicateFilter* option.
825 *
826 * No checks are made to see if the listener has already been added.
827 * Multiple calls passing the same listener will result in the listener being added, and called, multiple times.
828 *
829 * @param eventName The name of the event to listen to.
830 * @param listener The listener function, which will be invoked with a [[ScanResult]] object.
831 * @param once <div class="tsd-signature-symbol">Default =&nbsp;false</div>
832 * Whether the listener should just be triggered only once and then discarded.
833 * @returns The updated [[BarcodePicker]] object.
834 */
835 public on(
836 // tslint:disable-next-line:unified-signatures
837 eventName: "processFrame",
838 listener: (scanResult: ScanResult) => void,
839 // tslint:disable-next-line:bool-param-default
840 once?: boolean
841 ): BarcodePicker;
842 /**
843 * Add the listener function to the listeners array for the [[scan]] event, fired when new barcodes
844 * are recognized in the image frame. The returned barcodes are affected by [[ScanSettings]]'s *codeDuplicateFilter*
845 * option.
846 *
847 * No checks are made to see if the listener has already been added.
848 * Multiple calls passing the same listener will result in the listener being added, and called, multiple times.
849 *
850 * @param eventName The name of the event to listen to.
851 * @param listener The listener function, which will be invoked with a [[ScanResult]] object.
852 * @param once <div class="tsd-signature-symbol">Default =&nbsp;false</div>
853 * Whether the listener should just be triggered only once and then discarded.
854 * @returns The updated [[BarcodePicker]] object.
855 */
856 public on(
857 // tslint:disable-next-line:unified-signatures
858 eventName: "scan",
859 listener: (scanResult: ScanResult) => void,
860 // tslint:disable-next-line:bool-param-default
861 once?: boolean
862 ): BarcodePicker;
863 /**
864 * Add the listener function to the listeners array for the [[scanError]] event, fired when an error occurs
865 * during scanning initialization and execution. The barcode picker will be automatically paused when this happens.
866 *
867 * No checks are made to see if the listener has already been added.
868 * Multiple calls passing the same listener will result in the listener being added, and called, multiple times.
869 *
870 * @param eventName The name of the event to listen to.
871 * @param listener The listener function, which will be invoked with an `ScanditEngineError` object.
872 * @param once <div class="tsd-signature-symbol">Default =&nbsp;false</div>
873 * Whether the listener should just be triggered only once and then discarded.
874 * @returns The updated [[BarcodePicker]] object.
875 */
876 // tslint:disable-next-line:bool-param-default
877 public on(eventName: "scanError", listener: (error: Error) => void, once?: boolean): BarcodePicker;
878 public on(eventName: EventName, listener: ListenerFn, once: boolean = false): BarcodePicker {
879 if (eventName === "ready") {
880 if (this.isReadyToWork) {
881 listener();
882 } else {
883 this.eventEmitter.once(eventName, listener, this);
884 }
885 } else {
886 if (once === true) {
887 this.eventEmitter.once(eventName, listener, this);
888 } else {
889 this.eventEmitter.on(eventName, listener, this);
890 }
891 }
892
893 return this;
894 }
895
896 /**
897 * Remove the specified listener from the given event's listener array.
898 *
899 * @param eventName The name of the event from which to remove the listener.
900 * @param listener The listener function to be removed.
901 * @returns The updated [[BarcodePicker]] object.
902 */
903 public removeListener(eventName: EventName, listener: ListenerFn): BarcodePicker {
904 this.eventEmitter.removeListener(eventName, listener);
905
906 return this;
907 }
908
909 /**
910 * Remove all listeners from the given event's listener array.
911 *
912 * @param eventName The name of the event from which to remove all listeners.
913 * @returns The updated [[BarcodePicker]] object.
914 */
915 public removeAllListeners(eventName: EventName): BarcodePicker {
916 this.eventEmitter.removeAllListeners(eventName);
917
918 return this;
919 }
920
921 /**
922 * *See the [[on]] method.*
923 *
924 * @param eventName The name of the event to listen to.
925 * @param listener The listener function.
926 * @param once <div class="tsd-signature-symbol">Default =&nbsp;false</div>
927 * Whether the listener should just be triggered only once and then discarded.
928 * @returns The updated [[BarcodePicker]] object.
929 */
930 // tslint:disable-next-line:bool-param-default
931 public addListener(eventName: EventName, listener: ListenerFn, once?: boolean): BarcodePicker {
932 return this.on(eventName, listener, once);
933 }
934
935 /**
936 * Add the listener function to the listeners array for the [[ready]] event, fired when the external
937 * Scandit Engine library has been loaded and the barcode picker can thus start to scan barcodes.
938 * If the library has already been loaded the listener is called immediately.
939 *
940 * No checks are made to see if the listener has already been added.
941 * Multiple calls passing the same listener will result in the listener being added, and called, multiple times.
942 *
943 * @deprecated Use the [[on]] method instead.
944 *
945 * @param listener The listener function.
946 * @returns The updated [[BarcodePicker]] object.
947 */
948 public onReady(listener: () => void): BarcodePicker {
949 console.warn(
950 "The onReady(<listener>) method is deprecated and will be removed in the next" +
951 ' major library version. Please use on("ready", <listener>) instead.'
952 );
953
954 return this.on("ready", listener);
955 }
956
957 /**
958 * Add the listener function to the listeners array for the [[scan]] event, fired when new barcodes
959 * are recognized in the image frame. The returned barcodes are affected
960 * by the [[ScanSettings.setCodeDuplicateFilter]] option.
961 *
962 * No checks are made to see if the listener has already been added.
963 * Multiple calls passing the same listener will result in the listener being added, and called, multiple times.
964 *
965 * @deprecated Use the [[on]] method instead.
966 *
967 * @param listener The listener function, which will be invoked with a [[ScanResult]] object.
968 * @param once Whether the listener should just be triggered only once and then discarded.
969 * @returns The updated [[BarcodePicker]] object.
970 */
971 public onScan(listener: (scanResult: ScanResult) => void, once: boolean = false): BarcodePicker {
972 console.warn(
973 "The onScan(<listener>) method is deprecated and will be removed in the next" +
974 ' major library version. Please use on("scan", <listener>) instead.'
975 );
976
977 return this.on("scan", listener, once);
978 }
979
980 /**
981 * Remove the specified listener from the [[scan]] event's listener array.
982 *
983 * @deprecated Use the [[removeListener]] method instead.
984 *
985 * @param listener The listener function to be removed.
986 * @returns The updated [[BarcodePicker]] object.
987 */
988 public removeScanListener(listener: (scanResult: ScanResult) => void): BarcodePicker {
989 console.warn(
990 "The removeScanListener(<listener>) method is deprecated and will be removed in the next" +
991 ' major library version. Please use removeListener("scan", <listener>) instead.'
992 );
993
994 return this.removeListener("scan", listener);
995 }
996
997 /**
998 * Remove all listeners from the [[scan]] event's listener array.
999 *
1000 * @deprecated Use the [[removeAllListeners]] method instead.
1001 *
1002 * @returns The updated [[BarcodePicker]] object.
1003 */
1004 public removeScanListeners(): BarcodePicker {
1005 console.warn(
1006 "The removeScanListeners() method is deprecated and will be removed in the next" +
1007 ' major library version. Please use removeAllListeners("scan") instead.'
1008 );
1009
1010 return this.removeAllListeners("scan");
1011 }
1012
1013 /**
1014 * Add the listener function to the listeners array for the [[scanError]] event, fired when an error occurs
1015 * during scanning initialization and execution. The barcode picker will be automatically paused when this happens.
1016 *
1017 * No checks are made to see if the listener has already been added.
1018 * Multiple calls passing the same listener will result in the listener being added, and called, multiple times.
1019 *
1020 * @deprecated Use the [[on]] method instead.
1021 *
1022 * @param listener The listener function, which will be invoked with an `ScanditEngineError` object.
1023 * @param once Whether the listener should just be triggered only once and then discarded.
1024 * @returns The updated [[BarcodePicker]] object.
1025 */
1026 // tslint:disable-next-line:no-identical-functions
1027 public onScanError(listener: (error: Error) => void, once: boolean = false): BarcodePicker {
1028 console.warn(
1029 "The onScanError(<listener>) method is deprecated and will be removed in the next" +
1030 ' major library version. Please use on("scanError", <listener>) instead.'
1031 );
1032
1033 return this.on("scanError", listener, once);
1034 }
1035
1036 /**
1037 * Remove the specified listener from the [[scanError]] event's listener array.
1038 *
1039 * @deprecated Use the [[removeListener]] method instead.
1040 *
1041 * @param listener The listener function to be removed.
1042 * @returns The updated [[BarcodePicker]] object.
1043 */
1044 // tslint:disable-next-line:no-identical-functions
1045 public removeScanErrorListener(listener: (error: Error) => void): BarcodePicker {
1046 console.warn(
1047 "The removeScanErrorListener(<listener>) method is deprecated and will be removed in the next" +
1048 ' major library version. Please use removeListener("scanError", <listener>) instead.'
1049 );
1050
1051 return this.removeListener("scanError", listener);
1052 }
1053
1054 /**
1055 * Remove all listeners from the [[scanError]] event's listener array.
1056 *
1057 * @deprecated Use the [[removeAllListeners]] method instead.
1058 *
1059 * @returns The updated [[BarcodePicker]] object.
1060 */
1061 // tslint:disable-next-line:no-identical-functions
1062 public removeScanErrorListeners(): BarcodePicker {
1063 console.warn(
1064 "The removeScanErrorListeners() method is deprecated and will be removed in the next" +
1065 ' major library version. Please use removeAllListeners("scanError") instead.'
1066 );
1067
1068 return this.removeAllListeners("scanError");
1069 }
1070
1071 /**
1072 * Add the listener function to the listeners array for the [[submitFrame]] event, fired when a new frame is submitted
1073 * to the engine to be processed. As the frame is not processed yet, the [[ScanResult.barcodes]] property will
1074 * always be empty (no results yet).
1075 *
1076 * No checks are made to see if the listener has already been added.
1077 * Multiple calls passing the same listener will result in the listener being added, and called, multiple times.
1078 *
1079 * @deprecated Use the [[on]] method instead.
1080 *
1081 * @param listener The listener function, which will be invoked with a [[ScanResult]] object.
1082 * @param once Whether the listener should just be triggered only once and then discarded.
1083 * @returns The updated [[BarcodePicker]] object.
1084 */
1085 // tslint:disable-next-line:no-identical-functions
1086 public onSubmitFrame(listener: (scanResult: ScanResult) => void, once: boolean = false): BarcodePicker {
1087 console.warn(
1088 "The onSubmitFrame(<listener>) method is deprecated and will be removed in the next" +
1089 ' major library version. Please use on("submitFrame", <listener>) instead.'
1090 );
1091
1092 return this.on("submitFrame", listener, once);
1093 }
1094
1095 /**
1096 * Remove the specified listener from the [[submitFrame]] event's listener array.
1097 *
1098 * @deprecated Use the [[removeListener]] method instead.
1099 *
1100 * @param listener The listener function to be removed.
1101 * @returns The updated [[BarcodePicker]] object.
1102 */
1103 // tslint:disable-next-line:no-identical-functions
1104 public removeSubmitFrameListener(listener: (scanResult: ScanResult) => void): BarcodePicker {
1105 console.warn(
1106 "The removeSubmitFrameListener(<listener>) method is deprecated and will be removed in the next" +
1107 ' major library version. Please use removeListener("submitFrame", <listener>) instead.'
1108 );
1109
1110 return this.removeListener("submitFrame", listener);
1111 }
1112
1113 /**
1114 * Remove all listeners from the [[submitFrame]] event's listener array.
1115 *
1116 * @deprecated Use the [[removeAllListeners]] method instead.
1117 *
1118 * @returns The updated [[BarcodePicker]] object.
1119 */
1120 // tslint:disable-next-line:no-identical-functions
1121 public removeSubmitFrameListeners(): BarcodePicker {
1122 console.warn(
1123 "The removeSubmitFrameListeners() method is deprecated and will be removed in the next" +
1124 ' major library version. Please use removeAllListeners("submitFrame") instead.'
1125 );
1126
1127 return this.removeAllListeners("submitFrame");
1128 }
1129
1130 /**
1131 * Add the listener function to the listeners array for the [[processFrame]] event, fired when a new frame is
1132 * processed. This event is fired on every frame, independently from the number of recognized barcodes (can be none).
1133 * The returned barcodes are affected by the [[ScanSettings.setCodeDuplicateFilter]] option.
1134 *
1135 * No checks are made to see if the listener has already been added.
1136 * Multiple calls passing the same listener will result in the listener being added, and called, multiple times.
1137 *
1138 * @deprecated Use the [[on]] method instead.
1139 *
1140 * @param listener The listener function, which will be invoked with a [[ScanResult]] object.
1141 * @param once Whether the listener should just be triggered only once and then discarded.
1142 * @returns The updated [[BarcodePicker]] object.
1143 */
1144 // tslint:disable-next-line:no-identical-functions
1145 public onProcessFrame(listener: (scanResult: ScanResult) => void, once: boolean = false): BarcodePicker {
1146 console.warn(
1147 "The onProcessFrame(<listener>) method is deprecated and will be removed in the next" +
1148 ' major library version. Please use on("processFrame", <listener>) instead.'
1149 );
1150
1151 return this.on("processFrame", listener, once);
1152 }
1153
1154 /**
1155 * Remove the specified listener from the [[processFrame]] event's listener array.
1156 *
1157 * @deprecated Use the [[removeListener]] method instead.
1158 *
1159 * @param listener The listener function to be removed.
1160 * @returns The updated [[BarcodePicker]] object.
1161 */
1162 // tslint:disable-next-line:no-identical-functions
1163 public removeProcessFrameListener(listener: (scanResult: ScanResult) => void): BarcodePicker {
1164 console.warn(
1165 "The removeProcessFrameListener(<listener>) method is deprecated and will be removed in the next" +
1166 ' major library version. Please use removeListener("processFrame", <listener>) instead.'
1167 );
1168
1169 return this.removeListener("processFrame", listener);
1170 }
1171
1172 /**
1173 * Remove all listeners from the [[processFrame]] event's listener array.
1174 *
1175 * @deprecated Use the [[removeAllListeners]] method instead.
1176 *
1177 * @returns The updated [[BarcodePicker]] object.
1178 */
1179 // tslint:disable-next-line:no-identical-functions
1180 public removeProcessFrameListeners(): BarcodePicker {
1181 console.warn(
1182 "The removeProcessFrameListeners() method is deprecated and will be removed in the next" +
1183 ' major library version. Please use removeAllListeners("processFrame") instead.'
1184 );
1185
1186 return this.removeAllListeners("processFrame");
1187 }
1188
1189 /**
1190 * Set the GUI style for the picker.
1191 *
1192 * In "single image mode" this method has no effect.
1193 *
1194 * When the GUI style is set to *laser* or *viewfinder*, the GUI will flash on barcode recognition.
1195 * Note that the GUI will flash if there's at least a barcode not rejected via [[ScanResult.rejectCode]].
1196 *
1197 * @param guiStyle The new GUI style to be applied.
1198 * @returns The updated [[BarcodePicker]] object.
1199 */
1200 public setGuiStyle(guiStyle: BarcodePicker.GuiStyle): BarcodePicker {
1201 this.barcodePickerGui.setGuiStyle(guiStyle);
1202
1203 return this;
1204 }
1205
1206 /**
1207 * Set the fit type for the video element of the picker.
1208 *
1209 * If the "cover" type is selected the maximum available search area for barcode detection is (continuously) adjusted
1210 * automatically according to the visible area of the picker.
1211 *
1212 * In "single image mode" this method has no effect.
1213 *
1214 * @param objectFit The new fit type to be applied.
1215 * @returns The updated [[BarcodePicker]] object.
1216 */
1217 public setVideoFit(objectFit: BarcodePicker.ObjectFit): BarcodePicker {
1218 this.barcodePickerGui.setVideoFit(objectFit);
1219
1220 return this;
1221 }
1222
1223 /**
1224 * Access the currently set or default camera, requesting user permissions if needed.
1225 * This method is meant to be used after the picker has been initialized with disabled camera access
1226 * (*accessCamera*=false) or after [[pauseScanning]] has been called with the pause camera stream option.
1227 * Calling this doesn't do anything if the camera is already being accessed.
1228 *
1229 * Depending on device features and user permissions for camera access, any of the following errors
1230 * could be the rejected result of the returned promise:
1231 * - `PermissionDeniedError`
1232 * - `NotAllowedError`
1233 * - `NotFoundError`
1234 * - `AbortError`
1235 * - `NotReadableError`
1236 * - `InternalError`
1237 * - `NoCameraAvailableError`
1238 *
1239 * In "single image mode" this method has no effect.
1240 *
1241 * @returns A promise resolving to the updated [[BarcodePicker]] object when the camera is accessed.
1242 */
1243 public async accessCamera(): Promise<BarcodePicker> {
1244 if (!this.cameraAccess || this.cameraManager.activeCamera == null) {
1245 await this.cameraManager.setupCameras();
1246 this.cameraAccess = true;
1247 }
1248
1249 return this;
1250 }
1251
1252 /**
1253 * Create a new parser object.
1254 *
1255 * @param dataFormat The format of the input data for the parser.
1256 * @returns The newly created parser.
1257 */
1258 public createParserForFormat(dataFormat: Parser.DataFormat): Parser {
1259 return this.scanner.createParserForFormat(dataFormat);
1260 }
1261
1262 /**
1263 * Reassign the barcode picker to a different HTML element.
1264 *
1265 * All the barcode picker elements inside the current origin element will be moved to the new given one.
1266 *
1267 * If an invalid element is given, a `NoOriginElementError` error is thrown.
1268 *
1269 * @param originElement The HTMLElement into which all the necessary elements for the picker will be moved.
1270 * @returns The updated [[BarcodePicker]] object.
1271 */
1272 public reassignOriginElement(originElement: HTMLElement): BarcodePicker {
1273 if (!BrowserHelper.isValidHTMLElement(originElement)) {
1274 throw new CustomError({
1275 name: "NoOriginElementError",
1276 message: "A valid origin HTML element must be given"
1277 });
1278 }
1279
1280 this.barcodePickerGui.reassignOriginElement(originElement);
1281
1282 return this;
1283 }
1284
1285 /**
1286 * Set the target frames per second to be processed by the scanning engine.
1287 *
1288 * The final speed is limited by the camera framerate (usually 30 FPS) and the frame processing time of the device.
1289 * By setting this to lower numbers devices can save power by performing less work during scanning operations,
1290 * depending on device speed (faster devices can "sleep" for longer periods).
1291 *
1292 * In "single image mode" this method has no effect.
1293 *
1294 * @param targetScanningFPS The target frames per second to be processed.
1295 * Must be a number bigger than 0, by default set to 30.
1296 * @returns The updated [[BarcodePicker]] object.
1297 */
1298 public setTargetScanningFPS(targetScanningFPS: number): BarcodePicker {
1299 if (targetScanningFPS <= 0) {
1300 targetScanningFPS = 30;
1301 }
1302 this.targetScanningFPS = targetScanningFPS;
1303
1304 return this;
1305 }
1306
1307 /**
1308 * @returns The internally used initialized (and possibly configured) [[Scanner]] object instance.
1309 */
1310 public getScanner(): Scanner {
1311 return this.scanner;
1312 }
1313
1314 /**
1315 * Clear the internal scanner session.
1316 *
1317 * This removes all recognized barcodes from the scanner session and allows them to be scanned again in case a custom
1318 * *codeDuplicateFilter* option was set in the [[ScanSettings]].
1319 *
1320 * @returns The updated [[BarcodePicker]] object.
1321 */
1322 public clearSession(): BarcodePicker {
1323 this.scanner.clearSession();
1324
1325 return this;
1326 }
1327
1328 /**
1329 * Set the area of the laser displayed when the GUI style is set to *laser* (the laser will match the width and be
1330 * vertically centered).
1331 * Note that this functionality affects UI only and doesn't change the actual *searchArea* option set via
1332 * [[ScanSettings]]. If no area is passed, the default automatic size behaviour is set, where the laser will match
1333 * the current area of the image in which barcodes are searched, controlled via the *searchArea* option in
1334 * [[ScanSettings]].
1335 *
1336 * @param area The new search area, by default the area will match [[ScanSettings]]'s *searchArea* option.
1337 * @returns The updated [[BarcodePicker]] object.
1338 */
1339 public setLaserArea(area?: SearchArea): BarcodePicker {
1340 this.barcodePickerGui.setLaserArea(area);
1341
1342 return this;
1343 }
1344
1345 /**
1346 * Set the area of the viewfinder displayed when the GUI style is set to *viewfinder*.
1347 * Note that this functionality affects UI only and doesn't change the actual search area set via [[ScanSettings]].
1348 * If no area is passed, the default automatic size behaviour is set, where the viewfinder will match the current area
1349 * of the image in which barcodes are searched, controlled via the *searchArea* option in [[ScanSettings]].
1350 *
1351 * @param area The new search area, by default the area will match the [[ScanSettings]]'s *searchArea*.
1352 * @returns The updated [[BarcodePicker]] object.
1353 */
1354 public setViewfinderArea(area?: SearchArea): BarcodePicker {
1355 this.barcodePickerGui.setViewfinderArea(area);
1356
1357 return this;
1358 }
1359
1360 private triggerFatalError(error: Error): void {
1361 this.fatalError = error;
1362 console.error(error);
1363 }
1364
1365 private handleScanResult(scanResult: ScanResult): void {
1366 this.eventEmitter.emit("processFrame", scanResult);
1367
1368 if (scanResult.barcodes.length !== 0) {
1369 // This will get executed only after the other existing listeners for "processFrame" and "scan" are executed
1370 this.eventEmitter.once("scan", () => {
1371 if (
1372 scanResult.barcodes.some(barcode => {
1373 return !scanResult.rejectedCodes.has(barcode);
1374 })
1375 ) {
1376 this.barcodePickerGui.flashGUI();
1377 if (this.playSoundOnScan) {
1378 this.beepSound.play();
1379 }
1380 if (this.vibrateOnScan && this.vibrateFunction != null) {
1381 this.vibrateFunction.call(navigator, 300);
1382 }
1383 }
1384 });
1385 this.eventEmitter.emit("scan", scanResult);
1386 }
1387 }
1388
1389 private scheduleVideoProcessing(timeout: number = 0): void {
1390 window.setTimeout(async () => {
1391 await this.videoProcessing();
1392 }, timeout); // Leave some breathing room for other operations
1393 }
1394
1395 private async scheduleNextVideoProcessing(processingStartTime: number): Promise<void> {
1396 if (this.targetScanningFPS < 60) {
1397 if (this.averageProcessingTime == null) {
1398 this.averageProcessingTime = performance.now() - processingStartTime;
1399 } else {
1400 this.averageProcessingTime = this.averageProcessingTime * 0.9 + (performance.now() - processingStartTime) * 0.1;
1401 }
1402 const nextProcessingCallDelay: number = Math.max(0, 1000 / this.targetScanningFPS - this.averageProcessingTime);
1403 if (Math.round(nextProcessingCallDelay) <= 16) {
1404 await this.videoProcessing();
1405 } else {
1406 this.scheduleVideoProcessing(nextProcessingCallDelay);
1407 }
1408 } else {
1409 await this.videoProcessing();
1410 }
1411 }
1412
1413 private async processVideoFrame(highQualitySingleFrameMode: boolean): Promise<void> {
1414 const imageData: Uint8ClampedArray | undefined = this.barcodePickerGui.getVideoImageData();
1415
1416 // This could happen in very weird situations and should be temporary
1417 // istanbul ignore if
1418 if (imageData == null) {
1419 return;
1420 }
1421
1422 if (!this.scanningPaused) {
1423 if (this.eventEmitter.listenerCount("submitFrame") > 0) {
1424 this.eventEmitter.emit(
1425 "submitFrame",
1426 new ScanResult([], imageData.slice(), <ImageSettings>this.scanner.getImageSettings())
1427 );
1428 }
1429
1430 try {
1431 const scanResult: ScanResult = await this.scanner.processImage(imageData, highQualitySingleFrameMode);
1432 // Paused status could have changed in the meantime
1433 if (!this.scanningPaused) {
1434 this.handleScanResult(scanResult);
1435 }
1436 } catch (error) {
1437 this.pauseScanning();
1438 this.eventEmitter.emit("scanError", error);
1439 }
1440 }
1441 }
1442
1443 private async videoProcessing(): Promise<void> {
1444 if (this.destroyed) {
1445 return;
1446 }
1447
1448 if (
1449 this.cameraManager.activeCamera == null ||
1450 this.cameraManager.activeCamera.currentResolution == null ||
1451 this.fatalError != null ||
1452 this.scanningPaused ||
1453 !this.scanner.isReady() ||
1454 this.scanner.isBusyProcessing() ||
1455 this.latestVideoTimeProcessed === this.barcodePickerGui.getVideoCurrentTime()
1456 ) {
1457 this.scheduleVideoProcessing();
1458
1459 return;
1460 }
1461
1462 if (this.latestVideoTimeProcessed == null) {
1463 // Show active GUI if needed, as now it's the moment the scanner is ready and used for the first time
1464 await this.resumeScanning();
1465 }
1466
1467 const processingStartTime: number = performance.now();
1468 this.latestVideoTimeProcessed = this.barcodePickerGui.getVideoCurrentTime();
1469
1470 await this.processVideoFrame(false);
1471 await this.scheduleNextVideoProcessing(processingStartTime);
1472 }
1473
1474 private handleScannerReady(): void {
1475 this.isReadyToWork = true;
1476 this.eventEmitter.emit("ready");
1477 }
1478}
1479
1480// istanbul ignore next
1481export namespace BarcodePicker {
1482 /**
1483 * Fired when the external Scandit Engine library has been loaded and the barcode picker can thus start to scan
1484 * barcodes.
1485 *
1486 * @asMemberOf BarcodePicker
1487 * @event
1488 */
1489 // @ts-ignore
1490 declare function ready(): void;
1491 /**
1492 * Fired when a new frame is submitted to the engine to be processed. As the frame is not processed yet, the
1493 * [[ScanResult.barcodes]] property will always be empty (no results yet).
1494 *
1495 * @asMemberOf BarcodePicker
1496 * @event
1497 * @param scanResult The result of the scanning operation on the image.
1498 */
1499 // @ts-ignore
1500 declare function submitFrame(scanResult: ScanResult): void;
1501 /**
1502 * Fired when a new frame is processed by the engine. This event is fired on every frame, independently from the
1503 * number of recognized barcodes (can be none). The returned barcodes are affected by [[ScanSettings]]'s
1504 * *codeDuplicateFilter* option.
1505 *
1506 * @asMemberOf BarcodePicker
1507 * @event
1508 * @param scanResult The result of the scanning operation on the image.
1509 */
1510 // @ts-ignore
1511 declare function processFrame(scanResult: ScanResult): void;
1512 /**
1513 * Fired when new barcodes are recognized in the image frame. The returned barcodes are affected by [[ScanSettings]]'s
1514 * *codeDuplicateFilter* option.
1515 *
1516 * @asMemberOf BarcodePicker
1517 * @event
1518 * @param scanResult The result of the scanning operation on the image.
1519 */
1520 // @ts-ignore
1521 declare function scan(scanResult: ScanResult): void;
1522 /**
1523 * Fired when an error occurs during scanning initialization and execution. The barcode picker will be automatically
1524 * paused when this happens.
1525 *
1526 * @asMemberOf BarcodePicker
1527 * @event
1528 * @param error The ScanditEngineError that was triggered.
1529 */
1530 // @ts-ignore
1531 declare function scanError(error: Error): void;
1532
1533 /**
1534 * GUI style to be used by a barcode picker, used to hint barcode placement in the frame.
1535 */
1536 export enum GuiStyle {
1537 /**
1538 * No GUI is shown to indicate where the barcode should be placed.
1539 * Be aware that the Scandit logo continues to be displayed as showing it is part of the license agreement.
1540 */
1541 NONE = "none",
1542 /**
1543 * A laser line is shown.
1544 */
1545 LASER = "laser",
1546 /**
1547 * A rectangular viewfinder with rounded corners is shown.
1548 */
1549 VIEWFINDER = "viewfinder"
1550 }
1551
1552 /**
1553 * Fit type used to control the resizing (scale) of the barcode picker to fit in its container *originElement*.
1554 */
1555 export enum ObjectFit {
1556 /**
1557 * Scale to maintain aspect ratio while fitting within the *originElement*'s content box.
1558 * Aspect ratio is preserved, so the barcode picker will be "letterboxed" if its aspect ratio
1559 * does not match the aspect ratio of the box.
1560 */
1561 CONTAIN = "contain",
1562 /**
1563 * Scale to maintain aspect ratio while filling the *originElement*'s entire content box.
1564 * Aspect ratio is preserved, so the barcode picker will be clipped to fit if its aspect ratio
1565 * does not match the aspect ratio of the box.
1566 */
1567 COVER = "cover"
1568 }
1569}