Framework-free JSX syntax in pure JS using HTM and native DOM creation.
Dette scriptet burde ikke installeres direkte. Det er et bibliotek for andre script å inkludere med det nye metadirektivet // @require https://update.greasyfork.org/scripts/591770/1906907/Vanilla%20HTM.js
// ==UserScript==
// @name Vanilla HTM
// @namespace Violentmonkey Scripts
// @version 1.0.1
// @grant none
// @author Paesss
// @license Apache-2.0
// @description Framework-free JSX syntax in pure JS using HTM and native DOM creation.
// ==/UserScript==
(function (global) {
"use strict";
/* ========================================================================
* Bundled Library: HTM ([email protected] / mini)
* Copyright 2018 Google Inc. All Rights Reserved.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* ======================================================================== */
const htm = (function () {
const MODE_SLASH = 0;
const MODE_TEXT = 1;
const MODE_WHITESPACE = 2;
const MODE_TAGNAME = 3;
const MODE_COMMENT = 4;
const MODE_PROP_SET = 5;
const MODE_PROP_APPEND = 6;
const build = function (statics) {
const fields = arguments;
const h = this;
let mode = MODE_TEXT;
let buffer = "";
let quote = "";
let current = [0];
let char, propName;
const commit = (field) => {
if (
mode === MODE_TEXT &&
(field || (buffer = buffer.replace(/^\s*\n\s*|\s*\n\s*$/g, "")))
) {
current.push(field ? fields[field] : buffer);
} else if (mode === MODE_TAGNAME && (field || buffer)) {
current[1] = field ? fields[field] : buffer;
mode = MODE_WHITESPACE;
} else if (mode === MODE_WHITESPACE && buffer === "..." && field) {
current[2] = Object.assign(current[2] || {}, fields[field]);
} else if (mode === MODE_WHITESPACE && buffer && !field) {
(current[2] = current[2] || {})[buffer] = true;
} else if (mode >= MODE_PROP_SET) {
if (mode === MODE_PROP_SET) {
(current[2] = current[2] || {})[propName] = field
? buffer
? buffer + fields[field]
: fields[field]
: buffer;
mode = MODE_PROP_APPEND;
} else if (field || buffer) {
current[2][propName] += field ? buffer + fields[field] : buffer;
}
}
buffer = "";
};
for (let i = 0; i < statics.length; i++) {
if (i) {
if (mode === MODE_TEXT) {
commit();
}
commit(i);
}
for (let j = 0; j < statics[i].length; j++) {
char = statics[i][j];
if (mode === MODE_TEXT) {
if (char === "<") {
commit();
current = [current, "", null];
mode = MODE_TAGNAME;
} else {
buffer += char;
}
} else if (mode === MODE_COMMENT) {
if (buffer === "--" && char === ">") {
mode = MODE_TEXT;
buffer = "";
} else {
buffer = char + buffer[0];
}
} else if (quote) {
if (char === quote) {
quote = "";
} else {
buffer += char;
}
} else if (char === '"' || char === "'") {
quote = char;
} else if (char === ">") {
commit();
mode = MODE_TEXT;
} else if (!mode);
else if (char === "=") {
mode = MODE_PROP_SET;
propName = buffer;
buffer = "";
} else if (
char === "/" &&
(mode < MODE_PROP_SET || statics[i][j + 1] === ">")
) {
commit();
if (mode === MODE_TAGNAME) {
current = current[0];
}
mode = current;
(current = current[0]).push(h.apply(null, mode.slice(1)));
mode = MODE_SLASH;
} else if (
char === " " ||
char === "\t" ||
char === "\n" ||
char === "\r"
) {
commit();
mode = MODE_WHITESPACE;
} else {
buffer += char;
}
if (mode === MODE_TAGNAME && buffer === "!--") {
mode = MODE_COMMENT;
current = current[0];
}
}
}
commit();
return current.length > 2 ? current.slice(1) : current[1];
};
return build;
})();
// ========================================================================
// DOM & Hyperscript Setup
// ========================================================================
const SVG_NS = "http://www.w3.org/2000/svg";
const SVG_TAGS = new Set([
"svg",
"path",
"circle",
"rect",
"line",
"polyline",
"polygon",
"ellipse",
"g",
"use",
"text",
"tspan",
"symbol",
"defs",
"clipPath",
"mask",
"pattern",
"marker",
"foreignObject",
]);
/**
* Appends dynamic children into a parent DOM container
*/
function appendChildren(parent, children) {
for (let i = 0; i < children.length; i++) {
const child = children[i];
if (child == null || typeof child === "boolean") {
continue;
}
if (Array.isArray(child)) {
appendChildren(parent, child);
} else if (child instanceof Node) {
parent.appendChild(child);
} else {
parent.appendChild(document.createTextNode(String(child)));
}
}
}
/**
* Hyperscript factory function
*/
function h(tag, props, ...children) {
// 1. Functional Components
if (typeof tag === "function") {
const normalizedProps = Object.assign({}, props);
normalizedProps.children =
children.length === 0
? undefined
: children.length === 1
? children[0]
: children;
return tag(normalizedProps);
}
// 2. Document Fragments (<>...</>)
if (!tag) {
const fragment = document.createDocumentFragment();
appendChildren(fragment, children);
return fragment;
}
// 3. Namespace Resolution (SVG Support)
const isSvg = typeof tag === "string" && SVG_TAGS.has(tag);
const el = isSvg
? document.createElementNS(SVG_NS, tag)
: document.createElement(tag);
// 4. Property & Attribute Binding
if (props) {
for (let key in props) {
const val = props[key];
if (val === undefined || val === null) continue;
// Ref Callback / Object
if (key === "ref") {
if (typeof val === "function") val(el);
else if (val && typeof val === "object") val.current = el;
continue;
}
// Forced Property Set (.value, .checked, etc.)
if (key.charCodeAt(0) === 46 /* '.' */) {
el[key.slice(1)] = val;
continue;
}
// Event Listeners (e.g. onClick, onInput)
if (
key.charCodeAt(0) === 111 /* 'o' */ &&
key.charCodeAt(1) === 110 /* 'n' */
) {
const eventName = key.slice(2).toLowerCase();
el.addEventListener(eventName, val);
continue;
}
// Attribute Normalization
let attrName = key;
if (key === "className") attrName = "class";
else if (key === "htmlFor") attrName = "for";
// Style Props (Object or String)
if (key === "style") {
if (typeof val === "object" && val !== null) {
Object.assign(el.style, val);
} else {
el.style.cssText = String(val);
}
continue;
}
// Dataset Attributes
if (key === "dataset" && typeof val === "object") {
Object.assign(el.dataset, val);
continue;
}
// Boolean Attributes
if (typeof val === "boolean") {
if (val) {
el.setAttribute(attrName, "");
} else {
el.removeAttribute(attrName);
}
continue;
}
// Standard Properties vs Attributes
if (
!isSvg &&
attrName in el &&
attrName !== "list" &&
attrName !== "form"
) {
try {
el[attrName] = val;
} catch (_) {
el.setAttribute(attrName, String(val));
}
} else {
el.setAttribute(attrName, String(val));
}
}
}
// 5. Append Children
appendChildren(el, children);
return el;
}
// Bind HTM directly to the hyperscript factory and assign globally
global.html = htm.bind(h);
})(typeof globalThis !== "undefined" ? globalThis : window);