import { HTMLWidget, publish } from "@hpcc-js/common"; import { IObserverHandle } from "@hpcc-js/util"; import { OJSRuntime } from "./ojsRuntime"; import { OMDRuntime } from "./omdRuntime"; import { OJSRuntimeError, OJSSyntaxError } from "./util"; import "../src/observable.css"; function stringify(value: any): string { if (value instanceof Element) { return value.outerHTML; } const type = typeof value; switch (type) { case "function": return "ƒ()"; case "object": if (Array.isArray(value)) { return "[Array]"; } break; case "string": case "number": case "bigint": case "boolean": case "symbol": case "undefined": break; } if (value?.toString) { return value.toString(); } return value; } function throttle(func, interval) { let timeout; return function (this) { const context = this; const args = arguments; const later = function () { timeout = false; }; if (!timeout) { func.apply(context, args); timeout = true; setTimeout(later, interval); } }; } export class Observable extends HTMLWidget { constructor() { super(); } @publish("ojs", "string", "Mode (ojs|omd)") mode: publish; @publish("", "string", "OJS | OMD Text") text: publish; @publish({}, "object", "Plugins") plugins: publish; @publish(false, "boolean", "Show Observable Values") showValues: publish; @publish(0, "number", "Increments on each enter.") enterCount: publish; _errors: OJSRuntimeError[] = []; errors(): OJSRuntimeError[] { return this._errors; } enter(domNode, element) { super.enter(domNode, element); this.enterCount(this.enterCount() + 1); } _watcher: IObserverHandle; protected _prevHash: string | undefined; update(domNode, element) { super.update(domNode, element); this._placeholderElement .style("width", null) .style("height", null) ; element .style("width", null) .style("height", null) ; element.classed("hide-values", !this.showValues() ? true : null); const hash = this.propertyHash(["mode", "text", "showValues", "enterCount"]); if (this._prevHash !== hash) { this._prevHash = hash; const context = this; const runtimeUpdated = throttle(function () { context.runtimeUpdated(); }, 500); element.html(""); const runtime = this.mode() === "ojs" ? new OJSRuntime(domNode, this.plugins()) : new OMDRuntime(domNode, this.plugins()); if (this._watcher) { this._watcher.release(); } this._watcher = runtime.watch(async variableValues => { const vars = runtime.latest(); this._errors = vars.map(n => { const { start, end } = n.variable.pos(); return new OJSRuntimeError(n.type, start, end, stringify(n.value)); }); runtimeUpdated(); }); runtime.evaluate("", this.text(), ".") .catch((e: OJSSyntaxError) => { this._errors = [new OJSRuntimeError("error", e.start, e.end, e.message)]; this.runtimeUpdated(); }); } } // Events --- runtimeUpdated() { } } Observable.prototype._class += " observable-md_Observable";