UNPKG

@plattar/plattar-qrcode

Version:

Allows embedding Plattar-Style QR Codes for existing websites.

290 lines (248 loc) 8.92 kB
const QRCodeStyling = require("qr-code-styling"); const hash = require("object-hash"); class BaseElement extends HTMLElement { constructor() { super(); } connectedCallback() { if (this.hasAttribute("url")) { this.renderQRCode(); } const observer = new MutationObserver((mutations) => { mutations.forEach((mutation) => { if (mutation.type === "attributes") { if (this.hasAttribute("url")) { this.renderQRCode(); } } }); }); observer.observe(this, { attributes: true }); } download(options) { const opt = options || { name: "plattar-qrcode", extension: "png" }; if (this._qrCode) { this._qrCode.download(opt); } } renderQRCode() { const url = this.hasAttribute("url") ? this.getAttribute("url") : undefined; if (!url) { console.warn("PlattarQR.renderQRCode() - required attribute \"url\" is missing or invalid, QR Code will not render"); return; } const width = this.hasAttribute("width") ? this.getAttribute("width") : "100%"; const height = this.hasAttribute("height") ? this.getAttribute("height") : "100%"; const margin = this.hasAttribute("margin") ? this.getAttribute("margin") : 0; const image = this.hasAttribute("image") ? this.getAttribute("image") : undefined; const color = this.hasAttribute("color") ? this.getAttribute("color") : "#000000"; const style = this.hasAttribute("qr-type") ? this.getAttribute("qr-type") : "default"; // used to check if anything has changed in the options // to avoid re-rendering this._optionsHash = '0'; this._options = this._options || { imageOptions: { hideBackgroundDots: true, imageSize: 0.4, margin: 0 }, dotsOptions: { type: "rounded" }, backgroundOptions: { color: "#ffffff" }, dotsOptionsHelper: { colorType: { single: true, gradient: false }, gradient: { linear: true, radial: false, color1: "#6a1a4c", color2: "#6a1a4c", rotation: "0" } }, cornersSquareOptions: { type: "extra-rounded" }, cornersSquareOptionsHelper: { colorType: { single: true, gradient: false }, gradient: { linear: true, radial: false, color1: "#000000", color2: "#000000", rotation: "0" } }, cornersDotOptions: { type: "dot" }, cornersDotOptionsHelper: { colorType: { single: true, gradient: false }, gradient: { linear: true, radial: false, color1: "#000000", color2: "#000000", rotation: "0" } }, backgroundOptionsHelper: { colorType: { single: true, gradient: false }, gradient: { linear: true, radial: false, color1: "#ffffff", color2: "#ffffff", rotation: "0" } }, width: 1024, height: 1024, type: "canvas" }; this._options.margin = margin; this._options.image = image; // set the colors this._options.dotsOptions.color = color; this._options.cornersDotOptions.color = color; this._options.cornersSquareOptions.color = color; switch (style) { case "dots": this._options.dotsOptions.type = "dots"; break; case "default": default: this._options.dotsOptions.type = "rounded"; } const shortenURL = this.hasAttribute("shorten") ? this.getAttribute("shorten") : "false"; if (shortenURL && shortenURL.toLowerCase() === "true") { this._ShortenURL(url).then((newURL) => { // make sure by time promise is resolved that the original url hasn't been updated const updatedURL = this.hasAttribute("url") ? this.getAttribute("url") : undefined; if (updatedURL === url) { this._GenerateQRCode(newURL, width, height); } }).catch((_err) => { console.warn(_err); // ignore error and just generate normal QR Code // make sure by time promise is resolved that the original url hasn't been updated const updatedURL = this.hasAttribute("url") ? this.getAttribute("url") : undefined; if (updatedURL === url) { this._GenerateQRCode(url, width, height); } }); } else { this._GenerateQRCode(url, width, height); } } _UpdateCanvas(width, height) { if (!this._qrCode) { return; } const canvas = this._qrCode._domCanvas || this._qrCode._canvas; if (canvas) { if (canvas.style.width !== "100%") { canvas.style.width = "100%"; } if (canvas.style.height !== "100%") { canvas.style.height = "100%"; } } if (this._divContainer) { const div = this._divContainer; if (div.style.width !== width) { div.style.width = width; } if (div.style.height !== height) { div.style.height = height; } } } _GenerateQRCode(url, width, height) { this._options.data = url; const shadow = this.shadowRoot || this.attachShadow({ mode: 'open' }); const qrCode = this._qrCode; if (!qrCode) { const div = document.createElement("div"); div.style.display = "none"; shadow.appendChild(div); this._divContainer = div; this._qrCode = new QRCodeStyling(this._options); this._qrCode.append(div); this._UpdateCanvas(width, height); div.style.display = "flex"; return; } const newHash = hash({ options: this._options, width: width, height: height }); if (this._optionsHash !== newHash) { this._optionsHash = newHash; this._qrCode.update(this._options); this._UpdateCanvas(width, height); } } _IsFetchAPISupported() { return "fetch" in window; } _ShortenURL(url) { return new Promise((accept, reject) => { if (!this._IsFetchAPISupported()) { return reject(new Error("PlattarQR._ShortenURL() - fetch api not supported, cannot proceed")); } try { const b64Link = btoa(url); fetch("https://c.plattar.com/shorten", { cache: "no-store", method: "POST", headers: { "Content-Type": "application/json", }, body: JSON.stringify({ data: { attributes: { url: b64Link, isBase64: true } } }) }).then((response) => { if (!response.ok) { throw new Error("PlattarQR._ShortenURL() - response was invalid"); } return response.json(); }).then((json) => { return accept(json.data.attributes.url); }).catch(() => { return reject(new Error("PlattarQR._ShortenURL() - there was an unexpected issue generating short url")); }); } catch (err) { return reject(err); } }); } } module.exports = BaseElement;