feat(core): update session, security system and QR exchange

- Removed session creation and Lightning payment logic
- Refactored security system:
  * no more restrictions
  * all systems enabled on session creation
- Improved QR code exchange for mobile devices
This commit is contained in:
lockbitchat
2025-09-23 20:01:02 -04:00
parent 804b384271
commit 34094956b7
396 changed files with 126516 additions and 11881 deletions

7
node_modules/html5-qrcode/src/camera/core-impl.d.ts generated vendored Normal file
View File

@@ -0,0 +1,7 @@
import { Camera, CameraRenderingOptions, RenderedCamera, RenderingCallbacks } from "./core";
export declare class CameraImpl implements Camera {
private readonly mediaStream;
private constructor();
render(parentElement: HTMLElement, options: CameraRenderingOptions, callbacks: RenderingCallbacks): Promise<RenderedCamera>;
static create(videoConstraints: MediaTrackConstraints): Promise<Camera>;
}

340
node_modules/html5-qrcode/src/camera/core-impl.ts generated vendored Normal file
View File

@@ -0,0 +1,340 @@
/**
* @fileoverview
* Core camera library implementations.
*
* @author mebjas <minhazav@gmail.com>
*/
import {
Camera,
CameraCapabilities,
CameraCapability,
RangeCameraCapability,
CameraRenderingOptions,
RenderedCamera,
RenderingCallbacks,
BooleanCameraCapability
} from "./core";
/** Interface for a range value. */
interface RangeValue {
min: number;
max: number;
step: number;
}
/** Abstract camera capability class. */
abstract class AbstractCameraCapability<T> implements CameraCapability<T> {
protected readonly name: string;
protected readonly track: MediaStreamTrack;
constructor(name: string, track: MediaStreamTrack) {
this.name = name;
this.track = track;
}
public isSupported(): boolean {
// TODO(minhazav): Figure out fallback for getCapabilities()
// in firefox.
// https://developer.mozilla.org/en-US/docs/Web/API/Media_Capture_and_Streams_API/Constraints
if (!this.track.getCapabilities) {
return false;
}
return this.name in this.track.getCapabilities();
}
public apply(value: T): Promise<void> {
let constraint: any = {};
constraint[this.name] = value;
let constraints = { advanced: [ constraint ] };
return this.track.applyConstraints(constraints);
}
public value(): T | null {
let settings: any = this.track.getSettings();
if (this.name in settings) {
let settingValue = settings[this.name];
return settingValue;
}
return null;
}
}
abstract class AbstractRangeCameraCapability extends AbstractCameraCapability<number> {
constructor(name: string, track: MediaStreamTrack) {
super(name, track);
}
public min(): number {
return this.getCapabilities().min;
}
public max(): number {
return this.getCapabilities().max;
}
public step(): number {
return this.getCapabilities().step;
}
public apply(value: number): Promise<void> {
let constraint: any = {};
constraint[this.name] = value;
let constraints = {advanced: [ constraint ]};
return this.track.applyConstraints(constraints);
}
private getCapabilities(): RangeValue {
this.failIfNotSupported();
let capabilities: any = this.track.getCapabilities();
let capability: any = capabilities[this.name];
return {
min: capability.min,
max: capability.max,
step: capability.step,
};
}
private failIfNotSupported() {
if (!this.isSupported()) {
throw new Error(`${this.name} capability not supported`);
}
}
}
/** Zoom feature. */
class ZoomFeatureImpl extends AbstractRangeCameraCapability {
constructor(track: MediaStreamTrack) {
super("zoom", track);
}
}
/** Torch feature. */
class TorchFeatureImpl extends AbstractCameraCapability<boolean> {
constructor(track: MediaStreamTrack) {
super("torch", track);
}
}
/** Implementation of {@link CameraCapabilities}. */
class CameraCapabilitiesImpl implements CameraCapabilities {
private readonly track: MediaStreamTrack;
constructor(track: MediaStreamTrack) {
this.track = track;
}
zoomFeature(): RangeCameraCapability {
return new ZoomFeatureImpl(this.track);
}
torchFeature(): BooleanCameraCapability {
return new TorchFeatureImpl(this.track);
}
}
/** Implementation of {@link RenderedCamera}. */
class RenderedCameraImpl implements RenderedCamera {
private readonly parentElement: HTMLElement;
private readonly mediaStream: MediaStream;
private readonly surface: HTMLVideoElement;
private readonly callbacks: RenderingCallbacks;
private isClosed: boolean = false;
private constructor(
parentElement: HTMLElement,
mediaStream: MediaStream,
callbacks: RenderingCallbacks) {
this.parentElement = parentElement;
this.mediaStream = mediaStream;
this.callbacks = callbacks;
this.surface = this.createVideoElement(this.parentElement.clientWidth);
// Setup
parentElement.append(this.surface);
}
private createVideoElement(width: number): HTMLVideoElement {
const videoElement = document.createElement("video");
videoElement.style.width = `${width}px`;
videoElement.style.display = "block";
videoElement.muted = true;
videoElement.setAttribute("muted", "true");
(<any>videoElement).playsInline = true;
return videoElement;
}
private setupSurface() {
this.surface.onabort = () => {
throw "RenderedCameraImpl video surface onabort() called";
};
this.surface.onerror = () => {
throw "RenderedCameraImpl video surface onerror() called";
};
let onVideoStart = () => {
const videoWidth = this.surface.clientWidth;
const videoHeight = this.surface.clientHeight;
this.callbacks.onRenderSurfaceReady(videoWidth, videoHeight);
this.surface.removeEventListener("playing", onVideoStart);
};
this.surface.addEventListener("playing", onVideoStart);
this.surface.srcObject = this.mediaStream;
this.surface.play();
}
static async create(
parentElement: HTMLElement,
mediaStream: MediaStream,
options: CameraRenderingOptions,
callbacks: RenderingCallbacks)
: Promise<RenderedCamera> {
let renderedCamera = new RenderedCameraImpl(
parentElement, mediaStream, callbacks);
if (options.aspectRatio) {
let aspectRatioConstraint = {
aspectRatio: options.aspectRatio!
};
await renderedCamera.getFirstTrackOrFail().applyConstraints(
aspectRatioConstraint);
}
renderedCamera.setupSurface();
return renderedCamera;
}
private failIfClosed() {
if (this.isClosed) {
throw "The RenderedCamera has already been closed.";
}
}
private getFirstTrackOrFail(): MediaStreamTrack {
this.failIfClosed();
if (this.mediaStream.getVideoTracks().length === 0) {
throw "No video tracks found";
}
return this.mediaStream.getVideoTracks()[0];
}
//#region Public APIs.
public pause(): void {
this.failIfClosed();
this.surface.pause();
}
public resume(onResumeCallback: () => void): void {
this.failIfClosed();
let $this = this;
const onVideoResume = () => {
// Transition after 200ms to avoid the previous canvas frame being
// re-scanned.
setTimeout(onResumeCallback, 200);
$this.surface.removeEventListener("playing", onVideoResume);
};
this.surface.addEventListener("playing", onVideoResume);
this.surface.play();
}
public isPaused(): boolean {
this.failIfClosed();
return this.surface.paused;
}
public getSurface(): HTMLVideoElement {
this.failIfClosed();
return this.surface;
}
public getRunningTrackCapabilities(): MediaTrackCapabilities {
return this.getFirstTrackOrFail().getCapabilities();
}
public getRunningTrackSettings(): MediaTrackSettings {
return this.getFirstTrackOrFail().getSettings();
}
public async applyVideoConstraints(constraints: MediaTrackConstraints)
: Promise<void> {
if ("aspectRatio" in constraints) {
throw "Changing 'aspectRatio' in run-time is not yet supported.";
}
return this.getFirstTrackOrFail().applyConstraints(constraints);
}
public close(): Promise<void> {
if (this.isClosed) {
// Already closed.
return Promise.resolve();
}
let $this = this;
return new Promise((resolve, _) => {
let tracks = $this.mediaStream.getVideoTracks();
const tracksToClose = tracks.length;
var tracksClosed = 0;
$this.mediaStream.getVideoTracks().forEach((videoTrack) => {
$this.mediaStream.removeTrack(videoTrack);
videoTrack.stop();
++tracksClosed;
if (tracksClosed >= tracksToClose) {
$this.isClosed = true;
$this.parentElement.removeChild($this.surface);
resolve();
}
});
});
}
getCapabilities(): CameraCapabilities {
return new CameraCapabilitiesImpl(this.getFirstTrackOrFail());
}
//#endregion
}
/** Default implementation of {@link Camera} interface. */
export class CameraImpl implements Camera {
private readonly mediaStream: MediaStream;
private constructor(mediaStream: MediaStream) {
this.mediaStream = mediaStream;
}
async render(
parentElement: HTMLElement,
options: CameraRenderingOptions,
callbacks: RenderingCallbacks)
: Promise<RenderedCamera> {
return RenderedCameraImpl.create(
parentElement, this.mediaStream, options, callbacks);
}
static async create(videoConstraints: MediaTrackConstraints)
: Promise<Camera> {
if (!navigator.mediaDevices) {
throw "navigator.mediaDevices not supported";
}
let constraints: MediaStreamConstraints = {
audio: false,
video: videoConstraints
};
let mediaStream = await navigator.mediaDevices.getUserMedia(
constraints);
return new CameraImpl(mediaStream);
}
}

41
node_modules/html5-qrcode/src/camera/core.d.ts generated vendored Normal file
View File

@@ -0,0 +1,41 @@
export interface CameraDevice {
id: string;
label: string;
}
export interface CameraCapability<T> {
isSupported(): boolean;
apply(value: T): Promise<void>;
value(): T | null;
}
export interface RangeCameraCapability extends CameraCapability<number> {
min(): number;
max(): number;
step(): number;
}
export interface BooleanCameraCapability extends CameraCapability<boolean> {
}
export interface CameraCapabilities {
zoomFeature(): RangeCameraCapability;
torchFeature(): BooleanCameraCapability;
}
export type OnRenderSurfaceReady = (viewfinderWidth: number, viewfinderHeight: number) => void;
export interface RenderingCallbacks {
onRenderSurfaceReady: OnRenderSurfaceReady;
}
export interface RenderedCamera {
getSurface(): HTMLVideoElement;
pause(): void;
resume(onResumeCallback: () => void): void;
isPaused(): boolean;
close(): Promise<void>;
getRunningTrackCapabilities(): MediaTrackCapabilities;
getRunningTrackSettings(): MediaTrackSettings;
applyVideoConstraints(constraints: MediaTrackConstraints): Promise<void>;
getCapabilities(): CameraCapabilities;
}
export interface CameraRenderingOptions {
aspectRatio?: number;
}
export interface Camera {
render(parentElement: HTMLElement, options: CameraRenderingOptions, callbacks: RenderingCallbacks): Promise<RenderedCamera>;
}

180
node_modules/html5-qrcode/src/camera/core.ts generated vendored Normal file
View File

@@ -0,0 +1,180 @@
/**
* @module
* Core Camera interfaces.
*
* @author mebjas <minhazav@gmail.com>
*/
/** Camera device interface. */
export interface CameraDevice {
id: string;
label: string;
}
//#region Features
/** Generic capability of camera. */
export interface CameraCapability<T> {
/** Returns {@code true} if the capability is supported by the camera. */
isSupported(): boolean;
/** Apply the {@code value} to camera for this capability. */
apply(value: T): Promise<void>;
/** Returns current value of this capability. */
value(): T | null;
}
/** Capability of the camera that has range. */
export interface RangeCameraCapability extends CameraCapability<number> {
/** Min value allowed for this capability. */
min(): number;
/** Max value allowed for this capability. */
max(): number;
/** Steps allowed for this capability. */
step(): number;
}
/** Capability of camera that is boolean in nature. */
export interface BooleanCameraCapability extends CameraCapability<boolean> {}
/** Class exposing different capabilities of camera. */
export interface CameraCapabilities {
/** Zoom capability of the camera. */
zoomFeature(): RangeCameraCapability;
/** Torch capability of the camera. */
torchFeature(): BooleanCameraCapability;
}
//#endregion
/** Type for callback called when camera surface is ready. */
export type OnRenderSurfaceReady
= (viewfinderWidth: number, viewfinderHeight: number) => void;
/** Callbacks around camera rendering. */
export interface RenderingCallbacks {
onRenderSurfaceReady: OnRenderSurfaceReady;
}
/**
* Interface for a rendered camera that is actively showing feed on a surface.
*/
export interface RenderedCamera {
/**
* Returns the video surface.
*
* @throws error if method is called when scanner is not in scanning state.
*/
getSurface(): HTMLVideoElement;
/**
* Pauses the camera feed.
*
* @throws error if method is called when scanner is not in scanning state.
*/
pause(): void;
/**
* Resumes the camera feed, if it's in paused state.
*
* @param onResumeCallback callback that is called when camera resumes.
*
* @throws error if {@link RenderedCamera} instance is already closed.
*/
resume(onResumeCallback: () => void): void;
/**
* Returns {@code true} if the instance is paused.
*
* @throws error if {@link RenderedCamera} instance is already closed.
*/
isPaused(): boolean;
/**
* Closes the instance.
*
* <p> The instance cannot be used after closing.
*/
close(): Promise<void>;
// ---------------------------------------------------------------------------
// Direct Camera Access APIs.
//
// The APIs below are in flavour similar to what Javascript exposes.
// ---------------------------------------------------------------------------
/**
* Returns the capabilities of the running camera stream.
*
* Read more: https://developer.mozilla.org/en-US/docs/Web/API/MediaStreamTrack/getConstraints
*
* @returns the capabilities of a running video track.
* @throws error if {@link RenderedCamera} instance is already closed.
*/
getRunningTrackCapabilities(): MediaTrackCapabilities;
/**
* Returns the object containing the current values of each constrainable
* property of the running video track.
*
* Read more: https://developer.mozilla.org/en-US/docs/Web/API/MediaStreamTrack/getSettings
*
* @returns the supported settings of the running video track.
* @throws error if {@link RenderedCamera} instance is already closed.
*/
getRunningTrackSettings(): MediaTrackSettings;
/**
* Apply a video constraints on running video track from camera.
*
* Important: Changing aspectRatio while scanner is running is not supported
* with this API.
*
* @param {MediaTrackConstraints} specifies a variety of video or camera
* controls as defined in
* https://developer.mozilla.org/en-US/docs/Web/API/MediaTrackConstraints
* @returns a Promise which succeeds if the passed constraints are applied,
* fails otherwise.
* @throws error if {@link RenderedCamera} instance is already closed.
*/
applyVideoConstraints(constraints: MediaTrackConstraints): Promise<void>;
/**
* Returns all capabilities of the camera.
*/
getCapabilities(): CameraCapabilities;
}
/** Options for rendering camera feed. */
export interface CameraRenderingOptions {
/**
* Aspect ratio to setup the surface with.
*
* <p> Setting this value doesn't guarantee the exact value to be applied.
*/
aspectRatio?: number;
}
/** Interface for the camera. */
export interface Camera {
/**
* Renders camera to {@link HTMLVideoElement} as a child of
* {@code parentElement}.
*
* @params parentElement Parent HtmlElement to render camera feed into
* @params options rendering options
* @params callbacks callbacks associated with rendering
*
* @returns the {@link RenderedCamera} instance.
*/
render(
parentElement: HTMLElement,
options: CameraRenderingOptions,
callbacks: RenderingCallbacks)
: Promise<RenderedCamera>;
}

6
node_modules/html5-qrcode/src/camera/factories.d.ts generated vendored Normal file
View File

@@ -0,0 +1,6 @@
import { Camera } from "./core";
export declare class CameraFactory {
static failIfNotSupported(): Promise<CameraFactory>;
private constructor();
create(videoConstraints: MediaTrackConstraints): Promise<Camera>;
}

33
node_modules/html5-qrcode/src/camera/factories.ts generated vendored Normal file
View File

@@ -0,0 +1,33 @@
/**
* @fileoverview
* Set of factory implementations around Camera.
*
* @author mebjas <minhazav@gmail.com>
*/
import { Camera } from "./core";
import { CameraImpl } from "./core-impl";
/** Factory class for creating Camera. */
export class CameraFactory {
/**
* Returns {@link CameraFactory} if {@link navigator.mediaDevices} is
* supported else fails.
*/
public static async failIfNotSupported(): Promise<CameraFactory> {
if (!navigator.mediaDevices) {
throw "navigator.mediaDevices not supported";
}
return new CameraFactory();
}
private constructor() { /* No Op. */ }
/** Creates camera instance based on constraints. */
public async create(videoConstraints: MediaTrackConstraints)
: Promise<Camera> {
return CameraImpl.create(videoConstraints);
}
}

