var _addEventListener = require('./addEventListener'); var updateManager = require('./update-manager'); var warp10Parse = require('warp10/parse'); function getEventAttribute(el, attrName) { var virtualAttrs = el._vattrs; if (virtualAttrs) { return el._vattrs[attrName]; } else { var attrValue = el.getAttribute(attrName); if (attrValue) { return warp10Parse(attrValue); } } } var attachBubbleEventListeners = function() { var body = document.body; // Here's where we handle event delegation using our own mechanism // for delegating events. For each event that we have white-listed // as supporting bubble, we will attach a listener to the root // document.body element. When we get notified of a triggered event, // we again walk up the tree starting at the target associated // with the event to find any mappings for event. Each mapping // is from a DOM event type to a method of a widget. require('./bubble').forEach(function addBubbleHandler(eventType) { _addEventListener(body, eventType, function(event) { var propagationStopped = false; // Monkey-patch to fix #97 var oldStopPropagation = event.stopPropagation; event.stopPropagation = function() { oldStopPropagation.call(event); propagationStopped = true; }; updateManager.batchUpdate(function() { var curNode = event.target; if (!curNode) { return; } // Search up the tree looking DOM events mapped to target // widget methods var attrName = 'data-_on' + eventType; var target; // Attributes will have the following form: // on("|") do { if ((target = getEventAttribute(curNode, attrName))) { var targetMethod = target[0]; var targetWidgetId = target[1]; var targetArgs; if (target.length > 2) { targetArgs = target.slice(2); } var targetWidgetEl = document.getElementById(targetWidgetId); if (!targetWidgetEl) { // The target widget is not in the DOM anymore // which can happen when the widget and its // children are removed from the DOM while // processing the event. continue; } var targetWidget = targetWidgetEl.__widget; if (!targetWidget) { throw new Error('Widget not found: ' + targetWidgetId); } var targetFunc = targetWidget[targetMethod]; if (!targetFunc) { throw new Error('Method not found on widget ' + targetWidget.id + ': ' + targetMethod); } // Invoke the widget method targetWidget[targetMethod](event, curNode); if (propagationStopped) { break; } } } while((curNode = curNode.parentNode) && curNode.getAttribute); }); }); }); }; exports.init = function() { if (attachBubbleEventListeners) { // Only attach event listeners once... attachBubbleEventListeners(); attachBubbleEventListeners = null; // This is a one time thing } };