UNPKG

@schukai/monster

Version:

Monster is a simple library for creating fast, robust and lightweight websites.

290 lines (262 loc) 6.55 kB
/** * Copyright © schukai GmbH and all contributing authors, {{copyRightYear}}. All rights reserved. * Node module: @schukai/monster * * This source code is licensed under the GNU Affero General Public License version 3 (AGPLv3). * The full text of the license can be found at: https://www.gnu.org/licenses/agpl-3.0.en.html * * For those who do not wish to adhere to the AGPLv3, a commercial license is available. * Acquiring a commercial license allows you to use this software without complying with the AGPLv3 terms. * For more information about purchasing a commercial license, please contact schukai GmbH. * * SPDX-License-Identifier: AGPL-3.0 */ import { assembleMethodSymbol, CustomElement, registerCustomElement, } from "../../dom/customelement.mjs"; import "../notify/notify.mjs"; import { ViewerStyleSheet } from "./stylesheet/viewer.mjs"; import { instanceSymbol } from "../../constants.mjs"; import { isString } from "../../types/is.mjs"; import { getGlobal } from "../../types/global.mjs"; export { Viewer }; /** * @private * @type {symbol} */ const viewerElementSymbol = Symbol("viewerElement"); /** * The Viewer component is used to show a PDF, HTML or Image. * * @fragments /fragments/components/content/viewer * * @example /examples/components/content/pdf-viewer with a PDF * @example /examples/components/content/image-viewer with an image * @example /examples/components/content/html-viewer with HTML content * * @copyright schukai GmbH * @summary A simple viewer component for PDF, HTML and images. */ class Viewer extends CustomElement { /** * This method is called by the `instanceof` operator. * @return {symbol} */ static get [instanceSymbol]() { return Symbol.for("@schukai/monster/components/content/viewer@@instance"); } /** * To set the options via the HTML tag, the attribute `data-monster-options` must be used. * @see {@link https://monsterjs.org/en/doc/#configurate-a-monster-control} * * The individual configuration values can be found in the table. * * @property {Object} templates Template definitions * @property {string} templates.main Main template * @property {Object} classes Css classes * @property {Object} features Feature definitions */ get defaults() { return Object.assign({}, super.defaults, { templates: { main: getTemplate(), }, content: "<slot></slot>", classes: { viewer: "", }, features: {}, }); } /** * * @param {string} content * @returns {Viewer} */ setContent(content) { this.setOption("content", content); return this; } /** * * @param {Blob|URL|string} data * @param {boolean} navigation * @param {boolean} toolbar * @param {boolean} scrollbar */ setPDF(data, navigation = true, toolbar = true, scrollbar = false) { const hashes = "#toolbar=" + (toolbar ? "1" : "0") + "&navpanes=" + (navigation ? "1" : "0") + "&scrollbar=" + (scrollbar ? "1" : "0"); let pdfContent = ""; if (isBlob(data)) { pdfContent = URL.createObjectURL(data); pdfContent += hashes; } else if (isURL(data)) { pdfContent = data; // check if the url already contains the hashes if (pdfContent.indexOf("#") === -1) { pdfContent += hashes; } } else if (isString(data)) { //URL.createObjectURL(data); const blobObj = new Blob([atob(data)], { type: "application/pdf" }); const url = window.URL.createObjectURL(blobObj); pdfContent = data; } else { throw new Error("Blob or URL expected"); } const html = '<object data="' + pdfContent + '" width="100%" height="100%" type="application/pdf"></object>'; this.setContent(html); } /** * * @param {Blob|URL|string} data */ setImage(data) { if (isBlob(data)) { data = URL.createObjectURL(data); } else if (isURL(data)) { // nothing to do } else if (isString(data)) { // nothing to do } else { throw new Error("Blob or URL expected"); } const html = '<img src="' + data + '" alt="image" />'; this.setContent(html); } /** * * if the data is a string, it is interpreted as HTML. * if the data is an url, the HTML is loaded from the url and set as content. * if the data is an HTMLElement, the outerHTML is used as content. * * @param {HTMLElement|URL|string|Blob} data */ setHTML(data) { if (data instanceof Blob) { blobToText(data) .then((html) => { this.setHTML(html); }) .catch((error) => { throw new Error(error); }); return; } else if (data instanceof HTMLElement) { data = data.outerHTML; } else if (isString(data)) { // nothing to do } else if (isURL(data)) { // fetch element getGlobal() .fetch(data) .then((response) => { return response.text(); }) .then((html) => { this.setHTML(html); }) .catch((error) => { throw new Error(error); }); } else { throw new Error("HTMLElement or string expected"); } this.setContent(data); } /** * * @return {Viewer} */ [assembleMethodSymbol]() { super[assembleMethodSymbol](); initControlReferences.call(this); initEventHandler.call(this); } /** * * @return {string} */ static getTag() { return "monster-viewer"; } /** * @return {CSSStyleSheet[]} */ static getCSSStyleSheet() { return [ViewerStyleSheet]; } } /** * @private * @param variable * @return {boolean} */ function isURL(variable) { try { new URL(variable); return true; } catch (error) { return false; } } /** * @private * @param variable * @return {boolean} */ function isBlob(variable) { return variable instanceof Blob; } /** * @private * @param blob * @return {Promise<unknown>} */ function blobToText(blob) { return new Promise((resolve, reject) => { const reader = new FileReader(); reader.onloadend = () => resolve(reader.result); reader.onerror = reject; reader.readAsText(blob); }); } /** * @private * @return {Select} * @throws {Error} no shadow-root is defined */ function initControlReferences() { if (!this.shadowRoot) { throw new Error("no shadow-root is defined"); } this[viewerElementSymbol] = this.shadowRoot.getElementById("viewer"); } /** * @private */ function initEventHandler() { return this; } /** * @private * @return {string} */ function getTemplate() { // language=HTML return ` <div id="viewer" data-monster-role="viewer" part="viewer" data-monster-replace="path:content" data-monster-attributes="class path:classes.viewer"> </div>`; } registerCustomElement(Viewer);