View File

@@ -0,0 +1,3 @@
export declare class CameraPermissions {
static hasPermissions(): Promise<boolean>;
}

34
node_modules/html5-qrcode/src/camera/permissions.ts generated vendored Normal file
View File

@@ -0,0 +1,34 @@
/**
* @fileoverview
* Libraries associated with Camera Permissions.
*
* @author mebjas <minhazav@gmail.com>
*/
/**
* Permission management around Camera in javascript.
*
* TODO(mebjas): Migrate camera specific code / logic to this class / library.
*/
export class CameraPermissions {
/**
* Returns {@code true} if the web page already has access to user camera
* permissions.
*/
public static async hasPermissions(): Promise<boolean> {
// TODO(mebjas): Use Permissions Query API, once support is widespread.
// https://developer.mozilla.org/en-US/docs/Web/API/Permissions/query
let devices = await navigator.mediaDevices.enumerateDevices();
for (const device of devices) {
// Hacky way to check if camera permissions are granted. Device
// labels are only set in case user has granted permissions.
if(device.kind === "videoinput" && device.label) {
return true;
}
}
return false;
}
}

8
node_modules/html5-qrcode/src/camera/retriever.d.ts generated vendored Normal file
View File

@@ -0,0 +1,8 @@
import { CameraDevice } from "./core";
export declare class CameraRetriever {
static retrieve(): Promise<Array<CameraDevice>>;
private static rejectWithError;
private static isHttpsOrLocalhost;
private static getCamerasFromMediaDevices;
private static getCamerasFromMediaStreamTrack;
}

93
node_modules/html5-qrcode/src/camera/retriever.ts generated vendored Normal file
View File

@@ -0,0 +1,93 @@
/**
* @fileoverview
* Libraries associated with retrieving cameras.
*
* @author mebjas <minhazav@gmail.com>
*/
import { CameraDevice } from "./core";
import { Html5QrcodeStrings } from "../strings";
/** Class for retrieving cameras on the device. */
export class CameraRetriever {
/** Returns list of {@link CameraDevice} supported by the device. */
public static retrieve(): Promise<Array<CameraDevice>> {
if (navigator.mediaDevices) {
return CameraRetriever.getCamerasFromMediaDevices();
}
// Using deprecated api to support really old browsers.
var mst = <any>MediaStreamTrack;
if (MediaStreamTrack && mst.getSources) {
return CameraRetriever.getCamerasFromMediaStreamTrack();
}
return CameraRetriever.rejectWithError();
}
private static rejectWithError(): Promise<Array<CameraDevice>> {
// This can potentially happen if the page is loaded without SSL.
let errorMessage = Html5QrcodeStrings.unableToQuerySupportedDevices();
if (!CameraRetriever.isHttpsOrLocalhost()) {
errorMessage = Html5QrcodeStrings.insecureContextCameraQueryError();
}
return Promise.reject(errorMessage);
}
private static isHttpsOrLocalhost(): boolean {
if (location.protocol === "https:") {
return true;
}
const host = location.host.split(":")[0];
return host === "127.0.0.1" || host === "localhost";
}
private static async getCamerasFromMediaDevices(): Promise<Array<CameraDevice>> {
// Hacky approach to close any active stream if they are active.
const closeActiveStreams = (stream: MediaStream) => {
const tracks = stream.getVideoTracks();
for (const track of tracks) {
track.enabled = false;
track.stop();
stream.removeTrack(track);
}
};
// This should trigger the permission flow if required.
let mediaStream = await navigator.mediaDevices.getUserMedia(
{ audio: false, video: true });
let devices = await navigator.mediaDevices.enumerateDevices();
let results: Array<CameraDevice> = [];
for (const device of devices) {
if (device.kind === "videoinput") {
results.push({
id: device.deviceId,
label: device.label
});
}
}
closeActiveStreams(mediaStream);
return results;
}
private static getCamerasFromMediaStreamTrack()
: Promise<Array<CameraDevice>> {
return new Promise((resolve, _) => {
const callback = (sourceInfos: Array<any>) => {
const results: Array<CameraDevice> = [];
for (const sourceInfo of sourceInfos) {
if (sourceInfo.kind === "video") {
results.push({
id: sourceInfo.id,
label: sourceInfo.label
});
}
}
resolve(results);
}
var mst = <any>MediaStreamTrack;
mst.getSources(callback);
});
}
}

16
node_modules/html5-qrcode/src/code-decoder.d.ts generated vendored Normal file
View File

@@ -0,0 +1,16 @@
import { QrcodeResult, Html5QrcodeSupportedFormats, Logger, RobustQrcodeDecoderAsync } from "./core";
export declare class Html5QrcodeShim implements RobustQrcodeDecoderAsync {
private verbose;
private primaryDecoder;
private secondaryDecoder;
private readonly EXECUTIONS_TO_REPORT_PERFORMANCE;
private executions;
private executionResults;
private wasPrimaryDecoderUsedInLastDecode;
constructor(requestedFormats: Array<Html5QrcodeSupportedFormats>, useBarCodeDetectorIfSupported: boolean, verbose: boolean, logger: Logger);
decodeAsync(canvas: HTMLCanvasElement): Promise<QrcodeResult>;
decodeRobustlyAsync(canvas: HTMLCanvasElement): Promise<QrcodeResult>;
private getDecoder;
private possiblyLogPerformance;
possiblyFlushPerformanceReport(): void;
}

127
node_modules/html5-qrcode/src/code-decoder.ts generated vendored Normal file
View File

@@ -0,0 +1,127 @@
/**
* @fileoverview
* Shim layer for providing the decoding library.
*
* @author mebjas <minhazav@gmail.com>
*
* The word "QR Code" is registered trademark of DENSO WAVE INCORPORATED
* http://www.denso-wave.com/qrcode/faqpatent-e.html
*/
import {
QrcodeResult,
Html5QrcodeSupportedFormats,
Logger,
QrcodeDecoderAsync,
RobustQrcodeDecoderAsync,
} from "./core";
import { ZXingHtml5QrcodeDecoder } from "./zxing-html5-qrcode-decoder";
import { BarcodeDetectorDelegate } from "./native-bar-code-detector";
/**
* Shim layer for {@interface QrcodeDecoder}.
*
* Currently uses {@class ZXingHtml5QrcodeDecoder}, can be replace with another library.
*/
export class Html5QrcodeShim implements RobustQrcodeDecoderAsync {
private verbose: boolean;
private primaryDecoder: QrcodeDecoderAsync;
private secondaryDecoder: QrcodeDecoderAsync | undefined;
private readonly EXECUTIONS_TO_REPORT_PERFORMANCE = 100;
private executions: number = 0;
private executionResults: Array<number> = [];
private wasPrimaryDecoderUsedInLastDecode = false;
public constructor(
requestedFormats: Array<Html5QrcodeSupportedFormats>,
useBarCodeDetectorIfSupported: boolean,
verbose: boolean,
logger: Logger) {
this.verbose = verbose;
// Use BarcodeDetector library if enabled by config and is supported.
if (useBarCodeDetectorIfSupported
&& BarcodeDetectorDelegate.isSupported()) {
this.primaryDecoder = new BarcodeDetectorDelegate(
requestedFormats, verbose, logger);
// If 'BarcodeDetector' is supported, the library will alternate
// between 'BarcodeDetector' and 'zxing-js' to compensate for
// quality gaps between the two.
this.secondaryDecoder = new ZXingHtml5QrcodeDecoder(
requestedFormats, verbose, logger);
} else {
this.primaryDecoder = new ZXingHtml5QrcodeDecoder(
requestedFormats, verbose, logger);
}
}
async decodeAsync(canvas: HTMLCanvasElement): Promise<QrcodeResult> {
let startTime = performance.now();
try {
return await this.getDecoder().decodeAsync(canvas);
} finally {
this.possiblyLogPerformance(startTime);
}
}
async decodeRobustlyAsync(canvas: HTMLCanvasElement)
: Promise<QrcodeResult> {
let startTime = performance.now();
try {
return await this.primaryDecoder.decodeAsync(canvas);
} catch(error) {
if (this.secondaryDecoder) {
// Try fallback.
return this.secondaryDecoder.decodeAsync(canvas);
}
throw error;
} finally {
this.possiblyLogPerformance(startTime);
}
}
private getDecoder(): QrcodeDecoderAsync {
if (!this.secondaryDecoder) {
return this.primaryDecoder;
}
if (this.wasPrimaryDecoderUsedInLastDecode === false) {
this.wasPrimaryDecoderUsedInLastDecode = true;
return this.primaryDecoder;
}
this.wasPrimaryDecoderUsedInLastDecode = false;
return this.secondaryDecoder;
}
private possiblyLogPerformance(startTime: number) {
if (!this.verbose) {
return;
}
let executionTime = performance.now() - startTime;
this.executionResults.push(executionTime);
this.executions++;
this.possiblyFlushPerformanceReport();
}
// Dumps mean decoding latency to console for last
// EXECUTIONS_TO_REPORT_PERFORMANCE runs.
// TODO(mebjas): Can we automate instrumentation runs?
possiblyFlushPerformanceReport() {
if (this.executions < this.EXECUTIONS_TO_REPORT_PERFORMANCE) {
return;
}
let sum:number = 0;
for (let executionTime of this.executionResults) {
sum += executionTime;
}
let mean = sum / this.executionResults.length;
// eslint-disable-next-line no-console
console.log(`${mean} ms for ${this.executionResults.length} last runs.`);
this.executions = 0;
this.executionResults = [];
}
}

105
node_modules/html5-qrcode/src/core.d.ts generated vendored Normal file
View File

@@ -0,0 +1,105 @@
export declare enum Html5QrcodeSupportedFormats {
QR_CODE = 0,
AZTEC = 1,
CODABAR = 2,
CODE_39 = 3,
CODE_93 = 4,
CODE_128 = 5,
DATA_MATRIX = 6,
MAXICODE = 7,
ITF = 8,
EAN_13 = 9,
EAN_8 = 10,
PDF_417 = 11,
RSS_14 = 12,
RSS_EXPANDED = 13,
UPC_A = 14,
UPC_E = 15,
UPC_EAN_EXTENSION = 16
}
export declare enum DecodedTextType {
UNKNOWN = 0,
URL = 1
}
export declare function isValidHtml5QrcodeSupportedFormats(format: any): boolean;
export declare enum Html5QrcodeScanType {
SCAN_TYPE_CAMERA = 0,
SCAN_TYPE_FILE = 1
}
export declare class Html5QrcodeConstants {
static GITHUB_PROJECT_URL: string;
static SCAN_DEFAULT_FPS: number;
static DEFAULT_DISABLE_FLIP: boolean;
static DEFAULT_REMEMBER_LAST_CAMERA_USED: boolean;
static DEFAULT_SUPPORTED_SCAN_TYPE: Html5QrcodeScanType[];
}
export interface QrDimensions {
width: number;
height: number;
}
export type QrDimensionFunction = (viewfinderWidth: number, viewfinderHeight: number) => QrDimensions;
export interface QrBounds extends QrDimensions {
x: number;
y: number;
}
export declare class QrcodeResultFormat {
readonly format: Html5QrcodeSupportedFormats;
readonly formatName: string;
private constructor();
toString(): string;
static create(format: Html5QrcodeSupportedFormats): QrcodeResultFormat;
}
export interface QrcodeResultDebugData {
decoderName?: string;
}
export interface QrcodeResult {
text: string;
format?: QrcodeResultFormat;
bounds?: QrBounds;
decodedTextType?: DecodedTextType;
debugData?: QrcodeResultDebugData;
}
export interface Html5QrcodeResult {
decodedText: string;
result: QrcodeResult;
}
export declare class Html5QrcodeResultFactory {
static createFromText(decodedText: string): Html5QrcodeResult;
static createFromQrcodeResult(qrcodeResult: QrcodeResult): Html5QrcodeResult;
}
export declare enum Html5QrcodeErrorTypes {
UNKWOWN_ERROR = 0,
IMPLEMENTATION_ERROR = 1,
NO_CODE_FOUND_ERROR = 2
}
export interface Html5QrcodeError {
errorMessage: string;
type: Html5QrcodeErrorTypes;
}
export declare class Html5QrcodeErrorFactory {
static createFrom(error: any): Html5QrcodeError;
}
export type QrcodeSuccessCallback = (decodedText: string, result: Html5QrcodeResult) => void;
export type QrcodeErrorCallback = (errorMessage: string, error: Html5QrcodeError) => void;
export interface QrcodeDecoderAsync {
decodeAsync(canvas: HTMLCanvasElement): Promise<QrcodeResult>;
}
export interface RobustQrcodeDecoderAsync extends QrcodeDecoderAsync {
decodeRobustlyAsync(canvas: HTMLCanvasElement): Promise<QrcodeResult>;
}
export interface Logger {
log(message: string): void;
warn(message: string): void;
logError(message: string, isExperimental?: boolean): void;
logErrors(errors: Array<any>): void;
}
export declare class BaseLoggger implements Logger {
private verbose;
constructor(verbose: boolean);
log(message: string): void;
warn(message: string): void;
logError(message: string, isExperimental?: boolean): void;
logErrors(errors: Array<any>): void;
}
export declare function isNullOrUndefined(obj?: any): boolean;
export declare function clip(value: number, minValue: number, maxValue: number): number;

353
node_modules/html5-qrcode/src/core.ts generated vendored Normal file
View File

