UNPKG

@angular/core

Version:

Angular - the core framework

1 lines • 121 kB
{"version":3,"file":"event-dispatch.mjs","sources":["../../../../../../../packages/core/primitives/event-dispatch/src/attribute.ts","../../../../../../../packages/core/primitives/event-dispatch/src/property.ts","../../../../../../../packages/core/primitives/event-dispatch/src/cache.ts","../../../../../../../packages/core/primitives/event-dispatch/src/event_type.ts","../../../../../../../packages/core/primitives/event-dispatch/src/event.ts","../../../../../../../packages/core/primitives/event-dispatch/src/event_contract_container.ts","../../../../../../../packages/core/primitives/event-dispatch/src/char.ts","../../../../../../../packages/core/primitives/event-dispatch/src/event_info.ts","../../../../../../../packages/core/primitives/event-dispatch/src/action_resolver.ts","../../../../../../../packages/core/primitives/event-dispatch/src/restriction.ts","../../../../../../../packages/core/primitives/event-dispatch/src/dispatcher.ts","../../../../../../../packages/core/primitives/event-dispatch/src/event_dispatcher.ts","../../../../../../../packages/core/primitives/event-dispatch/src/earlyeventcontract.ts","../../../../../../../packages/core/primitives/event-dispatch/src/event_contract_defines.ts","../../../../../../../packages/core/primitives/event-dispatch/src/eventcontract.ts","../../../../../../../packages/core/primitives/event-dispatch/src/bootstrap_app_scoped.ts"],"sourcesContent":["/**\n * @license\n * Copyright Google LLC All Rights Reserved.\n *\n * Use of this source code is governed by an MIT-style license that can be\n * found in the LICENSE file at https://angular.dev/license\n */\n\nexport const Attribute = {\n /**\n * The jsaction attribute defines a mapping of a DOM event to a\n * generic event (aka jsaction), to which the actual event handlers\n * that implement the behavior of the application are bound. The\n * value is a semicolon separated list of colon separated pairs of\n * an optional DOM event name and a jsaction name. If the optional\n * DOM event name is omitted, 'click' is assumed. The jsaction names\n * are dot separated pairs of a namespace and a simple jsaction\n * name.\n *\n * See grammar in README.md for expected syntax in the attribute value.\n */\n JSACTION: 'jsaction' as const,\n};\n","/**\n * @license\n * Copyright Google LLC All Rights Reserved.\n *\n * Use of this source code is governed by an MIT-style license that can be\n * found in the LICENSE file at https://angular.dev/license\n */\n\n/** All properties that are used by jsaction. */\nexport const Property = {\n /**\n * The parsed value of the jsaction attribute is stored in this\n * property on the DOM node. The parsed value is an Object. The\n * property names of the object are the events; the values are the\n * names of the actions. This property is attached even on nodes\n * that don't have a jsaction attribute as an optimization, because\n * property lookup is faster than attribute access.\n */\n JSACTION: '__jsaction' as const,\n /**\n * The owner property references an a logical owner for a DOM node. JSAction\n * will follow this reference instead of parentNode when traversing the DOM\n * to find jsaction attributes. This allows overlaying a logical structure\n * over a document where the DOM structure can't reflect that structure.\n */\n OWNER: '__owner' as const,\n};\n\ndeclare global {\n interface Node {\n [Property.JSACTION]?: {[key: string]: string | undefined};\n [Property.OWNER]?: ParentNode;\n }\n}\n","/**\n * @license\n * Copyright Google LLC All Rights Reserved.\n *\n * Use of this source code is governed by an MIT-style license that can be\n * found in the LICENSE file at https://angular.dev/license\n */\n\nimport {Property} from './property';\n\n/**\n * Map from jsaction annotation to a parsed map from event name to action name.\n */\nconst parseCache: {[key: string]: {[key: string]: string | undefined}} = {};\n\n/**\n * Reads the jsaction parser cache from the given DOM Element.\n */\nexport function get(element: Element): {[key: string]: string | undefined} | undefined {\n return element[Property.JSACTION];\n}\n\n/**\n * Reads the jsaction parser cache for the given DOM element. If no cache is yet present,\n * creates an empty one.\n */\nexport function getDefaulted(element: Element): {[key: string]: string | undefined} {\n const cache = get(element) ?? {};\n set(element, cache);\n return cache;\n}\n\n/**\n * Writes the jsaction parser cache to the given DOM Element.\n */\nexport function set(element: Element, actionMap: {[key: string]: string | undefined}) {\n element[Property.JSACTION] = actionMap;\n}\n\n/**\n * Looks up the parsed action map from the source jsaction attribute value.\n *\n * @param text Unparsed jsaction attribute value.\n * @return Parsed jsaction attribute value, if already present in the cache.\n */\nexport function getParsed(text: string): {[key: string]: string | undefined} | undefined {\n return parseCache[text];\n}\n\n/**\n * Inserts the parse result for the given source jsaction value into the cache.\n *\n * @param text Unparsed jsaction attribute value.\n * @param parsed Attribute value parsed into the action map.\n */\nexport function setParsed(text: string, parsed: {[key: string]: string | undefined}) {\n parseCache[text] = parsed;\n}\n\n/**\n * Clears the jsaction parser cache from the given DOM Element.\n *\n * @param element .\n */\nexport function clear(element: Element) {\n if (Property.JSACTION in element) {\n delete element[Property.JSACTION];\n }\n}\n","/**\n * @license\n * Copyright Google LLC All Rights Reserved.\n *\n * Use of this source code is governed by an MIT-style license that can be\n * found in the LICENSE file at https://angular.dev/license\n */\n\n/*\n * Names of events that are special to jsaction. These are not all\n * event types that are legal to use in either HTML or the addEvent()\n * API, but these are the ones that are treated specially. All other\n * DOM events can be used in either addEvent() or in the value of the\n * jsaction attribute. Beware of browser specific events or events\n * that don't bubble though: If they are not mentioned here, then\n * event contract doesn't work around their peculiarities.\n */\nexport const EventType = {\n /**\n * Mouse middle click, introduced in Chrome 55 and not yet supported on\n * other browsers.\n */\n AUXCLICK: 'auxclick',\n\n /**\n * The change event fired by browsers when the `value` attribute of input,\n * select, and textarea elements are changed.\n */\n CHANGE: 'change',\n\n /**\n * The click event. In addEvent() refers to all click events, in the\n * jsaction attribute it refers to the unmodified click and Enter/Space\n * keypress events. In the latter case, a jsaction click will be triggered,\n * for accessibility reasons. See clickmod and clickonly, below.\n */\n CLICK: 'click',\n\n /**\n * Specifies the jsaction for a modified click event (i.e. a mouse\n * click with the modifier key Cmd/Ctrl pressed). This event isn't\n * separately enabled in addEvent(), because in the DOM, it's just a\n * click event.\n */\n CLICKMOD: 'clickmod',\n\n /**\n * Specifies the jsaction for a click-only event. Click-only doesn't take\n * into account the case where an element with focus receives an Enter/Space\n * keypress. This event isn't separately enabled in addEvent().\n */\n CLICKONLY: 'clickonly',\n\n /**\n * The dblclick event.\n */\n DBLCLICK: 'dblclick',\n\n /**\n * Focus doesn't bubble, but you can use it in addEvent() and\n * jsaction anyway. EventContract does the right thing under the\n * hood.\n */\n FOCUS: 'focus',\n\n /**\n * This event only exists in IE. For addEvent() and jsaction, use\n * focus instead; EventContract does the right thing even though\n * focus doesn't bubble.\n */\n FOCUSIN: 'focusin',\n\n /**\n * Analog to focus.\n */\n BLUR: 'blur',\n\n /**\n * Analog to focusin.\n */\n FOCUSOUT: 'focusout',\n\n /**\n * Submit doesn't bubble, so it cannot be used with event\n * contract. However, the browser helpfully fires a click event on\n * the submit button of a form (even if the form is not submitted by\n * a click on the submit button). So you should handle click on the\n * submit button instead.\n */\n SUBMIT: 'submit',\n\n /**\n * The keydown event. In addEvent() and non-click jsaction it represents the\n * regular DOM keydown event. It represents click actions in non-Gecko\n * browsers.\n */\n KEYDOWN: 'keydown',\n\n /**\n * The keypress event. In addEvent() and non-click jsaction it represents the\n * regular DOM keypress event. It represents click actions in Gecko browsers.\n */\n KEYPRESS: 'keypress',\n\n /**\n * The keyup event. In addEvent() and non-click jsaction it represents the\n * regular DOM keyup event. It represents click actions in non-Gecko\n * browsers.\n */\n KEYUP: 'keyup',\n\n /**\n * The mouseup event. Can either be used directly or used implicitly to\n * capture mouseup events. In addEvent(), it represents a regular DOM\n * mouseup event.\n */\n MOUSEUP: 'mouseup',\n\n /**\n * The mousedown event. Can either be used directly or used implicitly to\n * capture mouseenter events. In addEvent(), it represents a regular DOM\n * mouseover event.\n */\n MOUSEDOWN: 'mousedown',\n\n /**\n * The mouseover event. Can either be used directly or used implicitly to\n * capture mouseenter events. In addEvent(), it represents a regular DOM\n * mouseover event.\n */\n MOUSEOVER: 'mouseover',\n\n /**\n * The mouseout event. Can either be used directly or used implicitly to\n * capture mouseover events. In addEvent(), it represents a regular DOM\n * mouseout event.\n */\n MOUSEOUT: 'mouseout',\n\n /**\n * The mouseenter event. Does not bubble and fires individually on each\n * element being entered within a DOM tree.\n */\n MOUSEENTER: 'mouseenter',\n\n /**\n * The mouseleave event. Does not bubble and fires individually on each\n * element being entered within a DOM tree.\n */\n MOUSELEAVE: 'mouseleave',\n\n /**\n * The mousemove event.\n */\n MOUSEMOVE: 'mousemove',\n\n /**\n * The pointerup event. Can either be used directly or used implicitly to\n * capture pointerup events. In addEvent(), it represents a regular DOM\n * pointerup event.\n */\n POINTERUP: 'pointerup',\n\n /**\n * The pointerdown event. Can either be used directly or used implicitly to\n * capture pointerenter events. In addEvent(), it represents a regular DOM\n * mouseover event.\n */\n POINTERDOWN: 'pointerdown',\n\n /**\n * The pointerover event. Can either be used directly or used implicitly to\n * capture pointerenter events. In addEvent(), it represents a regular DOM\n * pointerover event.\n */\n POINTEROVER: 'pointerover',\n\n /**\n * The pointerout event. Can either be used directly or used implicitly to\n * capture pointerover events. In addEvent(), it represents a regular DOM\n * pointerout event.\n */\n POINTEROUT: 'pointerout',\n\n /**\n * The pointerenter event. Does not bubble and fires individually on each\n * element being entered within a DOM tree.\n */\n POINTERENTER: 'pointerenter',\n\n /**\n * The pointerleave event. Does not bubble and fires individually on each\n * element being entered within a DOM tree.\n */\n POINTERLEAVE: 'pointerleave',\n\n /**\n * The pointermove event.\n */\n POINTERMOVE: 'pointermove',\n\n /**\n * The pointercancel event.\n */\n POINTERCANCEL: 'pointercancel',\n\n /**\n * The gotpointercapture event is fired when\n * Element.setPointerCapture(pointerId) is called on a mouse input, or\n * implicitly when a touch input begins.\n */\n GOTPOINTERCAPTURE: 'gotpointercapture',\n\n /**\n * The lostpointercapture event is fired when\n * Element.releasePointerCapture(pointerId) is called, or implicitly after a\n * touch input ends.\n */\n LOSTPOINTERCAPTURE: 'lostpointercapture',\n\n /**\n * The error event. The error event doesn't bubble, but you can use it in\n * addEvent() and jsaction anyway. EventContract does the right thing under\n * the hood (except in IE8 which does not use error events).\n */\n ERROR: 'error',\n\n /**\n * The load event. The load event doesn't bubble, but you can use it in\n * addEvent() and jsaction anyway. EventContract does the right thing\n * under the hood.\n */\n LOAD: 'load',\n\n /**\n * The unload event.\n */\n UNLOAD: 'unload',\n\n /**\n * The touchstart event. Bubbles, will only ever fire in browsers with\n * touch support.\n */\n TOUCHSTART: 'touchstart',\n\n /**\n * The touchend event. Bubbles, will only ever fire in browsers with\n * touch support.\n */\n TOUCHEND: 'touchend',\n\n /**\n * The touchmove event. Bubbles, will only ever fire in browsers with\n * touch support.\n */\n TOUCHMOVE: 'touchmove',\n\n /**\n * The input event.\n */\n INPUT: 'input',\n\n /**\n * The scroll event.\n */\n SCROLL: 'scroll',\n\n /**\n * The toggle event. The toggle event doesn't bubble, but you can use it in\n * addEvent() and jsaction anyway. EventContract does the right thing\n * under the hood.\n */\n TOGGLE: 'toggle',\n\n /**\n * A custom event. The actual custom event type is declared as the 'type'\n * field in the event details. Supported in Firefox 6+, IE 9+, and all Chrome\n * versions.\n *\n * This is an internal name. Users should use jsaction's fireCustomEvent to\n * fire custom events instead of relying on this type to create them.\n */\n CUSTOM: '_custom',\n};\n\n/** All event types that do not bubble or capture and need a polyfill. */\nexport const MOUSE_SPECIAL_EVENT_TYPES = [\n EventType.MOUSEENTER,\n EventType.MOUSELEAVE,\n 'pointerenter',\n 'pointerleave',\n];\n\n/** All event types that are registered in the bubble phase. */\nexport const BUBBLE_EVENT_TYPES = [\n EventType.CLICK,\n EventType.DBLCLICK,\n EventType.FOCUSIN,\n EventType.FOCUSOUT,\n EventType.KEYDOWN,\n EventType.KEYUP,\n EventType.KEYPRESS,\n EventType.MOUSEOVER,\n EventType.MOUSEOUT,\n EventType.SUBMIT,\n EventType.TOUCHSTART,\n EventType.TOUCHEND,\n EventType.TOUCHMOVE,\n 'touchcancel',\n\n 'auxclick',\n 'change',\n 'compositionstart',\n 'compositionupdate',\n 'compositionend',\n 'beforeinput',\n 'input',\n 'select',\n\n 'copy',\n 'cut',\n 'paste',\n 'mousedown',\n 'mouseup',\n 'wheel',\n 'contextmenu',\n\n 'dragover',\n 'dragenter',\n 'dragleave',\n 'drop',\n 'dragstart',\n 'dragend',\n\n 'pointerdown',\n 'pointermove',\n 'pointerup',\n 'pointercancel',\n 'pointerover',\n 'pointerout',\n 'gotpointercapture',\n 'lostpointercapture',\n\n // Video events.\n 'ended',\n 'loadedmetadata',\n\n // Page visibility events.\n 'pagehide',\n 'pageshow',\n 'visibilitychange',\n\n // Content visibility events.\n 'beforematch',\n];\n\n/** All event types that are registered in the capture phase. */\nexport const CAPTURE_EVENT_TYPES = [\n EventType.FOCUS,\n EventType.BLUR,\n EventType.ERROR,\n EventType.LOAD,\n EventType.TOGGLE,\n];\n\n/**\n * Whether or not an event type should be registered in the capture phase.\n * @param eventType\n * @returns bool\n */\nexport const isCaptureEventType = (eventType: string) =>\n CAPTURE_EVENT_TYPES.indexOf(eventType) >= 0;\n\n/** All event types that are registered early. */\nconst EARLY_EVENT_TYPES = BUBBLE_EVENT_TYPES.concat(CAPTURE_EVENT_TYPES);\n\n/**\n * Whether or not an event type is registered in the early contract.\n */\nexport const isEarlyEventType = (eventType: string) => EARLY_EVENT_TYPES.indexOf(eventType) >= 0;\n","/**\n * @license\n * Copyright Google LLC All Rights Reserved.\n *\n * Use of this source code is governed by an MIT-style license that can be\n * found in the LICENSE file at https://angular.dev/license\n */\n\nimport {EventHandlerInfo} from './event_handler';\nimport {isCaptureEventType, EventType} from './event_type';\nimport {KeyCode} from './key_code';\n\n/**\n * Gets a browser event type, if it would differ from the JSAction event type.\n */\nexport function getBrowserEventType(eventType: string) {\n // Mouseenter and mouseleave events are not handled directly because they\n // are not available everywhere. In browsers where they are available, they\n // don't bubble and aren't visible at the container boundary. Instead, we\n // synthesize the mouseenter and mouseleave events from mouseover and\n // mouseout events, respectively. Cf. eventcontract.js.\n if (eventType === EventType.MOUSEENTER) {\n return EventType.MOUSEOVER;\n } else if (eventType === EventType.MOUSELEAVE) {\n return EventType.MOUSEOUT;\n } else if (eventType === EventType.POINTERENTER) {\n return EventType.POINTEROVER;\n } else if (eventType === EventType.POINTERLEAVE) {\n return EventType.POINTEROUT;\n }\n return eventType;\n}\n\n/**\n * Registers the event handler function with the given DOM element for\n * the given event type.\n *\n * @param element The element.\n * @param eventType The event type.\n * @param handler The handler function to install.\n * @param passive A boolean value that, if `true`, indicates that the function\n * specified by `handler` will never call `preventDefault()`.\n * @return Information needed to uninstall the event handler eventually.\n */\nexport function addEventListener(\n element: Element,\n eventType: string,\n handler: (event: Event) => void,\n passive?: boolean,\n): EventHandlerInfo {\n // All event handlers are registered in the bubbling\n // phase.\n //\n // All browsers support focus and blur, but these events only are propagated\n // in the capture phase. Very legacy browsers do not support focusin or\n // focusout.\n //\n // It would be a bad idea to register all event handlers in the\n // capture phase because then regular onclick handlers would not be\n // executed at all on events that trigger a jsaction. That's not\n // entirely what we want, at least for now.\n //\n // Error and load events (i.e. on images) do not bubble so they are also\n // handled in the capture phase.\n let capture = false;\n\n if (isCaptureEventType(eventType)) {\n capture = true;\n }\n\n const options = typeof passive === 'boolean' ? {capture, passive} : capture;\n element.addEventListener(eventType, handler, options);\n\n return {eventType, handler, capture, passive};\n}\n\n/**\n * Removes the event handler for the given event from the element.\n * the given event type.\n *\n * @param element The element.\n * @param info The information needed to deregister the handler, as returned by\n * addEventListener(), above.\n */\nexport function removeEventListener(element: Element, info: EventHandlerInfo) {\n if (element.removeEventListener) {\n // It's worth noting that some browser releases have been inconsistent on this, and unless\n // you have specific reasons otherwise, it's probably wise to use the same values used for\n // the call to addEventListener() when calling removeEventListener().\n const options = typeof info.passive === 'boolean' ? {capture: info.capture} : info.capture;\n element.removeEventListener(info.eventType, info.handler as EventListener, options);\n // `detachEvent` is an old DOM API.\n } else if ((element as any).detachEvent) {\n // `detachEvent` is an old DOM API.\n (element as any).detachEvent(`on${info.eventType}`, info.handler);\n }\n}\n\n/**\n * Cancels propagation of an event.\n * @param e The event to cancel propagation for.\n */\nexport function stopPropagation(e: Event) {\n e.stopPropagation ? e.stopPropagation() : (e.cancelBubble = true);\n}\n\n/**\n * Prevents the default action of an event.\n * @param e The event to prevent the default action for.\n */\nexport function preventDefault(e: Event) {\n e.preventDefault ? e.preventDefault() : (e.returnValue = false);\n}\n\n/**\n * Gets the target Element of the event. In Firefox, a text node may appear as\n * the target of the event, in which case we return the parent element of the\n * text node.\n * @param e The event to get the target of.\n * @return The target element.\n */\nexport function getTarget(e: Event): Element {\n let el = e.target as Element;\n\n // In Firefox, the event may have a text node as its target. We always\n // want the parent Element the text node belongs to, however.\n if (!el.getAttribute && el.parentNode) {\n el = el.parentNode as Element;\n }\n\n return el;\n}\n\n/**\n * Whether we are on a Mac. Not pulling in useragent just for this.\n */\nlet isMac: boolean = typeof navigator !== 'undefined' && /Macintosh/.test(navigator.userAgent);\n\n/**\n * Determines and returns whether the given event (which is assumed to be a\n * click event) is a middle click.\n * NOTE: There is not a consistent way to identify middle click\n * http://www.unixpapa.com/js/mouse.html\n */\nfunction isMiddleClick(e: Event): boolean {\n return (\n // `which` is an old DOM API.\n (e as any).which === 2 ||\n // `which` is an old DOM API.\n ((e as any).which == null &&\n // `button` is an old DOM API.\n (e as any).button === 4) // middle click for IE\n );\n}\n\n/**\n * Determines and returns whether the given event (which is assumed\n * to be a click event) is modified. A middle click is considered a modified\n * click to retain the default browser action, which opens a link in a new tab.\n * @param e The event.\n * @return Whether the given event is modified.\n */\nexport function isModifiedClickEvent(e: Event): boolean {\n return (\n // `metaKey` is an old DOM API.\n (isMac && (e as any).metaKey) ||\n // `ctrlKey` is an old DOM API.\n (!isMac && (e as any).ctrlKey) ||\n isMiddleClick(e) ||\n // `shiftKey` is an old DOM API.\n (e as any).shiftKey\n );\n}\n\n/** Whether we are on WebKit (e.g., Chrome). */\nexport const isWebKit: boolean =\n typeof navigator !== 'undefined' &&\n !/Opera/.test(navigator.userAgent) &&\n /WebKit/.test(navigator.userAgent);\n\n/** Whether we are on IE. */\nexport const isIe: boolean =\n typeof navigator !== 'undefined' &&\n (/MSIE/.test(navigator.userAgent) || /Trident/.test(navigator.userAgent));\n\n/** Whether we are on Gecko (e.g., Firefox). */\nexport const isGecko: boolean =\n typeof navigator !== 'undefined' &&\n !/Opera|WebKit/.test(navigator.userAgent) &&\n /Gecko/.test(navigator.product);\n\n/**\n * Determines and returns whether the given element is a valid target for\n * keypress/keydown DOM events that act like regular DOM clicks.\n * @param el The element.\n * @return Whether the given element is a valid action key target.\n */\nexport function isValidActionKeyTarget(el: Element): boolean {\n if (!('getAttribute' in el)) {\n return false;\n }\n if (isTextControl(el)) {\n return false;\n }\n if (isNativelyActivatable(el)) {\n return false;\n }\n // `isContentEditable` is an old DOM API.\n if ((el as any).isContentEditable) {\n return false;\n }\n\n return true;\n}\n\n/**\n * Whether an event has a modifier key activated.\n * @param e The event.\n * @return True, if a modifier key is activated.\n */\nfunction hasModifierKey(e: Event): boolean {\n return (\n // `ctrlKey` is an old DOM API.\n (e as any).ctrlKey ||\n // `shiftKey` is an old DOM API.\n (e as any).shiftKey ||\n // `altKey` is an old DOM API.\n (e as any).altKey ||\n // `metaKey` is an old DOM API.\n (e as any).metaKey\n );\n}\n\n/**\n * Determines and returns whether the given event has a target that already\n * has event handlers attached because it is a native HTML control. Used to\n * determine if preventDefault should be called when isActionKeyEvent is true.\n * @param e The event.\n * @return If preventDefault should be called.\n */\nexport function shouldCallPreventDefaultOnNativeHtmlControl(e: Event): boolean {\n const el = getTarget(e);\n const tagName = el.tagName.toUpperCase();\n const role = (el.getAttribute('role') || '').toUpperCase();\n\n if (tagName === 'BUTTON' || role === 'BUTTON') {\n return true;\n }\n if (!isNativeHTMLControl(el)) {\n return false;\n }\n if (tagName === 'A') {\n return false;\n }\n /**\n * Fix for physical d-pads on feature phone platforms; the native event\n * (ie. isTrusted: true) needs to fire to show the OPTION list. See\n * b/135288469 for more info.\n */\n if (tagName === 'SELECT') {\n return false;\n }\n if (processSpace(el)) {\n return false;\n }\n if (isTextControl(el)) {\n return false;\n }\n return true;\n}\n\n/**\n * Determines and returns whether the given event acts like a regular DOM click,\n * and should be handled instead of the click. If this returns true, the caller\n * will call preventDefault() to prevent a possible duplicate event.\n * This is represented by a keypress (keydown on Gecko browsers) on Enter or\n * Space key.\n * @param e The event.\n * @return True, if the event emulates a DOM click.\n */\nexport function isActionKeyEvent(e: Event): boolean {\n let key =\n // `which` is an old DOM API.\n (e as any).which ||\n // `keyCode` is an old DOM API.\n (e as any).keyCode;\n if (!key && (e as KeyboardEvent).key) {\n key = ACTION_KEY_TO_KEYCODE[(e as KeyboardEvent).key];\n }\n if (isWebKit && key === KeyCode.MAC_ENTER) {\n key = KeyCode.ENTER;\n }\n if (key !== KeyCode.ENTER && key !== KeyCode.SPACE) {\n return false;\n }\n const el = getTarget(e);\n if (e.type !== EventType.KEYDOWN || !isValidActionKeyTarget(el) || hasModifierKey(e)) {\n return false;\n }\n\n // For <input type=\"checkbox\">, we must only handle the browser's native click\n // event, so that the browser can toggle the checkbox.\n if (processSpace(el) && key === KeyCode.SPACE) {\n return false;\n }\n\n // If this element is non-focusable, ignore stray keystrokes (b/18337209)\n // Sscreen readers can move without tab focus, so any tabIndex is focusable.\n // See B/21809604\n if (!isFocusable(el)) {\n return false;\n }\n\n const type = (\n el.getAttribute('role') ||\n (el as HTMLInputElement).type ||\n el.tagName\n ).toUpperCase();\n const isSpecificTriggerKey = IDENTIFIER_TO_KEY_TRIGGER_MAPPING[type] % key === 0;\n const isDefaultTriggerKey = !(type in IDENTIFIER_TO_KEY_TRIGGER_MAPPING) && key === KeyCode.ENTER;\n const hasType = el.tagName.toUpperCase() !== 'INPUT' || !!(el as HTMLInputElement).type;\n return (isSpecificTriggerKey || isDefaultTriggerKey) && hasType;\n}\n\n/**\n * Checks whether a DOM element can receive keyboard focus.\n * This code is based on goog.dom.isFocusable, but simplified since we shouldn't\n * care about visibility if we're already handling a keyboard event.\n */\nfunction isFocusable(el: Element): boolean {\n return (\n (el.tagName in NATIVELY_FOCUSABLE_ELEMENTS || hasSpecifiedTabIndex(el)) &&\n !(el as HTMLInputElement).disabled\n );\n}\n\n/**\n * @param element Element to check.\n * @return Whether the element has a specified tab index.\n */\nfunction hasSpecifiedTabIndex(element: Element): boolean {\n // IE returns 0 for an unset tabIndex, so we must use getAttributeNode(),\n // which returns an object with a 'specified' property if tabIndex is\n // specified. This works on other browsers, too.\n const attrNode = element.getAttributeNode('tabindex'); // Must be lowercase!\n return attrNode != null && attrNode.specified;\n}\n\n/** Element tagnames that are focusable by default. */\nconst NATIVELY_FOCUSABLE_ELEMENTS: {[key: string]: number} = {\n 'A': 1,\n 'INPUT': 1,\n 'TEXTAREA': 1,\n 'SELECT': 1,\n 'BUTTON': 1,\n};\n\n/** @return True, if the Space key was pressed. */\nexport function isSpaceKeyEvent(e: Event): boolean {\n const key =\n // `which` is an old DOM API.\n (e as any).which ||\n // `keyCode` is an old DOM API.\n (e as any).keyCode;\n const el = getTarget(e);\n const elementName = ((el as HTMLInputElement).type || el.tagName).toUpperCase();\n return key === KeyCode.SPACE && elementName !== 'CHECKBOX';\n}\n\n/**\n * Determines whether the event corresponds to a non-bubbling mouse\n * event type (mouseenter, mouseleave, pointerenter, and pointerleave).\n *\n * During mouseover (mouseenter) and pointerover (pointerenter), the\n * relatedTarget is the element being entered from. During mouseout (mouseleave)\n * and pointerout (pointerleave), the relatedTarget is the element being exited\n * to.\n *\n * In both cases, if relatedTarget is outside target, then the corresponding\n * special event has occurred, otherwise it hasn't.\n *\n * @param e The mouseover/mouseout event.\n * @param type The type of the mouse special event.\n * @param element The element on which the jsaction for the\n * mouseenter/mouseleave event is defined.\n * @return True if the event is a mouseenter/mouseleave event.\n */\nexport function isMouseSpecialEvent(e: Event, type: string, element: Element): boolean {\n // `relatedTarget` is an old DOM API.\n const related = (e as any).relatedTarget as Node;\n\n return (\n ((e.type === EventType.MOUSEOVER && type === EventType.MOUSEENTER) ||\n (e.type === EventType.MOUSEOUT && type === EventType.MOUSELEAVE) ||\n (e.type === EventType.POINTEROVER && type === EventType.POINTERENTER) ||\n (e.type === EventType.POINTEROUT && type === EventType.POINTERLEAVE)) &&\n (!related || (related !== element && !element.contains(related)))\n );\n}\n\n/**\n * Creates a new EventLike object for a mouseenter/mouseleave event that's\n * derived from the original corresponding mouseover/mouseout event.\n * @param e The event.\n * @param target The element on which the jsaction for the mouseenter/mouseleave\n * event is defined.\n * @return A modified event-like object copied from the event object passed into\n * this function.\n */\nexport function createMouseSpecialEvent(e: Event, target: Element): Event {\n // We have to create a copy of the event object because we need to mutate\n // its fields. We do this for the special mouse events because the event\n // target needs to be retargeted to the action element rather than the real\n // element (since we are simulating the special mouse events with mouseover/\n // mouseout).\n //\n // Since we're making a copy anyways, we might as well attempt to convert\n // this event into a pseudo-real mouseenter/mouseleave event by adjusting\n // its type.\n //\n const copy: {-readonly [P in keyof Event]?: Event[P]} & {'_originalEvent'?: Event} = {};\n for (const property in e) {\n if (property === 'srcElement' || property === 'target') {\n continue;\n }\n const key = property as keyof Event;\n // Making a copy requires iterating through all properties of `Event`.\n const value = e[key];\n if (typeof value === 'function') {\n continue;\n }\n // Value should be the expected type, but the value of `key` is not known\n // statically.\n copy[key] = value as any;\n }\n if (e.type === EventType.MOUSEOVER) {\n copy['type'] = EventType.MOUSEENTER;\n } else if (e.type === EventType.MOUSEOUT) {\n copy['type'] = EventType.MOUSELEAVE;\n } else if (e.type === EventType.POINTEROVER) {\n copy['type'] = EventType.POINTERENTER;\n } else {\n copy['type'] = EventType.POINTERLEAVE;\n }\n copy['target'] = copy['srcElement'] = target;\n copy['bubbles'] = false;\n copy['_originalEvent'] = e;\n return copy as Event;\n}\n\n/**\n * Returns touch data extracted from the touch event: clientX, clientY, screenX\n * and screenY. If the event has no touch information at all, the returned\n * value is null.\n *\n * The fields of this Object are unquoted.\n *\n * @param event A touch event.\n */\nexport function getTouchData(\n event: TouchEvent,\n): {clientX: number; clientY: number; screenX: number; screenY: number} | null {\n const touch =\n (event.changedTouches && event.changedTouches[0]) || (event.touches && event.touches[0]);\n if (!touch) {\n return null;\n }\n return {\n clientX: touch.clientX,\n clientY: touch.clientY,\n screenX: touch.screenX,\n screenY: touch.screenY,\n };\n}\n\ndeclare interface SyntheticMouseEvent extends Event {\n // Redeclared from Event to indicate that it is not readonly.\n defaultPrevented: boolean;\n originalEventType: string;\n _propagationStopped?: boolean;\n}\n\n/**\n * Creates a new EventLike object for a \"click\" event that's derived from the\n * original corresponding \"touchend\" event for a fast-click implementation.\n *\n * It takes a touch event, adds common fields found in a click event and\n * changes the type to 'click', so that the resulting event looks more like\n * a real click event.\n *\n * @param event A touch event.\n * @return A modified event-like object copied from the event object passed into\n * this function.\n */\nexport function recreateTouchEventAsClick(event: TouchEvent): MouseEvent {\n const click: {-readonly [P in keyof MouseEvent]?: MouseEvent[P]} & Partial<SyntheticMouseEvent> =\n {};\n click['originalEventType'] = event.type;\n click['type'] = EventType.CLICK;\n for (const property in event) {\n if (property === 'type' || property === 'srcElement') {\n continue;\n }\n const key = property as keyof TouchEvent;\n // Making a copy requires iterating through all properties of `TouchEvent`.\n const value = event[key];\n if (typeof value === 'function') {\n continue;\n }\n // Value should be the expected type, but the value of `key` is not known\n // statically.\n click[key as keyof MouseEvent] = value as any;\n }\n\n // Ensure that the event has the most recent timestamp. This timestamp\n // may be used in the future to validate or cancel subsequent click events.\n click['timeStamp'] = Date.now();\n\n // Emulate preventDefault and stopPropagation behavior\n click['defaultPrevented'] = false;\n click['preventDefault'] = syntheticPreventDefault;\n click['_propagationStopped'] = false;\n click['stopPropagation'] = syntheticStopPropagation;\n\n // Emulate click coordinates using touch info\n const touch = getTouchData(event);\n if (touch) {\n click['clientX'] = touch.clientX;\n click['clientY'] = touch.clientY;\n click['screenX'] = touch.screenX;\n click['screenY'] = touch.screenY;\n }\n return click as MouseEvent;\n}\n\n/**\n * An implementation of \"preventDefault\" for a synthesized event. Simply\n * sets \"defaultPrevented\" property to true.\n */\nfunction syntheticPreventDefault(this: Event) {\n (this as SyntheticMouseEvent).defaultPrevented = true;\n}\n\n/**\n * An implementation of \"stopPropagation\" for a synthesized event. It simply\n * sets a synthetic non-standard \"_propagationStopped\" property to true.\n */\nfunction syntheticStopPropagation(this: Event) {\n (this as SyntheticMouseEvent)._propagationStopped = true;\n}\n\n/**\n * Mapping of KeyboardEvent.key values to\n * KeyCode values.\n */\nconst ACTION_KEY_TO_KEYCODE: {[key: string]: number} = {\n 'Enter': KeyCode.ENTER,\n ' ': KeyCode.SPACE,\n};\n\n/**\n * Mapping of HTML element identifiers (ARIA role, type, or tagName) to the\n * keys (enter and/or space) that should activate them. A value of zero means\n * that both should activate them.\n */\nexport const IDENTIFIER_TO_KEY_TRIGGER_MAPPING: {[key: string]: number} = {\n 'A': KeyCode.ENTER,\n 'BUTTON': 0,\n 'CHECKBOX': KeyCode.SPACE,\n 'COMBOBOX': KeyCode.ENTER,\n 'FILE': 0,\n 'GRIDCELL': KeyCode.ENTER,\n 'LINK': KeyCode.ENTER,\n 'LISTBOX': KeyCode.ENTER,\n 'MENU': 0,\n 'MENUBAR': 0,\n 'MENUITEM': 0,\n 'MENUITEMCHECKBOX': 0,\n 'MENUITEMRADIO': 0,\n 'OPTION': 0,\n 'RADIO': KeyCode.SPACE,\n 'RADIOGROUP': KeyCode.SPACE,\n 'RESET': 0,\n 'SUBMIT': 0,\n 'SWITCH': KeyCode.SPACE,\n 'TAB': 0,\n 'TREE': KeyCode.ENTER,\n 'TREEITEM': KeyCode.ENTER,\n};\n\n/**\n * Returns whether or not to process space based on the type of the element;\n * checks to make sure that type is not null.\n * @param element The element.\n * @return Whether or not to process space based on type.\n */\nfunction processSpace(element: Element): boolean {\n const type = (element.getAttribute('type') || element.tagName).toUpperCase();\n return type in PROCESS_SPACE;\n}\n\n/**\n * Returns whether or not the given element is a text control.\n * @param el The element.\n * @return Whether or not the given element is a text control.\n */\nfunction isTextControl(el: Element): boolean {\n const type = (el.getAttribute('type') || el.tagName).toUpperCase();\n return type in TEXT_CONTROLS;\n}\n\n/**\n * Returns if the given element is a native HTML control.\n * @param el The element.\n * @return If the given element is a native HTML control.\n */\nexport function isNativeHTMLControl(el: Element): boolean {\n return el.tagName.toUpperCase() in NATIVE_HTML_CONTROLS;\n}\n\n/**\n * Returns if the given element is natively activatable. Browsers emit click\n * events for natively activatable elements, even when activated via keyboard.\n * For these elements, we don't need to raise a11y click events.\n * @param el The element.\n * @return If the given element is a native HTML control.\n */\nfunction isNativelyActivatable(el: Element): boolean {\n return (\n el.tagName.toUpperCase() === 'BUTTON' ||\n (!!(el as HTMLInputElement).type && (el as HTMLInputElement).type.toUpperCase() === 'FILE')\n );\n}\n\n/**\n * HTML <input> types (not ARIA roles) which will auto-trigger a click event for\n * the Space key, with side-effects. We will not call preventDefault if space is\n * pressed, nor will we raise a11y click events. For all other elements, we can\n * suppress the default event (which has no desired side-effects) and handle the\n * keydown ourselves.\n */\nconst PROCESS_SPACE: {[key: string]: boolean} = {\n 'CHECKBOX': true,\n 'FILE': true,\n 'OPTION': true,\n 'RADIO': true,\n};\n\n/** TagNames and Input types for which to not process enter/space as click. */\nconst TEXT_CONTROLS: {[key: string]: boolean} = {\n 'COLOR': true,\n 'DATE': true,\n 'DATETIME': true,\n 'DATETIME-LOCAL': true,\n 'EMAIL': true,\n 'MONTH': true,\n 'NUMBER': true,\n 'PASSWORD': true,\n 'RANGE': true,\n 'SEARCH': true,\n 'TEL': true,\n 'TEXT': true,\n 'TEXTAREA': true,\n 'TIME': true,\n 'URL': true,\n 'WEEK': true,\n};\n\n/** TagNames that are native HTML controls. */\nconst NATIVE_HTML_CONTROLS: {[key: string]: boolean} = {\n 'A': true,\n 'AREA': true,\n 'BUTTON': true,\n 'DIALOG': true,\n 'IMG': true,\n 'INPUT': true,\n 'LINK': true,\n 'MENU': true,\n 'OPTGROUP': true,\n 'OPTION': true,\n 'PROGRESS': true,\n 'SELECT': true,\n 'TEXTAREA': true,\n};\n\n/** Exported for testing. */\nexport const testing = {\n setIsMac(value: boolean) {\n isMac = value;\n },\n};\n","/**\n * @license\n * Copyright Google LLC All Rights Reserved.\n *\n * Use of this source code is governed by an MIT-style license that can be\n * found in the LICENSE file at https://angular.dev/license\n */\n\nimport * as eventLib from './event';\nimport {EventHandlerInfo} from './event_handler';\n\n/**\n * An `EventContractContainerManager` provides the common interface for managing\n * containers.\n */\nexport interface EventContractContainerManager {\n addEventListener(\n eventType: string,\n getHandler: (element: Element) => (event: Event) => void,\n passive?: boolean,\n ): void;\n\n cleanUp(): void;\n}\n\n/**\n * Whether the user agent is running on iOS.\n */\nconst isIos = typeof navigator !== 'undefined' && /iPhone|iPad|iPod/.test(navigator.userAgent);\n\n/**\n * A class representing a container node and all the event handlers\n * installed on it. Used so that handlers can be cleaned up if the\n * container is removed from the contract.\n */\nexport class EventContractContainer implements EventContractContainerManager {\n /**\n * Array of event handlers and their corresponding event types that are\n * installed on this container.\n *\n */\n private handlerInfos: EventHandlerInfo[] = [];\n\n /**\n * @param element The container Element.\n */\n constructor(readonly element: Element) {}\n\n /**\n * Installs the provided installer on the element owned by this container,\n * and maintains a reference to resulting handler in order to remove it\n * later if desired.\n */\n addEventListener(\n eventType: string,\n getHandler: (element: Element) => (event: Event) => void,\n passive?: boolean,\n ) {\n // In iOS, event bubbling doesn't happen automatically in any DOM element,\n // unless it has an onclick attribute or DOM event handler attached to it.\n // This breaks JsAction in some cases. See \"Making Elements Clickable\"\n // section at http://goo.gl/2VoGnB.\n //\n // A workaround for this issue is to change the CSS cursor style to 'pointer'\n // for the container element, which magically turns on event bubbling. This\n // solution is described in the comments section at http://goo.gl/6pEO1z.\n //\n // We use a navigator.userAgent check here as this problem is present both\n // on Mobile Safari and thin WebKit wrappers, such as Chrome for iOS.\n if (isIos) {\n (this.element as HTMLElement).style.cursor = 'pointer';\n }\n this.handlerInfos.push(\n eventLib.addEventListener(this.element, eventType, getHandler(this.element), passive),\n );\n }\n\n /**\n * Removes all the handlers installed on this container.\n */\n cleanUp() {\n for (let i = 0; i < this.handlerInfos.length; i++) {\n eventLib.removeEventListener(this.element, this.handlerInfos[i]);\n }\n\n this.handlerInfos = [];\n }\n}\n","/**\n * @license\n * Copyright Google LLC All Rights Reserved.\n *\n * Use of this source code is governed by an MIT-style license that can be\n * found in the LICENSE file at https://angular.dev/license\n */\n\nexport const Char = {\n /**\n * The separator between the namespace and the action name in the\n * jsaction attribute value.\n */\n NAMESPACE_ACTION_SEPARATOR: '.' as const,\n\n /**\n * The separator between the event name and action in the jsaction\n * attribute value.\n */\n EVENT_ACTION_SEPARATOR: ':' as const,\n};\n","/**\n * @license\n * Copyright Google LLC All Rights Reserved.\n *\n * Use of this source code is governed by an MIT-style license that can be\n * found in the LICENSE file at https://angular.dev/license\n */\n\n/**\n * Records information about the action that should handle a given `Event`.\n */\nexport interface ActionInfo {\n name: string;\n element: Element;\n}\n\ntype ActionInfoInternal = [name: string, element: Element];\n\n/**\n * Records information for later handling of events. This type is\n * shared, and instances of it are passed, between the eventcontract\n * and the dispatcher jsbinary. Therefore, the fields of this type are\n * referenced by string literals rather than property literals\n * throughout the code.\n *\n * 'targetElement' is the element the action occurred on, 'actionElement'\n * is the element that has the jsaction handler.\n *\n * A null 'actionElement' identifies an EventInfo instance that didn't match a\n * jsaction attribute. This allows us to execute global event handlers with the\n * appropriate event type (including a11y clicks and custom events).\n * The declare portion of this interface creates a set of externs that make sure\n * renaming doesn't happen for EventInfo. This is important since EventInfo\n * is shared across multiple binaries.\n */\nexport declare interface EventInfo {\n eventType: string;\n event: Event;\n targetElement: Element;\n /** The element that is the container for this Event. */\n eic: Element;\n timeStamp: number;\n /**\n * The action parsed from the JSAction element.\n */\n eia?: ActionInfoInternal;\n /**\n * Whether this `Event` is a replay event, meaning no dispatcher was\n * installed when this `Event` was originally dispatched.\n */\n eirp?: boolean;\n /**\n * Whether this `Event` represents a `keydown` event that should be processed\n * as a `click`. Only used when a11y click events is on.\n */\n eiack?: boolean;\n /** Whether action resolution has already run on this `EventInfo`. */\n eir?: boolean;\n}\n\n/** Added for readability when accessing stable property names. */\nexport function getEventType(eventInfo: EventInfo) {\n return eventInfo.eventType;\n}\n\n/** Added for readability when accessing stable property names. */\nexport function setEventType(eventInfo: EventInfo, eventType: string) {\n eventInfo.eventType = eventType;\n}\n\n/** Added for readability when accessing stable property names. */\nexport function getEvent(eventInfo: EventInfo) {\n return eventInfo.event;\n}\n\n/** Added for readability when accessing stable property names. */\nexport function setEvent(eventInfo: EventInfo, event: Event) {\n eventInfo.event = event;\n}\n\n/** Added for readability when accessing stable property names. */\nexport function getTargetElement(eventInfo: EventInfo) {\n return eventInfo.targetElement;\n}\n\n/** Added for readability when accessing stable property names. */\nexport function setTargetElement(eventInfo: EventInfo, targetElement: Element) {\n eventInfo.targetElement = targetElement;\n}\n\n/** Added for readability when accessing stable property names. */\nexport function getContainer(eventInfo: EventInfo) {\n return eventInfo.eic;\n}\n\n/** Added for readability when accessing stable property names. */\nexport function setContainer(eventInfo: EventInfo, container: Element) {\n eventInfo.eic = container;\n}\n\n/** Added for readability when accessing stable property names. */\nexport function getTimestamp(eventInfo: EventInfo) {\n return eventInfo.timeStamp;\n}\n\n/** Added for readability when accessing stable property names. */\nexport function setTimestamp(eventInfo: EventInfo, timestamp: number) {\n eventInfo.timeStamp = timestamp;\n}\n\n/** Added for readability when accessing stable property names. */\nexport function getAction(eventInfo: EventInfo) {\n return eventInfo.eia;\n}\n\n/** Added for readability when accessing stable property names. */\nexport function setAction(eventInfo: EventInfo, actionName: string, actionElement: Element) {\n eventInfo.eia = [actionName, actionElement];\n}\n\n/** Added for readability when accessing stable property names. */\nexport function unsetAction(eventInfo: EventInfo) {\n eventInfo.eia = undefined;\n}\n\n/** Added for readability when accessing stable property names. */\nexport function getActionName(actionInfo: ActionInfoInternal) {\n return actionInfo[0];\n}\n\n/** Added for readability when accessing stable property names. */\nexport function getActionElement(actionInfo: ActionInfoInternal) {\n return actionInfo[1];\n}\n\n/** Added for readability when accessing stable property names. */\nexport function getIsReplay(eventInfo: EventInfo) {\n return eventInfo.eirp;\n}\n\n/** Added for readability when accessing stable property names. */\nexport function setIsReplay(eventInfo: EventInfo, replay: boolean) {\n eventInfo.eirp = replay;\n}\n\n/** Added for readability when accessing stable property names. */\nexport function getA11yClickKey(eventInfo: EventInfo) {\n return eventInfo.eiack;\n}\n\n/** Added for readability when accessing stable property names. */\nexport function setA11yClickKey(eventInfo: EventInfo, a11yClickKey: boolean) {\n eventInfo.eiack = a11yClickKey;\n}\n\n/** Added for readability when accessing stable property names. */\nexport function getResolved(eventInfo: EventInfo) {\n return eventInfo.eir;\n}\n\n/** Added for readability when accessing stable property names. */\nexport function setResolved(eventInfo: EventInfo, resolved: boolean) {\n eventInfo.eir = resolved;\n}\n\n/** Clones an `EventInfo` */\nexport function cloneEventInfo(eventInfo: EventInfo): EventInfo {\n return {\n eventType: eventInfo.eventType,\n event: eventInfo.event,\n targetElement: eventInfo.targetElement,\n eic: eventInfo.eic,\n eia: eventInfo.eia,\n timeStamp: eventInfo.timeStamp,\n eirp: eventInfo.eirp,\n eiack: eventInfo.eiack,\n eir: eventInfo.eir,\n };\n}\n\n/**\n * Utility function for creating an `EventInfo`.\n *\n * This can be used from code-size sensitive compilation units, as taking\n * parameters vs. an `Object` literal reduces code size.\n */\nexport function createEventInfoFromParameters(\n eventType: string,\n event: Event,\n targetElement: Element,\n container: Element,\n timestamp: number,\n action?: ActionInfoInternal,\n isReplay?: boolean,\n a11yClickKey?: boolean,\n): EventInfo {\n return {\n eventType,\n event,\n targetElement,\n eic: container,\n timeStamp: timestamp,\n eia: action,\n eirp: isReplay,\n eiack: a11yClickKey,\n };\n}\n\n/**\n * Utility function for creating an `EventInfo`.\n *\n * This should be used in compilation units that are less sensitive to code\n * size.\n */\nexport function createEventInfo({\n eventType,\n event,\n targetElement,\n container,\n timestamp,\n action,\n isReplay,\n a11yClickKey,\n}: {\n eventType: string;\n event: Event;\n targetElement: Element;\n container: Element;\n timestamp: number;\n action?: ActionInfo;\n isReplay?: boolean;\n a11yClickKey?: boolean;\n}): EventInfo {\n return {\n eventType,\n event,\n targetElement,\n eic: container,\n timeStamp: timestamp,\n eia: action ? [action.name, action.element] : undefined,\n eirp: isReplay,\n eiack: a11yClickKey,\n };\n}\n\n/**\n * Utility class around an `EventInfo`.\n *\n * This should be used in compilation units that are less sensitive to code\n * size.\n */\nexport class EventInfoWrapper {\n constructor(readonly eventInfo: EventInfo) {}\n\n getEventType() {\n return getEventType(this.