@polymer/polymer
Version:
The Polymer library makes it easy to create your own web components. Give your element some markup and properties, and then use it on a site. Polymer provides features like dynamic templates and data binding to reduce the amount of boilerplate you need to
759 lines (678 loc) • 29.6 kB
HTML
<!--
@license
Copyright (c) 2017 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>
<meta charset="utf-8">
<script src="../../../webcomponentsjs/webcomponents-lite.js"></script>
<script src="../../../web-component-tester/browser.js"></script>
<link rel="import" href="../../polymer.html">
<link rel="import" href="gestures-elements.html">
</head>
<body>
<script>
suite('simulate events', function() {
var app;
setup(function(done) {
app = document.createElement('x-app');
document.body.appendChild(app);
Polymer.RenderStatus.afterNextRender(null, done);
});
teardown(function() {
document.body.removeChild(app);
Polymer.Gestures.resetMouseCanceller();
});
test('tap on x-foo and check localTarget and rootTarget', function() {
var foo = app.$.foo;
foo.dispatchEvent(new CustomEvent('click', {bubbles: true, composed: true}));
assert.equal(app._testLocalTarget, app, 'local target');
assert.equal(app._testRootTarget, foo, 'root target');
let touches = [{
clientX: 0,
clientY: 0,
identifier: 1,
// target is set to the element with `addEventListener`, which is app
target: app
}];
let touchstart = new CustomEvent('touchstart', {bubbles: true, composed: true});
touchstart.changedTouches = touchstart.touches = touches;
foo.dispatchEvent(touchstart);
let touchend = new CustomEvent('touchend', {bubbles: true, composed: true});
touchend.touches = touchend.changedTouches = touches;
foo.dispatchEvent(touchend);
assert.equal(app._testLocalTarget, app, 'local target touch');
assert.equal(app._testRootTarget, foo, 'root target touch');
});
test('tap on x-foo.div and check target info', function() {
var foo = app.$.foo;
var div = foo.$.div;
div.dispatchEvent(new CustomEvent('click', {bubbles: true, composed: true}));
assert.equal(app._testLocalTarget, app, 'app local target');
assert.equal(app._testRootTarget, div, 'app root target');
assert.equal(foo._testLocalTarget, foo, 'foo local target');
assert.equal(foo._testRootTarget, div, 'foo root target');
let touches = [{
clientX: 0,
clientY: 0,
identifier: 1,
// target is set to the element with `addEventListener`, which is app
target: app
}];
let touchstart = new CustomEvent('touchstart', {bubbles: true, composed: true});
touchstart.touches = touchstart.changedTouches = touches;
let touchend = new CustomEvent('touchend', {composed: true, bubbles: true});
touchend.touches = touchend.changedTouches = touches;
div.dispatchEvent(touchstart);
div.dispatchEvent(touchend);
assert.equal(app._testLocalTarget, app, 'app local target touch');
assert.equal(app._testRootTarget, div, 'app root target touch');
assert.equal(foo._testLocalTarget, foo, 'foo local target touch');
assert.equal(foo._testRootTarget, div, 'foo root target touch');
});
test('HTMLElement.click triggers tap', function() {
// put the element off screen to prevent x/y weirdness from .click()
app.style.cssText = 'position: absolute; left: -1000px; top: -1000px;';
// make a mousedown *very* far away to tickle the distance check
var ev = new CustomEvent('mousedown', {composed: true});
ev.clientX = 1e8;
ev.clientY = 1e8;
app.dispatchEvent(ev);
app.click();
assert.equal(app._testLocalTarget, app, 'app local target');
assert.equal(app._testRootTarget, app, 'app root target');
});
});
suite('Event Setup and Teardown', function() {
var outer, inner;
suiteSetup(function(done) {
outer = document.createElement('x-setup');
document.body.appendChild(outer);
Polymer.RenderStatus.afterNextRender(null, function() {
inner = outer.$.inner;
done();
});
});
suite('setup', function() {
test('listeners block', function() {
assert.equal(outer.__polymerGesturesTouchAction, 'none');
var obj = outer.__polymerGestures;
assert.equal(obj.mousedown.downup, 2, 'mousedown downup');
assert.equal(obj.mousedown.track, 1, 'mousedown track');
assert.equal(obj.mousedown.tap, 1, 'mousedown tap');
assert.equal(obj.mousedown._count, 4, 'total mousedown');
assert.equal(obj.touchstart.downup, 2, 'touchstart downup');
assert.equal(obj.touchstart.tap, 1, 'touchstart tap');
assert.equal(obj.touchstart.track, 1, 'touchstart track');
assert.equal(obj.touchstart._count, 4, 'total touchstart');
assert.equal(obj.touchmove.track, 1, 'touchmove track');
assert.equal(obj.touchmove._count, 1, 'total touchmove');
assert.equal(obj.touchend.downup, 2, 'touchend downup');
assert.equal(obj.touchend.track, 1, 'touchend track');
assert.equal(obj.touchend.tap, 1, 'touchend tap');
assert.equal(obj.touchend._count, 4, 'total touchend');
});
test('on-*', function() {
assert.equal(inner.__polymerGesturesTouchAction, 'none');
var obj = inner.__polymerGestures;
assert.equal(obj.mousedown.downup, 2, 'mousedown downup');
assert.equal(obj.mousedown.track, 1, 'mousedown track');
assert.equal(obj.mousedown.tap, 1, 'mousedown tap');
assert.equal(obj.mousedown._count, 4, 'total mousedown');
assert.equal(obj.touchstart.downup, 2, 'touchstart downup');
assert.equal(obj.touchstart.tap, 1, 'touchstart tap');
assert.equal(obj.touchstart.track, 1, 'touchstart track');
assert.equal(obj.touchstart._count, 4, 'total touchstart');
assert.equal(obj.touchmove.track, 1, 'touchmove track');
assert.equal(obj.touchmove._count, 1, 'total touchmove');
assert.equal(obj.touchend.downup, 2, 'touchend downup');
assert.equal(obj.touchend.track, 1, 'touchend track');
assert.equal(obj.touchend.tap, 1, 'touchend tap');
assert.equal(obj.touchend._count, 4, 'total touchend');
});
test('dynamic', function(done) {
var el = document.createElement('x-dynamic');
Polymer.RenderStatus.afterNextRender(null, function() {
var obj = el.__polymerGestures;
assert(!obj);
el.setup();
obj = el.__polymerGestures;
assert(obj, 'gestures object exists');
assert.equal(obj.mousedown.tap, 1, 'mousedown tap');
assert.equal(obj.click.tap, 1, 'click tap');
assert.equal(obj.touchstart.tap, 1, 'touchstart tap');
assert.equal(obj.touchend.tap, 1, 'touchend tap');
done();
});
});
});
suite('teardown', function() {
test('dynamic', function(){
var el = document.createElement('x-dynamic');
var obj = el.__polymerGestures;
assert(!obj);
el.setup();
el.teardown();
obj = el.__polymerGestures;
assert(obj, 'gestures object exists');
assert.equal(obj.mousedown.tap, 0, 'mousedown tap');
assert.equal(obj.click.tap, 0, 'click tap');
assert.equal(obj.touchstart.tap, 0, 'touchstart tap');
assert.equal(obj.touchend.tap, 0, 'touchend tap');
});
});
});
suite('target finding', function() {
var div, divLocation;
setup(function() {
div = document.createElement('div');
div.style.cssText = 'height: 50px; width: 50px; background: red;';
div.id = 'target';
document.body.appendChild(div);
divLocation = div.getBoundingClientRect();
});
test('target finding returns null outside the window', function() {
var actual = Polymer.Gestures.deepTargetFind(-1, -1);
assert.equal(actual, null);
});
test('find the div in document', function() {
var x = divLocation.left, y = divLocation.top;
var actual = Polymer.Gestures.deepTargetFind(x, y);
assert.equal(actual, div);
});
test('find the div with a shadowroot', function() {
div.attachShadow({mode: 'open'});
var x = divLocation.left, y = divLocation.top;
var actual = Polymer.Gestures.deepTargetFind(x, y);
assert.equal(actual, div);
});
test('find the div inside a shadowroot', function() {
var divOwner = document.createElement('span');
document.body.appendChild(divOwner);
divOwner.attachShadow({mode: 'open'}).appendChild(div);
var bcr = divOwner.getBoundingClientRect();
var x = bcr.left+10, y = bcr.top+10;
var actual = Polymer.Gestures.deepTargetFind(x, y);
assert.equal(actual, div);
});
test('find the div with a shadowroot inside a shadowroot', function() {
div.attachShadow({mode: 'open'});
var divOwner = document.createElement('span');
document.body.appendChild(divOwner);
divOwner.attachShadow({mode: 'open'}).appendChild(div);
var bcr = divOwner.getBoundingClientRect();
var x = bcr.left, y = bcr.top;
var actual = Polymer.Gestures.deepTargetFind(x, y);
assert.equal(actual, div);
});
});
suite('Prevention', function() {
var el;
setup(function(done) {
el = document.createElement('x-prevent');
document.body.appendChild(el);
Polymer.RenderStatus.afterNextRender(null, done);
});
teardown(function() {
el.parentNode.removeChild(el);
Polymer.Gestures.resetMouseCanceller();
});
test('tap', function() {
var ev = new CustomEvent('mousedown', {
bubbles: true,
cancelable: true,
composed: true
});
el.dispatchEvent(ev);
assert.equal(el.stream.length, 1, 'one event dispatched');
assert.equal(el.stream[0].type, 'down', 'was down event');
assert.equal(el.stream[0].defaultPrevented, true, 'was prevented');
assert.equal(ev.defaultPrevented, true, 'base event was prevented');
});
test('track', function() {
var ev = new CustomEvent('mousedown', {
bubbles: true,
cancelable: true,
composed: true
});
ev.clientX = ev.clientY = 0;
el.dispatchEvent(ev);
assert.equal(el.stream.length, 1);
for (var i = 0; i < 10; i++) {
ev = new CustomEvent(
i === 9 ? 'mouseup' : 'mousemove',
{bubbles: true, cancelable: true, composed: true}
);
ev.clientX = ev.clientY = 10 * i;
el.dispatchEvent(ev);
}
assert.equal(el.stream.length, 2, 'expected only down and up');
assert.equal(el.stream[0].type, 'down', 'down was found');
assert.equal(el.stream[0].defaultPrevented, true, 'down was prevented');
assert.equal(el.stream[1].type, 'up', 'up was found');
});
test('nested track and tap with touch', function(done) {
el.parentNode.removeChild(el);
el = document.createElement('x-nested-prevent');
document.body.appendChild(el);
Polymer.RenderStatus.afterNextRender(null, function() {
var child = el.$.child;
var options = {bubbles: true, cancelable: true, composed: true};
var bgr = el.getBoundingClientRect();
var clientX = bgr.left + (bgr.width / 2);
var clientY = bgr.top + (bgr.bottom / 2);
var ev = new CustomEvent('touchstart', options);
ev.touches = ev.changedTouches = [
{
clientX: clientX,
clientY: clientY,
identifier: 1,
// target is set to the element with `addEventListener`, which is el
target: el
}
];
ev.clientX = clientX;
ev.clientY = clientY;
child.dispatchEvent(ev);
for (var i = 0; i < 10; i++) {
clientX += 1;
ev = new CustomEvent(i === 9 ? 'touchend' : 'touchmove', options);
ev.touches = ev.changedTouches = [
{
clientX: clientX,
clientY: clientY,
identifier: 1,
// target is set to the element with `addEventListener`, which is el
target: el
}
];
ev.clientX = clientX;
ev.clientY = clientY;
// tell gestures to not turn off mouse events
child.dispatchEvent(ev);
}
assert.equal(child.stream.length, 0, 'expected no taps on the child');
assert.notEqual(el.stream.length, 0, 'expected some tracks on the parent');
done();
});
});
});
suite('Buttons', function() {
var el;
setup(function(done) {
el = document.createElement('x-buttons');
document.body.appendChild(el);
Polymer.RenderStatus.afterNextRender(null, done);
});
teardown(function() {
el.parentNode.removeChild(el);
});
suite('Down and Up', function() {
test('Left Mouse Button Only', function() {
var options = {bubbles: true, composed: true};
var evLeftDown = new CustomEvent('mousedown', options);
// left button
evLeftDown.button = 0;
evLeftDown.clientX = 1;
var evLeftUp = new CustomEvent('mouseup', options);
var evRightDown = new CustomEvent('mousedown', options);
// right button
evRightDown.button = 2;
evRightDown.clientX = 2;
var evRightUp = new CustomEvent('mouseup', options);
el.dispatchEvent(evLeftDown);
el.dispatchEvent(evLeftUp);
el.dispatchEvent(evRightDown);
el.dispatchEvent(evRightUp);
assert.equal(el.stream.length, 2, 'only saw one up and down pair');
assert.equal(el.stream[0].type, 'down');
assert.equal(el.stream[1].type, 'up');
assert.equal(el.stream[0].detail.x, 1, 'only from the left button');
});
test('Recover from right click', function() {
var options = {bubbles: true, composed: true};
var evDown = new CustomEvent('mousedown', options);
var evMove = new CustomEvent('mousemove', options);
evMove.buttons = 0;
var evUp = new CustomEvent('mouseup', options);
el.dispatchEvent(evDown);
el.dispatchEvent(evMove);
el.dispatchEvent(evUp);
assert.equal(el.stream.length, 2, 'always get an up');
});
});
suite('Tap', function() {
test('Left Mouse Button Only', function() {
var evMid = new CustomEvent('click', {bubbles: true, composed: true});
evMid.button = 1;
var evLeft = new CustomEvent('click', {bubbles: true, composed: true});
evLeft.button = 0;
el.dispatchEvent(evMid);
el.dispatchEvent(evLeft);
assert.equal(el.stream.length, 1, 'only one tap');
});
});
suite('Track', function() {
test('Left Mouse Button Only', function() {
var options = {bubbles: true, composed: true};
var ev = new CustomEvent('mousedown', options);
ev.clientX = ev.clientY = 0;
el.dispatchEvent(ev);
for (var i = 0; i < 5; i++) {
ev = new CustomEvent('mousemove', options);
ev.clientX = 10 * i;
ev.clientY = 10 * i;
// left button until move 4
ev.buttons = (i > 3) ? 2 : 1;
el.dispatchEvent(ev);
}
el.dispatchEvent(new CustomEvent('mouseup', options));
// down, <skipped>, track:start, track:track, track:track, track:end, up
assert.equal(el.stream.length, 6);
assert.equal(el.stream[0].type, 'down');
assert.equal(el.stream[1].detail.state, 'start');
assert.equal(el.stream[2].detail.state, 'track');
assert.equal(el.stream[3].detail.state, 'track');
assert.equal(el.stream[4].type, 'up');
assert.equal(el.stream[5].detail.state, 'end');
});
});
});
suite('SD Polyfill', function() {
var el;
setup(function(done) {
el = document.createElement('x-document-listener');
document.body.appendChild(el);
Polymer.RenderStatus.afterNextRender(null, function() {
el.setup();
done();
});
});
teardown(function() {
el.teardown();
document.body.removeChild(el);
});
test('document listener works in SD polyfill', function() {
var ev = new CustomEvent('mousedown', {bubbles: true, composed: true});
el.dispatchEvent(ev);
assert.equal(el.stream.length, 1);
});
});
suite('Reference Cleanup', function() {
var el;
setup(function(done) {
el = document.createElement('x-buttons');
document.body.appendChild(el);
Polymer.RenderStatus.afterNextRender(null, done);
});
teardown(function() {
document.body.removeChild(el);
});
test('down and up clear document tracking', function() {
var ev = new CustomEvent('mousedown', {bubbles: true, composed: true});
el.dispatchEvent(ev);
// some recognizers do not track the document, like tap
var recognizers = Polymer.Gestures.recognizers.filter(function(r) {
return r.info.hasOwnProperty('movefn') &&
r.info.hasOwnProperty('upfn');
});
assert.isAbove(recognizers.length, 0, 'some recognizers track the document');
recognizers.forEach(function(r) {
assert.isFunction(r.info.movefn, r.name + ' movefn');
assert.isFunction(r.info.upfn, r.name + ' upfn');
});
ev = new CustomEvent('mouseup', {bubbles: true, composed: true});
el.dispatchEvent(ev);
recognizers.forEach(function(r) {
assert.isNull(r.info.movefn, r.name + ' movefn');
assert.isNull(r.info.upfn, r.name + ' upfn');
});
});
});
suite('Imperative API', function() {
var el, fn;
suiteSetup(function() {
el = document.createElement('x-imperative');
document.body.appendChild(el);
fn = function(e) { el.handle(e); };
});
suiteTeardown(function() {
document.body.removeChild(el);
});
test('add listeners with addListener', function() {
Polymer.Gestures.addListener(el, 'down', fn);
Polymer.Gestures.add(el, 'up', fn);
var ev = new CustomEvent('mousedown', {bubbles: true, composed: true});
el.dispatchEvent(ev);
assert.equal(el.stream.length, 1);
assert.equal(el.stream[0].type, 'down');
ev = new CustomEvent('mouseup', {bubbles: true, composed: true});
el.dispatchEvent(ev);
assert.equal(el.stream.length, 2);
assert.equal(el.stream[1].type, 'up');
});
test('remove listeners with removeListener', function() {
Polymer.Gestures.remove(el, 'down', fn);
Polymer.Gestures.removeListener(el, 'up', fn);
var ev = new CustomEvent('mousedown', {bubbles: true, composed: true});
el.dispatchEvent(ev);
ev = new CustomEvent('mouseup', {bubbles: true, composed: true});
el.dispatchEvent(ev);
assert.equal(el.stream.length, 2);
});
});
suite('setScrollDirection', function() {
test('mapping', function() {
var el = document.createElement('x-imperative');
var key = '__polymerGesturesTouchAction';
el.setScrollDirection('none');
assert.equal(el[key], 'none');
el.setScrollDirection('all');
assert.equal(el[key], 'auto');
el.setScrollDirection('x');
assert.equal(el[key], 'pan-x');
el.setScrollDirection('y');
assert.equal(el[key], 'pan-y');
});
});
suite('Regression Testing', function() {
test('#4459', function() {
Polymer.Gestures.add(document, 'tap', null);
document.dispatchEvent(new MouseEvent('mousedown', { detail: 1, clientX: -100, bubbles: true, composed: true }));
document.dispatchEvent(new MouseEvent('mouseup', { detail: 1, clientX: 100, bubbles: true, composed: true }));
document.dispatchEvent(new MouseEvent('click', { detail: 1, clientX: 100, bubbles: true, composed: true }));
Polymer.Gestures.remove(document, 'tap', null);
});
test('#5030', function() {
let count = 0;
const increment = function() { count++; };
Polymer.Gestures.add(window, 'tap', increment);
window.dispatchEvent(new MouseEvent('click', {bubbles: true}));
assert.equal(count, 1);
Polymer.Gestures.remove(window, 'tap', increment);
});
suite('native label click', function() {
test('native label click', function() {
let el = document.createElement('x-native-label');
document.body.appendChild(el);
let target = el.$.label;
// simulate the event sequence of a touch on the screen
let touches = [{
clientX: 0,
clientY: 0,
identifier: 1,
// target is set to the element with `addEventListener`, which is `target`
target
}];
let touchstart = new CustomEvent('touchstart', {bubbles: true, composed: true});
touchstart.changedTouches = touchstart.touches = touches;
target.dispatchEvent(touchstart);
let touchend = new CustomEvent('touchend', {bubbles: true, composed: true});
touchend.touches = touchend.changedTouches = touches;
target.dispatchEvent(touchend);
// simulate a mouse click on the label
let click = new MouseEvent('click', {bubbles: true, composed: true});
target.dispatchEvent(click);
// check that the mouse click on the label will activate the checkbox
assert.equal(el.$.check.checked, true, 'checkbox should be checked');
document.body.removeChild(el);
});
test('label click with nested element', function() {
let el = document.createElement('x-native-label-nested');
document.body.appendChild(el);
let target = el.$.label;
// simulate the event sequence of a touch on the screen
let touches = [{
clientX: 0,
clientY: 0,
identifier: 1,
// target is set to the element with `addEventListener`, which is `target`
target
}];
let touchstart = new CustomEvent('touchstart', {bubbles: true, composed: true});
touchstart.changedTouches = touchstart.touches = touches;
target.dispatchEvent(touchstart);
let touchend = new CustomEvent('touchend', {bubbles: true, composed: true});
touchend.touches = touchend.changedTouches = touches;
target.dispatchEvent(touchend);
// simulate a mouse click on the label
let click = new MouseEvent('click', {bubbles: true, composed: true});
target.dispatchEvent(click);
// check that the mouse click on the label will activate the checkbox
assert.equal(el.$.check.checked, true, 'checkbox should be checked');
document.body.removeChild(el);
});
});
suite('disabled', function() {
let shouldSkip = true;
suiteSetup(function() {
/*
* IE 11 does not dispatch events to elements with `disabled` attribute
* This is different from all other browsers, so skip these tests in IE 11
*/
const div = document.createElement('div');
div.setAttribute('disabled', '');
document.body.appendChild(div);
div.addEventListener('click', () => {
shouldSkip = false;
});
div.click();
document.body.removeChild(div);
});
setup(function() {
Polymer.Gestures.resetMouseCanceller();
});
test('click() function works as expected on disabled elements', function() {
if (shouldSkip) {
this.skip();
}
let el = document.createElement('x-disabled-tap');
document.body.appendChild(el);
el.$.disabled.click();
el.$.nested.click();
el.$.disabledEl.click();
assert.deepEqual(el.taps, ['button#nested', 'x-disabled#disabledEl']);
document.body.removeChild(el);
});
test('disabled elements don\'t fire taps', function() {
if (shouldSkip) {
this.skip();
}
let el = document.createElement('x-disabled-tap');
document.body.appendChild(el);
// tap an element with disabled attribute
let target = el.$.disabled;
// simulate the event sequence of a touch on the screen
let touches = [{
clientX: 0,
clientY: 0,
identifier: 1,
// target is set to the element with `addEventListener`, which is `target`
target
}];
let touchstart = new CustomEvent('touchstart', {bubbles: true, composed: true});
touchstart.changedTouches = touchstart.touches = touches;
target.dispatchEvent(touchstart);
let touchend = new CustomEvent('touchend', {bubbles: true, composed: true});
touchend.touches = touchend.changedTouches = touches;
target.dispatchEvent(touchend);
assert.deepEqual(el.taps, []);
// tap an element with a disabled ancestor
target = el.$.nested;
// simulate the event sequence of a touch on the screen
touches = [{
clientX: 0,
clientY: 0,
identifier: 1,
// target is set to the element with `addEventListener`, which is `target`
target
}];
touchstart = new CustomEvent('touchstart', {bubbles: true, composed: true});
touchstart.changedTouches = touchstart.touches = touches;
target.dispatchEvent(touchstart);
touchend = new CustomEvent('touchend', {bubbles: true, composed: true});
touchend.touches = touchend.changedTouches = touches;
target.dispatchEvent(touchend);
assert.deepEqual(el.taps, ['button#nested']);
Polymer.Gestures.resetMouseCanceller();
// tap a custom element with a `disabled` property
target = el.$.disabledEl;
// simulate the event sequence of a touch on the screen
touches = [{
clientX: 0,
clientY: 0,
identifier: 1,
// target is set to the element with `addEventListener`, which is `target`
target
}];
touchstart = new CustomEvent('touchstart', {bubbles: true, composed: true});
touchstart.changedTouches = touchstart.touches = touches;
target.dispatchEvent(touchstart);
touchend = new CustomEvent('touchend', {bubbles: true, composed: true});
touchend.touches = touchend.changedTouches = touches;
target.dispatchEvent(touchend);
assert.deepEqual(el.taps, ['button#nested', 'x-disabled#disabledEl']);
document.body.removeChild(el);
});
test('test all "disableable" elements', function() {
const el = document.createElement('all-disabled');
document.body.appendChild(el);
el.tapAll();
assert.deepEqual(el.taps, []);
document.body.removeChild(el);
});
});
suite('Clicks on the labelled elements from label touches do not throw.', function() {
let container;
setup(function() {
container = document.createElement('div');
container.innerHTML = `
<input type="text" id="anInput">
<label for="anInput">The label</label>
`;
document.body.appendChild(container);
});
teardown(function() {
document.body.removeChild(container);
});
test('test name', function() {
const input = container.querySelector('input#anInput');
const label = container.querySelector('label[for=anInput]');
const makeEvent =
(type) => new CustomEvent(type, {bubbles: true, composed: true});
label.dispatchEvent(makeEvent('touchstart'));
label.dispatchEvent(makeEvent('touchend'));
label.dispatchEvent(makeEvent('click'));
input.dispatchEvent(makeEvent('click'));
});
});
});
</script>
</body>
</html>