UNPKG

@schukai/monster

Version:

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

205 lines (184 loc) 5.99 kB
/** * Copyright © Volker Schukai 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 Volker Schukai. * * SPDX-License-Identifier: AGPL-3.0 */ import { Base } from "../types/base.mjs"; import { getGlobalFunction, getGlobalObject } from "../types/global.mjs"; import { validateInstance, validateString } from "../types/validate.mjs"; import { ATTRIBUTE_TEMPLATE_PREFIX } from "./constants.mjs"; import { getDocumentTheme } from "./theme.mjs"; import { instanceSymbol } from "../constants.mjs"; import { findElementWithIdUpwards } from "./util.mjs"; export { Template }; /** * The template class provides methods for creating templates. * * @license AGPLv3 * @since 1.6.0 * @copyright Volker Schukai * @summary A template class */ class Template extends Base { /** * * @param {HTMLTemplateElement} template * @throws {TypeError} value is not an instance of * @throws {TypeError} value is not a function * @throws {Error} the function is not defined */ constructor(template) { super(); const HTMLTemplateElement = getGlobalFunction("HTMLTemplateElement"); validateInstance(template, HTMLTemplateElement); this.template = template; } /** * This method is called by the `instanceof` operator. * @return {symbol} * @since 2.1.0 */ static get [instanceSymbol]() { return Symbol.for("@schukai/monster/dom/resource/template"); } /** * * @return {HTMLTemplateElement} */ getTemplateElement() { return this.template; } /** * * @return {DocumentFragment} * @throws {TypeError} value is not an instance of */ createDocumentFragment() { return this.template.content.cloneNode(true); } } /** * This method loads a template with the given ID and returns it. * * To do this, it first reads the theme of the document and looks for the `data-monster-theme-name` attribute in the HTML tag. * * ``` * <html data-monster-theme-name="my-theme"> * ``` * * If no theme was specified, the default theme is `monster`. * * Now it is looked if there is a template with the given ID and theme `id-theme` and if yes it is returned. * If there is no template a search for a template with the given ID `id` is done. If this is also not found, an error is thrown. * * You can call the method via the monster namespace `Monster.DOM.findDocumentTemplate()`. * * ``` * <script type="module"> * import {findTemplate} from '@schukai/monster/source/dom/template.mjs'; * findDocumentTemplate() * </script> * ``` * * @example * * import { findDocumentTemplate } from "https://cdn.jsdelivr.net/npm/@schukai/monster@latest/source/dom/template.mjs"; * * const template = document.createElement("template"); * template.id = "myTemplate"; * template.innerHTML = "<p>my default template</p>"; * document.body.appendChild(template); * * const themedTemplate = document.createElement("template"); * themedTemplate.id = "myTemplate-myTheme"; * themedTemplate.innerHTML = "<p>my themed template</p>"; * document.body.appendChild(themedTemplate); * * // loads the temple and since no theme is set the default template * const template1 = findDocumentTemplate("myTemplate"); * console.log(template1.createDocumentFragment()); * // ↦ '<p>my default template</p>' * * // now we set our own theme * document * .querySelector("html") * .setAttribute("data-monster-theme-name", "myTheme"); * * // now we don't get the default template, * // but the template with the theme in the id * const template2 = findDocumentTemplate("myTemplate"); * console.log(template2.createDocumentFragment()); * // ↦ '<p>my themed template</p>' * * @param {string} id * @param {Node} currentNode * @return {Monster.DOM.Template} * @license AGPLv3 * @since 1.7.0 * @copyright Volker Schukai * @throws {Error} template id not found. * @throws {TypeError} value is not a string */ export function findDocumentTemplate(id, currentNode) { validateString(id); const document = getGlobalObject("document"); const HTMLTemplateElement = getGlobalFunction("HTMLTemplateElement"); const DocumentFragment = getGlobalFunction("DocumentFragment"); const Document = getGlobalFunction("Document"); let prefixID; if ( !( currentNode instanceof Document || currentNode instanceof DocumentFragment ) ) { if (currentNode instanceof Node) { if (currentNode.hasAttribute(ATTRIBUTE_TEMPLATE_PREFIX)) { prefixID = currentNode.getAttribute(ATTRIBUTE_TEMPLATE_PREFIX); } currentNode = currentNode.getRootNode(); if ( !( currentNode instanceof Document || currentNode instanceof DocumentFragment ) ) { currentNode = currentNode.ownerDocument; } } if ( !( currentNode instanceof Document || currentNode instanceof DocumentFragment ) ) { currentNode = document; } } let template; const theme = getDocumentTheme(); if (prefixID) { const themedPrefixID = `${prefixID}-${id}-${theme.getName()}`; template = findElementWithIdUpwards(currentNode, themedPrefixID); if (template instanceof HTMLTemplateElement) { return new Template(template); } } const themedID = `${id}-${theme.getName()}`; template = findElementWithIdUpwards(currentNode, themedID); if (template instanceof HTMLTemplateElement) { return new Template(template); } template = findElementWithIdUpwards(currentNode, id); if (template instanceof HTMLTemplateElement) { return new Template(template); } throw new Error(`template ${id} not found.`); }