@@ -0,0 +1,353 @@
/**
* @fileoverview
* Core libraries, interfaces, enums shared across {@class Html5Qrcode} & {@class Html5QrcodeScanner}
*
* @author mebjas <minhazav@gmail.com>
*
* The word "QR Code" is registered trademark of DENSO WAVE INCORPORATED
* http://www.denso-wave.com/qrcode/faqpatent-e.html
*/
/**
* Code formats supported by this library.
*/
export enum Html5QrcodeSupportedFormats {
QR_CODE = 0,
AZTEC,
CODABAR,
CODE_39,
CODE_93,
CODE_128,
DATA_MATRIX,
MAXICODE,
ITF,
EAN_13,
EAN_8,
PDF_417,
RSS_14,
RSS_EXPANDED,
UPC_A,
UPC_E,
UPC_EAN_EXTENSION,
}
/** {@code Html5QrcodeSupportedFormats} to friendly name map. */
const html5QrcodeSupportedFormatsTextMap
: Map<Html5QrcodeSupportedFormats, string> = new Map(
[
[ Html5QrcodeSupportedFormats.QR_CODE, "QR_CODE" ],
[ Html5QrcodeSupportedFormats.AZTEC, "AZTEC" ],
[ Html5QrcodeSupportedFormats.CODABAR, "CODABAR" ],
[ Html5QrcodeSupportedFormats.CODE_39, "CODE_39" ],
[ Html5QrcodeSupportedFormats.CODE_93, "CODE_93" ],
[ Html5QrcodeSupportedFormats.CODE_128, "CODE_128" ],
[ Html5QrcodeSupportedFormats.DATA_MATRIX, "DATA_MATRIX" ],
[ Html5QrcodeSupportedFormats.MAXICODE, "MAXICODE" ],
[ Html5QrcodeSupportedFormats.ITF, "ITF" ],
[ Html5QrcodeSupportedFormats.EAN_13, "EAN_13" ],
[ Html5QrcodeSupportedFormats.EAN_8, "EAN_8" ],
[ Html5QrcodeSupportedFormats.PDF_417, "PDF_417" ],
[ Html5QrcodeSupportedFormats.RSS_14, "RSS_14" ],
[ Html5QrcodeSupportedFormats.RSS_EXPANDED, "RSS_EXPANDED" ],
[ Html5QrcodeSupportedFormats.UPC_A, "UPC_A" ],
[ Html5QrcodeSupportedFormats.UPC_E, "UPC_E" ],
[ Html5QrcodeSupportedFormats.UPC_EAN_EXTENSION, "UPC_EAN_EXTENSION" ]
]
);
/**
* Indicates the type of decoded text.
*
* Note: this is very experimental in nature at the moment.
*/
export enum DecodedTextType {
UNKNOWN = 0,
URL,
}
/** Returns true if the passed object instance is a valid format. */
export function isValidHtml5QrcodeSupportedFormats(format: any): boolean {
return Object.values(Html5QrcodeSupportedFormats).includes(format);
}
/**
* Types of scans supported by the library
*/
export enum Html5QrcodeScanType {
SCAN_TYPE_CAMERA = 0, // Camera based scanner.
SCAN_TYPE_FILE = 1 // File based scanner.
}
/**
* Constants used in QR code library.
*/
export class Html5QrcodeConstants {
static GITHUB_PROJECT_URL: string
= "https://github.com/mebjas/html5-qrcode";
static SCAN_DEFAULT_FPS = 2;
static DEFAULT_DISABLE_FLIP = false;
static DEFAULT_REMEMBER_LAST_CAMERA_USED = true;
static DEFAULT_SUPPORTED_SCAN_TYPE = [
Html5QrcodeScanType.SCAN_TYPE_CAMERA,
Html5QrcodeScanType.SCAN_TYPE_FILE];
}
/** Defines dimension for QR Code Scanner. */
export interface QrDimensions {
width: number;
height: number;
}
/**
* A function that takes in the width and height of the video stream
* and returns QrDimensions.
*
* Viewfinder refers to the video showing camera stream.
*/
export type QrDimensionFunction =
(viewfinderWidth: number, viewfinderHeight: number) => QrDimensions;
/**
* Defines bounds of detected QR code w.r.t the scan region.
*/
export interface QrBounds extends QrDimensions {
x: number;
y: number;
}
/** Format of detected code. */
export class QrcodeResultFormat {
public readonly format: Html5QrcodeSupportedFormats;
public readonly formatName: string;
private constructor(
format: Html5QrcodeSupportedFormats,
formatName: string) {
this.format = format;
this.formatName = formatName;
}
public toString(): string {
return this.formatName;
}
public static create(format: Html5QrcodeSupportedFormats) {
if (!html5QrcodeSupportedFormatsTextMap.has(format)) {
throw `${format} not in html5QrcodeSupportedFormatsTextMap`;
}
return new QrcodeResultFormat(
format, html5QrcodeSupportedFormatsTextMap.get(format)!);
}
}
/** Data class for QR code result used for debugging. */
export interface QrcodeResultDebugData {
/** Name of the decoder that was used for decoding. */
decoderName?: string;
}
/**
* Detailed scan result.
*/
export interface QrcodeResult {
/** Decoded text. */
text: string;
/** Format that was successfully scanned. */
format?: QrcodeResultFormat,
/**
* The bounds of the decoded QR code or bar code in the whole stream of
* image.
*
* Note: this is experimental, and not fully supported.
*/
bounds?: QrBounds;
/**
* If the decoded text from the QR code or bar code is of a known type like
* url or upi id or email id.
*
* Note: this is experimental, and not fully supported.
*/
decodedTextType?: DecodedTextType;
/** Data class for QR code result used for debugging. */
debugData?: QrcodeResultDebugData;
}
/**
* QrCode result object.
*/
export interface Html5QrcodeResult {
decodedText: string;
result: QrcodeResult;
}
/**
* Static factory for creating {@interface Html5QrcodeResult} instance.
*/
export class Html5QrcodeResultFactory {
static createFromText(decodedText: string): Html5QrcodeResult {
let qrcodeResult = {
text: decodedText
};
return {
decodedText: decodedText,
result: qrcodeResult
};
}
static createFromQrcodeResult(qrcodeResult: QrcodeResult)
: Html5QrcodeResult {
return {
decodedText: qrcodeResult.text,
result: qrcodeResult
};
}
}
/**
* Different kind of errors that can lead to scanning error.
*/
export enum Html5QrcodeErrorTypes {
UNKWOWN_ERROR = 0,
IMPLEMENTATION_ERROR = 1,
NO_CODE_FOUND_ERROR = 2
}
/**
* Interface for scan error response.
*/
export interface Html5QrcodeError {
errorMessage: string;
type: Html5QrcodeErrorTypes;
}
/**
* Static factory for creating {@interface Html5QrcodeError} instance.
*/
export class Html5QrcodeErrorFactory {
static createFrom(error: any): Html5QrcodeError {
return {
errorMessage: error,
type: Html5QrcodeErrorTypes.UNKWOWN_ERROR
};
}
}
/**
* Type for a callback for a successful code scan.
*/
export type QrcodeSuccessCallback
= (decodedText: string, result: Html5QrcodeResult) => void;
/**
* Type for a callback for failure during code scan.
*/
export type QrcodeErrorCallback
= (errorMessage: string, error: Html5QrcodeError) => void;
/** Code decoder interface. */
export interface QrcodeDecoderAsync {
/**
* Decodes content of the canvas to find a valid QR code or bar code.
*
* @param canvas a valid html5 canvas element.
*/
decodeAsync(canvas: HTMLCanvasElement): Promise<QrcodeResult>;
}
/**
* Code robust decoder interface.
*
* <p> A robust decoder may sacrifice latency of scanning for scanning quality.
* Ideal for file scan kind of operation.
*/
export interface RobustQrcodeDecoderAsync extends QrcodeDecoderAsync {
/**
* Decodes content of the canvas to find a valid QR code or bar code.
*
* <p>The method implementation will run the decoder more robustly at the
* expense of latency.
*
* @param canvas a valid html5 canvas element.
*/
decodeRobustlyAsync(canvas: HTMLCanvasElement): Promise<QrcodeResult>;
}
/** Interface for logger. */
export interface Logger {
log(message: string): void;
warn(message: string): void;
logError(message: string, isExperimental?: boolean): void;
logErrors(errors: Array<any>): void;
}
/**
* Base logger implementation based on browser console.
*
* This can be replaced by a custom implementation of logger.
*
*/
export class BaseLoggger implements Logger {
private verbose: boolean;
public constructor(verbose: boolean) {
this.verbose = verbose;
}
public log(message: string): void {
if (this.verbose) {
// eslint-disable-next-line no-console
console.log(message);
}
}
public warn(message: string): void {
if (this.verbose) {
// eslint-disable-next-line no-console
console.warn(message);
}
}
public logError(message: string, isExperimental?: boolean)
: void {
if (this.verbose || isExperimental === true) {
// eslint-disable-next-line no-console
console.error(message);
}
}
public logErrors(errors: Array<any>): void {
if (errors.length === 0) {
throw "Logger#logError called without arguments";
}
if (this.verbose) {
// eslint-disable-next-line no-console
console.error(errors);
}
}
}
//#region global functions
/** Returns true if the {@param obj} is null or undefined. */
export function isNullOrUndefined(obj?: any) {
return (typeof obj === "undefined") || obj === null;
}
/** Clips the {@code value} between {@code minValue} and {@code maxValue}. */
export function clip(value: number, minValue: number, maxValue: number) {
if (value > maxValue) {
return maxValue;
}
if (value < minValue) {
return minValue;
}
return value;
}
//#endregion

View File

@@ -0,0 +1,3 @@
export interface ExperimentalFeaturesConfig {
useBarCodeDetectorIfSupported?: boolean | undefined;
}

44
node_modules/html5-qrcode/src/experimental-features.ts generated vendored Normal file
View File

@@ -0,0 +1,44 @@
/**
* @fileoverview
* Core library for experimental features.
*
* @author mebjas <minhazav@gmail.com>
*
* Experimental features are those which have limited browser compatibility and
* hidden from official documentations. These features are not recommended by
* the author to be used in production unless explictly tested.
*
* Subset of the features are expected to upgrade to official feature list from
* time to time.
*
* The word "QR Code" is registered trademark of DENSO WAVE INCORPORATED
* http://www.denso-wave.com/qrcode/faqpatent-e.html
*/
/**
* Configuration for enabling or disabling experimental features in the library.
*
* These features will eventually upgrade as fully supported features in the
* library.
*/
export interface ExperimentalFeaturesConfig {
/**
* {@class BarcodeDetector} is being implemented by browsers at the moment.
* It has very limited browser support but as it gets available it could
* enable faster native code scanning experience.
*
* Set this flag to true, to enable using {@class BarcodeDetector} if
* supported. This is false by default.
*
* @deprecated This configuration has graduated to
* {@code Html5QrcodeCameraScanConfig} you can set it there directly. All
* documentation and future improvements shall be added to that one. This
* config will still work for backwards compatibility.
*
* Documentations:
* - https://developer.mozilla.org/en-US/docs/Web/API/BarcodeDetector
* - https://web.dev/shape-detection/#barcodedetector
*/
useBarCodeDetectorIfSupported?: boolean | undefined;
}

View File

@@ -0,0 +1,67 @@
import { Html5QrcodeScanType, QrcodeSuccessCallback, QrcodeErrorCallback } from "./core";
import { Html5QrcodeConfigs, Html5QrcodeCameraScanConfig } from "./html5-qrcode";
import { Html5QrcodeScannerState } from "./state-manager";
export interface Html5QrcodeScannerConfig extends Html5QrcodeCameraScanConfig, Html5QrcodeConfigs {
rememberLastUsedCamera?: boolean | undefined;
supportedScanTypes?: Array<Html5QrcodeScanType> | [];
showTorchButtonIfSupported?: boolean | undefined;
showZoomSliderIfSupported?: boolean | undefined;
defaultZoomValueIfSupported?: number | undefined;
}
export declare class Html5QrcodeScanner {
private elementId;
private config;
private verbose;
private currentScanType;
private sectionSwapAllowed;
private persistedDataManager;
private scanTypeSelector;
private logger;
private html5Qrcode;
private qrCodeSuccessCallback;
private qrCodeErrorCallback;
private lastMatchFound;
private cameraScanImage;
private fileScanImage;
private fileSelectionUi;
constructor(elementId: string, config: Html5QrcodeScannerConfig | undefined, verbose: boolean | undefined);
render(qrCodeSuccessCallback: QrcodeSuccessCallback, qrCodeErrorCallback: QrcodeErrorCallback | undefined): void;
pause(shouldPauseVideo?: boolean): void;
resume(): void;
getState(): Html5QrcodeScannerState;
clear(): Promise<void>;
getRunningTrackCapabilities(): MediaTrackCapabilities;
getRunningTrackSettings(): MediaTrackSettings;
applyVideoConstraints(videoConstaints: MediaTrackConstraints): Promise<void>;
private getHtml5QrcodeOrFail;
private createConfig;
private createBasicLayout;
private resetBasicLayout;
private setupInitialDashboard;
private createHeader;
private createSection;
private createCameraListUi;
private createPermissionButton;
private createPermissionsUi;
private createSectionControlPanel;
private renderFileScanUi;
private renderCameraSelection;
private createSectionSwap;
private startCameraScanIfPermissionExistsOnSwap;
private resetHeaderMessage;
private setHeaderMessage;
private showHideScanTypeSwapLink;
private insertCameraScanImageToScanRegion;
private insertFileScanImageToScanRegion;
private clearScanRegion;
private getDashboardSectionId;
private getDashboardSectionCameraScanRegionId;
private getDashboardSectionSwapLinkId;
private getScanRegionId;
private getDashboardId;
private getHeaderMessageContainerId;
private getCameraPermissionButtonId;
private getCameraScanRegion;
private getDashboardSectionSwapLink;
private getHeaderMessageDiv;
}

1137
node_modules/html5-qrcode/src/html5-qrcode-scanner.ts generated vendored Normal file

File diff suppressed because it is too large Load Diff

75
node_modules/html5-qrcode/src/html5-qrcode.d.ts generated vendored Normal file
View File

@@ -0,0 +1,75 @@
import { QrcodeErrorCallback, QrcodeSuccessCallback, Html5QrcodeSupportedFormats, Html5QrcodeResult, QrDimensions, QrDimensionFunction } from "./core";
import { CameraDevice, CameraCapabilities } from "./camera/core";
import { ExperimentalFeaturesConfig } from "./experimental-features";
import { Html5QrcodeScannerState } from "./state-manager";
export interface Html5QrcodeConfigs {
formatsToSupport?: Array<Html5QrcodeSupportedFormats> | undefined;
useBarCodeDetectorIfSupported?: boolean | undefined;
experimentalFeatures?: ExperimentalFeaturesConfig | undefined;
}
export interface Html5QrcodeFullConfig extends Html5QrcodeConfigs {
verbose: boolean | undefined;
}
export interface Html5QrcodeCameraScanConfig {
fps: number | undefined;
qrbox?: number | QrDimensions | QrDimensionFunction | undefined;
aspectRatio?: number | undefined;
disableFlip?: boolean | undefined;
videoConstraints?: MediaTrackConstraints | undefined;
}
export declare class Html5Qrcode {
private readonly logger;
private readonly elementId;
private readonly verbose;
private readonly qrcode;
private shouldScan;
private element;
private canvasElement;
private scannerPausedUiElement;
private hasBorderShaders;
private borderShaders;
private qrMatch;
private renderedCamera;
private foreverScanTimeout;
private qrRegion;
private context;
private lastScanImageFile;
private stateManagerProxy;
isScanning: boolean;
constructor(elementId: string, configOrVerbosityFlag?: boolean | Html5QrcodeFullConfig | undefined);
start(cameraIdOrConfig: string | MediaTrackConstraints, configuration: Html5QrcodeCameraScanConfig | undefined, qrCodeSuccessCallback: QrcodeSuccessCallback | undefined, qrCodeErrorCallback: QrcodeErrorCallback | undefined): Promise<null>;
pause(shouldPauseVideo?: boolean): void;
resume(): void;
getState(): Html5QrcodeScannerState;
stop(): Promise<void>;
scanFile(imageFile: File, showImage?: boolean): Promise<string>;
scanFileV2(imageFile: File, showImage?: boolean): Promise<Html5QrcodeResult>;
clear(): void;
static getCameras(): Promise<Array<CameraDevice>>;
getRunningTrackCapabilities(): MediaTrackCapabilities;
getRunningTrackSettings(): MediaTrackSettings;
getRunningTrackCameraCapabilities(): CameraCapabilities;
applyVideoConstraints(videoConstaints: MediaTrackConstraints): Promise<void>;
private getRenderedCameraOrFail;
private getSupportedFormats;
private getUseBarCodeDetectorIfSupported;
private validateQrboxSize;
private validateQrboxConfig;
private toQrdimensions;
private setupUi;
private createScannerPausedUiElement;
private scanContext;
private foreverScan;
private createVideoConstraints;
private computeCanvasDrawConfig;
private clearElement;
private possiblyUpdateShaders;
private possiblyCloseLastScanImageFile;
private createCanvasElement;
private getShadedRegionBounds;
private possiblyInsertShadingElement;
private insertShaderBorders;
private showPausedState;
private hidePausedState;
private getTimeoutFps;
}

1595
node_modules/html5-qrcode/src/html5-qrcode.ts generated vendored Normal file

File diff suppressed because it is too large Load Diff

4
node_modules/html5-qrcode/src/image-assets.d.ts generated vendored Normal file
View File

@@ -0,0 +1,4 @@
export declare const ASSET_CAMERA_SCAN: string;
export declare const ASSET_FILE_SCAN: string;
export declare const ASSET_INFO_ICON_16PX: string;
export declare const ASSET_CLOSE_ICON_16PX: string;

18
node_modules/html5-qrcode/src/image-assets.ts generated vendored Normal file
View File

