@schukai/monster
Version:
Monster is a simple library for creating fast, robust and lightweight websites.
290 lines (262 loc) • 6.55 kB
JavaScript
/**
* 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);