Vanilla HTM

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

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

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

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

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

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

You will need to install a user script manager extension to install this script.

(I already have a user script manager, let me install it!)

You will need to install an extension such as Stylus to install this style.

You will need to install an extension such as Stylus to install this style.

You will need to install an extension such as Stylus to install this style.

You will need to install a user style manager extension to install this style.

You will need to install a user style manager extension to install this style.

You will need to install a user style manager extension to install this style.

(I already have a user style manager, let me install it!)

// ==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);