@@ -0,0 +1,18 @@
/**
* @fileoverview - Exports base64 assets for gif images.
*
* @author mebjas <minhazav@gmail.com>
*
* The word "QR Code" is registered trademark of DENSO WAVE INCORPORATED
* http://www.denso-wave.com/qrcode/faqpatent-e.html
*/
const SVG_XML_PREFIX = "data:image/svg+xml;base64,";
export const ASSET_CAMERA_SCAN: string = SVG_XML_PREFIX + "PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAzNzEuNjQzIDM3MS42NDMiIHN0eWxlPSJlbmFibGUtYmFja2dyb3VuZDpuZXcgMCAwIDM3MS42NDMgMzcxLjY0MyIgeG1sOnNwYWNlPSJwcmVzZXJ2ZSI+PHBhdGggZD0iTTEwNS4wODQgMzguMjcxaDE2My43Njh2MjBIMTA1LjA4NHoiLz48cGF0aCBkPSJNMzExLjU5NiAxOTAuMTg5Yy03LjQ0MS05LjM0Ny0xOC40MDMtMTYuMjA2LTMyLjc0My0yMC41MjJWMzBjMC0xNi41NDItMTMuNDU4LTMwLTMwLTMwSDEyNS4wODRjLTE2LjU0MiAwLTMwIDEzLjQ1OC0zMCAzMHYxMjAuMTQzaC04LjI5NmMtMTYuNTQyIDAtMzAgMTMuNDU4LTMwIDMwdjEuMzMzYTI5LjgwNCAyOS44MDQgMCAwIDAgNC42MDMgMTUuOTM5Yy03LjM0IDUuNDc0LTEyLjEwMyAxNC4yMjEtMTIuMTAzIDI0LjA2MXYxLjMzM2MwIDkuODQgNC43NjMgMTguNTg3IDEyLjEwMyAyNC4wNjJhMjkuODEgMjkuODEgMCAwIDAtNC42MDMgMTUuOTM4djEuMzMzYzAgMTYuNTQyIDEzLjQ1OCAzMCAzMCAzMGg4LjMyNGMuNDI3IDExLjYzMSA3LjUwMyAyMS41ODcgMTcuNTM0IDI2LjE3Ny45MzEgMTAuNTAzIDQuMDg0IDMwLjE4NyAxNC43NjggNDUuNTM3YTkuOTg4IDkuOTg4IDAgMCAwIDguMjE2IDQuMjg4IDkuOTU4IDkuOTU4IDAgMCAwIDUuNzA0LTEuNzkzYzQuNTMzLTMuMTU1IDUuNjUtOS4zODggMi40OTUtMTMuOTIxLTYuNzk4LTkuNzY3LTkuNjAyLTIyLjYwOC0xMC43Ni0zMS40aDgyLjY4NWMuMjcyLjQxNC41NDUuODE4LjgxNSAxLjIxIDMuMTQyIDQuNTQxIDkuMzcyIDUuNjc5IDEzLjkxMyAyLjUzNCA0LjU0Mi0zLjE0MiA1LjY3Ny05LjM3MSAyLjUzNS0xMy45MTMtMTEuOTE5LTE3LjIyOS04Ljc4Ny0zNS44ODQgOS41ODEtNTcuMDEyIDMuMDY3LTIuNjUyIDEyLjMwNy0xMS43MzIgMTEuMjE3LTI0LjAzMy0uODI4LTkuMzQzLTcuMTA5LTE3LjE5NC0xOC42NjktMjMuMzM3YTkuODU3IDkuODU3IDAgMCAwLTEuMDYxLS40ODZjLS40NjYtLjE4Mi0xMS40MDMtNC41NzktOS43NDEtMTUuNzA2IDEuMDA3LTYuNzM3IDE0Ljc2OC04LjI3MyAyMy43NjYtNy42NjYgMjMuMTU2IDEuNTY5IDM5LjY5OCA3LjgwMyA0Ny44MzYgMTguMDI2IDUuNzUyIDcuMjI1IDcuNjA3IDE2LjYyMyA1LjY3MyAyOC43MzMtLjQxMyAyLjU4NS0uODI0IDUuMjQxLTEuMjQ1IDcuOTU5LTUuNzU2IDM3LjE5NC0xMi45MTkgODMuNDgzLTQ5Ljg3IDExNC42NjEtNC4yMjEgMy41NjEtNC43NTYgOS44Ny0xLjE5NCAxNC4wOTJhOS45OCA5Ljk4IDAgMCAwIDcuNjQ4IDMuNTUxIDkuOTU1IDkuOTU1IDAgMCAwIDYuNDQ0LTIuMzU4YzQyLjY3Mi0zNi4wMDUgNTAuODAyLTg4LjUzMyA1Ni43MzctMTI2Ljg4OC40MTUtMi42ODQuODIxLTUuMzA5IDEuMjI5LTcuODYzIDIuODM0LTE3LjcyMS0uNDU1LTMyLjY0MS05Ljc3Mi00NC4zNDV6bS0yMzIuMzA4IDQyLjYyYy01LjUxNCAwLTEwLTQuNDg2LTEwLTEwdi0xLjMzM2MwLTUuNTE0IDQuNDg2LTEwIDEwLTEwaDE1djIxLjMzM2gtMTV6bS0yLjUtNTIuNjY2YzAtNS41MTQgNC40ODYtMTAgMTAtMTBoNy41djIxLjMzM2gtNy41Yy01LjUxNCAwLTEwLTQuNDg2LTEwLTEwdi0xLjMzM3ptMTcuNSA5My45OTloLTcuNWMtNS41MTQgMC0xMC00LjQ4Ni0xMC0xMHYtMS4zMzNjMC01LjUxNCA0LjQ4Ni0xMCAxMC0xMGg3LjV2MjEuMzMzem0zMC43OTYgMjguODg3Yy01LjUxNCAwLTEwLTQuNDg2LTEwLTEwdi04LjI3MWg5MS40NTdjLS44NTEgNi42NjgtLjQzNyAxMi43ODcuNzMxIDE4LjI3MWgtODIuMTg4em03OS40ODItMTEzLjY5OGMtMy4xMjQgMjAuOTA2IDEyLjQyNyAzMy4xODQgMjEuNjI1IDM3LjA0IDUuNDQxIDIuOTY4IDcuNTUxIDUuNjQ3IDcuNzAxIDcuMTg4LjIxIDIuMTUtMi41NTMgNS42ODQtNC40NzcgNy4yNTEtLjQ4Mi4zNzgtLjkyOS44LTEuMzM1IDEuMjYxLTYuOTg3IDcuOTM2LTExLjk4MiAxNS41Mi0xNS40MzIgMjIuNjg4aC05Ny41NjRWMzBjMC01LjUxNCA0LjQ4Ni0xMCAxMC0xMGgxMjMuNzY5YzUuNTE0IDAgMTAgNC40ODYgMTAgMTB2MTM1LjU3OWMtMy4wMzItLjM4MS02LjE1LS42OTQtOS4zODktLjkxNC0yNS4xNTktMS42OTQtNDIuMzcgNy43NDgtNDQuODk4IDI0LjY2NnoiLz48cGF0aCBkPSJNMTc5LjEyOSA4My4xNjdoLTI0LjA2YTUgNSAwIDAgMC01IDV2MjQuMDYxYTUgNSAwIDAgMCA1IDVoMjQuMDZhNSA1IDAgMCAwIDUtNVY4OC4xNjdhNSA1IDAgMCAwLTUtNXpNMTcyLjYyOSAxNDIuODZoLTEyLjU2VjEzMC44YTUgNSAwIDEgMC0xMCAwdjE3LjA2MWE1IDUgMCAwIDAgNSA1aDE3LjU2YTUgNSAwIDEgMCAwLTEwLjAwMXpNMjE2LjU2OCA4My4xNjdoLTI0LjA2YTUgNSAwIDAgMC01IDV2MjQuMDYxYTUgNSAwIDAgMCA1IDVoMjQuMDZhNSA1IDAgMCAwIDUtNVY4OC4xNjdhNSA1IDAgMCAwLTUtNXptLTUgMjQuMDYxaC0xNC4wNlY5My4xNjdoMTQuMDZ2MTQuMDYxek0yMTEuNjY5IDEyNS45MzZIMTk3LjQxYTUgNSAwIDAgMC01IDV2MTQuMjU3YTUgNSAwIDAgMCA1IDVoMTQuMjU5YTUgNSAwIDAgMCA1LTV2LTE0LjI1N2E1IDUgMCAwIDAtNS01eiIvPjwvc3ZnPg==";
export const ASSET_FILE_SCAN: string = SVG_XML_PREFIX + "PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCA1OS4wMTggNTkuMDE4IiBzdHlsZT0iZW5hYmxlLWJhY2tncm91bmQ6bmV3IDAgMCA1OS4wMTggNTkuMDE4IiB4bWw6c3BhY2U9InByZXNlcnZlIj48cGF0aCBkPSJtNTguNzQxIDU0LjgwOS01Ljk2OS02LjI0NGExMC43NCAxMC43NCAwIDAgMCAyLjgyLTcuMjVjMC01Ljk1My00Ljg0My0xMC43OTYtMTAuNzk2LTEwLjc5NlMzNCAzNS4zNjEgMzQgNDEuMzE0IDM4Ljg0MyA1Mi4xMSA0NC43OTYgNTIuMTFjMi40NDEgMCA0LjY4OC0uODI0IDYuNDk5LTIuMTk2bDYuMDAxIDYuMjc3YS45OTguOTk4IDAgMCAwIDEuNDE0LjAzMiAxIDEgMCAwIDAgLjAzMS0xLjQxNHpNMzYgNDEuMzE0YzAtNC44NSAzLjk0Ni04Ljc5NiA4Ljc5Ni04Ljc5NnM4Ljc5NiAzLjk0NiA4Ljc5NiA4Ljc5Ni0zLjk0NiA4Ljc5Ni04Ljc5NiA4Ljc5NlMzNiA0Ni4xNjQgMzYgNDEuMzE0ek0xMC40MzEgMTYuMDg4YzAgMy4wNyAyLjQ5OCA1LjU2OCA1LjU2OSA1LjU2OHM1LjU2OS0yLjQ5OCA1LjU2OS01LjU2OGMwLTMuMDcxLTIuNDk4LTUuNTY5LTUuNTY5LTUuNTY5cy01LjU2OSAyLjQ5OC01LjU2OSA1LjU2OXptOS4xMzggMGMwIDEuOTY4LTEuNjAyIDMuNTY4LTMuNTY5IDMuNTY4cy0zLjU2OS0xLjYwMS0zLjU2OS0zLjU2OCAxLjYwMi0zLjU2OSAzLjU2OS0zLjU2OSAzLjU2OSAxLjYwMSAzLjU2OSAzLjU2OXoiLz48cGF0aCBkPSJtMzAuODgyIDI4Ljk4NyA5LjE4LTEwLjA1NCAxMS4yNjIgMTAuMzIzYTEgMSAwIDAgMCAxLjM1MS0xLjQ3NWwtMTItMTFhMSAxIDAgMCAwLTEuNDE0LjA2M2wtOS43OTQgMTAuNzI3LTQuNzQzLTQuNzQzYTEuMDAzIDEuMDAzIDAgMCAwLTEuMzY4LS4wNDRMNi4zMzkgMzcuNzY4YTEgMSAwIDEgMCAxLjMyMiAxLjUwMWwxNi4zMTMtMTQuMzYyIDcuMzE5IDcuMzE4YS45OTkuOTk5IDAgMSAwIDEuNDE0LTEuNDE0bC0xLjgyNS0xLjgyNHoiLz48cGF0aCBkPSJNMzAgNDYuNTE4SDJ2LTQyaDU0djI4YTEgMSAwIDEgMCAyIDB2LTI5YTEgMSAwIDAgMC0xLTFIMWExIDEgMCAwIDAtMSAxdjQ0YTEgMSAwIDAgMCAxIDFoMjlhMSAxIDAgMSAwIDAtMnoiLz48L3N2Zz4=";
export const ASSET_INFO_ICON_16PX : string = SVG_XML_PREFIX + "PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCA0NjAgNDYwIiBzdHlsZT0iZW5hYmxlLWJhY2tncm91bmQ6bmV3IDAgMCA0NjAgNDYwIiB4bWw6c3BhY2U9InByZXNlcnZlIj48cGF0aCBkPSJNMjMwIDBDMTAyLjk3NSAwIDAgMTAyLjk3NSAwIDIzMHMxMDIuOTc1IDIzMCAyMzAgMjMwIDIzMC0xMDIuOTc0IDIzMC0yMzBTMzU3LjAyNSAwIDIzMCAwem0zOC4zMzMgMzc3LjM2YzAgOC42NzYtNy4wMzQgMTUuNzEtMTUuNzEgMTUuNzFoLTQzLjEwMWMtOC42NzYgMC0xNS43MS03LjAzNC0xNS43MS0xNS43MVYyMDIuNDc3YzAtOC42NzYgNy4wMzMtMTUuNzEgMTUuNzEtMTUuNzFoNDMuMTAxYzguNjc2IDAgMTUuNzEgNy4wMzMgMTUuNzEgMTUuNzFWMzc3LjM2ek0yMzAgMTU3Yy0yMS41MzkgMC0zOS0xNy40NjEtMzktMzlzMTcuNDYxLTM5IDM5LTM5IDM5IDE3LjQ2MSAzOSAzOS0xNy40NjEgMzktMzkgMzl6Ii8+PC9zdmc+";
export const ASSET_CLOSE_ICON_16PX : string = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAAAQgAAAEIBarqQRAAAABl0RVh0U29mdHdhcmUAd3d3Lmlua3NjYXBlLm9yZ5vuPBoAAAE1SURBVDiNfdI7S0NBEAXgLya1otFgpbYSbISAgpXYi6CmiH9KCAiChaVga6OiWPgfRDQ+0itaGVNosXtluWwcuMzePfM4M3sq8lbHBubwg1dc4m1E/J/N4ghDPOIsfk/4xiEao5KX0McFljN4C9d4QTPXuY99jP3DsIoDPGM6BY5i5yI5R7O4q+ImFkJY2DCh3cAH2klyB+9J1xUMMAG7eCh1a+Mr+k48b5diXrFVwwLuS+BJ9MfR7+G0FHOHhTHhnXNWS87VDF4pcnfQK4Ep7XScNLmPTZgURNKKYENYWDpzW1BhscS1WHS8CDgURFJQrWcoF3c13KKbgg1BYQfy8xZWEzTTw1QZbAoKu8FqJnktdu5hcVSHmchiILzzuaDQvjBzV2m8yohCE1jHfPx/xhU+y4G/D75ELlRJsSYAAAAASUVORK5CYII=";

6
node_modules/html5-qrcode/src/index.d.ts generated vendored Normal file
View File

@@ -0,0 +1,6 @@
export { Html5Qrcode, Html5QrcodeFullConfig, Html5QrcodeCameraScanConfig } from "./html5-qrcode";
export { Html5QrcodeScanner } from "./html5-qrcode-scanner";
export { Html5QrcodeSupportedFormats, Html5QrcodeResult, QrcodeSuccessCallback, QrcodeErrorCallback } from "./core";
export { Html5QrcodeScannerState } from "./state-manager";
export { Html5QrcodeScanType } from "./core";
export { CameraCapabilities, CameraDevice } from "./camera/core";

32
node_modules/html5-qrcode/src/index.ts generated vendored Normal file
View File

@@ -0,0 +1,32 @@
/**
* @fileoverview - Global export file.
* HTML5 QR code & barcode scanning library.
* - Decode QR Code.
* - Decode different kinds of barcodes.
* - Decode using web cam, smart phone camera or using images on local file
* system.
*
* @author mebjas <minhazav@gmail.com>
*
* The word "QR Code" is registered trademark of DENSO WAVE INCORPORATED
* http://www.denso-wave.com/qrcode/faqpatent-e.html
*/
export {
Html5Qrcode,
Html5QrcodeFullConfig,
Html5QrcodeCameraScanConfig
} from "./html5-qrcode";
export { Html5QrcodeScanner } from "./html5-qrcode-scanner";
export {
Html5QrcodeSupportedFormats,
Html5QrcodeResult,
QrcodeSuccessCallback,
QrcodeErrorCallback
} from "./core";
export { Html5QrcodeScannerState } from "./state-manager";
export { Html5QrcodeScanType } from "./core";
export {
CameraCapabilities,
CameraDevice
} from "./camera/core";

View File

@@ -0,0 +1,16 @@
import { QrcodeResult, Html5QrcodeSupportedFormats, QrcodeDecoderAsync, Logger } from "./core";
export declare class BarcodeDetectorDelegate implements QrcodeDecoderAsync {
private readonly formatMap;
private readonly reverseFormatMap;
private verbose;
private logger;
private detector;
static isSupported(): boolean;
constructor(requestedFormats: Array<Html5QrcodeSupportedFormats>, verbose: boolean, logger: Logger);
decodeAsync(canvas: HTMLCanvasElement): Promise<QrcodeResult>;
private selectLargestBarcode;
private createBarcodeDetectorFormats;
private toHtml5QrcodeSupportedFormats;
private createReverseFormatMap;
private createDebugData;
}

View File

