JavaScript API
The loader defines a frozen object named window.MiracOzdemirWidget. The types below are for loader 1.1.0:
interface MiracOzdemirWidgetApi { readonly version: '1'; readonly build: string; // e.g. '1.1.0' readonly features: readonly string[]; // capabilities of the loader mount(el: Element): HTMLIFrameElement | null; unmount(el: Element): boolean; unmountAll(): number; scan(root?: ParentNode): number; update(el: Element, patch: WidgetPatch): boolean; setInputs(el: Element, values: Record<string, string | number | boolean>, options?: { autorun?: boolean }): boolean; calculate(el: Element, options?: { timeoutMs?: number }): Promise<{ data: unknown; meta: unknown; inputs: unknown }>; reset(el: Element): boolean; get(el: Element): WidgetInstance | null; getResult(el: Element): { data: unknown; meta: unknown; inputs: unknown } | null;}| Member | What it does |
|---|---|
version |
Protocol/loader major version ("1"). |
build |
Loader build version, semver ("1.1.0"). |
features |
Loader capabilities: update, inputs, calculate, reset, inherit-theme, kinds, custom-elements. |
mount(el) |
Reads the data-* options on el, validates them and builds an iframe. Returns the iframe; if the options are invalid, writes a console.warn and returns null. If the element is already mounted, returns the existing iframe (no duplicate widget). |
unmount(el) |
Removes the iframe and cleans the element. Returns true if removed. Pending calculate() calls are rejected with the code UNMOUNTED. |
unmountAll() |
Removes all widgets on the page; returns the number removed. |
scan(root?) |
Mounts the not-yet-mounted [data-mo-tool], [data-mo-search], [data-mo-category] elements inside root (default document); returns the number newly mounted. It also cleans up old instances that left the DOM. |
update(el, patch) |
Changes options, without reloading where possible (see below). Returns true if something changed. |
setInputs(el, values, { autorun }) |
Fills the inputs of a calculator widget. Returns false if el is not a mounted calculator. |
calculate(el, { timeoutMs }) |
Validates and calculates the form; returns a Promise. |
reset(el) |
Resets the form and the last result. |
get(el) |
Returns a snapshot of the instance, or null. |
getResult(el) |
Returns the last result ({ data, meta, inputs }) or null. |
The loader calls scan() once at DOMContentLoaded (or immediately if the script loaded late). Adding the script a second time only does another scan(); there is a single message listener.
Examples
Section titled “Examples”Adding a container later:
const container = document.createElement('div');container.dataset.moTool = 'kdv';container.dataset.locale = 'en';document.querySelector('#calculator').append(container);
window.MiracOzdemirWidget.mount(container);Rescanning a section (e.g. after HTML arrives via AJAX):
const added = window.MiracOzdemirWidget.scan(document.querySelector('#new-content'));console.log(`${added} widgets added`);Removing a widget:
window.MiracOzdemirWidget.unmount(container);update
Section titled “update”Changes options at runtime. It takes a patch object; invalid or unknown fields are ignored.
const widget = window.MiracOzdemirWidget;
widget.update(container, { theme: 'dark', accent: '0a7d5a', radius: 'lg' }); // instant, no reloadwidget.update(container, { accent: null }); // back to the site colourwidget.update(container, { locale: 'en' }); // the iframe is reloaded| Field | Values | Behaviour |
|---|---|---|
theme |
auto, light, dark, inherit |
live |
header |
true/false |
live |
density |
comfortable, compact |
live |
radius |
none, sm, md, lg |
live |
accent |
6 hex digits (# optional) or null |
live |
view |
full, result (calculator) |
live |
height |
px, 120–10000 | the iframe height is set immediately |
title |
text (≤ 200) | the iframe title |
locale |
tr, en |
reload |
tool |
tool key (calculator) | reload |
category |
category key (the key of a category widget, the filter of a search widget) | reload |
target |
_blank, event (search/category) |
reload |
“Live” changes are passed to the iframe in a config message; if the iframe is not ready yet they are queued, and if the embed page does not support the config feature (an older version) the loader reloads the iframe. A reload rebuilds the iframe’s src from the current options; the container element does not change. update also updates the container’s data-* attributes to the new values.
setInputs
Section titled “setInputs”widget.setInputs(container, { amount: '2500', mode: 'exclusive' }, { autorun: true }); // trueValues follow the same rules as data-input-*: field name ^[a-z][A-Za-z0-9_]{0,63}$, at most 50 fields, value ≤ 64 characters, decimals as strings; invalid ones are skipped. autorun: true calculates right after the inputs are applied. If the iframe is not ready yet, the call is queued. With an older embed page the loader puts the inputs in the address and reloads the iframe.
calculate
Section titled “calculate”try { const { data, meta, inputs } = await widget.calculate(container, { timeoutMs: 10_000 }); console.log(data.result, meta.asOf, inputs);} catch (error) { // error is not an Error but a { code, message, fields? } object console.warn(error.code, error.fields);}timeoutMs: clamped to 1000–60000, default 15000; it covers both waiting for readiness and the calculation.- The resolved value has the same shape as the response:
data(result,breakdown),metaand the requestinputs. - If the form is invalid it rejects with an API code such as
VALIDATION_ERRORand field errors (fields: [{ field, reason }]); the widget shows the same errors to the user. - Loader-side codes:
NOT_MOUNTED(the element is not mounted),NOT_READY(did not become ready in time),UNSUPPORTED(a search/category widget, or an older embed page that does not know the command),TIMEOUT,UNMOUNTED(the widget was removed while waiting). Codes such asNETWORK_ERRORandINVALID_RESPONSEcan also come from the embed page: Error codes.
reset, get, getResult
Section titled “reset, get, getResult”widget.reset(container); // true; clears the form and the last result
const snapshot = widget.get(container);// { id, kind, ready, height, title, tool: { key, version, title } | null, features,// options: { kind, key, locale, theme, header, height, accent, radius, density, view, autorun, target, category, inputs },// result: { data, meta, inputs } | null, inputs: {…} | null }
widget.getResult(container); // the last result, or nullsnapshot.inputs is the canonical input from the user’s latest edit (when the form is valid), otherwise null.
Waiting for the loader to be ready
Section titled “Waiting for the loader to be ready”Because the script loads async, the global may not be defined yet when your code runs. A safe helper:
function whenWidgetReady(callback) { if (window.MiracOzdemirWidget) return callback(window.MiracOzdemirWidget);
const existing = document.querySelector('script[src$="/widget.js"]'); const script = existing ?? Object.assign(document.createElement('script'), { src: 'https://api.mirac-ozdemir.com/widget.js', async: true }); script.addEventListener('load', () => callback(window.MiracOzdemirWidget), { once: true }); if (!existing) document.head.append(script);}
whenWidgetReady((widget) => widget.scan());For typings and ready-made loader helpers there is the SDK.
Feature detection
Section titled “Feature detection”widget.features lists the capabilities of the loader, and features in the ready event lists those of the embed page. If you write code that may run with an older loader (1.0), check before using members:
if (window.MiracOzdemirWidget?.features?.includes('update')) { window.MiracOzdemirWidget.update(container, { theme: 'dark' });} else { window.MiracOzdemirWidget?.unmount(container); container.dataset.theme = 'dark'; window.MiracOzdemirWidget?.mount(container);}TypeScript type
Section titled “TypeScript type”interface Window { MiracOzdemirWidget?: { readonly version: '1'; readonly build: string; readonly features: readonly string[]; mount(el: Element): HTMLIFrameElement | null; unmount(el: Element): boolean; unmountAll(): number; scan(root?: ParentNode): number; update(el: Element, patch: Record<string, unknown>): boolean; setInputs(el: Element, values: Record<string, string | number | boolean>, options?: { autorun?: boolean }): boolean; calculate(el: Element, options?: { timeoutMs?: number }): Promise<{ data: unknown; meta: unknown; inputs: unknown }>; reset(el: Element): boolean; get(el: Element): unknown | null; getResult(el: Element): unknown | null; };}The detailed types (WidgetPatch, event detail types, the manifest) are in the SDK package: Custom elements and SDK.