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

feat: integrate components in monster #127

parent 67d132a9
No related branches found
No related tags found
No related merge requests found
Showing
with 2519 additions and 863 deletions
......@@ -76,7 +76,7 @@ We do try to work around some browser bugs, but on the whole we don't use polyfi
However, many functions can be mapped via [polyfill.io](https://polyfill.io/) and thus the compatibility can be increased.
```html
<script id="polyfill" src="https://polyfill.io/v3/polyfill.min.js?features=Array.from,Array.isArray,Array.prototype.entries,Array.prototype.fill,Array.prototype.filter,Array.prototype.forEach,Array.prototype.includes,Array.prototype.indexOf,Array.prototype.keys,Array.prototype.lastIndexOf,Array.prototype.map,Array.prototype.reduce,Array.prototype.sort,ArrayBuffer,atob,Blob,CustomEvent,DataView,document,Document,DocumentFragment,Element,Event,fetch,globalThis,HTMLDocument,HTMLTemplateElement,Intl,JSON,Map,Math.log2,MutationObserver,Number.isInteger,Object.assign,Object.defineProperty,Object.entries,Object.freeze,Object.getOwnPropertyDescriptor,Object.getOwnPropertyNames,Object.getPrototypeOf,Object.keys,Promise,Reflect,Reflect.defineProperty,Reflect.get,Reflect.getOwnPropertyDescriptor,Reflect.setPrototypeOf,Set,String.prototype.endsWith,String.prototype.includes,String.prototype.matchAll,String.prototype.padStart,String.prototype.startsWith,String.prototype.trim,Symbol,Symbol.for,Symbol.hasInstance,Symbol.iterator,Uint16Array,Uint8Array,URL,WeakMap,WeakSet"
<script id="polyfill" src="https://polyfill.io/v3/polyfill.min.js?features=Array.from,Array.isArray,Array.prototype.entries,Array.prototype.fill,Array.prototype.filter,Array.prototype.forEach,Array.prototype.includes,Array.prototype.indexOf,Array.prototype.keys,Array.prototype.lastIndexOf,Array.prototype.map,Array.prototype.reduce,Array.prototype.sort,ArrayBuffer,atob,Blob,console,CustomEvent,DataView,document,Document,DocumentFragment,Element,Event,fetch,globalThis,HTMLDocument,HTMLTemplateElement,Intl,JSON,Map,Math.log2,MutationObserver,Number.isInteger,Object.assign,Object.defineProperty,Object.entries,Object.freeze,Object.getOwnPropertyDescriptor,Object.getOwnPropertyNames,Object.getPrototypeOf,Object.keys,Promise,Reflect,Reflect.defineProperty,Reflect.get,Reflect.getOwnPropertyDescriptor,Reflect.setPrototypeOf,Set,String.prototype.endsWith,String.prototype.includes,String.prototype.matchAll,String.prototype.padStart,String.prototype.startsWith,String.prototype.trim,Symbol,Symbol.for,Symbol.hasInstance,Symbol.iterator,Uint16Array,Uint8Array,URL,WeakMap,WeakSet"
crossorigin="anonymous"
referrerpolicy="no-referrer"></script>
```
......
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Drag and drop test</title>
<script type="importmap">
{
"imports": {
"@schukai/monster/": "https://cdn.skypack.dev/@schukai/monster/",
"@schukai/component-form/": "https://cdn.skypack.dev/@schukai/component-form/"
}
}
</script>
<style>
html, body {
height: 100%;
width: 100%;
margin: 0;
padding: 0;
}
.instructions {
display: none;
}
.instructions, .events, .output {
pointer-events: none;
}
.clear-events {
-webkit-appearance: none;
-moz-appearance: none;
cursor: pointer;
display: inline-block;
font-size: 1rem;
border: 2px solid #ccc;
background: white;
border-radius: 3px;
outline: none;
pointer-events: all;
}
.droptarget {
position: absolute;
right: 20px;
top: 20px;
width: 200px;
height: 200px;
background: #eee;
}
.status, .events {
font-family: monospace;
white-space: pre-wrap;
}
</style>
</head>
<body>
<h1>Drag and Drop test</h1>
<div class="output">
<h2>Status:</h2>
<p class="status"></p>
<h2>Events:
<button class="clear-events"></button>
</h2>
<p class="events"></p>
</div>
<div class="droptarget"></div>
<script>
(function () {
var logDragOverEvents = false;
var events = [];
var currentEvent = null;
var draggingInPage = false;
var draggingFiles = false;
var isFolder = false;
var detectedMimeTypes = null;
var draggingInDropTarget = false;
var eventFilesProperty = 'unknown';
var mimeTypeProperty = 'unknown';
var dragCounter = 0;
updateOutput();
document.body.addEventListener('dragenter', onBodyDragDropEvent);
document.body.addEventListener('dragover', onBodyDragDropEvent);
document.body.addEventListener('dragleave', onBodyDragDropEvent);
document.body.addEventListener('drop', onBodyDragDropEvent);
document.querySelector('.clear-events').addEventListener('click', function () {
events = [];
updateOutput();
});
var droptarget = document.querySelector('.droptarget');
droptarget.addEventListener('dragenter', onDropTargetEvent);
droptarget.addEventListener('dragover', onDropTargetEvent);
droptarget.addEventListener('dragleave', onDropTargetEvent);
droptarget.addEventListener('drop', onDropTargetEvent);
function onBodyDragDropEvent(event) {
draggingFiles = eventHasFiles(event);
var mimeTypes = getMimeTypesFromEvent(event);
detectedMimeTypes = mimeTypes;
if (event.type !== 'dragover' || logDragOverEvents) {
var loggedEvent = {
'@@class': getObjectClassName(event),
type: event.type,
target: event.target,
files: draggingFiles
};
if (event.srcElement !== undefined && event.srcElement !== event.target) {
loggedEvent.srcElement = event.srcElement;
}
if (event.fromElement) {
loggedEvent.fromElement = event.fromElement;
}
if (event.relatedTarget) {
loggedEvent.relatedTarget = event.relatedTarget;
}
if (event.defaultPrevented) {
loggedEvent.defaultPrevented = event.defaultPrevented;
}
if (event.currentTarget !== document.body) {
loggedEvent.currentTarget = event.currentTarget;
}
if (mimeTypes) {
loggedEvent.mimeTypes = mimeTypes;
}
events.push(formatValue(loggedEvent).replace(/\n\s*/g, ' '));
currentEvent = {
'@@class': getObjectClassName(event),
type: event.type,
target: event.target,
currentTarget: event.currentTarget,
relatedTarget: event.relatedTarget,
srcElement: event.srcElement
};
if (event.dataTransfer) {
currentEvent.dataTransfer = {
'@@class': getObjectClassName(event.dataTransfer),
dropEffect: event.dataTransfer.dropEffect,
effectAllowed: event.dataTransfer.effectAllowed,
types: asArray(event.dataTransfer.types)
};
currentEvent.dataTransfer.files = event.dataTransfer.files
&& asArray(event.dataTransfer.files, function (file) {
return {
'@@class': getObjectClassName(file),
name: file.name,
size: file.size,
type: file.type,
lastModified: file.lastModified,
lastModifiedDate: file.lastModifiedDate
};
});
currentEvent.dataTransfer.items = event.dataTransfer.items
&& asArray(event.dataTransfer.items, function (item) {
var entry = item.webkitGetAsEntry && item.webkitGetAsEntry();
var itemClone = {
'@@class': getObjectClassName(item),
kind: item.kind,
type: item.type
};
if (item.kind === 'file') {
itemClone['webkitGetAsEntry()'] = entry && {
'@@class': getObjectClassName(entry),
isDirectory: entry.isDirectory,
isFile: entry.isFile,
name: entry.name,
fullPath: entry.fullPath
};
}
return itemClone;
});
}
}
if (event.type !== 'dragleave') {
event.preventDefault();
}
if (event.type === 'dragenter') {
dragCounter += 1;
} else if (event.type === 'dragleave') {
dragCounter = dragCounter > 0 ? dragCounter - 1 : 0;
} else if (event.type === 'drop') {
dragCounter = 0;
}
draggingInPage = dragCounter > 0;
isFolder = eventHasFolders(event);
updateOutput();
}
var dropTargetCounter = 0;
function onDropTargetEvent(event) {
switch (event.type) {
case 'dragenter':
dropTargetCounter += 1;
draggingInDropTarget = true;
updateOutput();
break;
case 'dragleave':
dropTargetCounter -= 1;
draggingInDropTarget = (dropTargetCounter > 0);
updateOutput();
break;
case 'drop':
dropTargetCounter = 0;
draggingInDropTarget = false;
updateOutput();
}
// event.preventDefault();
}
function updateOutput() {
var status = {
'dragging anything on page?': draggingInPage,
'dragging files on page?': draggingFiles,
'dragging a folder?': isFolder,
'detected mime types': detectedMimeTypes,
'dragging in gray drop target?': draggingInDropTarget,
'client reports mime types on drag': DataTransfer && 'items' in DataTransfer.prototype,
'client supports webkitGetAsEntry': typeof DataTransferItem !== 'undefined' && !!DataTransferItem.prototype.webkitGetAsEntry,
'property used to detect files in drag events': eventFilesProperty,
'property used to detect mime types of dragged files': mimeTypeProperty,
'current event': currentEvent
};
document.querySelector('.status').innerText = formatValue(status);
document.querySelector('.events').innerText = events.join('\n');
document.querySelector('.clear-events').style.display = events.length ? '' : 'none';
}
function eventHasFiles(event) {
if (event.type === 'drop') {
return !!event.files;
}
var types = event.dataTransfer && event.dataTransfer && event.dataTransfer.types;
if (!types) {
return null;
} else if (typeof types.contains === 'function') {
eventFilesProperty = 'dataTransfer.types.contains()';
return types.contains('Files');
} else if (typeof types.indexOf === 'function') {
eventFilesProperty = 'dataTransfer.types.indexOf()';
return types.indexOf('Files') >= 0;
} else if (typeof types.length === 'number') {
eventFilesProperty = 'dataTransfer.item()';
return types.indexOf('Files') >= 0;
}
return false;
}
function eventHasFolders(event) {
var items = event.dataTransfer && event.dataTransfer.items;
if (items && items.length > 0) {
for (var index = 0; index < items.length; index++) {
if (items[index].webkitGetAsEntry) {
var entry = items[index].webkitGetAsEntry();
if (entry && entry.isDirectory) {
return true;
}
}
if (items[index].type === '') {
// This is prone to errors, since "" is the mime type of files without extension
// see README.md
return true;
}
}
}
return false;
}
function getMimeTypesFromEvent(event) {
var dataTransfer = event.dataTransfer || (event.originalEvent && event.originalEvent.dataTransfer);
if (!dataTransfer) {
return null;
} else if (dataTransfer.files && (dataTransfer.files.length > 0 || event.type === 'drop')) {
mimeTypeProperty = 'dataTransfer.files';
var mimeTypeArray = new Array(dataTransfer.files.length);
for (var i = 0; i < mimeTypeArray.length; i++) {
mimeTypeArray[i] = dataTransfer.files.item(i).type;
}
return mimeTypeArray;
} else if (dataTransfer.items && dataTransfer.items.length > 0) {
mimeTypeProperty = 'dataTransfer.items';
return [].slice.call(dataTransfer.items)
.filter(function (item) {
return item.kind === 'file';
})
.map(function (item) {
return item.type;
});
} else if ('mozItemCount' in dataTransfer) {
mimeTypeProperty = 'dataTransfer.mozItemCount';
var fakeMimeTypes = new Array(dataTransfer.mozItemCount);
for (var j = 0; j < fakeMimeTypes.length; j++) {
fakeMimeTypes[j] = 'unknown';
}
} else {
return null;
}
}
window.formatValue = formatValue;
function formatValue(value) {
return JSON.stringify(value, elementStringifier, 4)
.replace(/\{\n\s*"@@class": "([^"]+)",?/g, '$1 {')
.replace(/\[\n\s*"@@class{([^"}]+)\}",?/g, '$1 [')
.replace(/"([^"]+)": "@@undefined"/g, '"$1": undefined')
.replace(/"([^"]+)": "@@element\{([^}"\n]+?)\}"/g, '"$1": <$2>')
.replace(/"([^"]+)": /g, '$1: ');
function elementStringifier(key, value) {
if (value === undefined) {
return '@@undefined';
} else if (typeof value === 'object' && value instanceof HTMLElement) {
var elementName = value.nodeName.toLowerCase();
var id = ('#' + value.getAttribute('id')).replace(/^#(null)?$/, '');
var classes = (value.getAttribute('class') || '')
.trim()
.split(/\s+/)
.map(function (cls) {
return cls ? '.' + cls : ''
})
.join('');
return '@@element{' + elementName + id + classes + '}';
} else if (typeof value === 'object' && Array.isArray(value) && value['@@class']) {
return ['@@class{' + value['@@class'] + '}'].concat(value);
} else {
return value;
}
};
}
function getObjectClassName(obj) {
if (typeof obj !== 'object' || !obj) {
throw new TypeError();
}
var matches = Object.prototype.toString.call(obj).match(/^\[object (.+)\]$/);
if (!matches) {
throw new Error();
}
return matches[1];
}
function asArray(arrayLike, mappingFunction) {
if (Array.isArray(arrayLike)) {
return mappingFunction ? arrayLike.map(mappingFunction) : arrayLike;
} else {
var array = mappingFunction
? Array.prototype.map.call(arrayLike, mappingFunction)
: Array.prototype.slice.call(arrayLike);
array['@@class'] = getObjectClassName(arrayLike);
return array;
}
}
}());
</script>
</body>
</html>
\ No newline at end of file
<!DOCTYPE html>
<html lang="en" data-monster-theme-name="mytheme">
<head>
<meta charset="utf-8">
<title>TreeMenu | Web Components</title>
<link rel="stylesheet" type="text/css" href="demo.css">
<script type="importmap">
{
"imports": {
"@schukai/monster/": "https://cdn.skypack.dev/@schukai/monster/",
"@schukai/component-form/": "https://cdn.skypack.dev/@schukai/component-form/"
}
}
</script>
</head>
<body>
<h1 tabindex="0">TreeMenu</h1>
<div>
<div class="row">
<div class="sub" id="container" style="width:170px;height:300px">
</div>
</div>
</div>
<script type="module">
import {Updater} from "https://cdn.skypack.dev/@schukai/monster/source/dom/updater.mjs";
import {TreeMenu} from "../../../../application/source/tree-menu.mjs"
import {RestAPI} from "https://cdn.skypack.dev/@schukai/monster/source/data/datasource/restapi.js";
const datasource = new RestAPI({
url: 'https://monsterjs.org/assets/14-forms-treeselect-data.json?filter=${filter}'
}, {
url: 'https://httpbin.org/post'
});
const treeMenu = document.createElement('monster-tree-menu');
treeMenu.setOption('mapping.selector', 'dataset.*');
treeMenu.setOption('mapping.labelTemplate', '${localeLabel | index:en}');
treeMenu.setOption('mapping.valueTemplate', '${cid | tostring }');
treeMenu.setOption('mapping.parentTemplate', 'parentCID');
treeMenu.setOption('mapping.idTemplate', 'cid');
treeMenu.setOption('datasource', datasource);
// treeMenu.addEventListener('monster-change', (e) => console.log('1', e))
// treeMenu.addEventListener('monster-changed', (e) => console.log('2', e))
document.getElementById('container').appendChild(treeMenu);
</script>
</body>
</html>
\ No newline at end of file
/**
* Copyright schukai GmbH and contributors 2022. All Rights Reserved.
* Node module: @schukai/component-treemenu
* This file is licensed under the AGPLv3 License.
* License text available at https://www.gnu.org/licenses/agpl-3.0.en.html
*/
import {ATTRIBUTE_PREFIX} from "../dom/constants.mjs";
/**
* `data-monster-intend`
*
* @memberOf Monster.Components
* @since 1.8.0
* @type {string}
*/
const ATTRIBUTE_INTEND = ATTRIBUTE_PREFIX + 'intend';
export {
ATTRIBUTE_INTEND
}
\ No newline at end of file
/**
* Copyright 2023 schukai GmbH
* SPDX-License-Identifier: AGPL-3.0
*/
/**
* Constraints are used to define conditions that must be met by the value of a variable so that the value can be transferred to the system.
*
* @namespace Monster.Components
* @memberOf Monster
* @author schukai GmbH
*/
const ns = {};
/**
* Copyright schukai GmbH and contributors 2022. All Rights Reserved.
* Node module: @schukai/component-form
* This file is licensed under the AGPLv3 License.
* License text available at https://www.gnu.org/licenses/agpl-3.0.en.html
*/
import {addAttributeToken} from "../../dom/attributes.mjs";
import {ATTRIBUTE_ERRORMESSAGE} from "../../dom/constants.mjs";
export {CommonStylesheet}
/**
* @private
* @type {CSSStyleSheet}
*/
const CommonStylesheet = new CSSStyleSheet();
/**
* @private
* @type {number}
*/
let index = 0;
/**
* @private
*/
// language=CSS
`/*--*/
html, body, div, span, applet, object, iframe,
h1, h2, h3, h4, h5, h6, p, blockquote, pre,
a, abbr, acronym, address, big, cite, code,
del, dfn, em, img, ins, kbd, q, s, samp,
small, strike, strong, sub, sup, tt, var,
b, u, i, center,
dl, dt, dd, ol, ul, li,
fieldset, form, label, legend,
table, caption, tbody, tfoot, thead, tr, th, td,
article, aside, canvas, details, embed,
figure, figcaption, footer, header, hgroup,
menu, nav, output, ruby, section, summary,
time, mark, audio, video {
margin: 0;
padding: 0;
border: 0;
font-size: 100%;
font: inherit;
vertical-align: baseline;
}
/*--*/
* {
-webkit-box-sizing: border-box;
box-sizing: border-box;
font-family: var(--monster-font-family, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen-Sans, Ubuntu, Cantarell, "Helvetica Neue", sans-serif);
}
/*--*/
.visible {
tvisibility: visible !important;
}
/*--*/
.invisible {
visibility: hidden !important;
}
/*--*/
.hidden {
display: none !important;
}
/*--*/
.inline {
display: inline !important;
}
/*--*/
.block {
display: block !important;
}
/*--*/
.flex {
display: flex !important;
}
/*--*/
[data-monster-role=control] {
outline: none;
}
/*--*/
div[data-monster-role=popper] {
-webkit-box-sizing: border-box;
box-sizing: border-box;
display: none;
border: var(--monster-border-width, 1px) solid var(--monster-border-color, black);
padding: 0px;
z-index: 10;
background-color: white;
}
/*--*/
:host([disabled]) {
border-color: var(--monster-disabled-border-color, rgba(118, 118, 118, 0.3));
color: var(--monster-disabled-color, rgb(84, 84, 84));
background-color: var(--monster-disabled-background-color, rgba(239, 239, 239, 0.3));
}
/*--*/
:host([disabled]) * {
border-color: var(--monster-disabled-border-color, rgba(118, 118, 118, 0.3));
color: var(--monster-disabled-color, rgb(84, 84, 84));
}
/*--*/
:disabled {
border-color: var(--monster-disabled-border-color, rgba(118, 118, 118, 0.3));
color: var(--monster-disabled-color, rgb(84, 84, 84));
background-color: var(--monster-disabled-background-color, rgba(239, 239, 239, 0.3));
cursor: not-allowed;
pointer-events: none;
opacity: .55;
}
/*--*/
input:focus-visible {
outline: none;
}
`.split('/*--*/').forEach((style) => {
style = style.trim();
if (style === '') return;
try {
CommonStylesheet.insertRule(style, index);
index++
} catch (e) {
addAttributeToken(document.getRootNode().querySelector('html'), ATTRIBUTE_ERRORMESSAGE, e+"");
}
})
/**
* Copyright schukai GmbH and contributors 2022. All Rights Reserved.
* Node module: @schukai/component-treemenu
* This file is licensed under the AGPLv3 License.
* License text available at https://www.gnu.org/licenses/agpl-3.0.en.html
*/
import {ATTRIBUTE_ERRORMESSAGE} from "../../dom/constants.mjs";
export {TreeMenuStylesheet};
/**
* @private
* @type {CSSStyleSheet}
*/
const TreeMenuStylesheet = new CSSStyleSheet();
/**
* @private
* @type {number}
*/
let index = 0;
/**
* @private
*/
// language=CSS
`
:host {
border: 1px solid #08e808;
width: 100%;
height: 100%;
display: flex;
box-sizing: border-box;
}
/*--*/
[data-monster-role=control] {
overflow-x: hidden;
overflow-y: auto;
width: 100%;
height: 100%;
scrollbar-color: transparent transparent;
}
/*--*/
::-webkit-scrollbar {
width: 5px;
height: 10px;
}
/*--*/
::-webkit-scrollbar-thumb {
background: rgba(0,0,0,0.35);
}
/*--*/
::-webkit-scrollbar-track {
background: transparent;
}
/*--*/
[data-monster-role="button"] {
width: 100%;
border: 0;
background: none;
padding: 5px;
text-overflow: ellipsis;
white-space: nowrap;
display: block;
overflow: hidden;
text-align: left;
}
/*--*/
[data-monster-role=entry][data-monster-visibility=hidden] {
display: none;
}
/*--*/
[data-monster-role=entry] .dropzone {
background-color:red;
width: 100%;
height: 20px;
}
/*--*/
[data-monster-role=entry][data-monster-intend="0"] {
margin: 0px 0px 0px 0px;
display: flex;
font-size: 1rem;
}
/*--*/
[data-monster-role=entry][data-monster-intend="1"] {
margin: 0px 0px 0px 20px;
}
/*--*/
[data-monster-role=entry][data-monster-intend="2"] {
margin: 0px 0px 0px 40px;
font-size: 0.75rem;
}
/*--*/
[data-monster-role=entry][data-monster-intend="3"] {
margin: 0px 0px 0px 60px;
font-size: 0.70rem;
}
/*--*/
[data-monster-role=entry][data-monster-intend="4"] {
margin: 0px 0px 0px 80px;
font-size: 0.65rem;
}
/*--*/
[data-monster-role=entry][data-monster-intend="5"] {
margin: 0px 0px 0px 100px;
font-size: 0.65rem;
}
/*--*/
[data-monster-role=entry][data-monster-intend="6"] {
margin: 0px 0px 0px 110px; /* smaller intend */
font-size: 0.65rem;
}
/*--*/
[data-monster-role=entry][data-monster-intend="7"] {
margin: 0px 0px 0px 120px; /* smaller intend */
font-size: 0.65rem;
}
/*--*/
[data-monster-role=entry][data-monster-intend="8"] {
margin: 0px 0px 0px 130px; /* smaller intend */
font-size: 0.65rem;
}
`.split('/*--*/').forEach((style) => {
style = style.trim();
if (style === '') return;
try {
TreeMenuStylesheet.insertRule(style, index);
index++
} catch (e) {
addAttributeToken(document.getRootNode().querySelector('html'), ATTRIBUTE_ERRORMESSAGE, e + "");
}
})
/**
* Copyright schukai GmbH and contributors 2022. All Rights Reserved.
* Node module: @schukai/component-treemenu
* This file is licensed under the AGPLv3 License.
* License text available at https://www.gnu.org/licenses/agpl-3.0.en.html
*/
import {buildTree} from "../data/buildtree.mjs";
import {Datasource} from "../data/datasource.mjs";
import {addAttributeToken} from "../dom/attributes.mjs";
import {
ATTRIBUTE_DISABLED,
ATTRIBUTE_ERRORMESSAGE,
ATTRIBUTE_ROLE,
ATTRIBUTE_UPDATER_INSERT_REFERENCE
} from "../dom/constants.mjs";
import {
assembleMethodSymbol,
CustomElement,
initMethodSymbol,
registerCustomElement
} from "../dom/customelement.mjs";
import {findTargetElementFromEvent} from "../dom/events.mjs";
import {Formatter} from "../text/formatter.mjs";
import {isObject} from "../types/is.mjs";
import {Node} from "../types/node.mjs";
import {NodeRecursiveIterator} from "../types/noderecursiveiterator.mjs";
import {Observer} from "../types/observer.mjs";
import {ProxyObserver} from "../types/proxyobserver.mjs";
import {validateInstance} from "../types/validate.mjs";
import {ATTRIBUTE_INTEND} from "./constants.mjs";
import {CommonStylesheet} from "./stylesheets/common.mjs";
import {TreeMenuStylesheet} from "./stylesheets/tree-menu.mjs";
export {TreeMenu}
/**
* @private
* @type {symbol}
*/
const internalNodesSymbol = Symbol('internalNodes');
/**
* @private
* @type {symbol}
*/
const controlElementSymbol = Symbol('controlElement');
/**
* @private
* @type {symbol}
*/
const openEntryEventHandlerSymbol = Symbol('openEntryEventHandler');
/**
* @private
* @type {symbol}
*/
const dragstartEventHandlerSymbol = Symbol('dragstartEventHandler');
/**
* @private
* @type {symbol}
*/
const dragenterEventHandlerSymbol = Symbol('dragenterEventHandler');
/**
* @private
* @type {symbol}
*/
const dragleaveEventHandlerSymbol = Symbol('dragleaveEventHandler');
/**
* @private
* @type {symbol}
*/
const dragEventHandlerSymbol = Symbol('dragEventHandler');
/**
* @private
* @type {symbol}
*/
const dragoverEventHandlerSymbol = Symbol('dragoverEventHandler');
/**
* @private
* @type {symbol}
*/
const dropEventHandlerSymbol = Symbol('dropEventHandlerSymbol');
/**
* TreeMenu
*
* <img src="./images/tree-menu.png">
*
* You can create this control either by specifying the HTML tag `<monster-tree-menu />` directly in the HTML
*
* ```html
* <monster-tree-menu></monster-tree-menu>
* ```
*
* or using Javascript via the `document.createElement('monster-tree-menu');` method.
*
* ```javascript
* import {TreeMenu} from 'https://cdn.jsdelivr.net/npm/@schukai/component-treemenu@0.1.0/dist/modules/treemenu.js';
* document.createElement('monster-treemenu');
* ```
*
* @startuml tree-menu.png
* skinparam monochrome true
* skinparam shadowing false
* HTMLElement <|-- CustomElement
* CustomElement <|-- CustomControl
* CustomControl <|-- TreeMenu
* @enduml
* @since 1.0.0
* @copyright schukai GmbH
* @memberOf Monster.Components.TreeMenu
* @summary A TreeMenu control
* @fires Monster.Components.TreeMenu.event:monster-fetched
*/
class TreeMenu extends CustomElement {
/**
* This method is called internal and should not be called directly.
*
* The defaults can be set either directly in the object or via an attribute in the HTML tag.
* The value of the attribute `data-monster-options` in the HTML tag must be a JSON string.
*
* ```
* <monster-treemenu data-monster-options="{}"></monster-treemenu>
* ```
*
* Since 1.18.0 the JSON can be specified as a DataURI.
*
* ```
* new Monster.Types.DataUrl(btoa(JSON.stringify({
* shadowMode: 'open',
* })),'application/json',true).toString()
* ```
* @property {Object} toggleEventType=click,touch List of event types to be observed for opening the dropdown
* @property {Object} templates Template definitions
* @property {string} templates.main Main template
* @property {Datasource} datasource data source
* @property {Object} mapping
* @property {String} mapping.selector=* Path to select the appropriate entries
* @property {String} mapping.labelTemplate="" template with the label placeholders in the form ${name}, where name is the key
* @property {String} mapping.keyTemplate="" template with the key placeholders in the form ${name}, where name is the key
* @property {String} mapping.rootReferences=['0', undefined, null]
* @property {String} mapping.idTemplate=id
* @property {String} mapping.parentTemplate=parent
* @property {String} mapping.selection
*/
get defaults() {
return Object.assign({}, super.defaults, {
toggleEventType: ['click', 'touch'],
mapping: {
rootReferences: ['0', undefined, null],
idTemplate: 'id',
parentTemplate: 'parent',
selector: "*",
labelTemplate: "",
valueTemplate: "",
filter: undefined,
},
templates: {
main: getTemplate()
},
datasource: undefined,
entries: []
}, initOptionsFromArguments.call(this));
}
/**
* This method determines which attributes are to be monitored by `attributeChangedCallback()`.
*
* @return {string[]}
* @since 1.15.0
*/
static get observedAttributes() {
const list = super.observedAttributes;
//list.push(ATTRIBUTE_FORM_URL);
return list;
}
/**
*
*/
[initMethodSymbol]() {
const self = this;
super[initMethodSymbol]();
}
/**
*
* @return {Monster.Components.TreeMenu.Form}
*/
[assembleMethodSymbol]() {
const self = this;
super[assembleMethodSymbol]();
initControlReferences.call(self);
initEventhandler.call(self)
importEntriesFromDatasource.call(self)
initObserver.call(self);
return this;
}
/**
* This method is called internal and should not be called directly.
*
* @return {CSSStyleSheet[]}
*/
static getCSSStyleSheet() {
return [CommonStylesheet, TreeMenuStylesheet];
}
/**
* This method is called internal and should not be called directly.
*
* @return {string}
*/
static getTag() {
return "monster-tree-menu"
}
}
/**
* @private
*/
function initEventhandler() {
const self = this;
switchToConfig.call(self)
self[openEntryEventHandlerSymbol] = (event) => {
const container = findTargetElementFromEvent(event, ATTRIBUTE_ROLE, 'entry');
if (!(container instanceof HTMLElement)) {
return;
}
//let container = findClosestByAttribute(element, ATTRIBUTE_ROLE, 'option');
const index = container.getAttribute(ATTRIBUTE_UPDATER_INSERT_REFERENCE).split('-').pop()
const currentState = self.getOption('entries.' + index + '.state');
const newState = (currentState === 'close') ? 'open' : 'close';
self.setOption('entries.' + index + '.state', newState);
const newVisibility = (newState === 'open') ? 'visible' : 'hidden';
if (container.hasAttribute(ATTRIBUTE_INTEND)) {
const intend = container.getAttribute(ATTRIBUTE_INTEND)
let ref = container.nextElementSibling;
const childIntend = (parseInt(intend) + 1);
const cmp = (a, b) => {
if (newState === 'open') {
return a === b;
}
return a >= b;
};
while (ref &&
ref.hasAttribute(ATTRIBUTE_INTEND) && cmp(parseInt(ref.getAttribute(ATTRIBUTE_INTEND)), childIntend)) {
const refIndex = ref.getAttribute(ATTRIBUTE_UPDATER_INSERT_REFERENCE).split('-').pop()
self.setOption('entries.' + refIndex + '.visibility', newVisibility);
if (newState === 'close') {
self.setOption('entries.' + refIndex + '.state', 'close');
}
ref = ref.nextElementSibling;
}
}
}
let types = self.getOption('toggleEventType', ['click']);
for (const [, type] of Object.entries(types)) {
self.shadowRoot.addEventListener(type, self[openEntryEventHandlerSymbol]);
}
// for (const [, type] of Object.entries(types)) {
//
// self[controlElementSymbol].addEventListener(type, function (event) {
//
// const element = findTargetElementFromEvent(event, ATTRIBUTE_ROLE, 'entry');
// if (!(element instanceof HTMLElement)) {
// return;
// }
//
// toggle.call(self);
//
//
// })
//
// }
return self;
}
/**
* @private
* @this Form
*/
function initObserver() {
const self = this;
}
/**
* Import Menu Entries from dataset
*
* @since 1.0.0
* @param {array|object|Map|Set} data
* @return {TreeMenu}
* @throws {Error} map is not iterable
* @private
*/
function importEntries(data) {
const self = this;
self[internalNodesSymbol] = new Map();
const mappingOptions = self.getOption('mapping', {});
const filter = mappingOptions?.['filter'];
const rootReferences = mappingOptions?.['rootReferences'];
const id = self.getOption('mapping.idTemplate', 'id');
const parentID = self.getOption('mapping.parentTemplate', 'parent');
const selector = mappingOptions?.['selector'];
let nodes = buildTree(data, selector, id, parentID, {
filter,
rootReferences
});
let options = [];
for (const node of nodes) {
const iterator = new NodeRecursiveIterator(node);
for (const n of iterator) {
const formattedValues = formatKeyLabel.call(self, n);
const label = formattedValues.label;
const value = formattedValues.value;
const intend = n.level;
let visibility = intend > 0 ? 'hidden' : 'visible';
let state = 'close';
self[internalNodesSymbol].set(value, n);
options.push({
value,
label,
intend,
state,
visibility,
['has-children']: n.hasChildNodes()
})
}
}
self.setOption('entries', options);
return self;
}
/**
* @private
*/
function importEntriesFromDatasource() {
const self = this;
self.setAttribute(ATTRIBUTE_DISABLED, ATTRIBUTE_DISABLED);
const datasource = self.getOption('datasource');
if (!(datasource instanceof Datasource)) {
throw new Error('undefined datasource')
}
datasource.attachObserver(new Observer(function () {
if (isObject(this) && this instanceof ProxyObserver) {
importEntries.call(self, datasource.get())
}
}));
datasource.read().then(() => {
new Processing(() => {
self.removeAttribute(ATTRIBUTE_DISABLED);
}).run();
}).catch(e => {
addAttributeToken(self, ATTRIBUTE_ERRORMESSAGE, e.toString());
})
return self;
}
/**
*
* @param {Node} node
* @return {array<label, value>}
* @memberOf Monster.Components.TreeMenu
* @private
*/
function formatKeyLabel(node) {
const self = this;
validateInstance(node, Node);
const label = new Formatter(node.value).format(self.getOption('mapping.labelTemplate', ''));
const value = new Formatter(node.value).format(self.getOption('mapping.valueTemplate', ''));
return {
value,
label
}
}
/**
* @private
* @return {Monster.Components.TreeMenu.Form}
*/
function initControlReferences() {
const self = this;
if (!self.shadowRoot) {
throw new Error("no shadow-root is defined");
}
self[controlElementSymbol] = self.shadowRoot.querySelector('[data-monster-role=control]');
return self;
}
/**
*
* ```
* <monster-tree-menu data-monster-url="https://example.com/"></monster-tree-menu>
* ```
* @private
* @return {object}
*/
function initOptionsFromArguments() {
const self = this;
let options = {};
// let url = self.getAttribute(ATTRIBUTE_FORM_URL);
//
// if (isString(url)) {
// options['url'] = new URL(url, document.location).toString()
// }
return options;
}
function switchToConfig() {
const self = this
if (!self.shadowRoot) {
throw new Error("no shadow-root is defined");
}
self[dragoverEventHandlerSymbol] = (event) => {
const element = findTargetElementFromEvent(event, ATTRIBUTE_ROLE, 'entry');
event.preventDefault();
if(!(element instanceof HTMLElement)) {
return;
}
const dropzone=document.createElement('div');
dropzone.classList.add('dropzone')
element.prepend(dropzone);
console.log('over',element.outerHTML,event)
event.dataTransfer.dropEffect = "move";
}
self[dragenterEventHandlerSymbol] = (event) => {
const element = findTargetElementFromEvent(event, ATTRIBUTE_ROLE, 'entry');
console.log('enter',element.outerHTML,event)
event.dataTransfer.dropEffect = "move";
event.preventDefault();
}
self[dragleaveEventHandlerSymbol] = (event) => {
const element = findTargetElementFromEvent(event, ATTRIBUTE_ROLE, 'entry');
event.preventDefault();
if(!(element instanceof HTMLElement)) {
return;
}
console.log('leave',element.outerHTML,event)
event.dataTransfer.dropEffect = "move";
event.preventDefault();
}
self[dragEventHandlerSymbol] = (event) => {
console.log(event)
event.preventDefault();
}
self[dropEventHandlerSymbol] = (event) => {
const element = findTargetElementFromEvent(event, ATTRIBUTE_ROLE, 'entry');
console.log('drop',element.outerHTML,event)
event.preventDefault();
}
self[dragstartEventHandlerSymbol] = (event) => {
const element = findTargetElementFromEvent(event, ATTRIBUTE_ROLE, 'entry');
if (!(element instanceof HTMLElement)) {
return;
}
//let container = findClosestByAttribute(element, ATTRIBUTE_ROLE, 'option');
const index = element.getAttribute(ATTRIBUTE_UPDATER_INSERT_REFERENCE).split('-').pop()
const currentState = self.getOption('entries.' + index + '.state');
event.dataTransfer.setData("text/plain", '22');
event.dataTransfer.setData("text/html", '22');
event.dataTransfer.effectAllowed = "move";
}
self[controlElementSymbol].addEventListener("dragstart", self[dragstartEventHandlerSymbol]);
self[controlElementSymbol].addEventListener("dragenter", self[dragenterEventHandlerSymbol]);
self[controlElementSymbol].addEventListener("dragleave", self[dragleaveEventHandlerSymbol]);
self[controlElementSymbol].addEventListener("dragover", self[dragoverEventHandlerSymbol]);
self[controlElementSymbol].addEventListener("drop", self[dropEventHandlerSymbol]);
}
// /**
// * @private
// * @throws {Error} missing default slot
// * @throws {Error} no shadow-root is defined
// * @throws {Error} missing url
// * @throws {Error} we won't be able to read the data
// * @throws {Error} request failed
// * @throws {Error} not found
// * @throws {Error} undefined status or type
// * @fires Monster.Components.TreeMenu.event:monster-fetched
// */
// function initIntersectionObserver() {
// const self = this;
//
// if (self[intersectionObserverWasInitialized] === true) {
// return
// }
//
// self[intersectionObserverWasInitialized] = true;
//
// let options = {
// threshold: [0.5]
// }
//
// const callback = (entries, observer) => {
//
// for (const [, entry] of entries.entries()) {
// if (entry.isIntersecting === true) {
// if (!self.hasAttribute(ATTRIBUTE_FORM_RELOAD) || self.getAttribute(ATTRIBUTE_FORM_RELOAD).toLowerCase() === 'onshow') {
// observer.disconnect();
// }
//
// try {
// loadContent.call(self);
// } catch (e) {
// self.setAttribute(ATTRIBUTE_ERRORMESSAGE, e.toString());
// }
//
//
// }
// }
// }
//
// const observer = new IntersectionObserver(callback, options);
// observer.observe(self);
//
//
// }
/**
* @private
* @return {string}
*/
function getTemplate() {
// language=HTML
return `
<template id="entries">
<div data-monster-role="entry"
draggable="true"
data-monster-attributes="
data-monster-intend path:entries.intend,
data-monster-state path:entries.state,
data-monster-visibility path:entries.visibility,
data-monster-filtered path:entries.filtered,
data-monster-has-children path:entries.has-children">
<button data-monster-role="button"
data-monster-attributes="
type path:type,
role path:role,
value path:entries.value,
name path:name,
part path:type | prefix:option- | suffix: form" tabindex="-1">
<span data-monster-role="folder-handler"></span>
<span data-monster-replace="path:entries | index:label" part="entry-label"></span>
</button>
</template>
<div data-monster-role="control" part="control">
<div part="entries" data-monster-role="entries"
data-monster-insert="entries path:entries"
tabindex="-1"></div>
</div>
`;
}
registerCustomElement(TreeMenu);
......@@ -43,7 +43,7 @@ function random(min, max) {
* @private
* @type {number}
*/
var MAX = 1000000000;
const MAX = 1000000000;
Math.log2 =
Math.log2 ||
......
......@@ -16,6 +16,10 @@
* @author schukai GmbH
*/
export * from "./components/stylesheets/common.mjs";
export * from "./components/stylesheets/tree-menu.mjs";
export * from "./components/tree-menu.mjs";
export * from "./components/constants.mjs";
export * from "./text/formatter.mjs";
export * from "./text/generate-range-comparison-expression.mjs";
export * from "./text/util.mjs";
......
import chai from "chai"
import {chaiDom} from "../../util/chai-dom.mjs";
import {setupIntersectionObserverMock} from "../../util/intersection-mock.mjs";
import {initJSDOM} from "../../util/jsdom.mjs";
let expect = chai.expect;
chai.use(chaiDom);
//
// let mockintersectionobserver;
//
// let html1 = `
// <div id="test1">
// </div>
// `;
//
// describe('TreeMenu', function () {
//
// // jsdom-testing-mocks need jest
// let TreeMenu, viewport, mockViewport;
//
// after(function () {
// mockintersectionobserver.restore();
// })
//
// before(function (done) {
//
// initJSDOM().then(() => {
//
// mockintersectionobserver = setupIntersectionObserverMock();
//
// import("../../../source/components/tree-menu.mjs").then((m) => {
// TreeMenu = m['TreeMenu'];
// done()
// }).catch(e => done(e))
//
//
// });
// })
//
// describe('new TreeMenu', function () {
//
// beforeEach(() => {
// let mocks = document.getElementById('mocks');
// mocks.innerHTML = html1;
// })
//
// afterEach(() => {
// let mocks = document.getElementById('mocks');
// mocks.innerHTML = "";
// })
//
// describe('create from template', function () {
// beforeEach(() => {
// let mocks = document.getElementById('mocks');
// // language=HTML
// mocks.innerHTML = `
// <div id="test2">
// <monster-tree-menu></monster-tree-menu>
// </div>
// `;
// });
//
// afterEach(() => {
// let mocks = document.getElementById('mocks');
// mocks.innerHTML = "";
// })
//
// describe('create from template', function () {
// it('should contains monster-tree-menu', function () {
// expect(document.getElementById('test2')).is.instanceof(HTMLElement);
// });
// });
//
// });
//
// describe('document.createElement', function () {
// it('should instance of treemenu', function () {
// expect(document.createElement('monster-tree-menu')).is.instanceof(TreeMenu);
// });
// });
//
// });
//
// describe('document.createElement()', function () {
//
// let mocks;
// beforeEach(() => {
// mocks = document.getElementById('mocks');
// })
//
// afterEach(() => {
// mocks.innerHTML = "";
// })
//
// it('should have no options', function (done) {
//
// const treemenu = document.createElement('monster-tree-menu');
// mocks.appendChild(treemenu);
//
// setTimeout(() => {
// try {
// expect(document.getElementById('mocks')).is.instanceof(HTMLElement);
// } catch (e) {
// return done(e);
// }
//
// done();
// }, 0)
// });
// });
// });
\ No newline at end of file
......@@ -29,7 +29,6 @@ describe('Math', function () {
describe('.random()', function () {
it('a greater b should throw error ', function () {
expect(() => random(10, 2)).to.throw(Error);
});
......
......@@ -10,7 +10,6 @@ describe('Message', function () {
done(new Error('should throw'));
} catch (e) {
done();
return;
}
})
......
import {getGlobal} from "../../source/types/global.mjs";
const global = getGlobal();
export function setupIntersectionObserverMock(
{
root = null,
rootMargin = '',
thresholds = [],
disconnect = () => null,
observe = () => null,
takeRecords = () => [],
unobserve = () => null,
} = {}) {
const savedImplementation = window.IntersectionObserver;
let lastObject;
class MockIntersectionObserver {
constructor(callback, options) {
this.root = root;
this.rootMargin = rootMargin;
this.thresholds = thresholds;
this.disconnect = disconnect;
this.observe = observe;
this.takeRecords = takeRecords;
this.unobserve = unobserve;
this.callback = callback;
this.options = options;
lastObject = this;
}
enterNode() {
const entries = [];
entries.push({
isIntersecting: true
})
this['callback'](entries, this);
}
}
Object.defineProperty(window, 'IntersectionObserver', {
writable: true,
configurable: true,
value: MockIntersectionObserver
});
Object.defineProperty(global, 'IntersectionObserver', {
writable: true,
configurable: true,
value: MockIntersectionObserver
});
return {
restore: function () {
window.IntersectionObserver = savedImplementation;
},
getInstance: function () {
return lastObject;
}
}
}
\ No newline at end of file
......@@ -2,6 +2,7 @@
import {extend} from "../../source/data/extend.mjs";
import {getGlobal} from "../../source/types/global.mjs";
import {createStorage} from "./localstorage.mjs";
export const isBrowser = new Function("try {return this===window;}catch(e){ return false;}");
export const isNode = new Function("try {return this===global;}catch(e){return false;}");
......@@ -42,43 +43,49 @@ function initJSDOM(options) {
window.addEventListener("load", () => {
[
'self',
'HTMLCollection',
'NodeList',
'Blob',
'CSSStyleSheet',
'CSSStyleSheet',
'customElements',
'CustomEvent',
'CustomEvent',
'document',
'Document',
'DocumentFragment',
'DOMParser',
'Element',
'Element',
'ElementInternals',
'Event',
'EventTarget',
'getComputedStyle',
'HTMLButtonElement',
'HTMLCollection',
'HTMLDivElement',
'HTMLDocument',
'HTMLElement',
'HTMLFormElement',
'HTMLInputElement',
'HTMLScriptElement',
'HTMLSelectElement',
'HTMLTemplateElement',
'HTMLTextAreaElement',
'document',
'Document',
'Node',
'ShadowRoot',
'EventTarget',
'Event',
'CustomEvent',
'Element',
'HTMLElement',
'HTMLDivElement',
'customElements',
'DocumentFragment',
'DOMParser',
'InputEvent',
'KeyboardEvent',
'CSSStyleSheet',
'HTMLScriptElement',
'MutationObserver',
'HTMLTemplateElement',
'XMLSerializer',
'NodeFilter',
'navigator',
'InputEvent',
'Blob',
'CustomEvent'
'Node',
'NodeFilter',
'NodeList',
'self',
'ShadowRoot',
'XMLSerializer',
].forEach(key => {
g[key] = window[key]
});
g['localStorage'] = window['localStorage'] = createStorage()
resolve();
})
);
......
import {getGlobal} from "../../source/types/global.mjs";
function initWebSocket() {
if (typeof window === "object" && window['WebSocket']) return Promise.resolve();
if (typeof process === 'undefined' || process.title !== 'node') {
return Promise.resolve();
}
return import("ws").then((ws) => {
getGlobal().WebSocket = class extends ws['WebSocket'] {
......
/** this file was created automatically by the make target test-browser-monster */
import "./prepare.js";
import "../cases/components/tree-menu.mjs";
import "../cases/text/formatter.mjs";
import "../cases/text/util.mjs";
import "../cases/text/bracketed-key-value-hash.mjs";
......
......@@ -5,7 +5,7 @@
<title>Mocha Monster</title>
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
<link rel="stylesheet" href="mocha.css"/>
<script id="polyfill" src="https://polyfill.io/v3/polyfill.min.js?features=Array.from,Array.isArray,Array.prototype.entries,Array.prototype.fill,Array.prototype.filter,Array.prototype.forEach,Array.prototype.includes,Array.prototype.indexOf,Array.prototype.keys,Array.prototype.lastIndexOf,Array.prototype.map,Array.prototype.reduce,Array.prototype.sort,ArrayBuffer,atob,Blob,CustomEvent,DataView,document,Document,DocumentFragment,Element,Event,fetch,globalThis,HTMLDocument,HTMLTemplateElement,Intl,JSON,Map,Math.log2,MutationObserver,Number.isInteger,Object.assign,Object.defineProperty,Object.entries,Object.freeze,Object.getOwnPropertyDescriptor,Object.getOwnPropertyNames,Object.getPrototypeOf,Object.keys,Promise,Reflect,Reflect.defineProperty,Reflect.get,Reflect.getOwnPropertyDescriptor,Reflect.setPrototypeOf,Set,String.prototype.endsWith,String.prototype.includes,String.prototype.matchAll,String.prototype.padStart,String.prototype.startsWith,String.prototype.trim,Symbol,Symbol.for,Symbol.hasInstance,Symbol.iterator,Uint16Array,Uint8Array,URL,WeakMap,WeakSet"
<script id="polyfill" src="https://polyfill.io/v3/polyfill.min.js?features=Array.from,Array.isArray,Array.prototype.entries,Array.prototype.fill,Array.prototype.filter,Array.prototype.forEach,Array.prototype.includes,Array.prototype.indexOf,Array.prototype.keys,Array.prototype.lastIndexOf,Array.prototype.map,Array.prototype.reduce,Array.prototype.sort,ArrayBuffer,atob,Blob,console,CustomEvent,DataView,document,Document,DocumentFragment,Element,Event,fetch,globalThis,HTMLDocument,HTMLTemplateElement,Intl,JSON,Map,Math.log2,MutationObserver,Number.isInteger,Object.assign,Object.defineProperty,Object.entries,Object.freeze,Object.getOwnPropertyDescriptor,Object.getOwnPropertyNames,Object.getPrototypeOf,Object.keys,Promise,Reflect,Reflect.defineProperty,Reflect.get,Reflect.getOwnPropertyDescriptor,Reflect.setPrototypeOf,Set,String.prototype.endsWith,String.prototype.includes,String.prototype.matchAll,String.prototype.padStart,String.prototype.startsWith,String.prototype.trim,Symbol,Symbol.for,Symbol.hasInstance,Symbol.iterator,Uint16Array,Uint8Array,URL,WeakMap,WeakSet"
src="https://polyfill.io/v3/polyfill.min.js?features=Array.from,Array.isArray,Array.prototype.entries,Array.prototype.fill,Array.prototype.forEach,Array.prototype.indexOf,Array.prototype.keys,Array.prototype.lastIndexOf,Array.prototype.map,Array.prototype.reduce,Array.prototype.sort,ArrayBuffer,atob,DataView,document,DocumentFragment,Element,Event,globalThis,HTMLDocument,HTMLTemplateElement,JSON,Map,Math.log2,Number.isInteger,Object.assign,Object.defineProperty,Object.entries,Object.getOwnPropertyDescriptor,Object.getPrototypeOf,Object.keys,Promise,Reflect,Reflect.defineProperty,Reflect.get,Reflect.getOwnPropertyDescriptor,Reflect.setPrototypeOf,Set,String.prototype.endsWith,String.prototype.matchAll,String.prototype.padStart,String.prototype.startsWith,String.prototype.trim,Symbol,Symbol.iterator,WeakMap,WeakSet"
crossorigin="anonymous"
referrerpolicy="no-referrer"></script>
......@@ -15,7 +15,7 @@
<body>
<div id="headline" style="display: flex;align-items: center;justify-content: center;flex-direction: column;">
<h1 style='margin-bottom: 0.1em;'>Monster 3.51.5</h1>
<div id="lastupdate" style='font-size:0.7em'>last update Mi 1. Nov 19:32:07 CET 2023</div>
<div id="lastupdate" style='font-size:0.7em'>last update Mi 1. Nov 21:14:44 CET 2023</div>
</div>
<div id="mocks"></div>
<div id="mocha"></div>
......
This diff is collapsed.
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment