Sky Event Model
// EVENTS
class Event {
constructor (String type, Boolean bubbles = true, any data = null); // O(1)
readonly attribute String type; // O(1)
readonly attribute Boolean bubbles; // O(1)
attribute any data; // O(1)
readonly attribute EventTarget target; // O(1)
attribute Boolean handled; // O(1)
attribute any result; // O(1)
// TODO(ianh): do events get blocked at scope boundaries, e.g. focus events when both sides are in the scope?
// TODO(ianh): do events get retargetted, e.g. focus when leaving a custom element?
}
callback EventListener any (Event event);
// if the return value is not undefined:
// assign it to event.result
// set event.handled to true
abstract class EventTarget {
any dispatchEvent(Event event); // O(N) in total number of listeners for this type in the chain
// sets event.handled to false and event.result to undefined
// makes a record of the event target chain by calling getEventDispatchChain()
// invokes all the handlers on the chain in turn
// returns event.result
virtual Array<EventTarget> getEventDispatchChain(); // O(1) // returns []
void addEventListener(String type, EventListener listener); // O(1)
void removeEventListener(String type, EventListener listener); // O(N) in event listeners with that type
private Array<String> getRegisteredEventListenerTypes(); // O(N)
private Array<EventListener> getRegisteredEventListenersForType(String type); // O(N)
}
class CustomEventTarget : EventTarget { // implemented in JS
constructor (); // O(1)
attribute EventTarget parentNode; // getter O(1), setter O(N) in height of tree, throws if this would make a loop
virtual Array<EventTarget> getEventDispatchChain(); // O(N) in height of tree // implements EventTarget.getEventDispatchChain()
// let result = [];
// let node = this;
// while (node) {
// result.push(node);
// node = node.parentNode;
// }
// return result;
// you can inherit from this to make your object into an event target
// or you can inherit from EventTarget and implement your own getEventDispatchChain()
}