Zum Inhalt

src/host-web/obsidian-dom-polyfill.ts

// Obsidian DOM polyfill — adds the `createEl/createDiv/createSpan/empty`
// helpers Obsidian extends onto HTMLElement.prototype, so the engine code
// (which was written against the Obsidian API) runs unmodified in the
// standalone web build. Import once at the host entrypoint.

type ElInfo = {
  cls?: string | string[];
  text?: string;
  attr?: Record<string, string | number | boolean | null>;
  title?: string;
  type?: string;
  value?: string;
  href?: string;
  placeholder?: string;
};

function applyInfo(el: HTMLElement, info?: ElInfo) {
  if (!info) return;
  if (info.cls) {
    const classes = Array.isArray(info.cls) ? info.cls : info.cls.split(/\s+/);
    for (const c of classes) if (c) el.classList.add(c);
  }
  if (info.text != null) el.textContent = info.text;
  if (info.title) el.title = info.title;
  if (info.attr) {
    for (const [k, v] of Object.entries(info.attr)) {
      if (v == null) continue;
      el.setAttribute(k, String(v));
    }
  }
  if (info.type) (el as HTMLInputElement).type = info.type;
  if (info.value != null) (el as HTMLInputElement).value = info.value;
  if (info.href) (el as HTMLAnchorElement).href = info.href;
  if (info.placeholder) (el as HTMLInputElement).placeholder = info.placeholder;
}

const proto = HTMLElement.prototype as any;

if (typeof proto.createEl !== 'function') {
  proto.createEl = function <K extends keyof HTMLElementTagNameMap>(
    this: HTMLElement,
    tag: K,
    info?: ElInfo,
    callback?: (el: HTMLElementTagNameMap[K]) => void,
  ): HTMLElementTagNameMap[K] {
    const el = document.createElement(tag);
    applyInfo(el as unknown as HTMLElement, info);
    this.appendChild(el);
    if (callback) callback(el);
    return el;
  };
}

if (typeof proto.createDiv !== 'function') {
  proto.createDiv = function (
    this: HTMLElement,
    info?: ElInfo,
    callback?: (el: HTMLDivElement) => void,
  ): HTMLDivElement {
    return (this as any).createEl('div', info, callback);
  };
}

if (typeof proto.createSpan !== 'function') {
  proto.createSpan = function (
    this: HTMLElement,
    info?: ElInfo,
    callback?: (el: HTMLSpanElement) => void,
  ): HTMLSpanElement {
    return (this as any).createEl('span', info, callback);
  };
}

if (typeof proto.empty !== 'function') {
  proto.empty = function (this: HTMLElement): void {
    while (this.firstChild) this.removeChild(this.firstChild);
  };
}

declare global {
  interface HTMLElement {
    createEl<K extends keyof HTMLElementTagNameMap>(
      tag: K,
      info?: ElInfo,
      callback?: (el: HTMLElementTagNameMap[K]) => void,
    ): HTMLElementTagNameMap[K];
    createDiv(info?: ElInfo, callback?: (el: HTMLDivElement) => void): HTMLDivElement;
    createSpan(info?: ElInfo, callback?: (el: HTMLSpanElement) => void): HTMLSpanElement;
    empty(): void;
  }
}

export {};