@@ -0,0 +1,204 @@
/**
* @fileoverview
* {@interface QrcodeDecoder} wrapper around experimental BarcodeDetector API.
*
* @author mebjas <minhazav@gmail.com>
*
* Read more about the experimental feature here:
* https://developer.mozilla.org/en-US/docs/Web/API/BarcodeDetector
*
* The word "QR Code" is registered trademark of DENSO WAVE INCORPORATED
* http://www.denso-wave.com/qrcode/faqpatent-e.html
*/
import {
QrcodeResult,
QrcodeResultDebugData,
QrcodeResultFormat,
Html5QrcodeSupportedFormats,
QrcodeDecoderAsync,
Logger
} from "./core";
declare const BarcodeDetector: any;
/** Config for BarcodeDetector API. */
interface BarcodeDetectorConfig {
formats: Array<string>;
}
/**
* Interface for BarcodeDetector result.
*
* Forked from
* https://developer.mozilla.org/en-US/docs/Web/API/BarcodeDetector#methods
*/
interface BarcodeDetectorResult {
/**
* A DOMRectReadOnly, which returns the dimensions of a rectangle
* representing the extent of a detected barcode, aligned with the image.
*/
boundingBox: DOMRectReadOnly;
/**
* The x and y co-ordinates of the four corner points of the detected
* barcode relative to the image, starting with the top left and working
* clockwise. This may not be square due to perspective distortions within
* the image.
*/
cornerPoints: any;
/**
* The detected barcode format.
*/
format: string;
/**
* A String decoded from the barcode data.
*/
rawValue: string;
}
/**
* ZXing based Code decoder.
*/
export class BarcodeDetectorDelegate implements QrcodeDecoderAsync {
// All formats defined here
// https://developer.mozilla.org/en-US/docs/Web/API/Barcode_Detection_API#supported_barcode_formats
private readonly formatMap: Map<Html5QrcodeSupportedFormats, string>
= new Map([
[ Html5QrcodeSupportedFormats.QR_CODE, "qr_code" ],
[ Html5QrcodeSupportedFormats.AZTEC, "aztec" ],
[ Html5QrcodeSupportedFormats.CODABAR, "codabar" ],
[ Html5QrcodeSupportedFormats.CODE_39, "code_39" ],
[ Html5QrcodeSupportedFormats.CODE_93, "code_93" ],
[ Html5QrcodeSupportedFormats.CODE_128, "code_128" ],
[ Html5QrcodeSupportedFormats.DATA_MATRIX, "data_matrix" ],
[ Html5QrcodeSupportedFormats.ITF, "itf" ],
[ Html5QrcodeSupportedFormats.EAN_13, "ean_13" ],
[ Html5QrcodeSupportedFormats.EAN_8, "ean_8" ],
[ Html5QrcodeSupportedFormats.PDF_417, "pdf417" ],
[ Html5QrcodeSupportedFormats.UPC_A, "upc_a" ],
[ Html5QrcodeSupportedFormats.UPC_E, "upc_e" ]
]);
private readonly reverseFormatMap: Map<string, Html5QrcodeSupportedFormats>
= this.createReverseFormatMap();
private verbose: boolean;
private logger: Logger;
private detector: any;
/**
* Returns true if this API is supported by the browser.
*
* TODO(mebjas): Add checks like this
* https://web.dev/shape-detection/#featuredetection
* TODO(mebjas): Check for format supported by the BarcodeDetector using
* getSupportedFormats() API.
* @returns
*/
public static isSupported(): boolean {
if (!("BarcodeDetector" in window)) {
return false;
}
const dummyDetector = new BarcodeDetector({formats: [ "qr_code" ]});
return typeof dummyDetector !== "undefined";
}
public constructor(
requestedFormats: Array<Html5QrcodeSupportedFormats>,
verbose: boolean,
logger: Logger) {
if (!BarcodeDetectorDelegate.isSupported()) {
throw "Use html5qrcode.min.js without edit, Use "
+ "BarcodeDetectorDelegate only if it isSupported();";
}
this.verbose = verbose;
this.logger = logger;
// create new detector
const formats = this.createBarcodeDetectorFormats(requestedFormats);
this.detector = new BarcodeDetector(formats);
// check compatibility
if (!this.detector) {
throw "BarcodeDetector detector not supported";
}
}
async decodeAsync(canvas: HTMLCanvasElement): Promise<QrcodeResult> {
const barcodes: Array<BarcodeDetectorResult>
= await this.detector.detect(canvas);
if (!barcodes || barcodes.length === 0) {
throw "No barcode or QR code detected.";
}
// TODO(mebjas): Today BarcodeDetector library seems to be returning
// mutliple barcodes if supported. But the documentation around it is
// not the best. As of now, we are returning just the largest code
// found. In future it'd be desriable to return mutli codes if supported
// and found.
let largestBarcode = this.selectLargestBarcode(barcodes);
return {
text: largestBarcode.rawValue,
format: QrcodeResultFormat.create(
this.toHtml5QrcodeSupportedFormats(largestBarcode.format)),
debugData: this.createDebugData()
};
}
private selectLargestBarcode(barcodes: Array<BarcodeDetectorResult>)
: BarcodeDetectorResult {
let largestBarcode: BarcodeDetectorResult | null = null;
let maxArea = 0;
for (let barcode of barcodes) {
let area = barcode.boundingBox.width * barcode.boundingBox.height;
if (area > maxArea) {
maxArea = area;
largestBarcode = barcode;
}
}
if (!largestBarcode) {
throw "No largest barcode found";
}
return largestBarcode!;
}
private createBarcodeDetectorFormats(
requestedFormats: Array<Html5QrcodeSupportedFormats>):
BarcodeDetectorConfig {
let formats: Array<string> = [];
for (const requestedFormat of requestedFormats) {
if (this.formatMap.has(requestedFormat)) {
formats.push(
this.formatMap.get(requestedFormat)!);
} else {
this.logger.warn(`${requestedFormat} is not supported by`
+ "BarcodeDetectorDelegate");
}
}
return { formats: formats };
}
private toHtml5QrcodeSupportedFormats(barcodeDetectorFormat: string)
: Html5QrcodeSupportedFormats {
if (!this.reverseFormatMap.has(barcodeDetectorFormat)) {
throw `reverseFormatMap doesn't have ${barcodeDetectorFormat}`;
}
return this.reverseFormatMap.get(barcodeDetectorFormat)!;
}
private createReverseFormatMap(): Map<string, Html5QrcodeSupportedFormats> {
let result = new Map();
this.formatMap.forEach(
(value: string, key: Html5QrcodeSupportedFormats, _) => {
result.set(value, key);
});
return result;
}
private createDebugData(): QrcodeResultDebugData {
return { decoderName: "BarcodeDetector" };
}
}

29
node_modules/html5-qrcode/src/state-manager.d.ts generated vendored Normal file
View File

@@ -0,0 +1,29 @@
export declare enum Html5QrcodeScannerState {
UNKNOWN = 0,
NOT_STARTED = 1,
SCANNING = 2,
PAUSED = 3
}
export interface StateManagerTransaction {
execute(): void;
cancel(): void;
}
export interface StateManager {
startTransition(newState: Html5QrcodeScannerState): StateManagerTransaction;
directTransition(newState: Html5QrcodeScannerState): void;
getState(): Html5QrcodeScannerState;
}
export declare class StateManagerProxy {
private stateManager;
constructor(stateManager: StateManager);
startTransition(newState: Html5QrcodeScannerState): StateManagerTransaction;
directTransition(newState: Html5QrcodeScannerState): void;
getState(): Html5QrcodeScannerState;
canScanFile(): boolean;
isScanning(): boolean;
isStrictlyScanning(): boolean;
isPaused(): boolean;
}
export declare class StateManagerFactory {
static create(): StateManagerProxy;
}

193
node_modules/html5-qrcode/src/state-manager.ts generated vendored Normal file
View File

@@ -0,0 +1,193 @@
/**
* @fileoverview
* State handler.
*
* @author mebjas <minhazav@gmail.com>
*/
/** Different states of scanner */
export enum Html5QrcodeScannerState {
// Invalid internal state, do not set to this state.
UNKNOWN = 0,
// Indicates the scanning is not running or user is using file based
// scanning.
NOT_STARTED = 1,
// Camera scan is running.
SCANNING,
// Camera scan is paused but camera is running.
PAUSED,
}
/** Transaction for state transition. */
export interface StateManagerTransaction {
/**
* Executes the current transaction.
*/
execute(): void;
/**
* Cancels the current transaction.
*/
cancel(): void;
}
/** Manager class for states. */
export interface StateManager {
/**
* Start a transition to a new state. No other transitions will be allowed
* till this one is executed.
*
* @param newState new state to transition to.
*
* @returns transaction of type {@interface StateManagerTransaction}.
*
* @throws error if the new state is not a valid transition from current
* state.
*/
startTransition(newState: Html5QrcodeScannerState): StateManagerTransaction;
/**
* Directly execute a transition.
*
* @param newState new state to transition to.
*
* @throws error if the new state is not a valid transition from current
* state.
*/
directTransition(newState: Html5QrcodeScannerState): void;
/**
* Get current state.
*/
getState(): Html5QrcodeScannerState;
}
/**
* Implementation of {@interface StateManager} and
* {@interface StateManagerTransaction}.
*/
class StateManagerImpl implements StateManager, StateManagerTransaction {
private state: Html5QrcodeScannerState = Html5QrcodeScannerState.NOT_STARTED;
private onGoingTransactionNewState: Html5QrcodeScannerState
= Html5QrcodeScannerState.UNKNOWN;
public directTransition(newState: Html5QrcodeScannerState) {
this.failIfTransitionOngoing();
this.validateTransition(newState);
this.state = newState;
}
public startTransition(newState: Html5QrcodeScannerState): StateManagerTransaction {
this.failIfTransitionOngoing();
this.validateTransition(newState);
this.onGoingTransactionNewState = newState;
return this;
}
public execute() {
if (this.onGoingTransactionNewState
=== Html5QrcodeScannerState.UNKNOWN) {
throw "Transaction is already cancelled, cannot execute().";
}
const tempNewState = this.onGoingTransactionNewState;
this.onGoingTransactionNewState = Html5QrcodeScannerState.UNKNOWN;
this.directTransition(tempNewState);
}
public cancel() {
if (this.onGoingTransactionNewState
=== Html5QrcodeScannerState.UNKNOWN) {
throw "Transaction is already cancelled, cannot cancel().";
}
this.onGoingTransactionNewState = Html5QrcodeScannerState.UNKNOWN;
}
public getState(): Html5QrcodeScannerState {
return this.state;
}
//#region private methods
private failIfTransitionOngoing() {
if (this.onGoingTransactionNewState
!== Html5QrcodeScannerState.UNKNOWN) {
throw "Cannot transition to a new state, already under transition";
}
}
private validateTransition(newState: Html5QrcodeScannerState) {
switch(this.state) {
case Html5QrcodeScannerState.UNKNOWN:
throw "Transition from unknown is not allowed";
case Html5QrcodeScannerState.NOT_STARTED:
this.failIfNewStateIs(newState, [Html5QrcodeScannerState.PAUSED]);
break;
case Html5QrcodeScannerState.SCANNING:
// Both state transitions legal from here.
break;
case Html5QrcodeScannerState.PAUSED:
// Both state transitions legal from here.
break;
}
}
private failIfNewStateIs(
newState: Html5QrcodeScannerState,
disallowedStatesToTransition: Array<Html5QrcodeScannerState>) {
for (const disallowedState of disallowedStatesToTransition) {
if (newState === disallowedState) {
throw `Cannot transition from ${this.state} to ${newState}`;
}
}
}
//#endregion
}
export class StateManagerProxy {
private stateManager: StateManager;
constructor(stateManager: StateManager) {
this.stateManager = stateManager;
}
startTransition(newState: Html5QrcodeScannerState): StateManagerTransaction {
return this.stateManager.startTransition(newState);
}
directTransition(newState: Html5QrcodeScannerState) {
this.stateManager.directTransition(newState);
}
getState(): Html5QrcodeScannerState {
return this.stateManager.getState();
}
canScanFile(): boolean {
return this.stateManager.getState() === Html5QrcodeScannerState.NOT_STARTED;
}
isScanning(): boolean {
return this.stateManager.getState() !== Html5QrcodeScannerState.NOT_STARTED;
}
isStrictlyScanning(): boolean {
return this.stateManager.getState() === Html5QrcodeScannerState.SCANNING;
}
isPaused(): boolean {
return this.stateManager.getState() === Html5QrcodeScannerState.PAUSED;
}
}
/**
* Factory for creating instance of {@class StateManagerProxy}.
*/
export class StateManagerFactory {
public static create(): StateManagerProxy {
return new StateManagerProxy(new StateManagerImpl());
}
}

12
node_modules/html5-qrcode/src/storage.d.ts generated vendored Normal file
View File

@@ -0,0 +1,12 @@
export declare class PersistedDataManager {
private data;
private static LOCAL_STORAGE_KEY;
constructor();
hasCameraPermissions(): boolean;
getLastUsedCameraId(): string | null;
setHasPermission(hasPermission: boolean): void;
setLastUsedCameraId(lastUsedCameraId: string): void;
resetLastUsedCameraId(): void;
reset(): void;
private flush;
}

72
node_modules/html5-qrcode/src/storage.ts generated vendored Normal file
View File

@@ -0,0 +1,72 @@
/**
* @fileoverview
* Core storage related APIs.
*
* @author mebjas <minhazav@gmail.com>
*
* The word "QR Code" is registered trademark of DENSO WAVE INCORPORATED
* http://www.denso-wave.com/qrcode/faqpatent-e.html
*/
interface PersistedData {
hasPermission: boolean;
lastUsedCameraId: string | null;
}
class PersistedDataFactory {
static createDefault(): PersistedData {
return {
hasPermission: false,
lastUsedCameraId: null
};
}
}
export class PersistedDataManager {
private data: PersistedData = PersistedDataFactory.createDefault();
private static LOCAL_STORAGE_KEY: string = "HTML5_QRCODE_DATA";
constructor() {
let data = localStorage.getItem(PersistedDataManager.LOCAL_STORAGE_KEY);
if (!data) {
this.reset();
} else {
this.data = JSON.parse(data);
}
}
public hasCameraPermissions(): boolean {
return this.data.hasPermission;
}
public getLastUsedCameraId(): string | null {
return this.data.lastUsedCameraId;
}
public setHasPermission(hasPermission: boolean) {
this.data.hasPermission = hasPermission;
this.flush();
}
public setLastUsedCameraId(lastUsedCameraId: string) {
this.data.lastUsedCameraId = lastUsedCameraId;
this.flush();
}
public resetLastUsedCameraId() {
this.data.lastUsedCameraId = null;
this.flush();
}
public reset() {
this.data = PersistedDataFactory.createDefault();
this.flush();
}
private flush(): void {
localStorage.setItem(
PersistedDataManager.LOCAL_STORAGE_KEY,
JSON.stringify(this.data));
}
}

45
node_modules/html5-qrcode/src/strings.d.ts generated vendored Normal file
View File

@@ -0,0 +1,45 @@
export declare class Html5QrcodeStrings {
static codeParseError(exception: any): string;
static errorGettingUserMedia(error: any): string;
static onlyDeviceSupportedError(): string;
static cameraStreamingNotSupported(): string;
static unableToQuerySupportedDevices(): string;
static insecureContextCameraQueryError(): string;
static scannerPaused(): string;
}
export declare class Html5QrcodeScannerStrings {
static scanningStatus(): string;
static idleStatus(): string;
static errorStatus(): string;
static permissionStatus(): string;
static noCameraFoundErrorStatus(): string;
static lastMatch(decodedText: string): string;
static codeScannerTitle(): string;
static cameraPermissionTitle(): string;
static cameraPermissionRequesting(): string;
static noCameraFound(): string;
static scanButtonStopScanningText(): string;
static scanButtonStartScanningText(): string;
static torchOnButton(): string;
static torchOffButton(): string;
static torchOnFailedMessage(): string;
static torchOffFailedMessage(): string;
static scanButtonScanningStarting(): string;
static textIfCameraScanSelected(): string;
static textIfFileScanSelected(): string;
static selectCamera(): string;
static fileSelectionChooseImage(): string;
static fileSelectionChooseAnother(): string;
static fileSelectionNoImageSelected(): string;
static anonymousCameraPrefix(): string;
static dragAndDropMessage(): string;
static dragAndDropMessageOnlyImages(): string;
static zoom(): string;
static loadingImage(): string;
static cameraScanAltText(): string;
static fileScanAltText(): string;
}
export declare class LibraryInfoStrings {
static poweredBy(): string;
static reportIssues(): string;
}

