Skip to content

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.

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

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 reload
widget.update(container, { accent: null }); // back to the site colour
widget.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.

widget.setInputs(container, { amount: '2500', mode: 'exclusive' }, { autorun: true }); // true

Values 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.

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), meta and the request inputs.
  • If the form is invalid it rejects with an API code such as VALIDATION_ERROR and 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 as NETWORK_ERROR and INVALID_RESPONSE can also come from the embed page: Error codes.
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 null

snapshot.inputs is the canonical input from the user’s latest edit (when the form is valid), otherwise null.

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.

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);
}
global.d.ts
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.