Skip to content

events

Browser event wiring and form-data collection for the bridge.

The :class:EventManager attaches the DOM listeners that turn browser activity into router dispatches: delegated click navigation (data-nav / data-call), form submission, per-component event handlers declared via the data--drafter-handlers attribute, global window events, and double-press hotkeys. The module-level functions gather everything a dispatch needs from the DOM — form values (including asynchronous file uploads), custom event details, and component argument attributes — and package them into the {"values": ..., "payload": ...} envelope that handlers pass to the router, with per-value provenance entries so the router can merge by precedence (component arguments > event detail > form fields) and report collisions.

DOUBLE_PRESS_THRESHOLD = 600 module-attribute

Maximum gap, in milliseconds, between two presses of a hotkey combination for them to count as a double press and trigger the hotkey's callback.

DRAFTER_PAGE_LOADED_EVENT = 'drafter-page-loaded' module-attribute

Name of the custom window event dispatched after each page render, carrying the route, request id, and response id in its detail.

HANDLERS_MOUNTED_ATTR = 'data-drafter-handlers-mounted' module-attribute

Attribute set on an element once its per-element handlers are attached.

Persisted components survive page swaps with their listeners intact, so re-mounting on a later page load would dispatch every event twice (and leak listeners); this marker lets mount_event_handlers skip them.

EventManager(runtime) dataclass

Manages the DOM event listeners for one Drafter instance.

Mounts navigation click/submit delegation, per-component event handlers, global window events, and hotkeys, wrapping every callback through the runtime adapter so proxies are created and cleaned up correctly. Lookups for the instance's inner-frame elements (BODY/FORM) are scoped so that concurrent instances on the same page don't find each other's elements.

Attributes:

Name Type Description
runtime RuntimeAdapter

Runtime adapter used to wrap, clean up, and chain the event handlers and their promises.

click_handler Any

The currently mounted (wrapped) delegated click handler on the body element, or None before mounting.

submit_handler Any

The currently mounted (wrapped) submit handler on the form element, or None before mounting.

listeners dict[str, Callable]

Wrapped window-level listeners keyed by event name, kept so re-registration can remove and clean up the old handler.

hotkey_events dict[str, Callable[[], None]]

Callbacks keyed by lowercase key name, triggered on a double press of Ctrl/Meta plus that key.

last_press_time int

Timestamp in milliseconds of the last qualifying hotkey press, used to detect double presses.

hotkey_listener_ready bool

Whether the shared keydown listener backing all hotkeys has been attached to the document.

scope Any

Node that inner-frame lookups (BODY/FORM) are scoped to; defaults to the instance's document and is replaced with the instance's shadow root via set_scope().

clear_button_spinners()

Remove the loading mark from any buttons still spinning.

dispatch_page_loaded(route, request_id, response_id, fragment=False)

Dispatch the drafter-page-loaded custom event on the window.

Fired after every committed render, so components that defer work until their content is in place (auto-play audio and media, timers) can start, and so external code (e.g. tests or embedding hosts) can observe that a route finished loading. Listeners that only care about full page loads can ignore events whose fragment detail is true.

Parameters:

Name Type Description Default
route str

Name of the route that was rendered.

required
request_id int

Identifier of the request that produced the page.

required
response_id int

Identifier of the response that was rendered.

required
fragment bool

Whether the render injected a Fragment into part of the page rather than replacing the whole page.

False

mount_bug_report_download(callback)

Wire the bug-report page's download button to the given callback.

The button is rendered by the --bug-report system route with the well-known BUG_REPORT_DOWNLOAD id.

mount_event_handlers(root, do_navigation)

Mount event handlers for components with data--drafter-handlers attribute.

This sets up delegation for events like blur, change, focus, input, etc., that should trigger route dispatches.

Parameters:

Name Type Description Default
root Any

The root element to attach listeners to.

required
do_navigation Callable

Callback to handle navigation events.

required

mount_navigation(do_navigation)

Mount the delegated click and form-submit listeners for navigation.

Attaches a click handler to the instance's body element that intercepts clicks on elements carrying data-nav or data-call and turns them into "link" requests, and a submit handler on the instance's form element that turns submissions into "form" requests (resolving the target URL from the submitter's formaction, the form's action, or the current location). Previously mounted click handlers are removed and cleaned up first, and per-component event handlers are (re)mounted via mount_event_handlers. Both handlers collect event/form/argument data, wait for any pending file-upload promises, then invoke the navigation callback.

Parameters:

Name Type Description Default
do_navigation Callable

Callback invoked with the built Request to perform the route dispatch.

required

Raises:

Type Description
RuntimeError

If the instance's form root element cannot be found (via raise_bridge_system_error).

mount_stop_server_button(callback)

Wire the stop-server page's button to the given callback.

The button is rendered by the --stop-server system route with the well-known STOP_SERVER id.

mount_subtle_debug_entry(callback)

Wire the subtle production debug-entry button to the debug toggle.

Bound here with a proper proxied listener (not an inline onclick in the site HTML) so the click is handled by this instance's own bridge no matter which document (e.g. an iframe) the site renders into.

set_scope(scope)

Scope this manager's inner-frame lookups to the given node.

setup_events(event_handlers, key_handlers, activate=None)

Register global window event listeners and hotkey callbacks.

Parameters:

Name Type Description Default
event_handlers dict[str, Callable[[Any], Any]]

Mapping from window event name to the handler to invoke; each is wrapped and attached to the window, replacing any previously registered handler for that name.

required
key_handlers dict[str, Callable[[], None]]

Mapping from key combination (e.g. "ctrl+d") to the callback triggered on a double press of that hotkey.

required
activate Callable[[], None] | None