200
node_modules/html5-qrcode/src/strings.ts generated vendored Normal file
View File

@@ -0,0 +1,200 @@
/**
* @fileoverview
* Strings used by {@class Html5Qrcode} & {@class Html5QrcodeScanner}
*
* @author mebjas <minhazav@gmail.com>
*
* The word "QR Code" is registered trademark of DENSO WAVE INCORPORATED
* http://www.denso-wave.com/qrcode/faqpatent-e.html
*/
/**
* Strings used in {@class Html5Qrcode}.
*
* TODO(mebjas): Support internalization.
*/
export class Html5QrcodeStrings {
public static codeParseError(exception: any): string {
return `QR code parse error, error = ${exception}`;
}
public static errorGettingUserMedia(error: any): string {
return `Error getting userMedia, error = ${error}`;
}
public static onlyDeviceSupportedError(): string {
return "The device doesn't support navigator.mediaDevices , only "
+ "supported cameraIdOrConfig in this case is deviceId parameter "
+ "(string).";
}
public static cameraStreamingNotSupported(): string {
return "Camera streaming not supported by the browser.";
}
public static unableToQuerySupportedDevices(): string {
return "Unable to query supported devices, unknown error.";
}
public static insecureContextCameraQueryError(): string {
return "Camera access is only supported in secure context like https "
+ "or localhost.";
}
public static scannerPaused(): string {
return "Scanner paused";
}
}
/**
* Strings used in {@class Html5QrcodeScanner}.
*
* TODO(mebjas): Support internalization.
*/
export class Html5QrcodeScannerStrings {
public static scanningStatus(): string {
return "Scanning";
}
public static idleStatus(): string {
return "Idle";
}
public static errorStatus(): string {
return "Error";
}
public static permissionStatus(): string {
return "Permission";
}
public static noCameraFoundErrorStatus(): string {
return "No Cameras";
}
public static lastMatch(decodedText: string): string {
return `Last Match: ${decodedText}`;
}
public static codeScannerTitle(): string {
return "Code Scanner";
}
public static cameraPermissionTitle(): string {
return "Request Camera Permissions";
}
public static cameraPermissionRequesting(): string {
return "Requesting camera permissions...";
}
public static noCameraFound(): string {
return "No camera found";
}
public static scanButtonStopScanningText(): string {
return "Stop Scanning";
}
public static scanButtonStartScanningText(): string {
return "Start Scanning";
}
public static torchOnButton(): string {
return "Switch On Torch";
}
public static torchOffButton(): string {
return "Switch Off Torch";
}
public static torchOnFailedMessage(): string {
return "Failed to turn on torch";
}
public static torchOffFailedMessage(): string {
return "Failed to turn off torch";
}
public static scanButtonScanningStarting(): string {
return "Launching Camera...";
}
/**
* Text to show when camera scan is selected.
*
* This will be used to switch to file based scanning.
*/
public static textIfCameraScanSelected(): string {
return "Scan an Image File";
}
/**
* Text to show when file based scan is selected.
*
* This will be used to switch to camera based scanning.
*/
public static textIfFileScanSelected(): string {
return "Scan using camera directly";
}
public static selectCamera(): string {
return "Select Camera";
}
public static fileSelectionChooseImage(): string {
return "Choose Image";
}
public static fileSelectionChooseAnother(): string {
return "Choose Another";
}
public static fileSelectionNoImageSelected(): string {
return "No image choosen";
}
/** Prefix to be given to anonymous cameras. */
public static anonymousCameraPrefix(): string {
return "Anonymous Camera";
}
public static dragAndDropMessage(): string {
return "Or drop an image to scan";
}
public static dragAndDropMessageOnlyImages(): string {
return "Or drop an image to scan (other files not supported)";
}
/** Value for zoom. */
public static zoom(): string {
return "zoom";
}
public static loadingImage(): string {
return "Loading image...";
}
public static cameraScanAltText(): string {
return "Camera based scan";
}
public static fileScanAltText(): string {
return "Fule based scan";
}
}
/** Strings used in {@class LibraryInfoDiv} */
export class LibraryInfoStrings {
public static poweredBy(): string {
return "Powered by ";
}
public static reportIssues(): string {
return "Report issues";
}
}

6
node_modules/html5-qrcode/src/ui.d.ts generated vendored Normal file
View File

@@ -0,0 +1,6 @@
export declare class LibraryInfoContainer {
private infoDiv;
private infoIcon;
constructor();
renderInto(parent: HTMLElement): void;
}

152
node_modules/html5-qrcode/src/ui.ts generated vendored Normal file
View File

@@ -0,0 +1,152 @@
/**
* @fileoverview
* All structured UI classes.
*
* TODO(mebjas): Migrate all UI components to modular UI classes so they are
* easy to improve.
* TODO(mebjas): Add tests for all UI components.
* @author mebjas <minhazav@gmail.com>
*/
import { ASSET_CLOSE_ICON_16PX, ASSET_INFO_ICON_16PX } from "./image-assets";
import { LibraryInfoStrings } from "./strings";
type OnClickListener0 = () => void;
//#region Info Icon + Div
class LibraryInfoDiv {
private infoDiv: HTMLDivElement;
constructor() {
this.infoDiv = document.createElement("div");
}
public renderInto(parent: HTMLElement) {
this.infoDiv.style.position = "absolute";
this.infoDiv.style.top = "10px";
this.infoDiv.style.right = "10px";
this.infoDiv.style.zIndex = "2";
this.infoDiv.style.display = "none";
this.infoDiv.style.padding = "5pt";
this.infoDiv.style.border = "1px solid #171717";
this.infoDiv.style.fontSize = "10pt";
this.infoDiv.style.background = "rgb(0 0 0 / 69%)";
this.infoDiv.style.borderRadius = "5px";
this.infoDiv.style.textAlign = "center";
this.infoDiv.style.fontWeight = "400";
this.infoDiv.style.color = "white";
this.infoDiv.innerText = LibraryInfoStrings.poweredBy();
const projectLink = document.createElement("a");
projectLink.innerText = "ScanApp";
projectLink.href = "https://scanapp.org";
projectLink.target = "new";
projectLink.style.color = "white";
this.infoDiv.appendChild(projectLink);
const breakElemFirst = document.createElement("br");
const breakElemSecond = document.createElement("br");
this.infoDiv.appendChild(breakElemFirst);
this.infoDiv.appendChild(breakElemSecond);
const reportIssueLink = document.createElement("a");
reportIssueLink.innerText = LibraryInfoStrings.reportIssues();
reportIssueLink.href = "https://github.com/mebjas/html5-qrcode/issues";
reportIssueLink.target = "new";
reportIssueLink.style.color = "white";
this.infoDiv.appendChild(reportIssueLink);
parent.appendChild(this.infoDiv);
}
public show() {
this.infoDiv.style.display = "block";
}
public hide() {
this.infoDiv.style.display = "none";
}
}
class LibraryInfoIcon {
private infoIcon: HTMLImageElement;
private onTapIn: OnClickListener0;
private onTapOut: OnClickListener0;
private isShowingInfoIcon: boolean = true;
constructor(onTapIn: OnClickListener0, onTapOut: OnClickListener0) {
this.onTapIn = onTapIn;
this.onTapOut = onTapOut;
this.infoIcon = document.createElement("img");
}
public renderInto(parent: HTMLElement) {
this.infoIcon.alt = "Info icon";
this.infoIcon.src = ASSET_INFO_ICON_16PX;
this.infoIcon.style.position = "absolute";
this.infoIcon.style.top = "4px";
this.infoIcon.style.right = "4px";
this.infoIcon.style.opacity = "0.6";
this.infoIcon.style.cursor = "pointer";
this.infoIcon.style.zIndex = "2";
this.infoIcon.style.width = "16px";
this.infoIcon.style.height = "16px";
this.infoIcon.onmouseover = (_) => this.onHoverIn();
this.infoIcon.onmouseout = (_) => this.onHoverOut();
this.infoIcon.onclick = (_) => this.onClick();
parent.appendChild(this.infoIcon);
}
private onHoverIn() {
if (this.isShowingInfoIcon) {
this.infoIcon.style.opacity = "1";
}
}
private onHoverOut() {
if (this.isShowingInfoIcon) {
this.infoIcon.style.opacity = "0.6";
}
}
private onClick() {
if (this.isShowingInfoIcon) {
this.isShowingInfoIcon = false;
this.onTapIn();
this.infoIcon.src = ASSET_CLOSE_ICON_16PX;
this.infoIcon.style.opacity = "1";
} else {
this.isShowingInfoIcon = true;
this.onTapOut();
this.infoIcon.src = ASSET_INFO_ICON_16PX;
this.infoIcon.style.opacity = "0.6";
}
}
}
export class LibraryInfoContainer {
private infoDiv: LibraryInfoDiv;
private infoIcon: LibraryInfoIcon;
constructor() {
this.infoDiv = new LibraryInfoDiv();
this.infoIcon = new LibraryInfoIcon(() => {
this.infoDiv.show();
}, () => {
this.infoDiv.hide();
});
}
public renderInto(parent: HTMLElement) {
this.infoDiv.renderInto(parent);
this.infoIcon.renderInto(parent);
}
}
//#endregion

16
node_modules/html5-qrcode/src/ui/scanner/base.d.ts generated vendored Normal file
View File

@@ -0,0 +1,16 @@
export declare class PublicUiElementIdAndClasses {
static ALL_ELEMENT_CLASS: string;
static CAMERA_PERMISSION_BUTTON_ID: string;
static CAMERA_START_BUTTON_ID: string;
static CAMERA_STOP_BUTTON_ID: string;
static TORCH_BUTTON_ID: string;
static CAMERA_SELECTION_SELECT_ID: string;
static FILE_SELECTION_BUTTON_ID: string;
static ZOOM_SLIDER_ID: string;
static SCAN_TYPE_CHANGE_ANCHOR_ID: string;
static TORCH_BUTTON_CLASS_TORCH_ON: string;
static TORCH_BUTTON_CLASS_TORCH_OFF: string;
}
export declare class BaseUiElementFactory {
static createElement<Type extends HTMLElement>(elementType: string, elementId: string): Type;
}

81
node_modules/html5-qrcode/src/ui/scanner/base.ts generated vendored Normal file
View File

@@ -0,0 +1,81 @@
/**
* @fileoverview
* Contains base classes for different UI elements used in the scanner.
*
* @author mebjas <minhazav@gmail.com>
*
* The word "QR Code" is registered trademark of DENSO WAVE INCORPORATED
* http://www.denso-wave.com/qrcode/faqpatent-e.html
*/
/**
* Id and classes of UI elements, for developers to configure the theme of
* end to end scanner using css.
*/
export class PublicUiElementIdAndClasses {
//#region Public list of element IDs for major UI elements.
/** Class name added to all major UI elements used in scanner. */
static ALL_ELEMENT_CLASS = "html5-qrcode-element";
/** Id of the camera permission button. */
static CAMERA_PERMISSION_BUTTON_ID = "html5-qrcode-button-camera-permission";
/** Id of the camera start button. */
static CAMERA_START_BUTTON_ID = "html5-qrcode-button-camera-start";
/** Id of the camera stop button. */
static CAMERA_STOP_BUTTON_ID = "html5-qrcode-button-camera-stop";
/** Id of the torch button. */
static TORCH_BUTTON_ID = "html5-qrcode-button-torch";
/** Id of the select element used for camera selection. */
static CAMERA_SELECTION_SELECT_ID = "html5-qrcode-select-camera";
/** Id of the button used for file selection. */
static FILE_SELECTION_BUTTON_ID = "html5-qrcode-button-file-selection";
/** Id of the range input for zoom. */
static ZOOM_SLIDER_ID = "html5-qrcode-input-range-zoom";
/**
* Id of the anchor {@code <a>} element used for swapping between file scan
* and camera scan.
*/
static SCAN_TYPE_CHANGE_ANCHOR_ID = "html5-qrcode-anchor-scan-type-change";
//#endregion
//#region List of classes for specific use-cases.
/** Torch button class when torch is ON. */
static TORCH_BUTTON_CLASS_TORCH_ON = "html5-qrcode-button-torch-on";
/** Torch button class when torch is OFF. */
static TORCH_BUTTON_CLASS_TORCH_OFF = "html5-qrcode-button-torch-off";
//#endregion
}
/**
* Factory class for creating different base UI elements used by the scanner.
*/
export class BaseUiElementFactory {
/**
* Creates {@link HTMLElement} of given {@param elementType}.
*
* @param elementType Type of element to create, example
*/
public static createElement<Type extends HTMLElement>(
elementType: string, elementId: string): Type {
let element: Type = <Type>(document.createElement(elementType));
element.id = elementId;
element.classList.add(PublicUiElementIdAndClasses.ALL_ELEMENT_CLASS);
if (elementType === "button") {
element.setAttribute("type", "button");
}
return element;
}
}

View File

@@ -0,0 +1,17 @@
import { CameraDevice } from "../../camera/core";
export declare class CameraSelectionUi {
private readonly selectElement;
private readonly options;
private readonly cameras;
private constructor();
private render;
disable(): void;
isDisabled(): boolean;
enable(): void;
getValue(): string;
hasValue(value: string): boolean;
setValue(value: string): void;
hasSingleItem(): boolean;
numCameras(): number;
static create(parentElement: HTMLElement, cameras: Array<CameraDevice>): CameraSelectionUi;
}

View File

@@ -0,0 +1,129 @@
/**
* @fileoverview
* File for camera selection UI.
*
* @author mebjas <minhazav@gmail.com>
*
* The word "QR Code" is registered trademark of DENSO WAVE INCORPORATED
* http://www.denso-wave.com/qrcode/faqpatent-e.html
*/
import { CameraDevice } from "../../camera/core";
import {
BaseUiElementFactory,
PublicUiElementIdAndClasses
} from "./base";
import {
Html5QrcodeScannerStrings
} from "../../strings";
/** Class for rendering and handling camera selection UI. */
export class CameraSelectionUi {
private readonly selectElement: HTMLSelectElement;
private readonly options: Array<HTMLOptionElement>;
private readonly cameras: Array<CameraDevice>;
private constructor(cameras: Array<CameraDevice>) {
this.selectElement = BaseUiElementFactory
.createElement<HTMLSelectElement>(
"select",
PublicUiElementIdAndClasses.CAMERA_SELECTION_SELECT_ID);
this.cameras = cameras;
this.options = [];
}
/*eslint complexity: ["error", 10]*/
private render(
parentElement: HTMLElement) {
const cameraSelectionContainer = document.createElement("span");
cameraSelectionContainer.style.marginRight = "10px";
const numCameras = this.cameras.length;
if (numCameras === 0) {
throw new Error("No cameras found");
}
if (numCameras === 1) {
// If only one camera is found, don't show camera selection.
cameraSelectionContainer.style.display = "none";
} else {
// Otherwise, show the number of cameras found as well.
const selectCameraString = Html5QrcodeScannerStrings.selectCamera();
cameraSelectionContainer.innerText
= `${selectCameraString} (${this.cameras.length}) `;
}
let anonymousCameraId = 1;
for (const camera of this.cameras) {
const value = camera.id;
let name = camera.label == null ? value : camera.label;
// If no name is returned by the browser, replace it with custom
// camera label with a count.
if (!name || name === "") {
name = [
Html5QrcodeScannerStrings.anonymousCameraPrefix(),
anonymousCameraId++
].join(" ");
}
const option = document.createElement("option");
option.value = value;
option.innerText = name;
this.options.push(option);
this.selectElement.appendChild(option);
}
cameraSelectionContainer.appendChild(this.selectElement);
parentElement.appendChild(cameraSelectionContainer);
}
//#region Public APIs
public disable() {
this.selectElement.disabled = true;
}
public isDisabled() {
return this.selectElement.disabled === true;
}
public enable() {
this.selectElement.disabled = false;
}
public getValue(): string {
return this.selectElement.value;
}
public hasValue(value: string): boolean {
for (const option of this.options) {
if (option.value === value) {
return true;
}
}
return false;
}
public setValue(value: string) {
if (!this.hasValue(value)) {
throw new Error(`${value} is not present in the camera list.`);
}
this.selectElement.value = value;
}
public hasSingleItem() {
return this.cameras.length === 1;
}
public numCameras() {
return this.cameras.length;
}
//#endregion
/** Creates instance of {@link CameraSelectionUi} and renders it. */
public static create(
parentElement: HTMLElement,
cameras: Array<CameraDevice>): CameraSelectionUi {
let cameraSelectUi = new CameraSelectionUi(cameras);
cameraSelectUi.render(parentElement);
return cameraSelectUi;
}
}

