xhtm-dom

A tiny, framework-free DOM helper for writing HTML-like templates in plain JavaScript or TypeScript

이 스크립트는 직접 설치하는 용도가 아닙니다. 다른 스크립트에서 메타 지시문 // @require https://update.greasyfork.org/scripts/591770/1909822/xhtm-dom.js을(를) 사용하여 포함하는 라이브러리입니다.

이 스크립트를 설치하려면 Tampermonkey, Greasemonkey 또는 Violentmonkey와 같은 확장 프로그램이 필요합니다.

You will need to install an extension such as Tampermonkey or Violentmonkey to install this script.

이 스크립트를 설치하려면 Tampermonkey 또는 Violentmonkey와 같은 확장 프로그램이 필요합니다.

이 스크립트를 설치하려면 Tampermonkey 또는 Userscripts와 같은 확장 프로그램이 필요합니다.

이 스크립트를 설치하려면 Tampermonkey와 같은 확장 프로그램이 필요합니다.

이 스크립트를 설치하려면 유저 스크립트 관리자 확장 프로그램이 필요합니다.

(이미 유저 스크립트 관리자가 설치되어 있습니다. 설치를 진행합니다!)

이 스타일을 설치하려면 Stylus와 같은 확장 프로그램이 필요합니다.

이 스타일을 설치하려면 Stylus와 같은 확장 프로그램이 필요합니다.

이 스타일을 설치하려면 Stylus와 같은 확장 프로그램이 필요합니다.

이 스타일을 설치하려면 유저 스타일 관리자 확장 프로그램이 필요합니다.

이 스타일을 설치하려면 유저 스타일 관리자 확장 프로그램이 필요합니다.

이 스타일을 설치하려면 유저 스타일 관리자 확장 프로그램이 필요합니다.

(이미 유저 스타일 관리자가 설치되어 있습니다. 설치를 진행합니다!)

// ==UserScript==
// @name         xhtm-dom
// @namespace    https://greasyfork.org/users/1635096-paesss
// @version      1.8.0
// @author       Paesss
// @description  A tiny, framework-free DOM helper for writing HTML-like templates in plain JavaScript or TypeScript.
// @license      MIT
// @match        *://*/*
// ==/UserScript==

/* ========================================================================
* Bundled Library: XHTM ([email protected])
* Copyright (c) 2019 Dmitry Yv.
*
* MIT License
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
* ======================================================================== */

