UNPKG

marko

Version:

UI Components + streaming, async, high performance, HTML templating for Node.js and the browser.

145 lines (120 loc) 4.21 kB
var componentsUtil = require("@internal/components-util"); var runtimeId = componentsUtil.___runtimeId; var componentLookup = componentsUtil.___componentLookup; var getMarkoPropsFromEl = componentsUtil.___getMarkoPropsFromEl; var TEXT_NODE = 3; // We make our best effort to allow multiple marko runtimes to be loaded in the // same window. Each marko runtime will get its own unique runtime ID. var listenersAttachedKey = "$MDE" + runtimeId; var delegatedEvents = {}; function getEventFromEl(el, eventName) { var virtualProps = getMarkoPropsFromEl(el); var eventInfo = virtualProps[eventName]; if (typeof eventInfo === "string") { eventInfo = eventInfo.split(" "); if (eventInfo[2]) { eventInfo[2] = eventInfo[2] === "true"; } if (eventInfo.length == 4) { eventInfo[3] = parseInt(eventInfo[3], 10); } } return eventInfo; } function delegateEvent(node, eventName, target, event) { var targetMethod = target[0]; var targetComponentId = target[1]; var isOnce = target[2]; var extraArgs = target[3]; if (isOnce) { var virtualProps = getMarkoPropsFromEl(node); delete virtualProps[eventName]; } var targetComponent = componentLookup[targetComponentId]; if (!targetComponent) { return; } var targetFunc = typeof targetMethod === "function" ? targetMethod : targetComponent[targetMethod]; if (!targetFunc) { throw Error("Method not found: " + targetMethod); } if (extraArgs != null) { if (typeof extraArgs === "number") { extraArgs = targetComponent.___bubblingDomEvents[extraArgs]; } } // Invoke the component method if (extraArgs) { targetFunc.apply(targetComponent, extraArgs.concat(event, node)); } else { targetFunc.call(targetComponent, event, node); } } function addDelegatedEventHandler(eventType) { if (!delegatedEvents[eventType]) { delegatedEvents[eventType] = true; } } function addDelegatedEventHandlerToHost(eventType, host) { var listeners = (host[listenersAttachedKey] = host[listenersAttachedKey] || {}); if (!listeners[eventType]) { (host.body || host).addEventListener( eventType, (listeners[eventType] = function (event) { var curNode = event.target; if (!curNode) { return; } curNode = // event.target of an SVGElementInstance does not have a // `getAttribute` function in IE 11. // See https://github.com/marko-js/marko/issues/796 curNode.correspondingUseElement || // in some browsers the event target can be a text node // one example being dragenter in firefox. (curNode.nodeType === TEXT_NODE ? curNode.parentNode : curNode); // Search up the tree looking DOM events mapped to target // component methods var propName = "on" + eventType; var target; // Attributes will have the following form: // on<event_type>("<target_method>|<component_id>") if (event.bubbles) { var propagationStopped = false; // Monkey-patch to fix #97 var oldStopPropagation = event.stopPropagation; event.stopPropagation = function () { oldStopPropagation.call(event); propagationStopped = true; }; do { if ((target = getEventFromEl(curNode, propName))) { delegateEvent(curNode, propName, target, event); if (propagationStopped) { break; } } } while ((curNode = curNode.parentNode) && curNode.getAttribute); } else if ((target = getEventFromEl(curNode, propName))) { delegateEvent(curNode, propName, target, event); } }), true, ); } } function noop() {} exports.___handleNodeAttach = noop; exports.___handleNodeDetach = noop; exports.___delegateEvent = delegateEvent; exports.___getEventFromEl = getEventFromEl; exports.___addDelegatedEventHandler = addDelegatedEventHandler; exports.___init = function (host) { Object.keys(delegatedEvents).forEach(function (eventType) { addDelegatedEventHandlerToHost(eventType, host); }); };