View File

@@ -0,0 +1,16 @@
export type OnCameraZoomValueChangeCallback = (zoomValue: number) => void;
export declare class CameraZoomUi {
private zoomElementContainer;
private rangeInput;
private rangeText;
private onChangeCallback;
private constructor();
private render;
private onValueChange;
setValues(minValue: number, maxValue: number, defaultValue: number, step: number): void;
show(): void;
hide(): void;
setOnCameraZoomValueChangeCallback(onChangeCallback: OnCameraZoomValueChangeCallback): void;
removeOnCameraZoomValueChangeCallback(): void;
static create(parentElement: HTMLElement, renderOnCreate: boolean): CameraZoomUi;
}

View File

@@ -0,0 +1,126 @@
/**
* @fileoverview
* File for camera zooming UI.
*
* @author mebjas <minhazav@gmail.com>
*
* The word "QR Code" is registered trademark of DENSO WAVE INCORPORATED
* http://www.denso-wave.com/qrcode/faqpatent-e.html
*/
import {
BaseUiElementFactory,
PublicUiElementIdAndClasses
} from "./base";
import { Html5QrcodeScannerStrings } from "../../strings";
/** Callback when zoom value changes with the slider UI. */
export type OnCameraZoomValueChangeCallback = (zoomValue: number) => void;
/** Class for creating and managing zoom slider UI. */
export class CameraZoomUi {
private zoomElementContainer: HTMLDivElement;
private rangeInput: HTMLInputElement;
private rangeText: HTMLSpanElement;
private onChangeCallback: OnCameraZoomValueChangeCallback | null = null;
private constructor() {
this.zoomElementContainer = document.createElement("div");
this.rangeInput = BaseUiElementFactory.createElement<HTMLInputElement>(
"input", PublicUiElementIdAndClasses.ZOOM_SLIDER_ID);
this.rangeInput.type = "range";
this.rangeText = document.createElement("span");
// default values.
this.rangeInput.min = "1";
this.rangeInput.max = "5";
this.rangeInput.value = "1";
this.rangeInput.step = "0.1";
}
private render(
parentElement: HTMLElement,
renderOnCreate: boolean) {
// Style for the range slider.
this.zoomElementContainer.style.display
= renderOnCreate ? "block" : "none";
this.zoomElementContainer.style.padding = "5px 10px";
this.zoomElementContainer.style.textAlign = "center";
parentElement.appendChild(this.zoomElementContainer);
this.rangeInput.style.display = "inline-block";
this.rangeInput.style.width = "50%";
this.rangeInput.style.height = "5px";
this.rangeInput.style.background = "#d3d3d3";
this.rangeInput.style.outline = "none";
this.rangeInput.style.opacity = "0.7";
let zoomString = Html5QrcodeScannerStrings.zoom();
this.rangeText.innerText = `${this.rangeInput.value}x ${zoomString}`;
this.rangeText.style.marginRight = "10px";
// Bind values.
let $this = this;
this.rangeInput.addEventListener("input", () => $this.onValueChange());
this.rangeInput.addEventListener("change", () => $this.onValueChange());
this.zoomElementContainer.appendChild(this.rangeInput);
this.zoomElementContainer.appendChild(this.rangeText);
}
private onValueChange() {
let zoomString = Html5QrcodeScannerStrings.zoom();
this.rangeText.innerText = `${this.rangeInput.value}x ${zoomString}`;
if (this.onChangeCallback) {
this.onChangeCallback(parseFloat(this.rangeInput.value));
}
}
//#region Public APIs
public setValues(
minValue: number,
maxValue: number,
defaultValue: number,
step: number) {
this.rangeInput.min = minValue.toString();
this.rangeInput.max = maxValue.toString();
this.rangeInput.step = step.toString();
this.rangeInput.value = defaultValue.toString();
this.onValueChange();
}
public show() {
this.zoomElementContainer.style.display = "block";
}
public hide() {
this.zoomElementContainer.style.display = "none";
}
public setOnCameraZoomValueChangeCallback(
onChangeCallback: OnCameraZoomValueChangeCallback) {
this.onChangeCallback = onChangeCallback;
}
public removeOnCameraZoomValueChangeCallback() {
this.onChangeCallback = null;
}
//#endregion
/**
* Creates and renders the zoom slider if {@code renderOnCreate} is
* {@code true}.
*/
public static create(
parentElement: HTMLElement,
renderOnCreate: boolean): CameraZoomUi {
let cameraZoomUi = new CameraZoomUi();
cameraZoomUi.render(parentElement, renderOnCreate);
return cameraZoomUi;
}
}

View File

@@ -0,0 +1,19 @@
export type OnFileSelected = (file: File) => void;
export declare class FileSelectionUi {
private readonly fileBasedScanRegion;
private readonly fileScanInput;
private readonly fileSelectionButton;
private constructor();
hide(): void;
show(): void;
isShowing(): boolean;
resetValue(): void;
private createFileBasedScanRegion;
private fileBasedScanRegionDefaultBorder;
private fileBasedScanRegionActiveBorder;
private createDragAndDropMessage;
private setImageNameToButton;
private setInitialValueToButton;
private getFileScanInputId;
static create(parentElement: HTMLDivElement, showOnRender: boolean, onFileSelected: OnFileSelected): FileSelectionUi;
}

View File

@@ -0,0 +1,263 @@
/**
* @fileoverview
* File for file selection UI handling.
*
* @author mebjas <minhazav@gmail.com>
*
* The word "QR Code" is registered trademark of DENSO WAVE INCORPORATED
* http://www.denso-wave.com/qrcode/faqpatent-e.html
*/
import {Html5QrcodeScannerStrings} from "../../strings";
import {
BaseUiElementFactory,
PublicUiElementIdAndClasses
} from "./base";
/**
* Interface for callback when a file is selected by user using the button.
*/
export type OnFileSelected = (file: File) => void;
/** UI class for file selection handling. */
export class FileSelectionUi {
private readonly fileBasedScanRegion: HTMLDivElement;
private readonly fileScanInput: HTMLInputElement;
private readonly fileSelectionButton: HTMLButtonElement;
/** Creates object and renders. */
private constructor(
parentElement: HTMLDivElement,
showOnRender: boolean,
onFileSelected: OnFileSelected) {
this.fileBasedScanRegion = this.createFileBasedScanRegion();
this.fileBasedScanRegion.style.display
= showOnRender ? "block" : "none";
parentElement.appendChild(this.fileBasedScanRegion);
let fileScanLabel = document.createElement("label");
fileScanLabel.setAttribute("for", this.getFileScanInputId());
fileScanLabel.style.display = "inline-block";
this.fileBasedScanRegion.appendChild(fileScanLabel);
this.fileSelectionButton
= BaseUiElementFactory.createElement<HTMLButtonElement>(
"button",
PublicUiElementIdAndClasses.FILE_SELECTION_BUTTON_ID);
this.setInitialValueToButton();
// Bind click events with the label element.
this.fileSelectionButton.addEventListener("click", (_) => {
fileScanLabel.click();
});
fileScanLabel.append(this.fileSelectionButton);
this.fileScanInput
= BaseUiElementFactory.createElement<HTMLInputElement>(
"input", this.getFileScanInputId());
this.fileScanInput.type = "file";
this.fileScanInput.accept = "image/*";
this.fileScanInput.style.display = "none";
fileScanLabel.appendChild(this.fileScanInput);
let $this = this;
/*eslint complexity: ["error", 5]*/
this.fileScanInput.addEventListener("change", (e: Event) => {
if (e == null || e.target == null) {
return;
}
let target: HTMLInputElement = e.target as HTMLInputElement;
if (target.files && target.files.length === 0) {
return;
}
let fileList: FileList = target.files!;
const file: File = fileList[0];
let fileName = file.name;
$this.setImageNameToButton(fileName);
onFileSelected(file);
});
// Render drag and drop label
let dragAndDropMessage = this.createDragAndDropMessage();
this.fileBasedScanRegion.appendChild(dragAndDropMessage);
this.fileBasedScanRegion.addEventListener("dragenter", function(event) {
$this.fileBasedScanRegion.style.border
= $this.fileBasedScanRegionActiveBorder();
event.stopPropagation();
event.preventDefault();
});
this.fileBasedScanRegion.addEventListener("dragleave", function(event) {
$this.fileBasedScanRegion.style.border
= $this.fileBasedScanRegionDefaultBorder();
event.stopPropagation();
event.preventDefault();
});
this.fileBasedScanRegion.addEventListener("dragover", function(event) {
$this.fileBasedScanRegion.style.border
= $this.fileBasedScanRegionActiveBorder();
event.stopPropagation();
event.preventDefault();
});
/*eslint complexity: ["error", 10]*/
this.fileBasedScanRegion.addEventListener("drop", function(event) {
event.stopPropagation();
event.preventDefault();
$this.fileBasedScanRegion.style.border
= $this.fileBasedScanRegionDefaultBorder();
var dataTransfer = event.dataTransfer;
if (dataTransfer) {
let files = dataTransfer.files;
if (!files || files.length === 0) {
return;
}
let isAnyFileImage = false;
for (let i = 0; i < files.length; ++i) {
let file = files.item(i);
if (!file) {
continue;
}
let imageType = /image.*/;
// Only process images.
if (!file.type.match(imageType)) {
continue;
}
isAnyFileImage = true;
let fileName = file.name;
$this.setImageNameToButton(fileName);
onFileSelected(file);
dragAndDropMessage.innerText
= Html5QrcodeScannerStrings.dragAndDropMessage();
break;
}
// None of the files were images.
if (!isAnyFileImage) {
dragAndDropMessage.innerText
= Html5QrcodeScannerStrings
.dragAndDropMessageOnlyImages();
}
}
});
}
//#region Public APIs.
/** Hide the file selection UI. */
public hide() {
this.fileBasedScanRegion.style.display = "none";
this.fileScanInput.disabled = true;
}
/** Show the file selection UI. */
public show() {
this.fileBasedScanRegion.style.display = "block";
this.fileScanInput.disabled = false;
}
/** Returns {@code true} if UI container is displayed. */
public isShowing(): boolean {
return this.fileBasedScanRegion.style.display === "block";
}
/** Reset the file selection value */
public resetValue() {
this.fileScanInput.value = "";
this.setInitialValueToButton();
}
//#endregion
//#region private APIs
private createFileBasedScanRegion(): HTMLDivElement {
let fileBasedScanRegion = document.createElement("div");
fileBasedScanRegion.style.textAlign = "center";
fileBasedScanRegion.style.margin = "auto";
fileBasedScanRegion.style.width = "80%";
fileBasedScanRegion.style.maxWidth = "600px";
fileBasedScanRegion.style.border
= this.fileBasedScanRegionDefaultBorder();
fileBasedScanRegion.style.padding = "10px";
fileBasedScanRegion.style.marginBottom = "10px";
return fileBasedScanRegion;
}
private fileBasedScanRegionDefaultBorder() {
return "6px dashed #ebebeb";
}
/** Border when a file is being dragged over the file scan region. */
private fileBasedScanRegionActiveBorder() {
return "6px dashed rgb(153 151 151)";
}
private createDragAndDropMessage(): HTMLDivElement {
let dragAndDropMessage = document.createElement("div");
dragAndDropMessage.innerText
= Html5QrcodeScannerStrings.dragAndDropMessage();
dragAndDropMessage.style.fontWeight = "400";
return dragAndDropMessage;
}
private setImageNameToButton(imageFileName: string) {
const MAX_CHARS = 20;
if (imageFileName.length > MAX_CHARS) {
// Strip first 8
// Strip last 8
// Add 4 dots
let start8Chars = imageFileName.substring(0, 8);
let length = imageFileName.length;
let last8Chars = imageFileName.substring(length - 8, length);
imageFileName = `${start8Chars}....${last8Chars}`;
}
let newText = Html5QrcodeScannerStrings.fileSelectionChooseAnother()
+ " - "
+ imageFileName;
this.fileSelectionButton.innerText = newText;
}
private setInitialValueToButton() {
let initialText = Html5QrcodeScannerStrings.fileSelectionChooseImage()
+ " - "
+ Html5QrcodeScannerStrings.fileSelectionNoImageSelected();
this.fileSelectionButton.innerText = initialText;
}
private getFileScanInputId(): string {
return "html5-qrcode-private-filescan-input";
}
//#endregion
/**
* Creates a file selection UI and renders.
*
* @param parentElement parent div element to render the UI to.
* @param showOnRender if {@code true}, the UI will be shown upon render
* else hidden.
* @param onFileSelected callback to be called when file selection changes.
*
* @returns Instance of {@code FileSelectionUi}.
*/
public static create(
parentElement: HTMLDivElement,
showOnRender: boolean,
onFileSelected: OnFileSelected): FileSelectionUi {
let button = new FileSelectionUi(
parentElement, showOnRender, onFileSelected);
return button;
}
}

View File

@@ -0,0 +1,11 @@
import { Html5QrcodeScanType } from "../../core";
export declare class ScanTypeSelector {
private supportedScanTypes;
constructor(supportedScanTypes?: Array<Html5QrcodeScanType> | []);
getDefaultScanType(): Html5QrcodeScanType;
hasMoreThanOneScanType(): boolean;
isCameraScanRequired(): boolean;
static isCameraScanType(scanType: Html5QrcodeScanType): boolean;
static isFileScanType(scanType: Html5QrcodeScanType): boolean;
private validateAndReturnScanTypes;
}

View File

@@ -0,0 +1,94 @@
/**
* @fileoverview
* Util class to help with scan type selection in scanner class.
*
* @author mebjas <minhazav@gmail.com>
*
* The word "QR Code" is registered trademark of DENSO WAVE INCORPORATED
* http://www.denso-wave.com/qrcode/faqpatent-e.html
*/
import {
Html5QrcodeScanType,
Html5QrcodeConstants
} from "../../core";
/** Util class to help with scan type selection in scanner class. */
export class ScanTypeSelector {
private supportedScanTypes: Array<Html5QrcodeScanType>;
constructor(supportedScanTypes?: Array<Html5QrcodeScanType> | []) {
this.supportedScanTypes = this.validateAndReturnScanTypes(
supportedScanTypes);
}
/**
* Returns the default {@link Html5QrcodeScanType} scanner UI should be
* created with.
*/
public getDefaultScanType(): Html5QrcodeScanType {
return this.supportedScanTypes[0];
}
/**
* Returns {@code true} if more than one {@link Html5QrcodeScanType} are
* set.
*/
public hasMoreThanOneScanType(): boolean {
return this.supportedScanTypes.length > 1;
}
/** Returns {@code true} if camera scan is required at all. */
public isCameraScanRequired(): boolean {
for (const scanType of this.supportedScanTypes) {
if (ScanTypeSelector.isCameraScanType(scanType)) {
return true;
}
}
return false;
}
/** Returns {@code true} is {@code scanType} is camera based. */
public static isCameraScanType(scanType: Html5QrcodeScanType): boolean {
return scanType === Html5QrcodeScanType.SCAN_TYPE_CAMERA;
}
/** Returns {@code true} is {@code scanType} is file based. */
public static isFileScanType(scanType: Html5QrcodeScanType): boolean {
return scanType === Html5QrcodeScanType.SCAN_TYPE_FILE;
}
//#region Private methods.
/**
* Validates the input {@code supportedScanTypes}.
*
* Fails early if the config values is incorrectly set.
*/
private validateAndReturnScanTypes(
supportedScanTypes?:Array<Html5QrcodeScanType>):
Array<Html5QrcodeScanType> {
// If not set, use the default values and order.
if (!supportedScanTypes || supportedScanTypes.length === 0) {
return Html5QrcodeConstants.DEFAULT_SUPPORTED_SCAN_TYPE;
}
// Fail if more than expected number of values exist.
let maxExpectedValues
= Html5QrcodeConstants.DEFAULT_SUPPORTED_SCAN_TYPE.length;
if (supportedScanTypes.length > maxExpectedValues) {
throw `Max ${maxExpectedValues} values expected for `
+ "supportedScanTypes";
}
// Fail if any of the scan types are not supported.
for (const scanType of supportedScanTypes) {
if (!Html5QrcodeConstants.DEFAULT_SUPPORTED_SCAN_TYPE
.includes(scanType)) {
throw `Unsupported scan type ${scanType}`;
}
}
return supportedScanTypes;
}
//#endregion
}

