angularx-qrcode
Version:
Simple QRCode module generator for Angular 4-21 and Ionic 3-8 using node-qrcode
302 lines (297 loc) • 12.9 kB
JavaScript
import * as i0 from '@angular/core';
import { EventEmitter, inject, Renderer2, ViewChild, Output, Input, ChangeDetectionStrategy, Component } from '@angular/core';
import { DomSanitizer } from '@angular/platform-browser';
import { toDataURL, toCanvas, toString } from 'qrcode';
class QRCodeComponent {
allowEmptyString = false;
colorDark = '#000000ff';
colorLight = '#ffffffff';
cssClass = 'qrcode';
elementType = 'canvas';
errorCorrectionLevel = 'M';
imageSrc;
imageHeight;
imageWidth;
margin = 4;
qrdata = '';
scale = 4;
version;
width = 10;
// Accessibility features introduced in 13.0.4+
alt;
ariaLabel;
title;
qrCodeURL = new EventEmitter();
qrcElement;
context = null;
centerImage;
renderer = inject(Renderer2);
sanitizer = inject(DomSanitizer);
async ngOnChanges() {
await this.createQRCode();
}
isValidQrCodeText(data) {
if (this.allowEmptyString === false) {
return !(typeof data === 'undefined' || data === '' || data === 'null' || data === null);
}
return !(typeof data === 'undefined');
}
toDataURL(qrCodeConfig) {
return new Promise((resolve, reject) => {
toDataURL(this.qrdata, qrCodeConfig, (err, url) => {
if (err) {
reject(err);
}
else {
resolve(url);
}
});
});
}
toCanvas(canvas, qrCodeConfig) {
return new Promise((resolve, reject) => {
toCanvas(canvas, this.qrdata, qrCodeConfig, (error) => {
if (error) {
reject(error);
}
else {
resolve('success');
}
});
});
}
toSVG(qrCodeConfig) {
return new Promise((resolve, reject) => {
toString(this.qrdata, qrCodeConfig, (err, url) => {
if (err) {
reject(err);
}
else {
resolve(url);
}
});
});
}
renderElement(element) {
for (const node of this.qrcElement.nativeElement.childNodes) {
this.renderer.removeChild(this.qrcElement.nativeElement, node);
}
this.renderer.appendChild(this.qrcElement.nativeElement, element);
}
async createQRCode() {
// Set sensitive defaults
if (this.version && this.version > 40) {
console.warn('[angularx-qrcode] max value for `version` is 40');
this.version = 40;
}
else if (this.version && this.version < 1) {
console.warn('[angularx-qrcode]`min value for `version` is 1');
this.version = 1;
}
else if (this.version !== undefined && isNaN(this.version)) {
console.warn('[angularx-qrcode] version should be a number, defaulting to auto.');
this.version = undefined;
}
try {
if (!this.isValidQrCodeText(this.qrdata)) {
throw new Error('[angularx-qrcode] Field `qrdata` is empty, set \'allowEmptyString="true"\' to overwrite this behaviour.');
}
// This is a workaround to allow an empty string as qrdata
if (this.isValidQrCodeText(this.qrdata) && this.qrdata === '') {
this.qrdata = ' ';
}
const config = {
color: {
dark: this.colorDark,
light: this.colorLight,
},
errorCorrectionLevel: this.errorCorrectionLevel,
margin: this.margin,
scale: this.scale,
version: this.version,
width: this.width,
};
const centerImageSrc = this.imageSrc;
const centerImageHeight = this.imageHeight ? +this.imageHeight : 40;
const centerImageWidth = this.imageWidth ? +this.imageWidth : 40;
switch (this.elementType) {
case 'canvas': {
const canvasElement = this.renderer.createElement('canvas');
this.context = canvasElement.getContext('2d');
this.toCanvas(canvasElement, config)
.then(() => {
if (this.ariaLabel) {
this.renderer.setAttribute(canvasElement, 'aria-label', `${this.ariaLabel}`);
}
if (this.title) {
this.renderer.setAttribute(canvasElement, 'title', `${this.title}`);
}
if (centerImageSrc && this.context) {
this.centerImage = new Image(centerImageWidth, centerImageHeight);
if (centerImageSrc !== this.centerImage.src) {
this.centerImage.crossOrigin = 'anonymous';
this.centerImage.src = centerImageSrc;
}
if (centerImageHeight !== this.centerImage.height) {
this.centerImage.height = centerImageHeight;
}
if (centerImageWidth !== this.centerImage.width) {
this.centerImage.width = centerImageWidth;
}
const centerImage = this.centerImage;
if (centerImage) {
centerImage.onload = () => {
this.context?.drawImage(centerImage, canvasElement.width / 2 - centerImageWidth / 2, canvasElement.height / 2 - centerImageHeight / 2, centerImageWidth, centerImageHeight);
};
}
}
this.renderElement(canvasElement);
this.emitQRCodeURL(canvasElement);
})
.catch((e) => {
console.error('[angularx-qrcode] canvas error:', e);
});
break;
}
case 'svg': {
const svgParentElement = this.renderer.createElement('div');
this.toSVG(config)
.then((svgString) => {
this.renderer.setProperty(svgParentElement, 'innerHTML', svgString);
const svgElement = svgParentElement.firstChild;
this.renderer.setAttribute(svgElement, 'height', `${this.width}`);
this.renderer.setAttribute(svgElement, 'width', `${this.width}`);
this.renderElement(svgElement);
this.emitQRCodeURL(svgElement);
})
.catch((e) => {
console.error('[angularx-qrcode] svg error:', e);
});
break;
}
case 'url':
case 'img':
default: {
const imgElement = this.renderer.createElement('img');
this.toDataURL(config)
.then((dataUrl) => {
if (this.alt) {
imgElement.setAttribute('alt', this.alt);
}
if (this.ariaLabel) {
imgElement.setAttribute('aria-label', this.ariaLabel);
}
imgElement.setAttribute('src', dataUrl);
if (this.title) {
imgElement.setAttribute('title', this.title);
}
this.renderElement(imgElement);
this.emitQRCodeURL(imgElement);
})
.catch((e) => {
console.error('[angularx-qrcode] img/url error:', e);
});
}
}
}
catch (e) {
console.error('[angularx-qrcode] Error generating QR Code:', e.message);
}
}
convertBase64ImageUrlToBlob(base64ImageUrl) {
// split into two parts
const parts = base64ImageUrl.split(';base64,');
// hold the content/mime type f.e. image/png
const imageType = parts[0].split(':')[1];
// decode base64 string
const decodedData = atob(parts[1]);
// create unit8array of size same as row data length
const uInt8Array = new Uint8Array(decodedData.length);
// insert all character code into uint8array
for (let i = 0; i < decodedData.length; ++i) {
uInt8Array[i] = decodedData.charCodeAt(i);
}
// return blob image after conversion
return new Blob([uInt8Array], { type: imageType });
}
emitQRCodeURL(element) {
const className = element.constructor.name;
if (className === SVGSVGElement.name) {
const svgHTML = element.outerHTML;
const blob = new Blob([svgHTML], { type: 'image/svg+xml' });
const urlSvg = URL.createObjectURL(blob);
const urlSanitized = this.sanitizer.bypassSecurityTrustUrl(urlSvg);
this.qrCodeURL.emit(urlSanitized);
return;
}
let urlImage = '';
if (className === HTMLCanvasElement.name) {
urlImage = element.toDataURL('image/png');
}
if (className === HTMLImageElement.name) {
urlImage = element.src;
}
const blobData = this.convertBase64ImageUrlToBlob(urlImage);
const urlBlob = URL.createObjectURL(blobData);
const urlSanitized = this.sanitizer.bypassSecurityTrustUrl(urlBlob);
this.qrCodeURL.emit(urlSanitized);
}
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.5", ngImport: i0, type: QRCodeComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "21.2.5", type: QRCodeComponent, isStandalone: true, selector: "qrcode", inputs: { allowEmptyString: "allowEmptyString", colorDark: "colorDark", colorLight: "colorLight", cssClass: "cssClass", elementType: "elementType", errorCorrectionLevel: "errorCorrectionLevel", imageSrc: "imageSrc", imageHeight: "imageHeight", imageWidth: "imageWidth", margin: "margin", qrdata: "qrdata", scale: "scale", version: "version", width: "width", alt: "alt", ariaLabel: "ariaLabel", title: "title" }, outputs: { qrCodeURL: "qrCodeURL" }, viewQueries: [{ propertyName: "qrcElement", first: true, predicate: ["qrcElement"], descendants: true, static: true }], usesOnChanges: true, ngImport: i0, template: `<div #qrcElement [class]="cssClass"></div>`, isInline: true, changeDetection: i0.ChangeDetectionStrategy.OnPush });
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.5", ngImport: i0, type: QRCodeComponent, decorators: [{
type: Component,
args: [{
// eslint-disable-next-line @angular-eslint/component-selector
selector: 'qrcode',
changeDetection: ChangeDetectionStrategy.OnPush,
template: `<div #qrcElement [class]="cssClass"></div>`,
}]
}], propDecorators: { allowEmptyString: [{
type: Input
}], colorDark: [{
type: Input
}], colorLight: [{
type: Input
}], cssClass: [{
type: Input
}], elementType: [{
type: Input
}], errorCorrectionLevel: [{
type: Input
}], imageSrc: [{
type: Input
}], imageHeight: [{
type: Input
}], imageWidth: [{
type: Input
}], margin: [{
type: Input
}], qrdata: [{
type: Input
}], scale: [{
type: Input
}], version: [{
type: Input
}], width: [{
type: Input
}], alt: [{
type: Input
}], ariaLabel: [{
type: Input
}], title: [{
type: Input
}], qrCodeURL: [{
type: Output
}], qrcElement: [{
type: ViewChild,
args: ['qrcElement', { static: true }]
}] } });
/*
* Public API Surface of angularx-qrcode
*/
/**
* Generated bundle index. Do not edit.
*/
export { QRCodeComponent };
//# sourceMappingURL=angularx-qrcode.mjs.map