Vanilla XHTM

Framework-free JSX-like syntax in pure JS using XHTM and native DOM creation.

このスクリプトは単体で利用できません。右のようなメタデータを含むスクリプトから、ライブラリとして読み込まれます: // @require https://update.greasyfork.org/scripts/591770/1907698/Vanilla%20XHTM.js

スクリプトをインストールするには、Tampermonkey, GreasemonkeyViolentmonkey のような拡張機能のインストールが必要です。

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

スクリプトをインストールするには、TampermonkeyViolentmonkey のような拡張機能のインストールが必要です。

スクリプトをインストールするには、TampermonkeyUserscripts のような拡張機能のインストールが必要です。

このスクリプトをインストールするには、Tampermonkeyなどの拡張機能をインストールする必要があります。

このスクリプトをインストールするには、ユーザースクリプト管理ツールの拡張機能をインストールする必要があります。

(ユーザースクリプト管理ツールは設定済みなのでインストール!)

このスタイルをインストールするには、Stylusなどの拡張機能をインストールする必要があります。

このスタイルをインストールするには、Stylus などの拡張機能をインストールする必要があります。

このスタイルをインストールするには、Stylus tなどの拡張機能をインストールする必要があります。

このスタイルをインストールするには、ユーザースタイル管理用の拡張機能をインストールする必要があります。

このスタイルをインストールするには、ユーザースタイル管理用の拡張機能をインストールする必要があります。

このスタイルをインストールするには、ユーザースタイル管理用の拡張機能をインストールする必要があります。

(ユーザースタイル管理ツールは設定済みなのでインストール!)

このスクリプトの質問や評価の投稿はこちら通報はこちらへお寄せください
// ==UserScript==
// @name         Vanilla XHTM
// @namespace    https://greasyfork.org/users/1635096-paesss
// @version      1.8.0
// @author       Paesss
// @description  Framework-free JSX-like syntax in pure JS using XHTM and native DOM creation.
// @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 SVG_TAGS = new Set([
    "svg",
    "path",
    "circle",
    "rect",
    "line",
    "polyline",
    "polygon",
    "ellipse",
    "g",
    "use",
    "text",
    "tspan",
    "symbol",
    "defs",
    "clipPath",
    "mask",
    "pattern",
    "marker",
    "foreignObject",
  ]);
  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)));
    }
  }
  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 isSvg = typeof tag === "string" && SVG_TAGS.has(tag);
    const el = isSvg ? document.createElementNS(SVG_NS, tag) : document.createElement(tag);
    if (props)
      for (const key in props) {
        const val = props[key];
        if (val === void 0 || val === null) continue;
        if (key === "ref") {
          if (typeof val === "function") val(el);
          else if (val && typeof val === "object") val.current = el;
          continue;
        }
        if (key.charCodeAt(0) === 46) {
          el[key.slice(1)] = val;
          continue;
        }
        if (key.charCodeAt(0) === 111 && key.charCodeAt(1) === 110) {
          const eventName = key.slice(2).toLowerCase();
          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") {
          Object.assign(el.dataset, val);
          continue;
        }
        if (typeof val === "boolean") {
          if (val) el.setAttribute(attrName, "");
          else el.removeAttribute(attrName);
          continue;
        }
        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));
      }
    appendChildren(el, children);
    return el;
  }
  ((global) => {
    global.html = xhtm_default.bind(h);
  })(typeof globalThis !== "undefined" ? globalThis : window);
})();