Select Git revision
template.mjs

Volker Schukai authored
template.mjs 5.89 KiB
/**
* 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 { 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 schukai GmbH
* @memberOf Monster.DOM
* @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 schukai GmbH
* @memberOf Monster.DOM
* @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.`);
}