marko
Version:
UI Components + streaming, async, high performance, HTML templating for Node.js and the browser.
145 lines (120 loc) • 4.21 kB
JavaScript
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);
});
};