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