Skip to content

Commit

Permalink
fix(event): in IE, fix #1128, angular#589, pointer event will be tran…
Browse files Browse the repository at this point in the history
…sformed in IE
  • Loading branch information
JiaLiPassion committed Sep 11, 2018
1 parent 31fc127 commit 98dd51f
Show file tree
Hide file tree
Showing 3 changed files with 211 additions and 8 deletions.
4 changes: 2 additions & 2 deletions file-size-limit.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
{
"path": "dist/zone.min.js",
"checkTarget": true,
"limit": 42050
"limit": 45000
}
]
}
}
69 changes: 63 additions & 6 deletions lib/common/events.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,14 +10,26 @@
* @suppress {missingRequire}
*/

import {ADD_EVENT_LISTENER_STR, attachOriginToPatched, FALSE_STR, ObjectGetPrototypeOf, REMOVE_EVENT_LISTENER_STR, TRUE_STR, ZONE_SYMBOL_PREFIX, zoneSymbol} from './utils';
import {ADD_EVENT_LISTENER_STR, attachOriginToPatched, FALSE_STR, isIE, isIEOrEdge, ObjectGetPrototypeOf, REMOVE_EVENT_LISTENER_STR, TRUE_STR, ZONE_SYMBOL_PREFIX, zoneSymbol} from './utils';

/** @internal **/
interface EventTaskData extends TaskData {
// use global callback or not
readonly useG?: boolean;
}

const pointerEventsMap: {[key: string]: string} = {
'MSPointerCancel': 'pointercancel',
'MSPointerDown': 'pointerdown',
'MSPointerEnter': 'pointerenter',
'MSPointerHover': 'pointerhover',
'MSPointerLeave': 'pointerleave',
'MSPointerMove': 'pointermove',
'MSPointerOut': 'pointerout',
'MSPointerOver': 'pointerover',
'MSPointerUp': 'pointerup'
};

let passiveSupported = false;

