diff --git a/src/declaration/events.js b/src/declaration/events.js index cee3c734d8..8fac392ac8 100644 --- a/src/declaration/events.js +++ b/src/declaration/events.js @@ -8,7 +8,7 @@ // imports - var EVENT_PREFIX = scope.api.instance.events.EVENT_PREFIX; + var api = scope.api.instance.events; var log = window.logFlags || {}; // polymer-element declarative api: events feature @@ -24,57 +24,12 @@ // for each attribute for (var i=0, a; a=this.attributes[i]; i++) { // does it have magic marker identifying it as an event delegate? - if (hasEventPrefix(a.name)) { + if (api.hasEventPrefix(a.name)) { // if so, add the info to delegates - delegates[removeEventPrefix(a.name)] = a.value; + delegates[api.removeEventPrefix(a.name)] = a.value; } } }, - parseLocalEvents: function() { - // extract data from all templates into delegates - var t$ = this.querySelectorAll('template'); - for (var i=0, l=t$.length, t; (i 0) && console.log('[%s] addHostListeners:', this.localName, events); this.addNodeListeners(this, events, this.hostEventListener); }, - // event listeners inside a shadow-root - addInstanceListeners: function(root, template) { - var events = template.delegates; - if (events) { - log.events && (Object.keys(events).length > 0) && console.log('[%s:root] addInstanceListeners:', this.localName, events); - this.addNodeListeners(root, events, this.instanceEventListener); - } - }, addNodeListeners: function(node, events, listener) { // note: conditional inside loop as optimization // for empty 'events' object @@ -75,69 +74,26 @@ Platform.flush(); } }, - instanceEventListener: function(event) { - listenLocal(this, event); - } - }; - - // TODO(sjmiles): much of the below privatized only because of the vague - // notion this code is too fiddly and we need to revisit the core feature - - function listenLocal(host, event) { - if (!event.cancelBubble) { - event.on = EVENT_PREFIX + event.type; - log.events && console.group("[%s]: listenLocal [%s]", host.localName, event.on); - if (!event.path) { - _listenLocalNoEventPath(host, event); - } else { - _listenLocal(host, event); + prepareBinding: function(path, name, node) { + // if lhs an event prefix, + if (events.hasEventPrefix(name)) { + // provide an event-binding callback + return function(model, name, node) { + console.log('event: [%s].%s => [%s].%s()"', node.localName, name, model.localName, name); + node.addEventListener(events.removeEventPrefix(name), + function(event) { + var ctrlr = findController(node); + if (ctrlr && ctrlr.dispatchMethod) { + ctrlr.dispatchMethod(ctrlr, path, [event, event.detail, node]); + } + } + ); + }; } - log.events && console.groupEnd(); } - } + }; - function _listenLocal(host, event) { - var c = null; - // use `some` to stop iterating after the first matching target - Array.prototype.some.call(event.path, function(t) { - // if we hit host, stop - if (t === host) { - return true; - } - // find a controller for target `t`, unless we already found `host` - // as a controller - c = (c === host) ? c : findController(t); - // if we have a controller, dispatch the event, return 'true' if - // handler returns true - if (c && handleEvent(c, t, event)) { - return true; - } - }, this); - } - - // TODO(sorvell): remove when ShadowDOM polyfill supports event path. - // Note that findController will not return the expected - // controller when when the event target is a distributed node. - // This because we cannot traverse from a composed node to a node - // in shadowRoot. - // This will be addressed via an event path api - // https://www.w3.org/Bugs/Public/show_bug.cgi?id=21066 - function _listenLocalNoEventPath(host, event) { - log.events && console.log('event.path() not supported for', event.type); - var t = event.target, c = null; - // if we hit dirt or host, stop - while (t && t != host) { - // find a controller for target `t`, unless we already found `host` - // as a controller - c = (c === host) ? c : findController(t); - // if we have a controller, dispatch the event, return 'true' if - // handler returns true - if (c && handleEvent(c, t, event)) { - return true; - } - t = t.parentNode; - } - } + var prefixLength = EVENT_PREFIX.length; function findController(node) { while (node.parentNode) { @@ -146,26 +102,6 @@ return node.host; }; - function handleEvent(ctrlr, node, event) { - var h = node.getAttribute && node.getAttribute(event.on); - if (h && handleIfNotHandled(node, event)) { - log.events && console.log('[%s] found handler name [%s]', ctrlr.localName, h); - ctrlr.dispatchMethod(node, h, [event, event.detail, node]); - } - return event.cancelBubble; - }; - - function handleIfNotHandled(node, event) { - var list = event[HANDLED_LIST]; - if (!list) { - list = event[HANDLED_LIST] = []; - } - if (list.indexOf(node) < 0) { - list.push(node); - return true; - } - } - // exports scope.api.instance.events = events; diff --git a/src/instance/mdv.js b/src/instance/mdv.js index eb8798e6cf..744b591bf1 100644 --- a/src/instance/mdv.js +++ b/src/instance/mdv.js @@ -8,16 +8,27 @@ // imports var log = window.logFlags || 0; + var events = scope.api.instance.events; - // use an MDV syntax + // expressionista - var mdv_syntax = new PolymerExpressions(); + var syntax = new PolymerExpressions(); + + var expressionista = { + // <[node] [name] = {{path}}> + prepareBinding: function(path, name, node) { + // if not an event, delegate to the standard syntax + return events.prepareBinding(path, name, node) + || syntax.prepareBinding(path, name, node); + } + }; // element api supporting mdv var mdv = { + syntax: expressionista, instanceTemplate: function(template) { - return template.createInstance(this, mdv_syntax); + return template.createInstance(this, this.syntax); }, bind: function(name, model, path) { // note: binding is a prepare signal. This allows us to be sure that any @@ -26,7 +37,10 @@ this.prepareElement(); } var property = this.propertyForAttribute(name); - if (property) { + if (!property) { + return this.super(arguments); + } else { + // clean out the closets this.unbind(name); // use n-way Polymer binding var observer = this.bindProperty(property, model, path); @@ -37,8 +51,6 @@ // does not update due to not changing. this.reflectPropertyToAttribute(property); return this.bindings[name] = observer; - } else { - return this.super(arguments); } }, asyncUnbindAll: function() {