diff --git a/bower.json b/bower.json index 7a91f7d9..d84907cb 100644 --- a/bower.json +++ b/bower.json @@ -9,9 +9,6 @@ "keywords": [ "PointerEvents" ], - "dependencies": { - "WeakMap": "Polymer/WeakMap#master" - }, "license": "BSD", "private": true, "ignore": [ diff --git a/build.json b/build.json index c9378877..86428b53 100644 --- a/build.json +++ b/build.json @@ -1,5 +1,4 @@ [ - "../WeakMap/weakmap.js", "src/boot.js", "src/touch-action.js", "src/PointerEvent.js", diff --git a/pointerevents.js b/pointerevents.js index e5995cca..74a32de1 100644 --- a/pointerevents.js +++ b/pointerevents.js @@ -7,7 +7,6 @@ var thisFile = 'pointerevents.js'; var scopeName = 'PointerEventsPolyfill'; var modules = [ - '../WeakMap/weakmap.js', 'src/boot.js', 'src/touch-action.js', 'src/PointerEvent.js', diff --git a/src/PointerEvent.js b/src/PointerEvent.js index c1f7e2c9..3dc5545a 100644 --- a/src/PointerEvent.js +++ b/src/PointerEvent.js @@ -19,16 +19,6 @@ * @return {Event} A new PointerEvent of type `inType` and initialized with properties from `inDict`. */ (function(scope) { - // test for DOM Level 4 Events - var NEW_MOUSE_EVENT = false; - var HAS_BUTTONS = false; - try { - var ev = new MouseEvent('click', {buttons: 1}); - NEW_MOUSE_EVENT = true; - HAS_BUTTONS = ev.buttons === 1; - ev = null; - } catch(e) { - } var MOUSE_PROPS = [ 'bubbles', @@ -45,6 +35,8 @@ 'metaKey', 'button', 'relatedTarget', + 'pageX', + 'pageY' ]; var MOUSE_DEFAULTS = [ @@ -61,73 +53,23 @@ false, false, 0, - null + null, + 0, + 0 ]; function PointerEvent(inType, inDict) { - inDict = inDict || {}; - // According to the w3c spec, - // http://www.w3.org/TR/DOM-Level-3-Events/#events-MouseEvent-button - // MouseEvent.button == 0 can mean either no mouse button depressed, or the - // left mouse button depressed. - // - // As of now, the only way to distinguish between the two states of - // MouseEvent.button is by using the deprecated MouseEvent.which property, as - // this maps mouse buttons to positive integers > 0, and uses 0 to mean that - // no mouse button is held. - // - // MouseEvent.which is derived from MouseEvent.button at MouseEvent creation, - // but initMouseEvent does not expose an argument with which to set - // MouseEvent.which. Calling initMouseEvent with a buttonArg of 0 will set - // MouseEvent.button == 0 and MouseEvent.which == 1, breaking the expectations - // of app developers. - // - // The only way to propagate the correct state of MouseEvent.which and - // MouseEvent.button to a new MouseEvent.button == 0 and MouseEvent.which == 0 - // is to call initMouseEvent with a buttonArg value of -1. - // - // This is fixed with DOM Level 4's use of buttons - var buttons = inDict.buttons; - // touch has two possible buttons state: 0 and 1, rely on being told the right one - if (!HAS_BUTTONS && !buttons && inType !== 'touch') { - switch (inDict.which) { - case 1: buttons = 1; break; - case 2: buttons = 4; break; - case 3: buttons = 2; break; - default: buttons = 0; - } - } - - var e; - if (NEW_MOUSE_EVENT) { - e = new MouseEvent(inType, inDict); - } else { - e = document.createEvent('MouseEvent'); + inDict = inDict || Object.create(null); - // import values from the given dictionary - var props = {}, p; - for(var i = 0; i < MOUSE_PROPS.length; i++) { - p = MOUSE_PROPS[i]; - props[p] = inDict[p] || MOUSE_DEFAULTS[i]; - } + var e = document.createEvent('Event'); + e.initEvent(inType, inDict.bubbles || false, inDict.cancelable || false); - // define the properties inherited from MouseEvent - e.initMouseEvent( - inType, props.bubbles, props.cancelable, props.view, props.detail, - props.screenX, props.screenY, props.clientX, props.clientY, props.ctrlKey, - props.altKey, props.shiftKey, props.metaKey, props.button, props.relatedTarget - ); - } - - // make the event pass instanceof checks - e.__proto__ = PointerEvent.prototype; - - // define the buttons property according to DOM Level 3 spec - if (!HAS_BUTTONS) { - // IE 10 has buttons on MouseEvent.prototype as a getter w/o any setting - // mechanism - Object.defineProperty(e, 'buttons', {get: function(){ return buttons; }, enumerable: true}); + // define inherited MouseEvent properties + for(var i = 0, p; i < MOUSE_PROPS.length; i++) { + p = MOUSE_PROPS[i]; + e[p] = inDict[p] || MOUSE_DEFAULTS[i]; } + e.buttons = inDict.buttons || 0; // Spec requires that pointers without pressure specified use 0.5 for down // state and 0 for up state. @@ -135,27 +77,22 @@ if (inDict.pressure) { pressure = inDict.pressure; } else { - pressure = buttons ? 0.5 : 0; + pressure = e.buttons ? 0.5 : 0; } // define the properties of the PointerEvent interface - Object.defineProperties(e, { - pointerId: { value: inDict.pointerId || 0, enumerable: true }, - width: { value: inDict.width || 0, enumerable: true }, - height: { value: inDict.height || 0, enumerable: true }, - pressure: { value: pressure, enumerable: true }, - tiltX: { value: inDict.tiltX || 0, enumerable: true }, - tiltY: { value: inDict.tiltY || 0, enumerable: true }, - pointerType: { value: inDict.pointerType || '', enumerable: true }, - hwTimestamp: { value: inDict.hwTimestamp || 0, enumerable: true }, - isPrimary: { value: inDict.isPrimary || false, enumerable: true } - }); + e.pointerId = inDict.pointerId || 0; + e.width = inDict.width || 0; + e.height = inDict.height || 0; + e.pressure = pressure; + e.tiltX = inDict.tiltX || 0; + e.tiltY = inDict.tiltY || 0; + e.pointerType = inDict.pointerType || ''; + e.hwTimestamp = inDict.hwTimestamp || 0; + e.isPrimary = inDict.isPrimary || false; return e; } - // PointerEvent extends MouseEvent - PointerEvent.prototype = Object.create(MouseEvent.prototype); - // attach to window if (!scope.PointerEvent) { scope.PointerEvent = PointerEvent; diff --git a/src/dispatcher.js b/src/dispatcher.js index 253c0ac3..8336c066 100644 --- a/src/dispatcher.js +++ b/src/dispatcher.js @@ -37,7 +37,9 @@ 'type', 'target', 'currentTarget', - 'which' + 'which', + 'pageX', + 'pageY' ]; var CLONE_DEFAULTS = [ @@ -72,6 +74,8 @@ '', null, null, + 0, + 0, 0 ]; @@ -90,13 +94,11 @@ * - pointercancel: a pointer will no longer generate events */ var dispatcher = { - targets: new WeakMap(), - handledEvents: new WeakMap(), pointermap: new scope.PointerMap(), eventMap: {}, // Scope objects for native events. // This exists for ease of testing. - eventSources: {}, + eventSources: Object.create(null), eventSourceList: [], /** * Add a new event source that will generate pointer events. @@ -186,7 +188,7 @@ // This is used to prevent multiple dispatch of pointerevents from // platform events. This can happen when two elements in different scopes // are set up to create pointer events, which is relevant to Shadow DOM. - if (this.handledEvents.get(inEvent)) { + if (inEvent._handledByPE) { return; } var type = inEvent.type; @@ -194,7 +196,7 @@ if (fn) { fn(inEvent); } - this.handledEvents.set(inEvent, true); + inEvent._handledByPE = true; }, // set up event listeners listen: function(target, events) { @@ -232,7 +234,7 @@ if (inEvent.preventDefault) { e.preventDefault = inEvent.preventDefault; } - this.targets.set(e, this.targets.get(inEvent) || inEvent.target); + e._target = e._target || inEvent.target; return e; }, // make and dispatch an event in one call @@ -248,7 +250,7 @@ * properties. */ cloneEvent: function(inEvent) { - var eventCopy = {}, p; + var eventCopy = Object.create(null), p; for (var i = 0; i < CLONE_PROPS.length; i++) { p = CLONE_PROPS[i]; eventCopy[p] = inEvent[p] || CLONE_DEFAULTS[i]; @@ -277,7 +279,7 @@ return this.captureInfo.target; } } - return this.targets.get(inEvent); + return inEvent._target; }, setCapture: function(inPointerId, inTarget) { if (this.captureInfo) { @@ -288,7 +290,7 @@ this.implicitRelease = this.releaseCapture.bind(this, inPointerId); document.addEventListener('pointerup', this.implicitRelease); document.addEventListener('pointercancel', this.implicitRelease); - this.targets.set(e, inTarget); + e._target = inTarget; this.asyncDispatchEvent(e); }, releaseCapture: function(inPointerId) { @@ -298,7 +300,7 @@ this.captureInfo = null; document.removeEventListener('pointerup', this.implicitRelease); document.removeEventListener('pointercancel', this.implicitRelease); - this.targets.set(e, t); + e._target = t; this.asyncDispatchEvent(e); } }, diff --git a/src/mouse.js b/src/mouse.js index 040d247e..6e2b8979 100644 --- a/src/mouse.js +++ b/src/mouse.js @@ -10,6 +10,13 @@ // radius around touchend that swallows mouse events var DEDUP_DIST = 25; + var WHICH_TO_BUTTONS = [0, 1, 4, 2]; + + var HAS_BUTTONS = false; + try { + HAS_BUTTONS = new MouseEvent('test', {buttons: 1}).buttons === 1; + } catch (e) {} + // handler block for native mouse events var mouseEvents = { POINTER_ID: 1, @@ -51,6 +58,9 @@ e.pointerId = this.POINTER_ID; e.isPrimary = true; e.pointerType = this.POINTER_TYPE; + if (!HAS_BUTTONS) { + e.buttons = WHICH_TO_BUTTONS[e.which] || 0; + } return e; }, mousedown: function(inEvent) { diff --git a/src/touch.js b/src/touch.js index ff831a9e..912c982e 100644 --- a/src/touch.js +++ b/src/touch.js @@ -24,7 +24,6 @@ // handler block for native touch events var touchEvents = { - scrollType: new WeakMap(), events: [ 'touchstart', 'touchmove', @@ -49,21 +48,21 @@ var a = el.getAttribute(ATTRIB); var st = this.touchActionToScrollType(a); if (st) { - this.scrollType.set(el, st); + el._scrollType = st; dispatcher.listen(el, this.events); // set touch-action on shadows as well allShadows(el).forEach(function(s) { - this.scrollType.set(s, st); + s._scrollType = st; dispatcher.listen(s, this.events); }, this); } }, elementRemoved: function(el) { - this.scrollType['delete'](el); + el._scrollType = undefined; dispatcher.unlisten(el, this.events); // remove touch-action from shadow allShadows(el).forEach(function(s) { - this.scrollType['delete'](s); + s._scrollType = undefined; dispatcher.unlisten(s, this.events); }, this); }, @@ -73,9 +72,9 @@ var oldSt = this.touchActionToScrollType(oldValue); // simply update scrollType if listeners are already established if (st && oldSt) { - this.scrollType.set(el, st); + el._scrollType = st; allShadows(el).forEach(function(s) { - this.scrollType.set(s, st); + s._scrollType = st; }, this); } else if (oldSt) { this.elementRemoved(el); @@ -145,6 +144,7 @@ return ret; }, touchToPointer: function(inTouch) { + var cte = this.currentTouchEvent; var e = dispatcher.cloneEvent(inTouch); // Spec specifies that pointerId 1 is reserved for Mouse. // Touch identifiers can start at 0. @@ -157,34 +157,35 @@ e.cancelable = true; e.detail = this.clickCount; e.button = 0; - e.buttons = this.typeToButtons(this.currentTouchEvent); + e.buttons = this.typeToButtons(cte.type); e.width = inTouch.webkitRadiusX || inTouch.radiusX || 0; e.height = inTouch.webkitRadiusY || inTouch.radiusY || 0; e.pressure = inTouch.webkitForce || inTouch.force || 0.5; e.isPrimary = this.isPrimaryTouch(inTouch); e.pointerType = this.POINTER_TYPE; + // forward touch preventDefaults + var self = this; + e.preventDefault = function() { + self.scrolling = false; + self.firstXY = null; + cte.preventDefault(); + }; return e; }, processTouches: function(inEvent, inFunction) { var tl = inEvent.changedTouches; - this.currentTouchEvent = inEvent.type; - var pointers = touchMap(tl, this.touchToPointer, this); - // forward touch preventDefaults - pointers.forEach(function(p) { - p.preventDefault = function() { - this.scrolling = false; - this.firstXY = null; - inEvent.preventDefault(); - }; - }, this); - pointers.forEach(inFunction, this); + this.currentTouchEvent = inEvent; + for (var i = 0, t; i < tl.length; i++) { + t = tl[i]; + inFunction.call(this, this.touchToPointer(t)); + } }, // For single axis scrollers, determines whether the element should emit // pointer events or behave as a scroller shouldScroll: function(inEvent) { if (this.firstXY) { var ret; - var scrollAxis = this.scrollType.get(inEvent.currentTarget); + var scrollAxis = inEvent.currentTarget._scrollType; if (scrollAxis === 'none') { // this element is a touch-action: none, should never scroll ret = false; @@ -231,7 +232,7 @@ // index in pointermap. if (key !== 1 && !this.findTouch(tl, key - 2)) { var p = value.out; - d.push(this.touchToPointer(p)); + d.push(p); } }, this); d.forEach(this.cancelOut, this); diff --git a/tests/constructor.js b/tests/constructor.js index 1c29ac5d..159709c8 100644 --- a/tests/constructor.js +++ b/tests/constructor.js @@ -5,10 +5,6 @@ */ suite('Constructor', function() { - test('PointerEvent extends MouseEvent', function() { - var p = new PointerEvent(); - expect(p).to.be.an.instanceof(MouseEvent); - }); test('PointerEvents have the required properties', function() { var props = [ @@ -25,7 +21,6 @@ suite('Constructor', function() { 'shiftKey', 'metaKey', 'button', - 'which', 'relatedTarget', 'pointerId', 'width', @@ -51,38 +46,10 @@ suite('Constructor', function() { expect(p.button).to.equal(2); }); - test('Readonly properties must be readonly', function() { - var props = [ - 'pointerId', - 'width', - 'height', - 'pressure', - 'tiltX', - 'tiltY', - 'pointerType', - 'hwTimestamp', - 'isPrimary' - ]; - var p = new PointerEvent(); - var v; - props.forEach(function(k) { - v = p[k]; - p[k] = NaN; - expect(p[k]).to.equal(v); - }); - }); - test('Button properties are used for pressure', function() { var p = new PointerEvent('foo', {buttons: 1}); expect(p.pressure).to.equal(0.5); // test for buttons property - var m = document.createEvent('MouseEvent'); - m.initEvent('test', false, false, null, null, 0, 0, 0, 0, false, false, false, false, 0, null); - // only run this test if .buttons is not supported - if (m.buttons === undefined) { - p = new PointerEvent('baz', {button: 0, which: 1}); - expect(p.pressure).to.equal(0.5); - } p = new PointerEvent('bar'); expect(p.pressure).to.equal(0); }); diff --git a/tests/loader.js b/tests/loader.js index 6dd11594..3c454310 100644 --- a/tests/loader.js +++ b/tests/loader.js @@ -16,9 +16,6 @@ suite('Loader', function() { test('PointerMap', function() { expect(pep.PointerMap).to.be.ok; }); - test('WeakMap', function() { - expect(WeakMap).to.be.ok; - }); test('Dispatcher', function() { expect(pep.dispatcher).to.be.ok; });