(function() {
	"use strict";
	var FIELD = "";
	var QUOTES = "";
	var CACHE = new WeakMap();
	function htm(statics) {
		let tree = CACHE.get(statics);
		if (!tree) CACHE.set(statics, tree = parse(statics));
		let nodes = tree.map((node) => build(this, node, arguments));
		return nodes.length < 2 ? nodes[0] : nodes;
	}
	var build = (h, node, args) => {
		if (typeof node === "number") return args[node];
		if (typeof node !== "object") return node;
		let props = null;
		if (node.p) {
			props = {};
			for (let e of node.p) e.length < 2 ? Object.assign(props, args[e[0]]) : props[part(e[0], args)] = part(e[1], args);
		}
		return h(part(node.t, args), props, ...node.c.map((c) => build(h, c, args)));
	};
	var part = (v, args) => typeof v === "number" ? args[v] : Array.isArray(v) ? v.map((x) => (x = typeof x === "number" ? args[x] : x, x == null || x === false ? "" : x)).join("") : v;
	var parse = (statics) => {
		let prev = 0, current = [null], field = 0, args, name, value, quotes = [], quote = 0, last, level = 0, pre = false;
		const evaluate = (str, parts = [], raw) => {
			let i = 0;
			str = !raw && str === QUOTES ? quotes[quote++].slice(1, -1) : str.replace(/\ue001/g, (m) => quotes[quote++]);
			if (!str) return str;
			str.replace(/\ue000/g, (match, idx) => {
				if (idx) parts.push(str.slice(i, idx));
				i = idx + 1;
				return parts.push(++field);
			});
			if (i < str.length) parts.push(str.slice(i));
			return parts.length > 1 ? parts : parts[0];
		};
		const up = () => {
			args = current, current = args[0], last = args[1];
			current.push({
				t: last,
				p: args[2],
				c: args.slice(3)
			});
			if (pre === level--) pre = false;
		};
		let str = statics.join(FIELD);
		if (str.indexOf("<!") >= 0) str = str.replace(/<!--[^]*?-->/g, "").replace(/<!\[CDATA\[[^]*\]\]>/g, "");
		str = str.replace(/('|")[^\1]*?\1/g, (match) => (quotes.push(match), QUOTES));
		str.replace(/(?:^|>)((?:[^<]|<[^\w\ue000\/?!>])*)(?:$|<)/g, (match, text, idx, str) => {
			let tag, close;
			if (idx) str.slice(prev, idx).replace(/(\S)\/$/, "$1 /").split(/\s+/).map((part, i) => {
				if (part[0] === "/") {
					part = part.slice(1);
					if (EMPTY[part]) return;
					close = tag || part || 1;
				} else if (!i) {
					tag = evaluate(part);
					if (typeof tag === "string") {
						tag = tag.toLowerCase();
						while (CLOSE[current[1] + tag]) up();
					}
					current = [
						current,
						tag,
						null
					];
					level++;
					if (!pre && PRE[tag]) pre = level;
					if (EMPTY[tag]) close = tag;
				} else if (part) {
					let props = current[2] || (current[2] = []);
					if (part.slice(0, 3) === "...") props.push([++field]);
					else {
						[name, value] = part.split("=");
						props.push([evaluate(name), value ? evaluate(value) : true]);
					}
				}
			});
			if (close) {
				if (!current[0]) err(`Wrong close tag \`${close}\``);
				up();
				while (last !== close && CLOSE[last]) up();
			}
			prev = idx + match.length;
			if (!pre) text = text.replace(/\s*\n\s*/g, "").replace(/\s+/g, " ");
			if (text) evaluate((last = 0, text), current, true);
		});
		if (current[0] && CLOSE[current[1]]) up();
		if (level) err(`Unclosed \`${current[1]}\`.`);
		return current.slice(1);
	};
	var err = (msg) => {
		throw SyntaxError(msg);
	};
	var EMPTY = htm.empty = {};
	var CLOSE = htm.close = {};
	var PRE = htm.pre = {};
	"area base basefont bgsound br col command embed frame hr image img input keygen link meta param source track wbr ! !doctype ? ?xml".split(" ").map((v) => htm.empty[v] = true);
	var close = {
		"li": "",
		"dt": "dd",
		"dd": "dt",
		"p": "address article aside blockquote details div dl fieldset figcaption figure footer form h1 h2 h3 h4 h5 h6 header hgroup hr main menu nav ol pre section table",
		"rt": "rp",
		"rp": "rt",
		"optgroup": "",
		"option": "optgroup",
		"caption": "tbody thead tfoot tr colgroup",
		"colgroup": "thead tbody tfoot tr caption",
		"thead": "tbody tfoot caption",
		"tbody": "tfoot caption",
		"tfoot": "caption",
		"tr": "tbody tfoot",
		"td": "th tr",
		"th": "td tr tbody"
	};
	for (let tag in close) for (let closer of [...close[tag].split(" "), tag]) htm.close[tag] = htm.close[tag + closer] = true;
	"pre textarea".split(" ").map((v) => htm.pre[v] = true);
	var xhtm_default = htm;
	var SVG_NS = "http://www.w3.org/2000/svg";
	var BOOLEAN_PROPS = new Set([
		"allowFullscreen",
		"async",
		"autofocus",
		"autoplay",
		"checked",
		"controls",
		"default",
		"defer",
		"disabled",
		"formNoValidate",
		"hidden",
		"inert",
		"loop",
		"multiple",
		"muted",
		"noValidate",
		"open",
		"readOnly",
		"required",
		"reversed",
		"selected"
	]);
	var SVG_TAGS = new Map([
		["animate", "animate"],
		["animatemotion", "animateMotion"],
		["animatetransform", "animateTransform"],
		["circle", "circle"],
		["clippath", "clipPath"],
		["defs", "defs"],
		["desc", "desc"],
		["ellipse", "ellipse"],
		["feblend", "feBlend"],
		["fecolormatrix", "feColorMatrix"],
		["fecomponenttransfer", "feComponentTransfer"],
		["fecomposite", "feComposite"],
		["feconvolvematrix", "feConvolveMatrix"],
		["fediffuselighting", "feDiffuseLighting"],
		["fedisplacementmap", "feDisplacementMap"],
		["fedistantlight", "feDistantLight"],
		["fedropshadow", "feDropShadow"],
		["filter", "filter"],
		["feflood", "feFlood"],
		["fefunca", "feFuncA"],
		["fefuncb", "feFuncB"],
		["fefuncg", "feFuncG"],
		["fefuncr", "feFuncR"],
		["fegaussianblur", "feGaussianBlur"],
		["feimage", "feImage"],
		["femerge", "feMerge"],
		["femergenode", "feMergeNode"],
		["femorphology", "feMorphology"],
		["feoffset", "feOffset"],
		["fepointlight", "fePointLight"],
		["fespecularlighting", "feSpecularLighting"],
		["fespotlight", "feSpotLight"],
		["fetile", "feTile"],
		["feturbulence", "feTurbulence"],
		["foreignobject", "foreignObject"],
		["g", "g"],
		["glyph", "glyph"],
		["glyphref", "glyphRef"],
		["hatch", "hatch"],
		["hatchpath", "hatchpath"],
		["hkern", "hkern"],
		["image", "image"],
		["line", "line"],
		["lineargradient", "linearGradient"],
		["marker", "marker"],
		["mask", "mask"],
		["metadata", "metadata"],
		["mesh", "mesh"],
		["meshgradient", "meshgradient"],
		["meshpatch", "meshpatch"],
		["meshrow", "meshrow"],
		["missing-glyph", "missing-glyph"],
		["mpath", "mpath"],
		["path", "path"],
		["pattern", "pattern"],
		["polygon", "polygon"],
		["polyline", "polyline"],
		["radialgradient", "radialGradient"],
		["rect", "rect"],
		["set", "set"],
		["solidcolor", "solidcolor"],
		["stop", "stop"],
		["svg", "svg"],
		["symbol", "symbol"],
		["text", "text"],
		["textpath", "textPath"],
		["tspan", "tspan"],
		["use", "use"],
		["view", "view"],
		["vkern", "vkern"]
	]);
	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 (isNode(child)) parent.appendChild(child);
			else parent.appendChild(document.createTextNode(String(child)));
		}
	}
	function isNode(value) {
		return value !== null && typeof value === "object" && "nodeType" in value && typeof value.nodeType === "number" && "nodeName" in value && typeof value.nodeName === "string";
	}
	function setDataset(el, dataset) {
		if ("dataset" in el && el.dataset) {
			Object.assign(el.dataset, dataset);
			return;
		}
		for (const key in dataset) {
			const value = dataset[key];
			const attribute = `data-${key.replace(/[A-Z]/g, (letter) => `-${letter.toLowerCase()}`)}`;
			if (value == null) el.removeAttribute(attribute);
			else el.setAttribute(attribute, String(value));
		}
	}
	function h(tag, props, ...children) {
		if (typeof tag === "function") {
			const normalizedProps = Object.assign({}, props);
			normalizedProps.children = children.length === 0 ? void 0 : children.length === 1 ? children[0] : children;
			return tag(normalizedProps);
		}
		if (!tag) {
			const fragment = document.createDocumentFragment();
			appendChildren(fragment, children);
			return fragment;
		}
		const svgTag = typeof tag === "string" ? SVG_TAGS.get(tag.toLowerCase()) : void 0;
		const isSvg = svgTag !== void 0;
		const el = isSvg ? document.createElementNS(SVG_NS, svgTag) : document.createElement(tag);
		if (props) for (const key in props) {
			const val = props[key];
			if (key === "ref") {
				if (typeof val === "function") val(el);
				else if (val && typeof val === "object") val.current = el;
				continue;
			}
			if (key === "children") continue;
			if (key.charCodeAt(0) === 46) {
				el[key.slice(1)] = val;
				continue;
			}
			if (val === void 0 || val === null) continue;
			if (key.charCodeAt(0) === 111 && key.charCodeAt(1) === 110) {
				const eventName = key.slice(2).toLowerCase();
				if (typeof val === "function" || typeof val === "object" && val !== null) el.addEventListener(eventName, val);
				continue;
			}
			let attrName = key;
			if (key === "className") attrName = "class";
			else if (key === "htmlFor") attrName = "for";
			if (key === "style") {
				const styledEl = el;
				if (typeof val === "object" && val !== null) Object.assign(styledEl.style, val);
				else styledEl.style.cssText = String(val);
				continue;
			}
			if (key === "dataset" && typeof val === "object") {
				setDataset(el, val);
				continue;
			}
			if (typeof val === "boolean") {
				if (!isSvg && BOOLEAN_PROPS.has(key)) el[key] = val;
				if (val) el.setAttribute(attrName, "");
				else el.removeAttribute(attrName);
				continue;
			}
			el.setAttribute(attrName, String(val));
		}
		appendChildren(el, children);
		return el;
	}
	var boundHtm = xhtm_default.bind(h);
	function html(statics, ...args) {
		const result = boundHtm(statics, ...args);
		if (Array.isArray(result)) {
			const fragment = document.createDocumentFragment();
			appendChildren(fragment, result);
			return fragment;
		}
		if (!isNode(result)) return document.createTextNode(String(result ?? ""));
		return result;
	}
	(typeof globalThis !== "undefined" ? globalThis : window).html = html;
})();