mirror of
https://github.com/Polymer/polymer.git
synced 2025-02-25 18:55:30 -06:00
Support composed flag
This commit is contained in:
@@ -741,10 +741,67 @@ subject to an additional IP rights grant found at http://polymer.github.io/PATEN
|
||||
_activeElement: activeElementDescriptor
|
||||
});
|
||||
|
||||
// https://github.com/w3c/webcomponents/issues/513#issuecomment-224183937
|
||||
var alwaysComposed = {
|
||||
blur: true,
|
||||
focus: true,
|
||||
focusin: true,
|
||||
focusout: true,
|
||||
click: true,
|
||||
dblclick: true,
|
||||
mousedown: true,
|
||||
mouseenter: true,
|
||||
mouseleave: true,
|
||||
mousemove: true,
|
||||
mouseout: true,
|
||||
mouseover: true,
|
||||
mouseup: true,
|
||||
wheel: true,
|
||||
beforeinput: true,
|
||||
input: true,
|
||||
keydown: true,
|
||||
keyup: true,
|
||||
compositionstart: true,
|
||||
compositionupdate: true,
|
||||
compositionend: true,
|
||||
touchstart: true,
|
||||
touchend: true,
|
||||
touchmove: true,
|
||||
touchcancel: true,
|
||||
pointerover: true,
|
||||
pointerenter: true,
|
||||
pointerdown: true,
|
||||
pointermove: true,
|
||||
pointerup: true,
|
||||
pointercancel: true,
|
||||
pointerout: true,
|
||||
pointerleave: true,
|
||||
gotpointercapture: true,
|
||||
lostpointercapture: true,
|
||||
dragstart: true,
|
||||
drag: true,
|
||||
dragenter: true,
|
||||
dragleave: true,
|
||||
dragover: true,
|
||||
drop: true,
|
||||
dragend: true,
|
||||
DOMActivate: true,
|
||||
DOMFocusIn: true,
|
||||
DOMFocusOut: true,
|
||||
keypress: true
|
||||
};
|
||||
|
||||
var EventMixin = {
|
||||
|
||||
__patched: 'Event',
|
||||
|
||||
get composed() {
|
||||
if (this.isTrusted && this.__composed === undefined) {
|
||||
this.__composed = alwaysComposed[this.type] || false;
|
||||
}
|
||||
return this.__composed || false;
|
||||
},
|
||||
|
||||
composedPath() {
|
||||
if (!this.__composedPath) {
|
||||
var composedPath = [];
|
||||
@@ -760,12 +817,16 @@ subject to an additional IP rights grant found at http://polymer.github.io/PATEN
|
||||
current = contents[contents.length - 1];
|
||||
} else if (current.assignedSlot) {
|
||||
current = current.assignedSlot;
|
||||
} else if (current.host && this.composed) {
|
||||
current = current.host;
|
||||
} else {
|
||||
current = current.parentNode || current.host;
|
||||
current = current.parentNode;
|
||||
}
|
||||
}
|
||||
// event composedPath includes window in most recent native implementations
|
||||
composedPath.push(window);
|
||||
// event composedPath includes window when composed = true in most recent native implementations
|
||||
if (composedPath[composedPath.length - 1] === document) {
|
||||
composedPath.push(window);
|
||||
}
|
||||
this.__composedPath = composedPath;
|
||||
}
|
||||
return this.__composedPath;
|
||||
@@ -788,7 +849,26 @@ subject to an additional IP rights grant found at http://polymer.github.io/PATEN
|
||||
|
||||
};
|
||||
|
||||
ShadyDom.addEventListener = function(type, fn, capture) {
|
||||
function mixinComposedFlag(Base) {
|
||||
var klazz = class extends Base {
|
||||
constructor(type, options) {
|
||||
super(type, options);
|
||||
if (options && options.composed) {
|
||||
this.__composed = true;
|
||||
} else {
|
||||
this.__composed = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
return klazz;
|
||||
}
|
||||
|
||||
ShadyDom.origEvent = Event;
|
||||
ShadyDom.PatchedEvent = mixinComposedFlag(Event);
|
||||
|
||||
ShadyDom.PatchedCustomEvent = mixinComposedFlag(CustomEvent);
|
||||
|
||||
ShadyDom.addEventListener = function(type, fn, optionsOrCapture) {
|
||||
if (!this.__eventListenerCount) {
|
||||
this.__eventListenerCount = 0;
|
||||
}
|
||||
@@ -796,20 +876,25 @@ subject to an additional IP rights grant found at http://polymer.github.io/PATEN
|
||||
var wrappedFn = function(e) {
|
||||
if (!e.__target) {
|
||||
e.__target = e.target;
|
||||
e.__relatedTarget = e.relatedTarget;
|
||||
var proto = ShadyDom.patchImpl.prototypeForObject(e);
|
||||
if (proto) {
|
||||
e.__proto__ = proto;
|
||||
}
|
||||
}
|
||||
return fn(e);
|
||||
if (e.composedPath().indexOf(this) > -1) {
|
||||
return fn(e);
|
||||
} else {
|
||||
e.stopImmediatePropagation();
|
||||
}
|
||||
}
|
||||
fn.__eventWrapper = wrappedFn;
|
||||
return origAddEventListener.call(this, type, wrappedFn, capture);
|
||||
return origAddEventListener.call(this, type, wrappedFn, optionsOrCapture);
|
||||
};
|
||||
|
||||
ShadyDom.removeEventListener = function(type, fn, capture) {
|
||||
ShadyDom.removeEventListener = function(type, fn, optionsOrCapture) {
|
||||
var wrapper = fn.__eventWrapper;
|
||||
origRemoveEventListener.call(this, type, wrapper || fn, capture);
|
||||
origRemoveEventListener.call(this, type, wrapper || fn, optionsOrCapture);
|
||||
if (wrapper) {
|
||||
fn.__eventWrapper = null;
|
||||
this.__eventListenerCount--;
|
||||
@@ -830,7 +915,9 @@ subject to an additional IP rights grant found at http://polymer.github.io/PATEN
|
||||
Document: ShadyDom.extendAll({__patched: 'Document'},
|
||||
NodeMixin, FragmentMixin, ElementMixin, UnderActiveElementMixin),
|
||||
|
||||
Event: EventMixin
|
||||
Event: EventMixin,
|
||||
|
||||
CustomEvent: ShadyDom.extendAll({__patched: 'CustomEvent'}, EventMixin)
|
||||
|
||||
};
|
||||
ShadyDom.ownerRootForNode = mixinImpl.ownerRootForNode;
|
||||
|
||||
@@ -94,7 +94,7 @@ subject to an additional IP rights grant found at http://polymer.github.io/PATEN
|
||||
case Node.COMMENT_NODE:
|
||||
return Mixins.Node;
|
||||
}
|
||||
if (obj instanceof Event) {
|
||||
if (obj instanceof ShadyDom.origEvent) {
|
||||
return Mixins.Event;
|
||||
}
|
||||
},
|
||||
@@ -139,6 +139,8 @@ subject to an additional IP rights grant found at http://polymer.github.io/PATEN
|
||||
function patchEvents() {
|
||||
Node.prototype.addEventListener = ShadyDom.addEventListener;
|
||||
Node.prototype.removeEventListener = ShadyDom.removeEventListener;
|
||||
window.Event = ShadyDom.PatchedEvent;
|
||||
window.CustomEvent = ShadyDom.PatchedCustomEvent;
|
||||
}
|
||||
|
||||
if (ShadyDom.force || !Element.prototype.createShadowRoot) {
|
||||
|
||||
170
test/unit/shady-events.html
Normal file
170
test/unit/shady-events.html
Normal file
@@ -0,0 +1,170 @@
|
||||
<!doctype html>
|
||||
<!--
|
||||
@license
|
||||
Copyright (c) 2016 The Polymer Project Authors. All rights reserved.
|
||||
This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt
|
||||
The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
|
||||
The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt
|
||||
Code distributed by Google as part of the polymer project is also
|
||||
subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
|
||||
-->
|
||||
<html>
|
||||
<head>
|
||||
<script src="../../../webcomponentsjs/webcomponents-lite.js"></script>
|
||||
<script src="../../../web-component-tester/browser.js"></script>
|
||||
<script>
|
||||
ShadyDom = {force: true};
|
||||
</script>
|
||||
<link rel="import" href="../../polymer.html">
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<dom-module id="x-event-patched">
|
||||
<template>
|
||||
<div>
|
||||
<div id="candidate" on-foo="fooHandler"></div>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
HTMLImports.whenReady(function() {
|
||||
Polymer({
|
||||
properties: {
|
||||
events: {
|
||||
type: Object,
|
||||
value: function() {
|
||||
return {};
|
||||
}
|
||||
}
|
||||
},
|
||||
listeners: {
|
||||
"foo": "hostFooHandler"
|
||||
},
|
||||
is: 'x-event-patched',
|
||||
connected: function() {
|
||||
this.listen(this.shadowRoot, "rootFooHandler");
|
||||
},
|
||||
fooHandler: function(e) {
|
||||
this.events.div = {
|
||||
path: e.composedPath(),
|
||||
target: e.target
|
||||
};
|
||||
},
|
||||
hostFooHandler: function(e) {
|
||||
this.events.host = {
|
||||
path: e.composedPath && e.composedPath(),
|
||||
target: e.target
|
||||
};
|
||||
},
|
||||
rootFooHandler: function(e) {
|
||||
this.events.root = {
|
||||
path: e.composedPath && e.composedPath(),
|
||||
target: e.target
|
||||
};
|
||||
}
|
||||
});
|
||||
});
|
||||
</script>
|
||||
</dom-module>
|
||||
|
||||
<dom-module id="x-event-scoped">
|
||||
<template>
|
||||
<div id="scoped" on-composed="childHandler" on-scoped="childHandler"></div>
|
||||
</template>
|
||||
<script>
|
||||
HTMLImports.whenReady(function() {
|
||||
Polymer({
|
||||
is: 'x-event-scoped',
|
||||
properties: {
|
||||
hostEvents: {
|
||||
type: Array,
|
||||
value: function() {
|
||||
return [];
|
||||
}
|
||||
},
|
||||
childEvents: {
|
||||
type: Array,
|
||||
value: function() {
|
||||
return [];
|
||||
}
|
||||
}
|
||||
},
|
||||
listeners: {
|
||||
'composed': 'hostHandler',
|
||||
'scoped': 'hostHandler'
|
||||
},
|
||||
hostHandler: function(e) {
|
||||
this.hostEvents.push(e.type);
|
||||
},
|
||||
childHandler: function(e) {
|
||||
this.childEvents.push(e.type);
|
||||
},
|
||||
fireComposed: function() {
|
||||
this.fire('composed', null, {node: this.$.scoped});
|
||||
},
|
||||
fireScoped: function(){
|
||||
this.fire('scoped', null, {node: this.$.scoped, composed: false});
|
||||
}
|
||||
});
|
||||
});
|
||||
</script>
|
||||
</dom-module>
|
||||
|
||||
<test-fixture id="patch">
|
||||
<template>
|
||||
<x-event-patched></x-event-patched>
|
||||
</template>
|
||||
</test-fixture>
|
||||
|
||||
<test-fixture id="globalpatch">
|
||||
<template>
|
||||
<div></div>
|
||||
</template>
|
||||
</test-fixture>
|
||||
|
||||
<test-fixture id="scoping">
|
||||
<template>
|
||||
<x-event-scoped></x-event-scoped>
|
||||
</template>
|
||||
</test-fixture>
|
||||
|
||||
<script>
|
||||
suite('ShadyDOM event patching', function() {
|
||||
test('event.composedPath is consistent', function() {
|
||||
var el = fixture('patch');
|
||||
var e = new Event('foo', {bubbles: true, composed: true})
|
||||
el.$.candidate.dispatchEvent(e);
|
||||
assert.property(el.events, 'div');
|
||||
assert.equal(el.events.div.target, el.$.candidate);
|
||||
assert.property(el.events, 'host');
|
||||
assert.equal(el.events.host.path, el.events.div.path);
|
||||
assert.equal(el.events.host.target, el);
|
||||
// assert.property(el.events, 'root');
|
||||
// assert.equal(el.events.root.path, el.events.div.path);
|
||||
// assert.equal(el.events.root.target, el.$.candidate);
|
||||
});
|
||||
|
||||
test('event patching works on non Polymer elements', function() {
|
||||
var el = fixture('globalpatch');
|
||||
var path;
|
||||
el.addEventListener('foo', function(e) {
|
||||
path = e.composedPath();
|
||||
});
|
||||
var e = new Event('foo', {composed: true});
|
||||
el.dispatchEvent(e);
|
||||
assert.deepEqual([el, el.parentNode, document.body, document.documentElement, document, window], path);
|
||||
});
|
||||
|
||||
test('events handle the `composed` flag correctly', function() {
|
||||
var el = fixture('scoping');
|
||||
el.fireScoped();
|
||||
el.fireComposed();
|
||||
assert.equal(el.hostEvents.length, 1);
|
||||
assert.equal(el.hostEvents[0], 'composed');
|
||||
assert.equal(el.childEvents.length, 2);
|
||||
assert.equal(el.childEvents[0], 'scoped');
|
||||
assert.equal(el.childEvents[1], 'composed');
|
||||
});
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
Reference in New Issue
Block a user