Skip to content
Snippets Groups Projects
Verified Commit bee1a662 authored by Volker Schukai's avatar Volker Schukai :alien:
Browse files

chore: tidy and doc

parent 049dd1d9
No related branches found
No related tags found
No related merge requests found
Showing
with 442 additions and 303 deletions
...@@ -19,27 +19,26 @@ export { buildMap, PARENT, assembleParts }; ...@@ -19,27 +19,26 @@ export { buildMap, PARENT, assembleParts };
const PARENT = "^"; const PARENT = "^";
/** /**
* With the help of the function `buildMap()`, maps can be easily created from data objects. * Maps can be easily created from data objects with the help of the function `buildMap()`.
* *
* Either a simple definition `a.b.c` or a template `${a.b.c}` can be specified as the path. * The path can be specified as either a simple definition a.b.c or a template ${a.b.c}.
* Key and value can be either a definition or a template. The key does not have to be defined. * Key and value can be either a definition or a template. The key does not have to be defined.
*
* The templates determine the appearance of the keys and the value of the map. Either a single value * The templates determine the appearance of the keys and the value of the map. Either a single value
* `id` can be taken or a composite key `${id} ${name}` can be used. * id can be taken or a composite key ${id} ${name} can be used.
* *
* If you want to access values of the parent data set, you have to use the `^` character `${id} ${^.name}`. * If you want to access values of the parent data set, you have to use the ^ character, for example ${id} ${^.name}.
* *
* @externalExample ../../example/data/buildmap.mjs * @externalExample ../../example/data/buildmap.mjs
* @param {*} subject * @param {*} subject - The data object from which the map will be created
* @param {string|Monster.Data~exampleSelectorCallback} selector * @param {string|Monster.Data~exampleSelectorCallback} selector - The path to the data object, or a callback that returns a map.
* @param {string} [valueTemplate] * @param {string} [valueTemplate] - A template for the value of the map.
* @param {string} [keyTemplate] * @param {string} [keyTemplate] - A template for the key of the map.
* @param {Monster.Data~exampleFilterCallback} [filter] * @param {Monster.Data~exampleFilterCallback} [filter] - A callback function to filter out values.
* @return {*} * @return {*} - The created map.
* @memberOf Monster.Data * @memberOf Monster.Data
* @throws {TypeError} value is neither a string nor a function * @throws {TypeError} - If the value is neither a string nor a function.
* @throws {TypeError} the selector callback must return a map * @throws {TypeError} - If the selector callback does not return a map.
*/ **/
function buildMap(subject, selector, valueTemplate, keyTemplate, filter) { function buildMap(subject, selector, valueTemplate, keyTemplate, filter) {
return assembleParts(subject, selector, filter, function (v, k, m) { return assembleParts(subject, selector, filter, function (v, k, m) {
k = build(v, keyTemplate, k); k = build(v, keyTemplate, k);
...@@ -49,13 +48,66 @@ function buildMap(subject, selector, valueTemplate, keyTemplate, filter) { ...@@ -49,13 +48,66 @@ function buildMap(subject, selector, valueTemplate, keyTemplate, filter) {
} }
/** /**
* The assembleParts function is a private function that helps in building a map from a subject object based on a provided
* selector. The selector can either be a string or a callback function. This function is meant to be used as a
* helper function by other functions in the module.
*
* The function takes four parameters:
*
* subject: The subject object from which the map is to be built
* selector: The selector to determine the structure of the map. It can be a string or a callback function.
* filter (optional): A callback function that can be used to filter values based on some criteria.
* callback: A function to be called for each element in the map.
* If the selector parameter is a callback function, it is executed passing the subject as its argument,
* and the resulting value must be an instance of Map. Otherwise, if the selector parameter is a string,
* buildFlatMap is called to build a flat map with keys and values extracted from the subject object based on the selector.
*
* If the filter parameter is provided, it will be used to filter out certain elements from the map, based on some
* criteria. The callback will be passed the value, key, and map object, and if it returns false, the element will be skipped.
*
* For each element in the map, the callback function is called with the following parameters:
*
* v: The value of the element
* k: The key of the element
* m: The map object
* The function returns a new map with the processed values. If map is not an instance of Map, an empty map will be returned.
*
* Example Usage:
*
* ```javascript
* const obj = {
* name: "John",
* age: 30,
* address: {
* city: "New York",
* state: "NY",
* country: "USA",
* },
* };
*
* const selector = "address";
*
* const map = assembleParts(obj, selector, null, function (v, k, m) {
* this.set(k, v);
* });
*
* console.log(map);
* // Output: Map(3) {
* // "address.city" => "New York",
* // "address.state" => "NY",
* // "address.country" => "USA"
* // }
* ```
*
*
* @private * @private
* @param {*} subject * @param {*} subject - The subject object from which the map is to be built.
* @param {string|Monster.Data~exampleSelectorCallback} selector * @param {string|Monster.Data~exampleSelectorCallback} selector - The selector to determine the structure of the map. It can be a string or a callback function.
* @param {Monster.Data~exampleFilterCallback} [filter] * @param {Monster.Data~exampleFilterCallback} [filter] - A callback function that can be used to filter values based on some criteria.
* @param {function} callback * @param {function} callback - A function to be called for each element in the map.
* @return {Map} * @return {Map} - A new map with the processed values.
* @throws {TypeError} selector is neither a string nor a function * @throws {TypeError} - When selector is neither a string nor a function.
* @memberOf Monster.Data
*/ */
function assembleParts(subject, selector, filter, callback) { function assembleParts(subject, selector, filter, callback) {
const result = new Map(); const result = new Map();
......
...@@ -33,19 +33,102 @@ const rootSymbol = Symbol("root"); ...@@ -33,19 +33,102 @@ const rootSymbol = Symbol("root");
*/ */
/** /**
* With the help of the function `buildTree()`, nodes can be easily created from data objects. * Creates a tree structure from a given subject using a selector and specified ID and parent ID keys.
* *
* @param {*} subject * The buildTree function is a powerful tool for creating tree-like data structures from plain JavaScript
* @param {string|Monster.Data~exampleSelectorCallback} selector * objects. It takes in four required parameters: the subject object that you want to turn into a tree, a
* @param {string} idKey * selector that identifies which parts of the subject to use when building the tree, and two keys
* @param {string} parentIDKey * (idKey and parentIDKey) that specify which properties in the subject represent the unique identifiers
* @param {buildTreeOptions} [options] * and parent-child relationships between nodes in the tree.
* @return {*} *
* @memberOf Monster.Data * Optionally, you can also pass in an options object to further configure the behavior of the function,
* @throws {TypeError} value is neither a string nor a function * such as specifying which values should be treated as roots of the tree, or providing a custom filter
* @throws {TypeError} the selector callback must return a map * function to only include certain nodes in the final output.
* @throws {Error} the object has no value for the specified id *
* The buildTree function works by first using the assembleParts helper function to extract the relevant
* parts of the subject based on the selector, and then iterates over the resulting map to create Node
* objects and organize them into parent-child relationships based on the values of the idKey and parentIDKey properties.
*
* The resulting NodeList represents the tree structure, with each Node object containing the original
* object data as well as additional metadata about its position in the tree. You can then use the childNodes
* property of each Node to access its children, or the parent property to access its parent.
*
* Overall, the buildTree function is a flexible and powerful way to transform flat data into hierarchical
* structures, and can be especially useful in scenarios such as displaying folder structures or
* visualizing complex data relationships.
*
* Let's say you have an array of data objects representing a file system directory structure, and you want
* to turn it into a tree-like structure where each node represents a folder or file, and child nodes
* represent the contents of the folder:
*
* ```javascript
* const fileSystem = [
* { id: 'folder1', name: 'Folder 1', type: 'folder', parent: null },
* { id: 'file1', name: 'File 1', type: 'file', parent: 'folder1' },
* { id: 'file2', name: 'File 2', type: 'file', parent: 'folder1' },
* { id: 'subfolder1', name: 'Subfolder 1', type: 'folder', parent: 'folder1' },
* { id: 'file3', name: 'File 3', type: 'file', parent: 'subfolder1' },
* { id: 'file4', name: 'File 4', type: 'file', parent: 'subfolder1' },
* { id: 'subfolder2', name: 'Subfolder 2', type: 'folder', parent: 'folder1' },
* { id: 'file5', name: 'File 5', type: 'file', parent: 'subfolder2' },
* { id: 'file6', name: 'File 6', type: 'file', parent: 'subfolder2' },
* { id: 'folder2', name: 'Folder 2', type: 'folder', parent: null },
* { id: 'file7', name: 'File 7', type: 'file', parent: 'folder2' },
* { id: 'file8', name: 'File 8', type: 'file', parent: 'folder2' },
* { id: 'subfolder3', name: 'Subfolder 3', type: 'folder', parent: 'folder2' },
* { id: 'file9', name: 'File 9', type: 'file', parent: 'subfolder3' },
* { id: 'file10', name: 'File 10', type: 'file', parent: 'subfolder3' },
* ];
*
* const tree = buildTree(fileSystem, 'id', 'id', 'parent', { rootReferences: [null] });
*
* console.log(tree.toString());
* ```
*
* The buildTree function takes in the array of data objects, as well as some configuration options specifying
* the keys to use for identifying nodes and their parent-child relationships. In this example, we use the id
* key to identify nodes, and the parent key to specify the parent of each node.
*
* The resulting tree object is a nested tree structure, where each node is an object representing a file or
* folder, and has child nodes representing its contents. The toString method of the tree object
* can be used to print out the tree in a readable format:
*
* ```markdown
* - Folder 1
* - File 1
* - File 2
* - Subfolder 1
* - File 3
* - File 4
* - Subfolder 2
* - File 5
* - File 6
* - Folder 2
* - File 7
* - File 8
* - Subfolder 3
* - File 9
* - File 10
* ```
*
* @memberof Monster.Data
*
* @param {*} subject - The object or array to build the tree from.
* @param {string|Monster.Data~exampleSelectorCallback} selector - Either a string to specify a property of each object to use as a selector, or a selector function to generate a map of objects.
* @param {string} idKey - The property key to use as the unique ID of each node.
* @param {string} parentIDKey - The property key to use as the parent ID of each node.
* @param {object} [options] - Additional options to modify the function behavior.
* @param {Array<*>} [options.rootReferences=[null, undefined]] - An array of values to treat as root references when creating the tree.
* @param {function} [options.filter] - A filter function to apply to each node.
*
* @return {*} The resulting tree structure as a NodeList.
*
* @throws {TypeError} selector is neither a string nor a function.
* @throws {TypeError} the selector callback must return a map.
* @throws {Error} the object has no value for the specified id.
*
* @license AGPLv3 * @license AGPLv3
*
* @since 1.26.0 * @since 1.26.0
*/ */
function buildTree(subject, selector, idKey, parentIDKey, options) { function buildTree(subject, selector, idKey, parentIDKey, options) {
......
...@@ -11,7 +11,6 @@ import { Datasource } from "../datasource.mjs"; ...@@ -11,7 +11,6 @@ import { Datasource } from "../datasource.mjs";
export { DomStorage }; export { DomStorage };
/** /**
* The DomStorage is a class that stores data in memory. * The DomStorage is a class that stores data in memory.
* *
...@@ -53,7 +52,7 @@ class DomStorage extends Datasource { ...@@ -53,7 +52,7 @@ class DomStorage extends Datasource {
}, },
write: { write: {
selector: undefined, selector: undefined,
} },
}); });
} }
...@@ -78,13 +77,12 @@ class DomStorage extends Datasource { ...@@ -78,13 +77,12 @@ class DomStorage extends Datasource {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
try { try {
let data = JSON.parse(storage.innerHTML); let data = JSON.parse(storage.innerHTML);
self.set(data) self.set(data);
resolve(data); resolve(data);
} catch (e) { } catch (e) {
reject(e); reject(e);
} }
; });
})
} }
/** /**
...@@ -93,7 +91,6 @@ class DomStorage extends Datasource { ...@@ -93,7 +91,6 @@ class DomStorage extends Datasource {
* @throws {Error} There are no storage element * @throws {Error} There are no storage element
*/ */
write() { write() {
const self = this; const self = this;
let selector = self.getOption("write.selector"); let selector = self.getOption("write.selector");
...@@ -113,10 +110,6 @@ class DomStorage extends Datasource { ...@@ -113,10 +110,6 @@ class DomStorage extends Datasource {
} catch (e) { } catch (e) {
reject(e); reject(e);
} }
}) });
} }
} }
...@@ -127,7 +127,8 @@ class RestAPI extends Server { ...@@ -127,7 +127,8 @@ class RestAPI extends Server {
if (!init["method"]) init["method"] = "GET"; if (!init["method"]) init["method"] = "GET";
let callback = self.getOption("read.responseCallback"); let callback = self.getOption("read.responseCallback");
if(!callback) callback = (obj) => { if (!callback)
callback = (obj) => {
self.set(self.transformServerPayload.call(self, obj)); self.set(self.transformServerPayload.call(self, obj));
}; };
...@@ -199,7 +200,6 @@ function fetchData(init, key, callback) { ...@@ -199,7 +200,6 @@ function fetchData(init, key, callback) {
obj = JSON.parse(body); obj = JSON.parse(body);
response[rawDataSymbol] = obj; response[rawDataSymbol] = obj;
} catch (e) { } catch (e) {
if (body.length > 100) { if (body.length > 100) {
body = `${body.substring(0, 97)}...`; body = `${body.substring(0, 97)}...`;
......
...@@ -576,7 +576,6 @@ function transform(value) { ...@@ -576,7 +576,6 @@ function transform(value) {
throw new Error("type not supported"); throw new Error("type not supported");
case "map": case "map":
map = new Map(); map = new Map();
while (args.length > 0) { while (args.length > 0) {
...@@ -592,7 +591,6 @@ function transform(value) { ...@@ -592,7 +591,6 @@ function transform(value) {
return map.get(value); return map.get(value);
case "equals": case "equals":
if (args.length === 0) { if (args.length === 0) {
throw new Error("missing value parameter"); throw new Error("missing value parameter");
} }
...@@ -631,11 +629,10 @@ function transform(value) { ...@@ -631,11 +629,10 @@ function transform(value) {
case "money": case "money":
case "currency": case "currency":
try { try {
locale = getLocaleOfDocument(); locale = getLocaleOfDocument();
} catch (e) { } catch (e) {
throw new Error("unsupported locale or missing format (" + e.message + ")"); throw new Error(`unsupported locale or missing format (${e.message})`);
} }
const currency = value.substring(0, 3); const currency = value.substring(0, 3);
...@@ -672,9 +669,8 @@ function transform(value) { ...@@ -672,9 +669,8 @@ function transform(value) {
try { try {
locale = getLocaleOfDocument(); locale = getLocaleOfDocument();
return date.toLocaleTimeString(locale); return date.toLocaleTimeString(locale);
} catch (e) { } catch (e) {
throw new Error("unsupported locale or missing format (" + e.message + ")"); throw new Error(`unsupported locale or missing format (${e.message})`);
} }
case "datetime": case "datetime":
...@@ -686,9 +682,8 @@ function transform(value) { ...@@ -686,9 +682,8 @@ function transform(value) {
try { try {
locale = getLocaleOfDocument(); locale = getLocaleOfDocument();
return date.toLocaleString(locale); return date.toLocaleString(locale);
} catch (e) { } catch (e) {
throw new Error("unsupported locale or missing format (" + e.message + ")"); throw new Error(`unsupported locale or missing format (${e.message})`);
} }
case "date": case "date":
...@@ -700,12 +695,10 @@ function transform(value) { ...@@ -700,12 +695,10 @@ function transform(value) {
try { try {
locale = getLocaleOfDocument(); locale = getLocaleOfDocument();
return date.toLocaleDateString(locale); return date.toLocaleDateString(locale);
} catch (e) { } catch (e) {
throw new Error("unsupported locale or missing format (" + e.message + ")"); throw new Error(`unsupported locale or missing format (${e.message})`);
} }
case "year": case "year":
date = new Date(value); date = new Date(value);
if (isNaN(date.getTime())) { if (isNaN(date.getTime())) {
......
...@@ -286,7 +286,6 @@ class CustomElement extends HTMLElement { ...@@ -286,7 +286,6 @@ class CustomElement extends HTMLElement {
}; };
} }
/** /**
* This method updates the labels of the element. * This method updates the labels of the element.
* The labels are defined in the options object. * The labels are defined in the options object.
...@@ -315,7 +314,7 @@ class CustomElement extends HTMLElement { ...@@ -315,7 +314,7 @@ class CustomElement extends HTMLElement {
if (isString(def)) { if (isString(def)) {
const text = translations.getText(key, def); const text = translations.getText(key, def);
if (text !== def) { if (text !== def) {
this.setOption("labels." + key, text); this.setOption(`labels.${key}`, text);
} }
continue; continue;
} else if (isObject(def)) { } else if (isObject(def)) {
...@@ -327,14 +326,13 @@ class CustomElement extends HTMLElement { ...@@ -327,14 +326,13 @@ class CustomElement extends HTMLElement {
throw new Error("Invalid labels definition"); throw new Error("Invalid labels definition");
} }
if (text !== d) { if (text !== d) {
this.setOption("labels." + key + "." + k, text); this.setOption(`labels.${key}.${k}`, text);
} }
} }
continue; continue;
} }
throw new Error("Invalid labels definition"); throw new Error("Invalid labels definition");
} }
return this; return this;
} }
...@@ -352,7 +350,6 @@ class CustomElement extends HTMLElement { ...@@ -352,7 +350,6 @@ class CustomElement extends HTMLElement {
throw new Error("the method getTag must be overwritten by the derived class."); throw new Error("the method getTag must be overwritten by the derived class.");
} }
/** /**
* At this point a `CSSStyleSheet` object can be returned. If the environment does not * At this point a `CSSStyleSheet` object can be returned. If the environment does not
* support a constructor, then an object can also be built using the following detour. * support a constructor, then an object can also be built using the following detour.
...@@ -422,8 +419,7 @@ class CustomElement extends HTMLElement { ...@@ -422,8 +419,7 @@ class CustomElement extends HTMLElement {
try { try {
value = new Pathfinder(this[internalSymbol].getRealSubject()["options"]).getVia(path); value = new Pathfinder(this[internalSymbol].getRealSubject()["options"]).getVia(path);
} catch (e) { } catch (e) {}
}
if (value === undefined) return defaultValue; if (value === undefined) return defaultValue;
return value; return value;
...@@ -493,8 +489,7 @@ class CustomElement extends HTMLElement { ...@@ -493,8 +489,7 @@ class CustomElement extends HTMLElement {
try { try {
initShadowRoot.call(self); initShadowRoot.call(self);
elements = self.shadowRoot.childNodes; elements = self.shadowRoot.childNodes;
} catch (e) { } catch (e) {}
}
try { try {
initCSSStylesheet.call(this); initCSSStylesheet.call(this);
...@@ -545,8 +540,7 @@ class CustomElement extends HTMLElement { ...@@ -545,8 +540,7 @@ class CustomElement extends HTMLElement {
* @return {void} * @return {void}
* @since 1.7.0 * @since 1.7.0
*/ */
disconnectedCallback() { disconnectedCallback() {}
}
/** /**
* The custom element has been moved into a new document (e.g. someone called document.adoptNode(el)). * The custom element has been moved into a new document (e.g. someone called document.adoptNode(el)).
...@@ -554,8 +548,7 @@ class CustomElement extends HTMLElement { ...@@ -554,8 +548,7 @@ class CustomElement extends HTMLElement {
* @return {void} * @return {void}
* @since 1.7.0 * @since 1.7.0
*/ */
adoptedCallback() { adoptedCallback() {}
}
/** /**
* Called when an observed attribute has been added, removed, updated, or replaced. Also called for initial * Called when an observed attribute has been added, removed, updated, or replaced. Also called for initial
...@@ -792,8 +785,7 @@ function parseOptionsJSON(data) { ...@@ -792,8 +785,7 @@ function parseOptionsJSON(data) {
try { try {
let dataUrl = parseDataURL(data); let dataUrl = parseDataURL(data);
data = dataUrl.content; data = dataUrl.content;
} catch (e) { } catch (e) {}
}
try { try {
obj = JSON.parse(data); obj = JSON.parse(data);
...@@ -815,7 +807,6 @@ function initHtmlContent() { ...@@ -815,7 +807,6 @@ function initHtmlContent() {
} catch (e) { } catch (e) {
let html = this.getOption("templates.main", ""); let html = this.getOption("templates.main", "");
if (isString(html) && html.length > 0) { if (isString(html) && html.length > 0) {
const mapping = this.getOption("templateMapping", {}); const mapping = this.getOption("templateMapping", {});
if (isObject(mapping)) { if (isObject(mapping)) {
html = new Formatter(mapping).format(html); html = new Formatter(mapping).format(html);
...@@ -938,7 +929,7 @@ function initShadowRoot() { ...@@ -938,7 +929,7 @@ function initShadowRoot() {
*/ */
function registerCustomElement(element) { function registerCustomElement(element) {
validateFunction(element); validateFunction(element);
const customElements = getGlobalObject("customElements") const customElements = getGlobalObject("customElements");
if (customElements === undefined) { if (customElements === undefined) {
throw new Error("customElements is not supported."); throw new Error("customElements is not supported.");
} }
......
...@@ -5,9 +5,9 @@ ...@@ -5,9 +5,9 @@
* License text available at https://www.gnu.org/licenses/agpl-3.0.en.html * License text available at https://www.gnu.org/licenses/agpl-3.0.en.html
*/ */
import {getWindow} from './util.mjs'; import { getWindow } from "./util.mjs";
export {convertToPixels, getDeviceDPI} export { convertToPixels, getDeviceDPI };
/** /**
* Stores the DPI of the device. * Stores the DPI of the device.
...@@ -20,7 +20,7 @@ export {convertToPixels, getDeviceDPI} ...@@ -20,7 +20,7 @@ export {convertToPixels, getDeviceDPI}
let CURRENT_DEVICE_DPI = function () { let CURRENT_DEVICE_DPI = function () {
let i = 0; let i = 0;
for (i = 56; i < 2000; i++) { for (i = 56; i < 2000; i++) {
if (getWindow().matchMedia("(max-resolution: " + i + "dpi)").matches === true) { if (getWindow().matchMedia(`(max-resolution: ${i}dpi)`).matches === true) {
return i; return i;
} }
} }
...@@ -36,14 +36,13 @@ let CURRENT_DEVICE_DPI = function () { ...@@ -36,14 +36,13 @@ let CURRENT_DEVICE_DPI = function () {
*/ */
function getDeviceDPI() { function getDeviceDPI() {
// only call the function once // only call the function once
if (typeof CURRENT_DEVICE_DPI === 'function') { if (typeof CURRENT_DEVICE_DPI === "function") {
CURRENT_DEVICE_DPI = CURRENT_DEVICE_DPI(); CURRENT_DEVICE_DPI = CURRENT_DEVICE_DPI();
} }
return getWindow().devicePixelRatio * CURRENT_DEVICE_DPI; return getWindow().devicePixelRatio * CURRENT_DEVICE_DPI;
} }
/** /**
* Converts a CSS value to pixels. * Converts a CSS value to pixels.
* *
...@@ -79,30 +78,28 @@ function convertToPixels(value, parentElement = document.documentElement, fontSi ...@@ -79,30 +78,28 @@ function convertToPixels(value, parentElement = document.documentElement, fontSi
const number = parseFloat(num); const number = parseFloat(num);
const dpi = getDeviceDPI(); const dpi = getDeviceDPI();
if (unit === 'px') { if (unit === "px") {
return number; return number;
} else if (unit === 'em') { } else if (unit === "em") {
const fontSize = parseFloat(window.getComputedStyle(fontSizeElement).fontSize); const fontSize = parseFloat(window.getComputedStyle(fontSizeElement).fontSize);
return number * fontSize; return number * fontSize;
} else if (unit === 'rem') { } else if (unit === "rem") {
const rootFontSize = parseFloat(window.getComputedStyle(parentElement).fontSize); const rootFontSize = parseFloat(window.getComputedStyle(parentElement).fontSize);
return number * rootFontSize; return number * rootFontSize;
} else if (unit === '%') { } else if (unit === "%") {
const parentWidth = parseFloat(window.getComputedStyle(parentElement).width); const parentWidth = parseFloat(window.getComputedStyle(parentElement).width);
return (number * parentWidth) / 100; return (number * parentWidth) / 100;
} else if (unit === 'in') { } else if (unit === "in") {
return number * dpi; return number * dpi;
} else if (unit === 'cm') { } else if (unit === "cm") {
return (number * dpi) / 2.54; return (number * dpi) / 2.54;
} else if (unit === 'mm') { } else if (unit === "mm") {
return (number * dpi) / 25.4; return (number * dpi) / 25.4;
} else if (unit === 'pt') { } else if (unit === "pt") {
return (number * dpi) / 72; return (number * dpi) / 72;
} else if (unit === 'pc') { } else if (unit === "pc") {
return (number * dpi) / 6; return (number * dpi) / 6;
} else { } else {
throw new Error(`Unsupported unit: ${unit}`); throw new Error(`Unsupported unit: ${unit}`);
} }
} }
...@@ -74,14 +74,17 @@ class ResourceManager extends Base { ...@@ -74,14 +74,17 @@ class ResourceManager extends Base {
* @property {Array} resources.data=[] array with {@link Monster.DOM.Resource.Data} objects * @property {Array} resources.data=[] array with {@link Monster.DOM.Resource.Data} objects
*/ */
get internalDefaults() { get internalDefaults() {
return Object.assign({}, { return Object.assign(
{},
{
document: getGlobalObject("document"), document: getGlobalObject("document"),
resources: { resources: {
scripts: [], scripts: [],
stylesheets: [], stylesheets: [],
data: [], data: [],
}, },
}); },
);
} }
/** /**
......
...@@ -57,7 +57,6 @@ function getSlottedNodes(query, name) { ...@@ -57,7 +57,6 @@ function getSlottedNodes(query, name) {
return result; return result;
} }
/** /**
* @private * @private
* @param {String|undefined} query * @param {String|undefined} query
......
...@@ -17,7 +17,7 @@ import { ...@@ -17,7 +17,7 @@ import {
ATTRIBUTE_UPDATER_INSERT_REFERENCE, ATTRIBUTE_UPDATER_INSERT_REFERENCE,
ATTRIBUTE_UPDATER_REMOVE, ATTRIBUTE_UPDATER_REMOVE,
ATTRIBUTE_UPDATER_REPLACE, ATTRIBUTE_UPDATER_REPLACE,
ATTRIBUTE_UPDATER_SELECT_THIS ATTRIBUTE_UPDATER_SELECT_THIS,
} from "../dom/constants.mjs"; } from "../dom/constants.mjs";
import { Base } from "../types/base.mjs"; import { Base } from "../types/base.mjs";
......
...@@ -152,7 +152,6 @@ function getDocumentFragmentFromString(html) { ...@@ -152,7 +152,6 @@ function getDocumentFragmentFromString(html) {
return template.content; return template.content;
} }
/** /**
* Recursively searches upwards from a given element to find an ancestor element * Recursively searches upwards from a given element to find an ancestor element
* with a specified ID, considering both normal DOM and shadow DOM. * with a specified ID, considering both normal DOM and shadow DOM.
......
...@@ -33,7 +33,6 @@ const translationsLinkSymbol = Symbol.for("@schukai/monster/i18n/translations@@l ...@@ -33,7 +33,6 @@ const translationsLinkSymbol = Symbol.for("@schukai/monster/i18n/translations@@l
* @see {@link https://datatracker.ietf.org/doc/html/rfc3066} * @see {@link https://datatracker.ietf.org/doc/html/rfc3066}
*/ */
class Provider extends BaseWithOptions { class Provider extends BaseWithOptions {
/** /**
* This method is called by the `instanceof` operator. * This method is called by the `instanceof` operator.
* @returns {symbol} * @returns {symbol}
...@@ -48,7 +47,6 @@ class Provider extends BaseWithOptions { ...@@ -48,7 +47,6 @@ class Provider extends BaseWithOptions {
* @return {Promise} * @return {Promise}
*/ */
getTranslations(locale) { getTranslations(locale) {
if (locale === undefined) { if (locale === undefined) {
locale = getLocaleOfDocument(); locale = getLocaleOfDocument();
} }
...@@ -68,7 +66,6 @@ class Provider extends BaseWithOptions { ...@@ -68,7 +66,6 @@ class Provider extends BaseWithOptions {
* @return {Provider} * @return {Provider}
*/ */
assignToElement(locale, element) { assignToElement(locale, element) {
if (locale === undefined) { if (locale === undefined) {
locale = getLocaleOfDocument(); locale = getLocaleOfDocument();
} }
...@@ -86,7 +83,6 @@ class Provider extends BaseWithOptions { ...@@ -86,7 +83,6 @@ class Provider extends BaseWithOptions {
} }
return this.getTranslations(locale).then((obj) => { return this.getTranslations(locale).then((obj) => {
let translations = null; let translations = null;
if (hasObjectLink(element, translationsLinkSymbol)) { if (hasObjectLink(element, translationsLinkSymbol)) {
const objects = getLinkedObjects(element, translationsLinkSymbol); const objects = getLinkedObjects(element, translationsLinkSymbol);
...@@ -102,15 +98,11 @@ class Provider extends BaseWithOptions { ...@@ -102,15 +98,11 @@ class Provider extends BaseWithOptions {
} }
translations.assignTranslations(obj); translations.assignTranslations(obj);
} else { } else {
addToObjectLink(element, translationsLinkSymbol, obj); addToObjectLink(element, translationsLinkSymbol, obj);
} }
return obj; return obj;
}); });
} }
} }
...@@ -91,7 +91,6 @@ class Embed extends Provider { ...@@ -91,7 +91,6 @@ class Embed extends Provider {
} }
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
if (this.translateElement === null) { if (this.translateElement === null) {
reject(new Error("Text not found")); reject(new Error("Text not found"));
return; return;
...@@ -127,7 +126,6 @@ class Embed extends Provider { ...@@ -127,7 +126,6 @@ class Embed extends Provider {
}); });
} }
/** /**
* Initializes the translations for the current document. * Initializes the translations for the current document.
* *
...@@ -137,7 +135,7 @@ class Embed extends Provider { ...@@ -137,7 +135,7 @@ class Embed extends Provider {
* @returns {Promise<unknown[]>} * @returns {Promise<unknown[]>}
*/ */
static assignTranslationsToElement(element) { static assignTranslationsToElement(element) {
const d = getDocument() const d = getDocument();
if (!(element instanceof HTMLElement)) { if (!(element instanceof HTMLElement)) {
element = d.querySelector("body"); element = d.querySelector("body");
...@@ -157,5 +155,4 @@ class Embed extends Provider { ...@@ -157,5 +155,4 @@ class Embed extends Provider {
return Promise.all(promises); return Promise.all(promises);
} }
} }
...@@ -15,7 +15,6 @@ import {validateInteger, validateObject, validateString} from "../types/validate ...@@ -15,7 +15,6 @@ import {validateInteger, validateObject, validateString} from "../types/validate
import { Locale, parseLocale } from "./locale.mjs"; import { Locale, parseLocale } from "./locale.mjs";
import { translationsLinkSymbol } from "./provider.mjs"; import { translationsLinkSymbol } from "./provider.mjs";
export { Translations, getDocumentTranslations }; export { Translations, getDocumentTranslations };
/** /**
...@@ -211,11 +210,10 @@ class Translations extends Base { ...@@ -211,11 +210,10 @@ class Translations extends Base {
* @memberOf Monster.I18n * @memberOf Monster.I18n
*/ */
function getDocumentTranslations(element) { function getDocumentTranslations(element) {
const d = getDocument();
const d = getDocument()
if (!(element instanceof HTMLElement)) { if (!(element instanceof HTMLElement)) {
element = d.querySelector('[' + ATTRIBUTE_OBJECTLINK + '~="' + translationsLinkSymbol.toString() + '"]'); element = d.querySelector(`[${ATTRIBUTE_OBJECTLINK}~="${translationsLinkSymbol.toString()}"]`);
if (element === null) { if (element === null) {
throw new Error("Cannot find element with translations. Add a translations object to the document."); throw new Error("Cannot find element with translations. Add a translations object to the document.");
} }
...@@ -238,7 +236,4 @@ function getDocumentTranslations(element) { ...@@ -238,7 +236,4 @@ function getDocumentTranslations(element) {
} }
throw new Error("Missing translations."); throw new Error("Missing translations.");
} }
export {generateRangeComparisonExpression} export { generateRangeComparisonExpression };
/** /**
* Generates a comparison expression for a comma-separated string of ranges and single values. * The `generateRangeComparisonExpression()` function is function that generates a string representation
* of a comparison expression based on a range of values. It takes three arguments:
*
* - expression (required): a string representation of a range of values in the format of start1-end1,start2-end2,value3....
* - valueName (required): a string representing the name of the value that is being compared to the range of values.
* - options (optional): an object containing additional options to customize the comparison expression.
*
* The generateRangeComparisonExpression() function returns a string representation of the comparison expression.
*
* ## Options
* The options parameter is an object that can have the following properties:
*
* urlEncode (boolean, default: false): if set to true, URL encodes the comparison operators.
* andOp (string, default: '&&'): the logical AND operator to use in the expression.
* orOp (string, default: '||'): the logical OR operator to use in the expression.
* eqOp (string, default: '=='): the equality operator to use in the expression.
* geOp (string, default: '>='): the greater than or equal to operator to use in the expression.
* leOp (string, default: '<='): the less than or equal to operator to use in the expression.
*
* Examples
*
* ```javascript
* const expression = '0-10,20-30';
* const valueName = 'age';
* const options = { urlEncode: true, andOp: 'and', orOp: 'or', eqOp: '=', geOp: '>=', leOp: '<=' };
* const comparisonExpression = generateRangeComparisonExpression(expression, valueName, options);
*
* console.log(comparisonExpression); // age%3E%3D0%20and%20age%3C%3D10%20or%20age%3E%3D20%20and%20age%3C%3D30
* ```
*
* In this example, the generateRangeComparisonExpression() function generates a string representation of the comparison
* expression for the expression and valueName parameters with the specified options. The resulting comparison
* expression is 'age>=0 and age<=10 or age>=20 and age<=30', URL encoded according to the urlEncode option.
*
* @param {string} expression - The string expression to generate the comparison for. * @param {string} expression - The string expression to generate the comparison for.
* @param {string} valueName - The name of the value to compare against. * @param {string} valueName - The name of the value to compare against.
* @param {Object} [options] - The optional parameters. * @param {Object} [options] - The optional parameters.
...@@ -13,33 +46,29 @@ export {generateRangeComparisonExpression} ...@@ -13,33 +46,29 @@ export {generateRangeComparisonExpression}
* @param {string} [options.leOp='<='] - The comparison operator for less than or equal to to use. * @param {string} [options.leOp='<='] - The comparison operator for less than or equal to to use.
* @returns {string} The generated comparison expression. * @returns {string} The generated comparison expression.
* @throws {Error} If the input is invalid. * @throws {Error} If the input is invalid.
* @memberOf Monster.Text
* @summary Generates a comparison expression based on a range of values.
*/ */
function generateRangeComparisonExpression(expression, valueName, options = {}) { function generateRangeComparisonExpression(expression, valueName, options = {}) {
const { const { urlEncode = false, andOp = "&&", orOp = "||", eqOp = "==", geOp = ">=", leOp = "<=" } = options;
urlEncode = false, const ranges = expression.split(",");
andOp = '&&', let comparison = "";
orOp = '||',
eqOp = '==',
geOp = '>=',
leOp = '<=',
} = options;
const ranges = expression.split(',');
let comparison = '';
for (let i = 0; i < ranges.length; i++) { for (let i = 0; i < ranges.length; i++) {
const range = ranges[i].trim(); const range = ranges[i].trim();
if (range === '') { if (range === "") {
throw new Error(`Invalid range '${range}'`); throw new Error(`Invalid range '${range}'`);
} else if (range.includes('-')) { } else if (range.includes("-")) {
const [start, end] = range.split('-').map(s => (s === '' ? null : parseFloat(s))); const [start, end] = range.split("-").map((s) => (s === "" ? null : parseFloat(s)));
if ((start !== null && isNaN(start)) || (end !== null && isNaN(end))) { if ((start !== null && isNaN(start)) || (end !== null && isNaN(end))) {
throw new Error(`Invalid value in range '${range}'`); throw new Error(`Invalid value in range '${range}'`);
} }
if (start !== null && end !== null && start > end) { if (start !== null && end !== null && start > end) {
throw new Error(`Invalid range '${range}'`); throw new Error(`Invalid range '${range}'`);
} }
const compStart = start !== null ? `${valueName}${urlEncode ? encodeURIComponent(geOp) : geOp}${start}` : ''; const compStart =
const compEnd = end !== null ? `${valueName}${urlEncode ? encodeURIComponent(leOp) : leOp}${end}` : ''; start !== null ? `${valueName}${urlEncode ? encodeURIComponent(geOp) : geOp}${start}` : "";
const compRange = `${compStart}${compStart && compEnd ? ` ${andOp} ` : ''}${compEnd}`; const compEnd = end !== null ? `${valueName}${urlEncode ? encodeURIComponent(leOp) : leOp}${end}` : "";
const compRange = `${compStart}${compStart && compEnd ? ` ${andOp} ` : ""}${compEnd}`;
comparison += ranges.length > 1 ? `(${compRange})` : compRange; comparison += ranges.length > 1 ? `(${compRange})` : compRange;
} else { } else {
const value = parseFloat(range); const value = parseFloat(range);
......
...@@ -14,13 +14,13 @@ import {ProxyObserver} from "./proxyobserver.mjs"; ...@@ -14,13 +14,13 @@ import {ProxyObserver} from "./proxyobserver.mjs";
import { validateObject } from "./validate.mjs"; import { validateObject } from "./validate.mjs";
import { isObject } from "./is.mjs"; import { isObject } from "./is.mjs";
export {equipWithInternal} export { equipWithInternal };
/** /**
* @private * @private
* @type {string} * @type {string}
*/ */
const propertyName = 'internalDefaults' const propertyName = "internalDefaults";
/** /**
* This function extends the given object with the following methods: * This function extends the given object with the following methods:
...@@ -45,7 +45,7 @@ function equipWithInternal() { ...@@ -45,7 +45,7 @@ function equipWithInternal() {
Object.defineProperty(self, propertyName, { Object.defineProperty(self, propertyName, {
get: function () { get: function () {
return {}; return {};
} },
}); });
} }
...@@ -61,7 +61,7 @@ function equipWithInternal() { ...@@ -61,7 +61,7 @@ function equipWithInternal() {
self["attachInternalObserver"] = (observer) => { self["attachInternalObserver"] = (observer) => {
self[internalSymbol].attachObserver(observer); self[internalSymbol].attachObserver(observer);
return self; return self;
} };
/** /**
* Detach a observer * Detach a observer
...@@ -72,7 +72,7 @@ function equipWithInternal() { ...@@ -72,7 +72,7 @@ function equipWithInternal() {
self["detachInternalObserver"] = (observer) => { self["detachInternalObserver"] = (observer) => {
self[internalSymbol].detachObserver(observer); self[internalSymbol].detachObserver(observer);
return self; return self;
} };
/** /**
* Check if a observer is attached * Check if a observer is attached
...@@ -82,8 +82,7 @@ function equipWithInternal() { ...@@ -82,8 +82,7 @@ function equipWithInternal() {
*/ */
self["containsInternalObserver"] = (observer) => { self["containsInternalObserver"] = (observer) => {
return self[internalSymbol].containsObserver(observer); return self[internalSymbol].containsObserver(observer);
} };
/** /**
* Set an internal value, nested internals can be specified by path `a.b.c` * Set an internal value, nested internals can be specified by path `a.b.c`
...@@ -95,7 +94,7 @@ function equipWithInternal() { ...@@ -95,7 +94,7 @@ function equipWithInternal() {
self["setInternal"] = (path, value) => { self["setInternal"] = (path, value) => {
new Pathfinder(self[internalSymbol].getSubject()).setVia(path, value); new Pathfinder(self[internalSymbol].getSubject()).setVia(path, value);
return self; return self;
} };
/** /**
* set multiple internals at once * set multiple internals at once
...@@ -111,7 +110,7 @@ function equipWithInternal() { ...@@ -111,7 +110,7 @@ function equipWithInternal() {
extend(self[internalSymbol].getSubject(), defaults, options); extend(self[internalSymbol].getSubject(), defaults, options);
return self; return self;
} };
/** /**
* nested internals can be specified by path `a.b.c` * nested internals can be specified by path `a.b.c`
...@@ -124,14 +123,12 @@ function equipWithInternal() { ...@@ -124,14 +123,12 @@ function equipWithInternal() {
let value; let value;
try { try {
value = new Pathfinder(self[internalSymbol] value = new Pathfinder(self[internalSymbol].getRealSubject()).getVia(path);
.getRealSubject()).getVia(path); } catch (e) {}
} catch (e) {
}
if (value === undefined) return defaultValue; if (value === undefined) return defaultValue;
return value; return value;
} };
} }
/** /**
...@@ -141,9 +138,8 @@ function equipWithInternal() { ...@@ -141,9 +138,8 @@ function equipWithInternal() {
* @return {boolean} * @return {boolean}
*/ */
function hasGetter(obj, prop) { function hasGetter(obj, prop) {
while (isObject(obj)) { while (isObject(obj)) {
if (Object.getOwnPropertyDescriptor(obj, prop)?.['get']) { if (Object.getOwnPropertyDescriptor(obj, prop)?.["get"]) {
return true; return true;
} }
obj = Object.getPrototypeOf(obj); obj = Object.getPrototypeOf(obj);
...@@ -168,8 +164,7 @@ function parseOptionsJSON(data) { ...@@ -168,8 +164,7 @@ function parseOptionsJSON(data) {
try { try {
let dataUrl = parseDataURL(data); let dataUrl = parseDataURL(data);
data = dataUrl.content; data = dataUrl.content;
} catch (e) { } catch (e) {}
}
try { try {
obj = JSON.parse(data); obj = JSON.parse(data);
......
...@@ -5,14 +5,46 @@ ...@@ -5,14 +5,46 @@
* License text available at https://www.gnu.org/licenses/agpl-3.0.en.html * License text available at https://www.gnu.org/licenses/agpl-3.0.en.html
*/ */
const ENV_AWS_LAMBDA = 'aws-lambda'; /**
const ENV_GOOGLE_FUNCTIONS = 'google-functions'; * @memberOf Monster.Util
const ENV_ELECTRON = 'electron'; * @type {string}
const ENV_NODE = 'node'; */
const ENV_BROWSER = 'browser'; const ENV_AWS_LAMBDA = "aws-lambda";
const ENV_WEB_WORKER = 'web-worker'; /**
const ENV_DENO = 'deno'; * @memberOf Monster.Util
const ENV_UNKNOWN = 'unknown'; * @type {string}
*/
const ENV_GOOGLE_FUNCTIONS = "google-functions";
/**
* @memberOf Monster.Util
* @type {string}
*/
const ENV_ELECTRON = "electron";
/**
* @memberOf Monster.Util
* @type {string}
*/
const ENV_NODE = "node";
/**
* @memberOf Monster.Util
* @type {string}
*/
const ENV_BROWSER = "browser";
/**
* @memberOf Monster.Util
* @type {string}
*/
const ENV_WEB_WORKER = "web-worker";
/**
* @memberOf Monster.Util
* @type {string}
*/
const ENV_DENO = "deno";
/**
* @memberOf Monster.Util
* @type {string}
*/
const ENV_UNKNOWN = "unknown";
/** /**
* Detects and returns the current runtime environment. * Detects and returns the current runtime environment.
...@@ -33,29 +65,17 @@ const ENV_UNKNOWN = 'unknown'; ...@@ -33,29 +65,17 @@ const ENV_UNKNOWN = 'unknown';
*/ */
function detectRuntimeEnvironment() { function detectRuntimeEnvironment() {
// AWS Lambda environment // AWS Lambda environment
if ( if (typeof process !== "undefined" && process.env != null && process.env.AWS_LAMBDA_FUNCTION_NAME) {
typeof process !== 'undefined' &&
process.env != null &&
process.env.AWS_LAMBDA_FUNCTION_NAME
) {
return ENV_AWS_LAMBDA; return ENV_AWS_LAMBDA;
} }
// Google Cloud Functions environment // Google Cloud Functions environment
if ( if (typeof process !== "undefined" && process.env != null && process.env.FUNCTION_NAME) {
typeof process !== 'undefined' &&
process.env != null &&
process.env.FUNCTION_NAME
) {
return ENV_GOOGLE_FUNCTIONS; return ENV_GOOGLE_FUNCTIONS;
} }
// Node.js environment // Node.js environment
if ( if (typeof process !== "undefined" && process.versions != null && process.versions.node != null) {
typeof process !== 'undefined' &&
process.versions != null &&
process.versions.node != null
) {
// Electron environment // Electron environment
if (process.versions.electron != null) { if (process.versions.electron != null) {
return ENV_ELECTRON; return ENV_ELECTRON;
...@@ -65,20 +85,20 @@ function detectRuntimeEnvironment() { ...@@ -65,20 +85,20 @@ function detectRuntimeEnvironment() {
// Browser environment // Browser environment
if ( if (
typeof window !== 'undefined' && typeof window !== "undefined" &&
typeof window.document !== 'undefined' && typeof window.document !== "undefined" &&
typeof navigator !== 'undefined' && typeof navigator !== "undefined" &&
typeof navigator.userAgent === 'string' typeof navigator.userAgent === "string"
) { ) {
// Web Worker environment // Web Worker environment
if (typeof self === 'object' && typeof importScripts === 'function') { if (typeof self === "object" && typeof importScripts === "function") {
return ENV_WEB_WORKER; return ENV_WEB_WORKER;
} }
return ENV_BROWSER; return ENV_BROWSER;
} }
// Deno environment // Deno environment
if (typeof Deno !== 'undefined') { if (typeof Deno !== "undefined") {
return ENV_DENO; return ENV_DENO;
} }
...@@ -95,4 +115,5 @@ export { ...@@ -95,4 +115,5 @@ export {
ENV_WEB_WORKER, ENV_WEB_WORKER,
ENV_DENO, ENV_DENO,
ENV_UNKNOWN, ENV_UNKNOWN,
detectRuntimeEnvironment} detectRuntimeEnvironment,
\ No newline at end of file };
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment