Docs
Developer guide
Integrate the extension into any http or https page. The extension finds forms by DOM attributes, not by a JS register API.
1. Add a marker element
Required: data-ext-fetch and data-ext-fetch-id. Put the marker where the icon should appear — the extension appends the button inside this element.
<div
data-ext-fetch="dereg-rebate"
data-ext-fetch-id="order-42"
data-ext-fetch-name="Dereg rebate"
data-ext-fetch-access-code="295161"
data-ext-fetch-vehicle-slug="SNY3466Y"
data-ext-fetch-intended-date="2026-10-02"
data-ext-fetch-with-screenshot="true"
>
Click the extension icon to fetch
</div>
2. Keep attributes in sync with your inputs
The extension does not read your <input> fields. It polls data-ext-fetch-* about every 500ms. Update the attributes whenever the user edits the form.
const marker = document.querySelector("[data-ext-fetch-id='order-42']");
accessCodeInput.addEventListener("input", () => {
marker.dataset.extFetchAccessCode = accessCodeInput.value;
});
dataset.extFetchAccessCode maps to data-ext-fetch-access-code. Same pattern for vehicle slug and intended date.
3. Listen for the result
The event name includes your form id: form-result--order-42.
window.addEventListener("form-result--order-42", (event) => {
const { formId, result } = event.detail;
// result.records [{ label, value }, ...]
// result.screenshot dereg rebate image, or null
// result.screenshotTF transfer fee image, or null
// result.transferFeeError set if combined TF step failed
// result.originalDeregRebateData
// result.originalTransferFeeData
});
4. Detect the extension
window.addEventListener("check-extension-info-result", (event) => {
const info = event.detail.extensionInfo;
// { version, name, short_name, icons, description } or missing
}, { once: true });
window.dispatchEvent(new CustomEvent("check-extension-info"));
If nothing replies within ~500ms, treat the extension as not installed. Re-check on visibilitychange and focus so a user who just enabled it is picked up.
Fetch modes
| Marker | Pages fetched |
|---|---|
data-ext-fetch="dereg-rebate" (default) |
Dereg rebate, then transfer fee |
Same + data-ext-fetch-single-fetch-only="true" |
Dereg rebate only |
data-ext-fetch="transfer-fee" |
Transfer fee only |
In combined mode, a transfer-fee failure is optional. You still get dereg rebate records, plus result.transferFeeError. A dereg-rebate failure is fatal and currently shows only in the side panel — the host page does not receive a window error event.
When the icon is disabled
The injected button stays disabled until all of these are set and valid:
- access code
- vehicle slug / carplate
- intended date that parses and is today or later
Side panel
Clicking the icon opens the side panel and starts the fetch. If the user opens the panel first, the extension asks the page to sync via lta:ask-sync-app-form-input-values. You do not need to handle that event unless you want a custom sync — attribute polling already covers it.
The panel can also run a standalone dereg-rebate form when no host marker is present.
Version gate
If a newer extension version is published, the icon shows a badge. On click:
- Available — user can Continue or open the update URL
- Urgent (below
minVersion) — fetch is blocked until they update
Iframes
The content script runs in every frame except LTA and Google. Put the marker inside the iframe document that owns the form. The result event fires on that iframe’s window, not the top page.
Firefox
Result event.detail is cloned with cloneInto so page scripts can read it. If your app re-dispatches that object into another context, clone it again the same way.
Known limits to plan around
data-ext-fetch-mask-credentialsis read into form state, but the icon-click payload does not currently forward it. Fetches default to masking on.- Hard fetch errors go to the side panel only. Host pages only see
form-result--{id}for success or partial TF failure. - Do not use
evt:register-form. The extension never listens for it. - Excluded sites:
vrl.lta.gov.sg,onemotoring.lta.gov.sg,www.google.com.
Minimal integration
<form id="lta-form">
<input name="accessCode" />
<input name="vehicleSlug" />
<input name="intendedDate" type="date" />
</form>
<div id="lta-marker"
data-ext-fetch="dereg-rebate"
data-ext-fetch-id="order-42"
data-ext-fetch-name="Order 42"></div>
<script>
const form = document.getElementById("lta-form");
const marker = document.getElementById("lta-marker");
function sync() {
const data = new FormData(form);
marker.dataset.extFetchAccessCode = data.get("accessCode") || "";
marker.dataset.extFetchVehicleSlug = data.get("vehicleSlug") || "";
marker.dataset.extFetchIntendedDate = data.get("intendedDate") || "";
}
form.addEventListener("input", sync);
sync();
window.addEventListener("form-result--order-42", (event) => {
console.log(event.detail.result.records);
});
</script>