View File

@@ -0,0 +1,28 @@
import { BooleanCameraCapability } from "../../camera/core";
export type OnTorchActionFailureCallback = (failureMessage: string) => void;
interface TorchButtonController {
disable(): void;
enable(): void;
setText(text: string): void;
}
export interface TorchButtonOptions {
display: string;
marginLeft: string;
}
export declare class TorchButton implements TorchButtonController {
private readonly torchButton;
private readonly onTorchActionFailureCallback;
private torchController;
private constructor();
private render;
updateTorchCapability(torchCapability: BooleanCameraCapability): void;
getTorchButton(): HTMLButtonElement;
hide(): void;
show(): void;
disable(): void;
enable(): void;
setText(text: string): void;
reset(): void;
static create(parentElement: HTMLElement, torchCapability: BooleanCameraCapability, torchButtonOptions: TorchButtonOptions, onTorchActionFailureCallback: OnTorchActionFailureCallback): TorchButton;
}
export {};

View File

@@ -0,0 +1,227 @@
/**
* @fileoverview
* File for torch related UI components and handling.
*
* @author mebjas <minhazav@gmail.com>
*
* The word "QR Code" is registered trademark of DENSO WAVE INCORPORATED
* http://www.denso-wave.com/qrcode/faqpatent-e.html
*/
import { BooleanCameraCapability } from "../../camera/core";
import { Html5QrcodeScannerStrings } from "../../strings";
import {
BaseUiElementFactory,
PublicUiElementIdAndClasses
} from "./base";
/**
* Interface for callback that will be called in case of torch action failures.
*/
export type OnTorchActionFailureCallback = (failureMessage: string) => void;
/** Interface for controlling torch button. */
interface TorchButtonController {
disable(): void;
enable(): void;
setText(text: string): void;
}
/** Controller class for handling torch / flash. */
class TorchController {
private readonly torchCapability: BooleanCameraCapability;
private readonly buttonController: TorchButtonController;
private readonly onTorchActionFailureCallback: OnTorchActionFailureCallback;
// Mutable states.
private isTorchOn: boolean = false;
constructor(
torchCapability: BooleanCameraCapability,
buttonController: TorchButtonController,
onTorchActionFailureCallback: OnTorchActionFailureCallback) {
this.torchCapability = torchCapability;
this.buttonController = buttonController;
this.onTorchActionFailureCallback = onTorchActionFailureCallback;
}
/** Returns {@code true} if torch is enabled. */
public isTorchEnabled(): boolean {
return this.isTorchOn;
}
/**
* Flips the state of the torch.
*
* <p> Turns torch On if current state is Off and vice-versa.
* <p> Modifies the UI state accordingly.
*
* @returns Promise that finishes when the async action is done.
*/
public async flipState(): Promise<void> {
this.buttonController.disable();
let isTorchOnExpected = !this.isTorchOn;
try {
await this.torchCapability.apply(isTorchOnExpected);
this.updateUiBasedOnLatestSettings(
this.torchCapability.value()!, isTorchOnExpected);
} catch (error) {
this.propagateFailure(isTorchOnExpected, error);
this.buttonController.enable();
}
}
private updateUiBasedOnLatestSettings(
isTorchOn: boolean,
isTorchOnExpected: boolean) {
if (isTorchOn === isTorchOnExpected) {
// Action succeeded, flip the state.
this.buttonController.setText(isTorchOnExpected
? Html5QrcodeScannerStrings.torchOffButton()
: Html5QrcodeScannerStrings.torchOnButton());
this.isTorchOn = isTorchOnExpected;
} else {
// Torch didn't get set as expected.
// Show warning.
this.propagateFailure(isTorchOnExpected);
}
this.buttonController.enable();
}
private propagateFailure(
isTorchOnExpected: boolean, error?: any) {
let errorMessage = isTorchOnExpected
? Html5QrcodeScannerStrings.torchOnFailedMessage()
: Html5QrcodeScannerStrings.torchOffFailedMessage();
if (error) {
errorMessage += "; Error = " + error;
}
this.onTorchActionFailureCallback(errorMessage);
}
/**
* Resets the state.
*
* <p>Note: Doesn't turn off the torch implicitly.
*/
public reset() {
this.isTorchOn = false;
}
}
/** Options for creating torch button. */
export interface TorchButtonOptions {
display: string;
marginLeft: string;
}
/** Helper class for creating Torch UI component. */
export class TorchButton implements TorchButtonController {
private readonly torchButton: HTMLButtonElement;
private readonly onTorchActionFailureCallback: OnTorchActionFailureCallback;
private torchController: TorchController;
private constructor(
torchCapability: BooleanCameraCapability,
onTorchActionFailureCallback: OnTorchActionFailureCallback) {
this.onTorchActionFailureCallback = onTorchActionFailureCallback;
this.torchButton
= BaseUiElementFactory.createElement<HTMLButtonElement>(
"button", PublicUiElementIdAndClasses.TORCH_BUTTON_ID);
this.torchController = new TorchController(
torchCapability,
/* buttonController= */ this,
onTorchActionFailureCallback);
}
private render(
parentElement: HTMLElement, torchButtonOptions: TorchButtonOptions) {
this.torchButton.innerText
= Html5QrcodeScannerStrings.torchOnButton();
this.torchButton.style.display = torchButtonOptions.display;
this.torchButton.style.marginLeft = torchButtonOptions.marginLeft;
let $this = this;
this.torchButton.addEventListener("click", async (_) => {
await $this.torchController.flipState();
if ($this.torchController.isTorchEnabled()) {
$this.torchButton.classList.remove(
PublicUiElementIdAndClasses.TORCH_BUTTON_CLASS_TORCH_OFF);
$this.torchButton.classList.add(
PublicUiElementIdAndClasses.TORCH_BUTTON_CLASS_TORCH_ON);
} else {
$this.torchButton.classList.remove(
PublicUiElementIdAndClasses.TORCH_BUTTON_CLASS_TORCH_ON);
$this.torchButton.classList.add(
PublicUiElementIdAndClasses.TORCH_BUTTON_CLASS_TORCH_OFF);
}
});
parentElement.appendChild(this.torchButton);
}
public updateTorchCapability(torchCapability: BooleanCameraCapability) {
this.torchController = new TorchController(
torchCapability,
/* buttonController= */ this,
this.onTorchActionFailureCallback);
}
/** Returns the torch button. */
public getTorchButton(): HTMLButtonElement {
return this.torchButton;
}
public hide() {
this.torchButton.style.display = "none";
}
public show() {
this.torchButton.style.display = "inline-block";
}
disable(): void {
this.torchButton.disabled = true;
}
enable(): void {
this.torchButton.disabled = false;
}
setText(text: string): void {
this.torchButton.innerText = text;
}
/**
* Resets the state.
*
* <p>Note: Doesn't turn off the torch implicitly.
*/
public reset() {
this.torchButton.innerText = Html5QrcodeScannerStrings.torchOnButton();
this.torchController.reset();
}
/**
* Factory method for creating torch button.
*
* @param parentElement parent HTML element to render torch button into
* @param torchCapability torch capability of the camera
* @param torchButtonOptions options for creating torch
* @param onTorchActionFailureCallback callback to be called in case of
* torch action failure.
*/
public static create(
parentElement: HTMLElement,
torchCapability: BooleanCameraCapability,
torchButtonOptions: TorchButtonOptions,
onTorchActionFailureCallback: OnTorchActionFailureCallback)
: TorchButton {
let button = new TorchButton(
torchCapability, onTorchActionFailureCallback);
button.render(parentElement, torchButtonOptions);
return button;
}
}

4
node_modules/html5-qrcode/src/utils.d.ts generated vendored Normal file
View File

@@ -0,0 +1,4 @@
import { Logger } from "./core";
export declare class VideoConstraintsUtil {
static isMediaStreamConstraintsValid(videoConstraints: MediaTrackConstraints, logger: Logger): boolean;
}

53
node_modules/html5-qrcode/src/utils.ts generated vendored Normal file
View File

@@ -0,0 +1,53 @@
/**
* @fileoverview
* Utils used by {@class Html5Qrcode} & {@class Html5QrcodeScanner}
*
* @author mebjas <minhazav@gmail.com>
*
* The word "QR Code" is registered trademark of DENSO WAVE INCORPORATED
* http://www.denso-wave.com/qrcode/faqpatent-e.html
*/
import { Logger } from "./core";
/**
* Utils around {@interface MediaTrackConstraints} for video.
*/
export class VideoConstraintsUtil {
public static isMediaStreamConstraintsValid(
videoConstraints: MediaTrackConstraints,
logger: Logger): boolean {
if (typeof videoConstraints !== "object") {
const typeofVideoConstraints = typeof videoConstraints;
logger.logError(
"videoConstraints should be of type object, the "
+ `object passed is of type ${typeofVideoConstraints}.`,
/* experimental= */ true);
return false;
}
// TODO(mebjas): Make this validity check more sophisticuated
// Following keys are audio controls, audio controls are not supported.
const bannedKeys = [
"autoGainControl",
"channelCount",
"echoCancellation",
"latency",
"noiseSuppression",
"sampleRate",
"sampleSize",
"volume"
];
const bannedkeysSet = new Set(bannedKeys);
const keysInVideoConstraints = Object.keys(videoConstraints);
for (const key of keysInVideoConstraints) {
if (bannedkeysSet.has(key)) {
logger.logError(
`${key} is not supported videoConstaints.`,
/* experimental= */ true);
return false;
}
}
return true;
}
}

View File

@@ -0,0 +1,15 @@
import { QrcodeResult, Html5QrcodeSupportedFormats, Logger, QrcodeDecoderAsync } from "./core";
export declare class ZXingHtml5QrcodeDecoder implements QrcodeDecoderAsync {
private readonly formatMap;
private readonly reverseFormatMap;
private hints;
private verbose;
private logger;
constructor(requestedFormats: Array<Html5QrcodeSupportedFormats>, verbose: boolean, logger: Logger);
decodeAsync(canvas: HTMLCanvasElement): Promise<QrcodeResult>;
private decode;
private createReverseFormatMap;
private toHtml5QrcodeSupportedFormats;
private createZXingFormats;
private createDebugData;
}

View File

@@ -0,0 +1,155 @@
/**
* @fileoverview
* {@interface QrcodeDecoder} wrapper around ZXing library.
*
* @author mebjas <minhazav@gmail.com>
*
* ZXing library forked from https://github.com/zxing-js/library.
*
* The word "QR Code" is registered trademark of DENSO WAVE INCORPORATED
* http://www.denso-wave.com/qrcode/faqpatent-e.html
*/
import * as ZXing from "../third_party/zxing-js.umd";
import {
QrcodeResult,
QrcodeResultDebugData,
QrcodeResultFormat,
Html5QrcodeSupportedFormats,
Logger,
QrcodeDecoderAsync
} from "./core";
/**
* ZXing based Code decoder.
*/
export class ZXingHtml5QrcodeDecoder implements QrcodeDecoderAsync {
private readonly formatMap: Map<Html5QrcodeSupportedFormats, any>
= new Map([
[Html5QrcodeSupportedFormats.QR_CODE, ZXing.BarcodeFormat.QR_CODE ],
[Html5QrcodeSupportedFormats.AZTEC, ZXing.BarcodeFormat.AZTEC ],
[Html5QrcodeSupportedFormats.CODABAR, ZXing.BarcodeFormat.CODABAR ],
[Html5QrcodeSupportedFormats.CODE_39, ZXing.BarcodeFormat.CODE_39 ],
[Html5QrcodeSupportedFormats.CODE_93, ZXing.BarcodeFormat.CODE_93 ],
[
Html5QrcodeSupportedFormats.CODE_128,
ZXing.BarcodeFormat.CODE_128 ],
[
Html5QrcodeSupportedFormats.DATA_MATRIX,
ZXing.BarcodeFormat.DATA_MATRIX ],
[
Html5QrcodeSupportedFormats.MAXICODE,
ZXing.BarcodeFormat.MAXICODE ],
[Html5QrcodeSupportedFormats.ITF, ZXing.BarcodeFormat.ITF ],
[Html5QrcodeSupportedFormats.EAN_13, ZXing.BarcodeFormat.EAN_13 ],
[Html5QrcodeSupportedFormats.EAN_8, ZXing.BarcodeFormat.EAN_8 ],
[Html5QrcodeSupportedFormats.PDF_417, ZXing.BarcodeFormat.PDF_417 ],
[Html5QrcodeSupportedFormats.RSS_14, ZXing.BarcodeFormat.RSS_14 ],
[
Html5QrcodeSupportedFormats.RSS_EXPANDED,
ZXing.BarcodeFormat.RSS_EXPANDED ],
[Html5QrcodeSupportedFormats.UPC_A, ZXing.BarcodeFormat.UPC_A ],
[Html5QrcodeSupportedFormats.UPC_E, ZXing.BarcodeFormat.UPC_E ],
[
Html5QrcodeSupportedFormats.UPC_EAN_EXTENSION,
ZXing.BarcodeFormat.UPC_EAN_EXTENSION ]
]);
private readonly reverseFormatMap: Map<any, Html5QrcodeSupportedFormats>
= this.createReverseFormatMap();
private hints: Map<any, any>;
private verbose: boolean;
private logger: Logger;
public constructor(
requestedFormats: Array<Html5QrcodeSupportedFormats>,
verbose: boolean,
logger: Logger) {
if (!ZXing) {
throw "Use html5qrcode.min.js without edit, ZXing not found.";
}
this.verbose = verbose;
this.logger = logger;
const formats = this.createZXingFormats(requestedFormats);
const hints = new Map();
hints.set(ZXing.DecodeHintType.POSSIBLE_FORMATS, formats);
// TODO(minhazav): Make this configurable by developers.
hints.set(ZXing.DecodeHintType.TRY_HARDER, false);
this.hints = hints;
}
decodeAsync(canvas: HTMLCanvasElement): Promise<QrcodeResult> {
return new Promise((resolve, reject) => {
try {
resolve(this.decode(canvas));
} catch (error) {
reject(error);
}
});
}
private decode(canvas: HTMLCanvasElement): QrcodeResult {
// Note: Earlier we used to instantiate the zxingDecoder once as state
// of this class and use it for each scans. There seems to be some
// stateful bug in ZXing library around RSS_14 likely due to
// https://github.com/zxing-js/library/issues/211.
// Recreating a new instance per scan doesn't lead to performance issues
// and temporarily mitigates this issue.
// TODO(mebjas): Properly fix this issue in ZXing library.
const zxingDecoder = new ZXing.MultiFormatReader(
this.verbose, this.hints);
const luminanceSource
= new ZXing.HTMLCanvasElementLuminanceSource(canvas);
const binaryBitmap
= new ZXing.BinaryBitmap(
new ZXing.HybridBinarizer(luminanceSource));
let result = zxingDecoder.decode(binaryBitmap);
return {
text: result.text,
format: QrcodeResultFormat.create(
this.toHtml5QrcodeSupportedFormats(result.format)),
debugData: this.createDebugData()
};
}
private createReverseFormatMap(): Map<any, Html5QrcodeSupportedFormats> {
let result = new Map();
this.formatMap.forEach(
(value: any, key: Html5QrcodeSupportedFormats, _) => {
result.set(value, key);
});
return result;
}
private toHtml5QrcodeSupportedFormats(zxingFormat: any)
: Html5QrcodeSupportedFormats {
if (!this.reverseFormatMap.has(zxingFormat)) {
throw `reverseFormatMap doesn't have ${zxingFormat}`;
}
return this.reverseFormatMap.get(zxingFormat)!;
}
private createZXingFormats(
requestedFormats: Array<Html5QrcodeSupportedFormats>):
Array<any> {
let zxingFormats = [];
for (const requestedFormat of requestedFormats) {
if (this.formatMap.has(requestedFormat)) {
zxingFormats.push(
this.formatMap.get(requestedFormat));
} else {
this.logger.logError(`${requestedFormat} is not supported by`
+ "ZXingHtml5QrcodeShim");
}
}
return zxingFormats;
}
private createDebugData(): QrcodeResultDebugData {
return { decoderName: "zxing-js" };
}
}