Events and basket state
The widget keeps your page informed through events. You register callbacks, the widget calls them as things happen, and you use them to keep your own UI in sync (for example a running total or an order summary). It also writes the current basket to a shared place for you to read.
The events surface
Koala events live on window.CTStore.koala.events. Each event listener is an array of callbacks, and each callback
receives (status, data), plus a third argument you can ignore. Register them before the loader script runs, as part of
the koala namespace, or add them to its arrays later: the widget reads the arrays when it fires (see
Widget loading for the one ordering rule).
There are five events; when each fires and the data each carries are documented in the reference page
Events.
Registering handlers
Register every handler on window.CTStore.koala.events:
window.CTStore = {
dataSource: {
/* ... */
},
koala: {
events: {
onReady: [(status) => {}],
onAvail: [(status, data) => {}],
onBundle: [(status, data) => {}],
onBasket: [(status, data) => {}],
onError: [(status, data) => console.error(data.message)],
},
},
};
One bundle event per state change
Each bundle is undecided, selected, or declined, and onBundle fires once for every bundle whose state changes,
whatever control the traveller used. A bundle leaving a state fires first (UNSELECT, UNDECLINE), then a bundle
entering one (SELECT, DECLINE). So declining a selected bundle fires UNSELECT then DECLINE; a global "No thanks"
fires DECLINE for every bundle and, when turned off, UNDECLINE for every bundle; selecting a bundle while all are
declined fires UNDECLINE for each, then SELECT. The state the widget starts with, a partner default or a selection
restored after a reload, fires no onBundle: read it from onBasket. UNDECLINE can therefore arrive without a
preceding DECLINE when the partner starts with everything declined.
When nothing will render
onAvail SUCCESS carries a count of the priced bundles the widget will render. When it is 0, the quote succeeded
but no bundle is sellable for this trip (the rejection reasons are in response.data.quotes), and the widget renders
nothing. It also prepares no request: basketPayload stays undefined, so there is nothing to send after the booking.
Treat that the same way as a quote failure and collapse any space your page reserved:
window.CTStore.koala.events.onAvail = [
(status, data) => {
if (status === 'ERROR' || data.count === 0) {
hideProtectionSection(); // your own function
}
},
];
On koala.events, the onBasket data is the Koala basket itself (read it directly):
window.CTStore.koala.events.onBasket = [
(status, data) => {
const { selectedBundles, declinedBundles } = data;
updateOrderSummary(selectedBundles); // your own function
},
];
On pages that also run the CarTrawler car-rental Smartblock, the basket change is additionally mirrored on the shared
window.CTStore.events bus, under a koala key. How the two event surfaces coexist is covered in
Alongside the car-rental Smartblock.
Basket state on window.CTStore.koala
Alongside the onBasket event, the widget writes the latest basket to window.CTStore.koala.basketPayload, so you can
read the current state at any time without waiting for an event. Its shape is documented in the reference page
Events. Its book property is what your application
sends after the booking to create the subscription or delete the quote (see
Creating the subscription).
The selection survives a page reload
Booking pages reload, sometimes on their own. The widget keeps the traveller's selection for one hour in the
browser's local storage, so a reload does not start empty. On the next load it quotes the trip again, reselects the
stored bundles that are still priced (a bundle the new quote did not price is silently dropped), rebuilds the prepared
request against the new quote, and fires onBasket once so your page can rebuild its order summary. No onBundle
event fires for a restored bundle.
The stored selection is scoped to your partner, the placement (dataSource.custom.context) and the trip: a choice made
at checkout never resurfaces on the confirmation page, and two tabs open on different trips do not interfere. A
traveller who cleared their selection starts from your configured defaults again, and so does one who comes back after
the hour. Only bundle ids are stored, nothing about the trip or the traveller.
If window.CTStore.koala.basketPayload is still present when the widget loads, for example in a single-page application
that keeps the global object across routes, that basket takes precedence over the stored selection and is restored
without firing onBasket.