Optional callable run before every registered handler (window events and hotkeys alike). The composition root passes a closure that pins this instance's server as the "current" one, so telemetry raised anywhere inside a handler routes to this instance's event bus even when several instances share the interpreter.

None

start_button_spinner(element)

Mark a pressed button as loading (spinner + no more clicks).

Only acts when the button_spinners configuration setting is on. The CSS class shows a spinner and disables pointer events; the mark is cleared by clear_button_spinners when a response commits.

teardown()

Remove this manager's window/document listeners and release them.

Called when the instance is torn down (e.g. before its code is re-run by the in-browser editor). Without this, the old instance's global listeners survive its DOM and keep routing events into its dead bridge. Element-level listeners (click/submit/component handlers) die with the instance's DOM and need no removal here; their proxies are released so the runtime does not retain them.

apply_form_transforms(form, form_values)

Apply declared data-transform decodings to collected form values.

Walks the form's controls and, for each field present in form_values whose element declares data-transform="json-decode", replaces the value with its JSON-decoded form in place. Each field name is processed at most once.

Parameters:

Name Type Description Default
form Any

The form element whose controls declare the transforms.

required
form_values dict[str, Any]

Mapping of collected form values, modified in place.

required

build_payload_entries(form_values, event_data, argument_data)

Tag every collected value with its source, for router-side provenance.

The router merges these by precedence (component arguments > event detail

form fields) and reports collisions, so all values are kept here.

collect_form_data(runtime, form, submitter)

Collect raw form values, starting file uploads asynchronously.

Builds a FormData snapshot of the form and groups every entry into a list per field name (pre-seeded with all multi-valued field names, so unchecked/empty multi-fields still appear). String values are stored directly; each file entry is stored as a pending-upload placeholder at its original position and replaced with the uploaded file data when its upload promise resolves. All fields are represented as lists until file uploads complete and cardinality normalization occurs (see normalize_form_data).

Parameters:

Name Type Description Default
runtime RuntimeAdapter

Runtime adapter used to create the FormData object, start file uploads, and chain their promises.

required
form Any

The form element whose values are being collected.

required
submitter Any

The element that submitted the form (passed to FormData so its name/value is included), or None.

required

Returns:

Type Description
tuple[dict[str, list[Any]], set[str], list[Any]]

Tuple of (grouped, multiple_names, upload_promises), where grouped maps each field name to its list of raw values (file entries still pending until the promises resolve), multiple_names is the set of field names that may carry multiple values, and upload_promises is a list of promises that must all resolve before grouped contains only real values.

ensure_uploads_resolved(grouped)

Verify that no pending file-upload placeholders remain.

collect_form_data stores a placeholder for each file entry until its upload promise commits the real data; this is called after all upload promises should have resolved, as a sanity check before the values are normalized.

Parameters:

Name Type Description Default
grouped dict[str, list[Any]]

Mapping from field name to its list of collected values.

required

Raises:

Type Description
RuntimeError

If any field still contains an unresolved file-upload placeholder.

get_all_event_data(runtime, originator, event, submitter, scope=None)

Collect all relevant data for an event, including form data and arguments.

get_multiple_field_names(form)

Get the names of all form fields that can have multiple values.

get_single_checkbox_names(form)

Get the names of checkbox fields that appear exactly once in a form.

Single checkboxes are special-cased by normalize_form_data: when unchecked they submit no value at all, so their absence is coerced to False rather than being omitted.

Parameters:

Name Type Description Default
form Any

The form element whose controls are inspected.

required

Returns:

Type Description
set[str]

Set of field names belonging to exactly one checkbox input.

group_form_data(form, form_data)

Convert FormData into its natural Python representation: a mapping from names to lists of submitted values.

json_decode_form_value(key, value, *, element)

JSON-decode a form field value, falling back to the raw value.

Applied to fields whose element carries data-transform="json-decode" (e.g. JSON-encoded component arguments). List values are decoded item by item; non-string items (e.g. file uploads) are passed through unchanged. A value that fails to decode is reported as a bridge warning and returned as-is.

Parameters:

Name Type Description Default
key str

Name of the form field, used in the warning message.

required
value Any

The submitted value, or list of values, to decode.

required
element Any

The form control the value came from, used to attribute the warning to a DOM id.

required

Returns:

Type Description
Any

The decoded value(s), or the original value(s) where decoding was skipped or failed.

normalize_form_data(grouped, multiple_names, single_checkbox_names)

Multi-valued fields are always lists. Scalar fields are scalars unless malformed or duplicate controls submitted more than one value.

process_form_data(runtime, form, submitter, base_data, argument_data)

Collect and finalize a form's data into an event-data envelope.

Collects the form's raw values (starting any file uploads), then — once all upload promises have resolved — normalizes cardinality, applies declared data-transform decodings, and merges values by precedence (component arguments > event detail > form fields). The result is wrapped as a {"values": ..., "payload": ...} envelope where payload carries per-value provenance entries.

Parameters:

Name Type Description Default
runtime RuntimeAdapter

Runtime adapter used to build the FormData object and chain the upload/finalization promises.

required
form Any

The form element whose data is being collected.

required
submitter Any

The element that submitted the form, or None.

required
base_data dict[str, Any]

Values taken from the triggering event's detail.

required
argument_data dict[str, Any]

Values taken from component argument attributes.

required

Returns:

Type Description
list[Any]

Single-element list whose entry resolves to the envelope: the wrapped envelope itself when there are no uploads, otherwise a promise chain that finalizes after all uploads complete. Callers pass this list to runtime.finish_promises and read the envelope from the last resolved entry.

unpack_event_bundle(bundle)

Split an event-data envelope into (merged values, provenance entries).

Tolerates legacy plain-dict bundles (no provenance) for safety.