if (typeof window !== 'undefined') {
Expand Down Expand Up @@ -87,6 +99,16 @@ export function patchEventTarget(
const PREPEND_EVENT_LISTENER = 'prependListener';
const PREPEND_EVENT_LISTENER_SOURCE = '.' + PREPEND_EVENT_LISTENER + ':';

Object.keys(pointerEventsMap).forEach(msEventName => {
const eventName = pointerEventsMap[msEventName];
zoneSymbolEventNames[eventName] = {};
zoneSymbolEventNames[eventName][FALSE_STR] = ZONE_SYMBOL_PREFIX + eventName + FALSE_STR;
zoneSymbolEventNames[eventName][TRUE_STR] = ZONE_SYMBOL_PREFIX + eventName + TRUE_STR;
zoneSymbolEventNames[msEventName] = {};
zoneSymbolEventNames[msEventName][FALSE_STR] = ZONE_SYMBOL_PREFIX + msEventName + FALSE_STR;
zoneSymbolEventNames[msEventName][TRUE_STR] = ZONE_SYMBOL_PREFIX + msEventName + TRUE_STR;
});

const invokeTask = function(task: any, target: any, event: Event) {
// for better performance, check isRemoved which is set
// by removeEventListener
Expand Down Expand Up @@ -122,7 +144,15 @@ export function patchEventTarget(
// event.target is needed for Samsung TV and SourceBuffer
// || global is needed https://github.com/angular/zone.js/issues/190
const target: any = this || event.target || _global;
const tasks = target[zoneSymbolEventNames[event.type][FALSE_STR]];
let tasks = target[zoneSymbolEventNames[event.type][FALSE_STR]];
if (isIEOrEdge) {
const pointerMappedEvent = pointerEventsMap[event.type];
const msTasks =
pointerMappedEvent && target[zoneSymbolEventNames[pointerMappedEvent]][FALSE_STR];
if (msTasks) {
tasks = tasks.concat(msTasks);
}
}
if (tasks) {
// invoke all tasks which attached to current target with given event.type and capture = false
// for performance concern, if task.length === 1, just invoke
Expand Down Expand Up @@ -154,7 +184,15 @@ export function patchEventTarget(
// event.target is needed for Samsung TV and SourceBuffer
// || global is needed https://github.com/angular/zone.js/issues/190
const target: any = this || event.target || _global;
const tasks = target[zoneSymbolEventNames[event.type][TRUE_STR]];
let tasks = target[zoneSymbolEventNames[event.type][TRUE_STR]];
if (isIEOrEdge) {
const pointerMappedEvent = pointerEventsMap[event.type];
const msTasks =
pointerMappedEvent && target[zoneSymbolEventNames[pointerMappedEvent]][FALSE_STR];
if (msTasks) {
tasks = tasks.concat(msTasks);
}
}
if (tasks) {
// invoke all tasks which attached to current target with given event.type and capture = false
// for performance concern, if task.length === 1, just invoke
Expand Down Expand Up @@ -350,7 +388,11 @@ export function patchEventTarget(
return;
}

const eventName = arguments[0];
let eventName = arguments[0];
if (isIEOrEdge) {
const msEventName = pointerEventsMap[eventName];
eventName = msEventName ? msEventName : eventName;
}
const options = arguments[2];

if (blackListedEvents) {
Expand Down Expand Up @@ -393,6 +435,13 @@ export function patchEventTarget(
}
let existingTasks = target[symbolEventName];
let isExisting = false;
if (isIEOrEdge && !existingTasks) {
const msEventName = pointerEventsMap[eventName];
if (msEventName) {
existingTasks =
target[zoneSymbolEventNames[msEventName][capture ? TRUE_STR : FALSE_STR]];
}
}
if (existingTasks) {
// already have task registered
isExisting = true;
Expand Down Expand Up @@ -427,7 +476,8 @@ export function patchEventTarget(
}
taskData.target = target;
taskData.capture = capture;
taskData.eventName = eventName;
// in pointer event, we need to use original event name here.
taskData.eventName = arguments[0];
taskData.isExisting = isExisting;

const data = useGlobalCallback ? OPTIMIZED_ZONE_EVENT_TASK_DATA : undefined;
Expand Down Expand Up @@ -489,7 +539,11 @@ export function patchEventTarget(

proto[REMOVE_EVENT_LISTENER] = function() {
const target = this || _global;
const eventName = arguments[0];
let eventName = arguments[0];
if (isIEOrEdge) {
const msEventName = pointerEventsMap[eventName];
eventName = msEventName ? msEventName : eventName;
}
const options = arguments[2];

let capture;
Expand Down Expand Up @@ -531,6 +585,9 @@ export function patchEventTarget(
// remove globalZoneAwareCallback and remove the task cache from target
(existingTask as any).allRemoved = true;
target[symbolEventName] = null;
if (isIEOrEdge) {
existingTask.eventName = arguments[0];
}
}
existingTask.zone.cancelTask(existingTask);
if (returnTarget) {
Expand Down
146 changes: 146 additions & 0 deletions test/browser/browser.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2662,5 +2662,151 @@ describe('Zone', function() {
});
}));
});

describe(
'pointer event in IE',
ifEnvSupports(
() => {
return getIEVersion() === 11;
},
() => {
const pointerEventsMap: {[key: string]: string} = {
'MSPointerCancel': 'pointercancel',
'MSPointerDown': 'pointerdown',
'MSPointerEnter': 'pointerenter',
'MSPointerHover': 'pointerhover',
'MSPointerLeave': 'pointerleave',
'MSPointerMove': 'pointermove',
'MSPointerOut': 'pointerout',
'MSPointerOver': 'pointerover',
'MSPointerUp': 'pointerup'
};

let div: HTMLDivElement;
beforeEach(() => {
div = document.createElement('div');
document.body.appendChild(div);
});
afterEach(() => {
document.body.removeChild(div);
});
Object.keys(pointerEventsMap).forEach(key => {
it(`${key} and ${pointerEventsMap[key]} should both be triggered`,
(done: DoneFn) => {
const logs: string[] = [];
div.addEventListener(key, (event: any) => {
expect(event.type).toEqual(pointerEventsMap[key]);
logs.push(`${key} triggered`);
});
div.addEventListener(pointerEventsMap[key], (event: any) => {
expect(event.type).toEqual(pointerEventsMap[key]);
logs.push(`${pointerEventsMap[key]} triggered`);
});
const evt1 = document.createEvent('Event');
evt1.initEvent(key, true, true);
div.dispatchEvent(evt1);

setTimeout(() => {
expect(logs).toEqual(
[`${key} triggered`, `${pointerEventsMap[key]} triggered`]);
});

const evt2 = document.createEvent('Event');
evt2.initEvent(pointerEventsMap[key], true, true);
div.dispatchEvent(evt2);

setTimeout(() => {
expect(logs).toEqual(
[`${key} triggered`, `${pointerEventsMap[key]} triggered`]);
});

setTimeout(done);
});

it(`${key} and ${
pointerEventsMap[key]} with same listener should not be triggered twice`,
(done: DoneFn) => {
const logs: string[] = [];
const listener = function(event: any) {
expect(event.type).toEqual(pointerEventsMap[key]);
logs.push(`${key} triggered`);
};
div.addEventListener(key, listener);
div.addEventListener(pointerEventsMap[key], listener);

const evt1 = document.createEvent('Event');
evt1.initEvent(key, true, true);
div.dispatchEvent(evt1);

setTimeout(() => {
expect(logs).toEqual([`${key} triggered`]);
});

const evt2 = document.createEvent('Event');
evt2.initEvent(pointerEventsMap[key], true, true);
div.dispatchEvent(evt2);

setTimeout(() => {
expect(logs).toEqual([`${pointerEventsMap[key]} triggered`]);
});

setTimeout(done);
});

it(`${key} and ${
pointerEventsMap
[key]} should be able to be removed with removeEventListener`,
(done: DoneFn) => {
const logs: string[] = [];
const listener1 = function(event: any) {
logs.push(`${key} triggered`);
};
const listener2 = function(event: any) {
logs.push(`${pointerEventsMap[key]} triggered`);
};
div.addEventListener(key, listener1);
div.addEventListener(pointerEventsMap[key], listener2);

div.removeEventListener(key, listener1);
div.removeEventListener(key, listener2);

const evt1 = document.createEvent('Event');
evt1.initEvent(key, true, true);
div.dispatchEvent(evt1);

setTimeout(() => {
expect(logs).toEqual([]);
});

const evt2 = document.createEvent('Event');
evt2.initEvent(pointerEventsMap[key], true, true);
div.dispatchEvent(evt2);

setTimeout(() => {
expect(logs).toEqual([]);
});

div.addEventListener(key, listener1);
div.addEventListener(pointerEventsMap[key], listener2);

div.removeEventListener(pointerEventsMap[key], listener1);
div.removeEventListener(pointerEventsMap[key], listener2);

div.dispatchEvent(evt1);

setTimeout(() => {
expect(logs).toEqual([]);
});

div.dispatchEvent(evt2);

setTimeout(() => {
expect(logs).toEqual([]);
});

setTimeout(done);
});
});
}));
});
});

0 comments on commit 98dd51f

Please sign in to comment.