You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
Cesium-Prequel/Specs/Core/ScreenSpaceEventHandlerSpec.js

1859 lines
44 KiB
JavaScript

import { Cartesian2 } from "../../Source/Cesium.js";
import { clone } from "../../Source/Cesium.js";
import { combine } from "../../Source/Cesium.js";
import { defined } from "../../Source/Cesium.js";
import { FeatureDetection } from "../../Source/Cesium.js";
import { KeyboardEventModifier } from "../../Source/Cesium.js";
import { ScreenSpaceEventHandler } from "../../Source/Cesium.js";
import { ScreenSpaceEventType } from "../../Source/Cesium.js";
import DomEventSimulator from "../DomEventSimulator.js";
describe("Core/ScreenSpaceEventHandler", function () {
var usePointerEvents;
var element;
var handler;
function createCloningSpy(name) {
var spy = jasmine.createSpy(name);
var cloningSpy = function () {
// deep clone arguments so they are captured correctly by the spy
var args = [].slice.apply(arguments).map(function (arg) {
return clone(arg, true);
});
spy.apply(this, args);
};
function createPropertyDescriptor(prop) {
return {
get: function () {
return spy[prop];
},
set: function (value) {
spy[prop] = value;
},
};
}
for (var prop in spy) {
if (spy.hasOwnProperty(prop)) {
Object.defineProperty(cloningSpy, prop, createPropertyDescriptor(prop));
}
}
return cloningSpy;
}
var eventsToStop = "pointerdown pointerup pointermove pointercancel mousedown mouseup mousemove touchstart touchend touchmove touchcancel dblclick wheel mousewheel DOMMouseScroll".split(
" "
);
function stop(event) {
event.stopPropagation();
event.preventDefault();
}
beforeAll(function () {
usePointerEvents = FeatureDetection.supportsPointerEvents();
});
beforeEach(function () {
// ignore events that bubble up to the document.
// this prevents triggering the browser's "middle click to scroll" behavior
eventsToStop.forEach(function (e) {
document.addEventListener(e, stop, false);
});
element = document.createElement("div");
element.style.position = "absolute";
element.style.top = "0";
element.style.left = "0";
document.body.appendChild(element);
element.disableRootEvents = true;
if (usePointerEvents) {
spyOn(element, "setPointerCapture");
}
handler = new ScreenSpaceEventHandler(element);
});
afterEach(function () {
document.body.removeChild(element);
handler = !handler.isDestroyed() && handler.destroy();
eventsToStop.forEach(function (e) {
document.removeEventListener(e, stop, false);
});
});
describe("setInputAction", function () {
it("throws if action is undefined", function () {
expect(function () {
handler.setInputAction();
}).toThrowDeveloperError();
});
it("throws if type is undefined", function () {
expect(function () {
handler.setInputAction(function () {});
}).toThrowDeveloperError();
});
});
describe("getInputAction", function () {
it("throws if type is undefined", function () {
expect(function () {
handler.getInputAction();
}).toThrowDeveloperError();
});
});
describe("removeInputAction", function () {
it("throws if type is undefined", function () {
expect(function () {
handler.removeInputAction();
}).toThrowDeveloperError();
});
});
var MouseButton = {
LEFT: 0,
MIDDLE: 1,
RIGHT: 2,
};
function keyForValue(obj, val) {
for (var key in obj) {
if (obj[key] === val) {
return key;
}
}
}
function createMouseSpec(specFunction, eventType, button, modifier) {
var specName = keyForValue(ScreenSpaceEventType, eventType) + " action";
if (defined(modifier)) {
specName +=
" with " + keyForValue(KeyboardEventModifier, modifier) + " modifier";
}
it(specName, function () {
var eventOptions = {
button: button,
ctrlKey: modifier === KeyboardEventModifier.CTRL,
altKey: modifier === KeyboardEventModifier.ALT,
shiftKey: modifier === KeyboardEventModifier.SHIFT,
};
specFunction(eventType, modifier, eventOptions);
});
}
function createAllMouseSpecCombinations(
specFunction,
possibleButtons,
possibleModifiers,
possibleEventTypes
) {
for (var i = 0; i < possibleButtons.length; ++i) {
var eventType = possibleEventTypes[i];
var button = possibleButtons[i];
for (var j = 0; j < possibleModifiers.length; ++j) {
var modifier = possibleModifiers[j];
createMouseSpec(specFunction, eventType, button, modifier);
}
}
}
function simulateMouseDown(element, options) {
if (usePointerEvents) {
DomEventSimulator.firePointerDown(
element,
combine(options, {
pointerType: "mouse",
})
);
} else {
DomEventSimulator.fireMouseDown(element, options);
}
}
function simulateMouseUp(element, options) {
if (usePointerEvents) {
DomEventSimulator.firePointerUp(
element,
combine(options, {
pointerType: "mouse",
})
);
} else {
DomEventSimulator.fireMouseUp(element, options);
}
}
function simulateMouseMove(element, options) {
if (usePointerEvents) {
DomEventSimulator.firePointerMove(
element,
combine(options, {
pointerType: "mouse",
})
);
} else {
DomEventSimulator.fireMouseMove(element, options);
}
}
describe("handles mouse down", function () {
function testMouseDownEvent(eventType, modifier, eventOptions) {
var action = createCloningSpy("action");
handler.setInputAction(action, eventType, modifier);
expect(handler.getInputAction(eventType, modifier)).toEqual(action);
function simulateInput() {
simulateMouseDown(
element,
combine(
{
clientX: 1,
clientY: 2,
},
eventOptions
)
);
}
simulateInput();
expect(action.calls.count()).toEqual(1);
expect(action).toHaveBeenCalledWith({
position: new Cartesian2(1, 2),
});
// should not be fired after removal
action.calls.reset();
handler.removeInputAction(eventType, modifier);
simulateInput();
expect(action).not.toHaveBeenCalled();
}
var possibleButtons = [
MouseButton.LEFT,
MouseButton.MIDDLE,
MouseButton.RIGHT,
];
var possibleModifiers = [
undefined,
KeyboardEventModifier.SHIFT,
KeyboardEventModifier.CTRL,
KeyboardEventModifier.ALT,
];
var possibleEventTypes = [
ScreenSpaceEventType.LEFT_DOWN,
ScreenSpaceEventType.MIDDLE_DOWN,
ScreenSpaceEventType.RIGHT_DOWN,
];
createAllMouseSpecCombinations(
testMouseDownEvent,
possibleButtons,
possibleModifiers,
possibleEventTypes
);
});
describe("handles mouse up", function () {
function testMouseUpEvent(eventType, modifier, eventOptions) {
var action = createCloningSpy("action");
handler.setInputAction(action, eventType, modifier);
expect(handler.getInputAction(eventType, modifier)).toEqual(action);
// down, then up
function simulateInput() {
simulateMouseDown(
element,
combine(
{
clientX: 1,
clientY: 2,
},
eventOptions
)
);
simulateMouseUp(
element,
combine(
{
clientX: 1,
clientY: 2,
},
eventOptions
)
);
}
simulateInput();
expect(action.calls.count()).toEqual(1);
expect(action).toHaveBeenCalledWith({
position: new Cartesian2(1, 2),
});
action.calls.reset();
// down, move, then up
function simulateInput2() {
simulateMouseDown(
element,
combine(
{
clientX: 1,
clientY: 2,
},
eventOptions
)
);
simulateMouseMove(
element,
combine(
{
clientX: 10,
clientY: 11,
},
eventOptions
)
);
simulateMouseUp(
element,
combine(
{
clientX: 10,
clientY: 11,
},
eventOptions
)
);
}
simulateInput2();
expect(action.calls.count()).toEqual(1);
expect(action).toHaveBeenCalledWith({
position: new Cartesian2(10, 11),
});
// should not be fired after removal
action.calls.reset();
handler.removeInputAction(eventType, modifier);
simulateInput();
simulateInput2();
expect(action).not.toHaveBeenCalled();
}
var possibleButtons = [
MouseButton.LEFT,
MouseButton.MIDDLE,
MouseButton.RIGHT,
];
var possibleModifiers = [
undefined,
KeyboardEventModifier.SHIFT,
KeyboardEventModifier.CTRL,
KeyboardEventModifier.ALT,
];
var possibleEventTypes = [
ScreenSpaceEventType.LEFT_UP,
ScreenSpaceEventType.MIDDLE_UP,
ScreenSpaceEventType.RIGHT_UP,
];
createAllMouseSpecCombinations(
testMouseUpEvent,
possibleButtons,
possibleModifiers,
possibleEventTypes
);
});
describe("handles mouse click", function () {
function testMouseClickEvent(eventType, modifier, eventOptions) {
var action = createCloningSpy("action");
handler.setInputAction(action, eventType, modifier);
expect(handler.getInputAction(eventType, modifier)).toEqual(action);
// down, then up
function simulateInput() {
simulateMouseDown(
element,
combine(
{
clientX: 1,
clientY: 2,
},
eventOptions
)
);
simulateMouseUp(
element,
combine(
{
clientX: 1,
clientY: 2,
},
eventOptions
)
);
}
simulateInput();
expect(action.calls.count()).toEqual(1);
expect(action).toHaveBeenCalledWith({
position: new Cartesian2(1, 2),
});
// mouse clicks are not fired if the mouse moves too far away from the down position
action.calls.reset();
simulateMouseDown(
element,
combine(
{
clientX: 1,
clientY: 2,
},
eventOptions
)
);
simulateMouseUp(
element,
combine(
{
clientX: 10,
clientY: 11,
},
eventOptions
)
);
expect(action).not.toHaveBeenCalled();
// should not be fired after removal
action.calls.reset();
handler.removeInputAction(eventType, modifier);
simulateInput();
expect(action).not.toHaveBeenCalled();
}
var possibleButtons = [
MouseButton.LEFT,
MouseButton.MIDDLE,
MouseButton.RIGHT,
];
var possibleModifiers = [
undefined,
KeyboardEventModifier.SHIFT,
KeyboardEventModifier.CTRL,
KeyboardEventModifier.ALT,
];
var possibleEventTypes = [
ScreenSpaceEventType.LEFT_CLICK,
ScreenSpaceEventType.MIDDLE_CLICK,
ScreenSpaceEventType.RIGHT_CLICK,
];
createAllMouseSpecCombinations(
testMouseClickEvent,
possibleButtons,
possibleModifiers,
possibleEventTypes
);
});
describe("handles mouse double click", function () {
function testMouseDoubleClickEvent(eventType, modifier, eventOptions) {
var action = createCloningSpy("action");
handler.setInputAction(action, eventType, modifier);
expect(handler.getInputAction(eventType, modifier)).toEqual(action);
function simulateInput() {
DomEventSimulator.fireDoubleClick(
element,
combine(
{
clientX: 1,
clientY: 2,
},
eventOptions
)
);
}
simulateInput();
expect(action.calls.count()).toEqual(1);
expect(action).toHaveBeenCalledWith({
position: new Cartesian2(1, 2),
});
// should not be fired after removal
action.calls.reset();
handler.removeInputAction(eventType, modifier);
simulateInput();
expect(action).not.toHaveBeenCalled();
}
var possibleButtons = [MouseButton.LEFT];
var possibleModifiers = [
undefined,
KeyboardEventModifier.SHIFT,
KeyboardEventModifier.CTRL,
KeyboardEventModifier.ALT,
];
var possibleEventTypes = [ScreenSpaceEventType.LEFT_DOUBLE_CLICK];
createAllMouseSpecCombinations(
testMouseDoubleClickEvent,
possibleButtons,
possibleModifiers,
possibleEventTypes
);
});
describe("handles mouse move", function () {
function testMouseMoveEvent(eventType, modifier, eventOptions) {
var action = createCloningSpy("action");
handler.setInputAction(action, eventType, modifier);
expect(handler.getInputAction(eventType, modifier)).toEqual(action);
function simulateInput() {
simulateMouseMove(
element,
combine(
{
clientX: 1,
clientY: 2,
},
eventOptions
)
);
simulateMouseMove(
element,
combine(
{
clientX: 2,
clientY: 3,
},
eventOptions
)
);
}
simulateInput();
expect(action.calls.count()).toEqual(2);
expect(action).toHaveBeenCalledWith({
startPosition: new Cartesian2(1, 2),
endPosition: new Cartesian2(2, 3),
});
// should not be fired after removal
action.calls.reset();
handler.removeInputAction(eventType, modifier);
simulateInput();
expect(action).not.toHaveBeenCalled();
}
var possibleButtons = [undefined];
var possibleModifiers = [
undefined,
KeyboardEventModifier.SHIFT,
KeyboardEventModifier.CTRL,
KeyboardEventModifier.ALT,
];
var possibleEventTypes = [ScreenSpaceEventType.MOUSE_MOVE];
createAllMouseSpecCombinations(
testMouseMoveEvent,
possibleButtons,
possibleModifiers,
possibleEventTypes
);
});
describe("handles mouse wheel", function () {
if ("onwheel" in document) {
describe("using standard wheel event", function () {
function testWheelEvent(eventType, modifier, eventOptions) {
var action = createCloningSpy("action");
handler.setInputAction(action, eventType, modifier);
expect(handler.getInputAction(eventType, modifier)).toEqual(action);
function simulateInput() {
DomEventSimulator.fireWheel(
element,
combine(
{
deltaY: 120,
},
eventOptions
)
);
}
simulateInput();
expect(action.calls.count()).toEqual(1);
// NOTE: currently the action is passed the value of 'wheelDelta' which is inverted from the standard 'deltaY'
expect(action).toHaveBeenCalledWith(-120);
// should not be fired after removal
action.calls.reset();
handler.removeInputAction(eventType, modifier);
simulateInput();
expect(action).not.toHaveBeenCalled();
}
var possibleButtons = [undefined];
var possibleModifiers = [
undefined,
KeyboardEventModifier.SHIFT,
KeyboardEventModifier.CTRL,
KeyboardEventModifier.ALT,
];
var possibleEventTypes = [ScreenSpaceEventType.WHEEL];
createAllMouseSpecCombinations(
testWheelEvent,
possibleButtons,
possibleModifiers,
possibleEventTypes
);
});
} else if (document.onmousewheel !== undefined) {
describe("using legacy mousewheel event", function () {
function testMouseWheelEvent(eventType, modifier, eventOptions) {
var action = createCloningSpy("action");
handler.setInputAction(action, eventType, modifier);
expect(handler.getInputAction(eventType, modifier)).toEqual(action);
function simulateInput() {
DomEventSimulator.fireMouseWheel(
element,
combine(
{
wheelDelta: -120,
},
eventOptions
)
);
}
simulateInput();
expect(action.calls.count()).toEqual(1);
expect(action).toHaveBeenCalledWith(-120);
// should not be fired after removal
action.calls.reset();
handler.removeInputAction(eventType, modifier);
simulateInput();
expect(action).not.toHaveBeenCalled();
}
var possibleButtons = [undefined];
var possibleModifiers = [
undefined,
KeyboardEventModifier.SHIFT,
KeyboardEventModifier.CTRL,
KeyboardEventModifier.ALT,
];
var possibleEventTypes = [ScreenSpaceEventType.WHEEL];
createAllMouseSpecCombinations(
testMouseWheelEvent,
possibleButtons,
possibleModifiers,
possibleEventTypes
);
});
}
});
it("handles touch start", function () {
var eventType = ScreenSpaceEventType.LEFT_DOWN;
var action = createCloningSpy("action");
handler.setInputAction(action, eventType);
expect(handler.getInputAction(eventType)).toEqual(action);
function simulateInput() {
var touchStartPosition = {
clientX: 1,
clientY: 2,
};
if (usePointerEvents) {
DomEventSimulator.firePointerDown(
element,
combine(
{
pointerType: "touch",
pointerId: 1,
},
touchStartPosition
)
);
} else {
DomEventSimulator.fireTouchStart(element, {
changedTouches: [
combine(
{
identifier: 0,
},
touchStartPosition
),
],
});
}
}
simulateInput();
expect(action.calls.count()).toEqual(1);
expect(action).toHaveBeenCalledWith({
position: new Cartesian2(1, 2),
});
// should not be fired after removal
action.calls.reset();
handler.removeInputAction(eventType);
simulateInput();
expect(action).not.toHaveBeenCalled();
});
it("handles touch move", function () {
var eventType = ScreenSpaceEventType.MOUSE_MOVE;
var action = createCloningSpy("action");
handler.setInputAction(action, eventType);
expect(handler.getInputAction(eventType)).toEqual(action);
// start, then move
function simulateInput() {
var touchStartPosition = {
clientX: 1,
clientY: 2,
};
var touchMovePosition = {
clientX: 10,
clientY: 11,
};
if (usePointerEvents) {
DomEventSimulator.firePointerDown(
element,
combine(
{
pointerType: "touch",
pointerId: 1,
},
touchStartPosition
)
);
DomEventSimulator.firePointerMove(
element,
combine(
{
pointerType: "touch",
pointerId: 1,
},
touchMovePosition
)
);
} else {
DomEventSimulator.fireTouchStart(element, {
changedTouches: [
combine(
{
identifier: 0,
},
touchStartPosition
),
],
});
DomEventSimulator.fireTouchMove(element, {
changedTouches: [
combine(
{
identifier: 0,
},
touchMovePosition
),
],
});
}
}
simulateInput();
expect(action.calls.count()).toEqual(1);
expect(action).toHaveBeenCalledWith({
startPosition: new Cartesian2(1, 2),
endPosition: new Cartesian2(10, 11),
});
// should not be fired after removal
action.calls.reset();
handler.removeInputAction(eventType);
simulateInput();
expect(action).not.toHaveBeenCalled();
});
it("handles touch end", function () {
var eventType = ScreenSpaceEventType.LEFT_UP;
var action = createCloningSpy("action");
handler.setInputAction(action, eventType);
expect(handler.getInputAction(eventType)).toEqual(action);
// start, then end
function simulateInput() {
var touchStartPosition = {
clientX: 1,
clientY: 2,
};
var touchEndPosition = {
clientX: 1,
clientY: 2,
};
if (usePointerEvents) {
DomEventSimulator.firePointerDown(
element,
combine(
{
pointerType: "touch",
pointerId: 1,
},
touchStartPosition
)
);
DomEventSimulator.firePointerUp(
element,
combine(
{
pointerType: "touch",
pointerId: 1,
},
touchEndPosition
)
);
} else {
DomEventSimulator.fireTouchStart(element, {
changedTouches: [
combine(
{
identifier: 0,
},
touchStartPosition
),
],
});
DomEventSimulator.fireTouchEnd(element, {
changedTouches: [
combine(
{
identifier: 0,
},
touchEndPosition
),
],
});
}
}
simulateInput();
expect(action.calls.count()).toEqual(1);
expect(action).toHaveBeenCalledWith({
position: new Cartesian2(1, 2),
});
action.calls.reset();
// start, move, then end
function simulateInput2() {
var touchStartPosition = {
clientX: 1,
clientY: 2,
};
var touchMovePosition = {
clientX: 10,
clientY: 11,
};
var touchEndPosition = {
clientX: 10,
clientY: 11,
};
if (usePointerEvents) {
DomEventSimulator.firePointerDown(
element,
combine(
{
pointerType: "touch",
pointerId: 1,
},
touchStartPosition
)
);
DomEventSimulator.firePointerMove(
element,
combine(
{
pointerType: "touch",
pointerId: 1,
},
touchMovePosition
)
);
DomEventSimulator.firePointerUp(
element,
combine(
{
pointerType: "touch",
pointerId: 1,
},
touchEndPosition
)
);
} else {
DomEventSimulator.fireTouchStart(element, {
changedTouches: [
combine(
{
identifier: 0,
},
touchStartPosition
),
],
});
DomEventSimulator.fireTouchMove(element, {
changedTouches: [
combine(
{
identifier: 0,
},
touchMovePosition
),
],
});
DomEventSimulator.fireTouchEnd(element, {
changedTouches: [
combine(
{
identifier: 0,
},
touchEndPosition
),
],
});
}
}
simulateInput2();
expect(action.calls.count()).toEqual(1);
expect(action).toHaveBeenCalledWith({
position: new Cartesian2(10, 11),
});
// should not be fired after removal
action.calls.reset();
handler.removeInputAction(eventType);
simulateInput();
simulateInput2();
expect(action).not.toHaveBeenCalled();
});
it("treats touch end as touch cancel", function () {
var eventType = ScreenSpaceEventType.LEFT_UP;
var action = createCloningSpy("action");
handler.setInputAction(action, eventType);
expect(handler.getInputAction(eventType)).toEqual(action);
// start, then end
function simulateInput() {
var touchStartPosition = {
clientX: 1,
clientY: 2,
};
var touchEndPosition = {
clientX: 1,
clientY: 2,
};
if (usePointerEvents) {
DomEventSimulator.firePointerDown(
element,
combine(
{
pointerType: "touch",
pointerId: 1,
},
touchStartPosition
)
);
DomEventSimulator.firePointerCancel(
element,
combine(
{
pointerType: "touch",
pointerId: 1,
},
touchEndPosition
)
);
} else {
DomEventSimulator.fireTouchStart(element, {
changedTouches: [
combine(
{
identifier: 0,
},
touchStartPosition
),
],
});
DomEventSimulator.fireTouchCancel(element, {
changedTouches: [
combine(
{
identifier: 0,
},
touchEndPosition
),
],
});
}
}
simulateInput();
expect(action.calls.count()).toEqual(1);
expect(action).toHaveBeenCalledWith({
position: new Cartesian2(1, 2),
});
action.calls.reset();
// start, move, then end
function simulateInput2() {
var touchStartPosition = {
clientX: 1,
clientY: 2,
};
var touchMovePosition = {
clientX: 10,
clientY: 11,
};
var touchEndPosition = {
clientX: 10,
clientY: 11,
};
if (usePointerEvents) {
DomEventSimulator.firePointerDown(
element,
combine(
{
pointerType: "touch",
pointerId: 1,
},
touchStartPosition
)
);
DomEventSimulator.firePointerMove(
element,
combine(
{
pointerType: "touch",
pointerId: 1,
},
touchMovePosition
)
);
DomEventSimulator.firePointerCancel(
element,
combine(
{
pointerType: "touch",
pointerId: 1,
},
touchEndPosition
)
);
} else {
DomEventSimulator.fireTouchStart(element, {
changedTouches: [
combine(
{
identifier: 0,
},
touchStartPosition
),
],
});
DomEventSimulator.fireTouchMove(element, {
changedTouches: [
combine(
{
identifier: 0,
},
touchMovePosition
),
],
});
DomEventSimulator.fireTouchCancel(element, {
changedTouches: [
combine(
{
identifier: 0,
},
touchEndPosition
),
],
});
}
}
simulateInput2();
expect(action.calls.count()).toEqual(1);
expect(action).toHaveBeenCalledWith({
position: new Cartesian2(10, 11),
});
// should not be fired after removal
action.calls.reset();
handler.removeInputAction(eventType);
simulateInput();
simulateInput2();
expect(action).not.toHaveBeenCalled();
});
it("handles touch pinch start", function () {
var eventType = ScreenSpaceEventType.PINCH_START;
var action = createCloningSpy("action");
handler.setInputAction(action, eventType);
expect(handler.getInputAction(eventType)).toEqual(action);
// touch 1, then touch 2
function simulateInput() {
var touch1StartPosition = {
clientX: 1,
clientY: 2,
};
var touch2StartPosition = {
clientX: 3,
clientY: 4,
};
if (usePointerEvents) {
DomEventSimulator.firePointerDown(
element,
combine(
{
pointerType: "touch",
pointerId: 1,
},
touch1StartPosition
)
);
DomEventSimulator.firePointerDown(
element,
combine(
{
pointerType: "touch",
pointerId: 2,
},
touch2StartPosition
)
);
} else {
DomEventSimulator.fireTouchStart(element, {
changedTouches: [
combine(
{
identifier: 0,
},
touch1StartPosition
),
],
});
DomEventSimulator.fireTouchStart(element, {
changedTouches: [
combine(
{
identifier: 0,
},
touch1StartPosition
),
combine(
{
identifier: 1,
},
touch2StartPosition
),
],
});
}
}
simulateInput();
expect(action.calls.count()).toEqual(1);
expect(action).toHaveBeenCalledWith({
position1: new Cartesian2(1, 2),
position2: new Cartesian2(3, 4),
});
// should not be fired after removal
action.calls.reset();
handler.removeInputAction(eventType);
simulateInput();
expect(action).not.toHaveBeenCalled();
});
it("handles touch pinch move", function () {
var eventType = ScreenSpaceEventType.PINCH_MOVE;
var action = createCloningSpy("action");
handler.setInputAction(action, eventType);
expect(handler.getInputAction(eventType)).toEqual(action);
// touch 1, then touch 2, then move
function simulateInput() {
var touch1StartPosition = {
clientX: 1,
clientY: 2,
};
var touch2StartPosition = {
clientX: 4,
clientY: 3,
};
var touch1MovePosition = {
clientX: 10,
clientY: 11,
};
var touch2MovePosition = {
clientX: 21,
clientY: 20,
};
if (usePointerEvents) {
DomEventSimulator.firePointerDown(
element,
combine(
{
pointerType: "touch",
pointerId: 1,
},
touch1StartPosition
)
);
DomEventSimulator.firePointerDown(
element,
combine(
{
pointerType: "touch",
pointerId: 2,
},
touch2StartPosition
)
);
DomEventSimulator.firePointerMove(
element,
combine(
{
pointerType: "touch",
pointerId: 1,
},
touch1MovePosition
)
);
DomEventSimulator.firePointerMove(
element,
combine(
{
pointerType: "touch",
pointerId: 2,
},
touch2MovePosition
)
);
} else {
DomEventSimulator.fireTouchStart(element, {
changedTouches: [
combine(
{
identifier: 0,
},
touch1StartPosition
),
],
});
DomEventSimulator.fireTouchStart(element, {
changedTouches: [
combine(
{
identifier: 0,
},
touch1StartPosition
),
combine(
{
identifier: 1,
},
touch2StartPosition
),
],
});
DomEventSimulator.fireTouchMove(element, {
changedTouches: [
combine(
{
identifier: 0,
},
touch1MovePosition
),
combine(
{
identifier: 1,
},
touch2MovePosition
),
],
});
}
}
simulateInput();
// original delta X: 3
// original delta Y: 1
// final delta X: 11
// final delta Y: 9
if (usePointerEvents) {
// because every pointer event is separate, one touch moves, then the other.
expect(action.calls.count()).toEqual(2);
// intermediate delta X: -6
// intermediate delta Y: -8
expect(action).toHaveBeenCalledWith({
distance: {
startPosition: new Cartesian2(0, Math.sqrt(3 * 3 + 1 * 1) * 0.25),
endPosition: new Cartesian2(0, Math.sqrt(-6 * -6 + -8 * -8) * 0.25),
},
angleAndHeight: {
startPosition: new Cartesian2(Math.atan2(1, 3), (3 + 2) * 0.125),
endPosition: new Cartesian2(Math.atan2(-8, -6), (3 + 11) * 0.125),
},
});
expect(action).toHaveBeenCalledWith({
distance: {
startPosition: new Cartesian2(0, Math.sqrt(-6 * -6 + -8 * -8) * 0.25),
endPosition: new Cartesian2(0, Math.sqrt(11 * 11 + 9 * 9) * 0.25),
},
angleAndHeight: {
startPosition: new Cartesian2(Math.atan2(-8, -6), (3 + 11) * 0.125),
endPosition: new Cartesian2(Math.atan2(9, 11), (11 + 20) * 0.125),
},
});
} else {
// touch events can move both touches simultaneously
expect(action.calls.count()).toEqual(1);
expect(action).toHaveBeenCalledWith({
distance: {
startPosition: new Cartesian2(0, Math.sqrt(3 * 3 + 1 * 1) * 0.25),
endPosition: new Cartesian2(0, Math.sqrt(11 * 11 + 9 * 9) * 0.25),
},
angleAndHeight: {
startPosition: new Cartesian2(Math.atan2(1, 3), (3 + 2) * 0.125),
endPosition: new Cartesian2(Math.atan2(9, 11), (11 + 20) * 0.125),
},
});
}
// should not be fired after removal
action.calls.reset();
handler.removeInputAction(eventType);
simulateInput();
expect(action).not.toHaveBeenCalled();
});
it("handles touch pinch release", function () {
var leftDownEventType = ScreenSpaceEventType.LEFT_DOWN;
var leftDownAction = createCloningSpy("LEFT_DOWN");
handler.setInputAction(leftDownAction, leftDownEventType);
var pinchStartEventType = ScreenSpaceEventType.PINCH_START;
var pinchStartAction = createCloningSpy("PINCH_START");
handler.setInputAction(pinchStartAction, pinchStartEventType);
var pinchEndEventType = ScreenSpaceEventType.PINCH_END;
var pinchEndAction = createCloningSpy("PINCH_END");
handler.setInputAction(pinchEndAction, pinchEndEventType);
var touch1Position = {
clientX: 1,
clientY: 2,
};
var touch2Position = {
clientX: 4,
clientY: 3,
};
if (usePointerEvents) {
DomEventSimulator.firePointerDown(
element,
combine(
{
pointerType: "touch",
pointerId: 1,
},
touch1Position
)
);
DomEventSimulator.firePointerDown(
element,
combine(
{
pointerType: "touch",
pointerId: 2,
},
touch2Position
)
);
// Releasing one of two fingers should not trigger
// PINCH_END or LEFT_DOWN:
leftDownAction.calls.reset();
DomEventSimulator.firePointerUp(
element,
combine(
{
pointerType: "touch",
pointerId: 1,
},
touch1Position
)
);
expect(pinchEndAction).not.toHaveBeenCalled();
expect(leftDownAction).not.toHaveBeenCalled();
pinchEndAction.calls.reset();
// Putting another finger down should not trigger
// PINCH_START:
pinchStartAction.calls.reset();
DomEventSimulator.firePointerDown(
element,
combine(
{
pointerType: "touch",
pointerId: 1,
},
touch2Position
)
);
expect(pinchStartAction).not.toHaveBeenCalled();
// Releasing both fingers should trigger PINCH_END:
DomEventSimulator.firePointerUp(
element,
combine(
{
pointerType: "touch",
pointerId: 1,
},
touch2Position
)
);
DomEventSimulator.firePointerUp(
element,
combine(
{
pointerType: "touch",
pointerId: 2,
},
touch2Position
)
);
expect(pinchEndAction).toHaveBeenCalled();
} else {
DomEventSimulator.fireTouchStart(element, {
changedTouches: [
combine({ identifier: 0 }, touch1Position),
combine({ identifier: 1 }, touch2Position),
],
});
leftDownAction.calls.reset();
// Releasing one of two fingers should not trigger
// PINCH_END or LEFT_DOWN:
DomEventSimulator.fireTouchEnd(element, {
changedTouches: [combine({ identifier: 0 }, touch1Position)],
});
expect(pinchEndAction).not.toHaveBeenCalled();
expect(leftDownAction).not.toHaveBeenCalled();
// Releasing both fingers should trigger PINCH_END:
pinchEndAction.calls.reset();
DomEventSimulator.fireTouchEnd(element, {
changedTouches: [combine({ identifier: 1 }, touch2Position)],
});
expect(pinchEndAction).toHaveBeenCalled();
}
});
it("handles touch click", function () {
var eventType = ScreenSpaceEventType.LEFT_CLICK;
var action = createCloningSpy("action");
handler.setInputAction(action, eventType);
expect(handler.getInputAction(eventType)).toEqual(action);
// start, then end
function simulateInput() {
var touchStartPosition = {
clientX: 1,
clientY: 2,
};
var touchEndPosition = {
clientX: 1,
clientY: 2,
};
if (usePointerEvents) {
DomEventSimulator.firePointerDown(
element,
combine(
{
pointerType: "touch",
pointerId: 1,
},
touchStartPosition
)
);
DomEventSimulator.firePointerUp(
element,
combine(
{
pointerType: "touch",
pointerId: 1,
},
touchEndPosition
)
);
} else {
DomEventSimulator.fireTouchStart(element, {
changedTouches: [
combine(
{
identifier: 0,
},
touchStartPosition
),
],
});
DomEventSimulator.fireTouchEnd(element, {
changedTouches: [
combine(
{
identifier: 0,
},
touchEndPosition
),
],
});
}
}
simulateInput();
expect(action.calls.count()).toEqual(1);
expect(action).toHaveBeenCalledWith({
position: new Cartesian2(1, 2),
});
// should not be fired after removal
action.calls.reset();
handler.removeInputAction(eventType);
simulateInput();
expect(action).not.toHaveBeenCalled();
});
it("handles touch and hold gesture", function () {
jasmine.clock().install();
var delay = ScreenSpaceEventHandler.touchHoldDelayMilliseconds;
var eventType = ScreenSpaceEventType.RIGHT_CLICK;
var action = createCloningSpy("action");
handler.setInputAction(action, eventType);
expect(handler.getInputAction(eventType)).toEqual(action);
// start, then end
function simulateInput(timeout) {
var touchStartPosition = {
clientX: 1,
clientY: 2,
};
var touchEndPosition = {
clientX: 1,
clientY: 2,
};
if (usePointerEvents) {
DomEventSimulator.firePointerDown(
element,
combine(
{
pointerType: "touch",
pointerId: 1,
},
touchStartPosition
)
);
jasmine.clock().tick(timeout);
DomEventSimulator.firePointerUp(
element,
combine(
{
pointerType: "touch",
pointerId: 1,
},
touchEndPosition
)
);
} else {
DomEventSimulator.fireTouchStart(element, {
changedTouches: [
combine(
{
identifier: 0,
},
touchStartPosition
),
],
});
jasmine.clock().tick(timeout);
DomEventSimulator.fireTouchEnd(element, {
changedTouches: [
combine(
{
identifier: 0,
},
touchEndPosition
),
],
});
}
}
simulateInput(delay + 1);
expect(action.calls.count()).toEqual(1);
expect(action).toHaveBeenCalledWith({
position: new Cartesian2(1, 2),
});
// Should not be fired if hold delay is less than touchHoldDelayMilliseconds.
action.calls.reset();
simulateInput(delay - 1);
expect(action).not.toHaveBeenCalled();
// Should not be fired after removal.
action.calls.reset();
handler.removeInputAction(eventType);
simulateInput(delay + 1);
expect(action).not.toHaveBeenCalled();
// Should not fire click action if touch and hold is triggered.
eventType = ScreenSpaceEventType.LEFT_CLICK;
handler.setInputAction(action, eventType);
simulateInput(delay + 1);
expect(action).not.toHaveBeenCalled();
jasmine.clock().uninstall();
});
it("treats touch cancel as touch end for touch clicks", function () {
var eventType = ScreenSpaceEventType.LEFT_CLICK;
var action = createCloningSpy("action");
handler.setInputAction(action, eventType);
expect(handler.getInputAction(eventType)).toEqual(action);
// start, then end
function simulateInput() {
var touchStartPosition = {
clientX: 1,
clientY: 2,
};
var touchEndPosition = {
clientX: 1,
clientY: 2,
};
if (usePointerEvents) {
DomEventSimulator.firePointerDown(
element,
combine(
{
pointerType: "touch",
pointerId: 1,
},
touchStartPosition
)
);
DomEventSimulator.firePointerCancel(
element,
combine(
{
pointerType: "touch",
pointerId: 1,
},
touchEndPosition
)
);
} else {
DomEventSimulator.fireTouchStart(element, {
changedTouches: [
combine(
{
identifier: 0,
},
touchStartPosition
),
],
});
DomEventSimulator.fireTouchCancel(element, {
changedTouches: [
combine(
{
identifier: 0,
},
touchEndPosition
),
],
});
}
}
simulateInput();
expect(action.calls.count()).toEqual(1);
expect(action).toHaveBeenCalledWith({
position: new Cartesian2(1, 2),
});
// should not be fired after removal
action.calls.reset();
handler.removeInputAction(eventType);
simulateInput();
expect(action).not.toHaveBeenCalled();
});
it("sets isDestroyed when destroyed", function () {
expect(handler.isDestroyed()).toEqual(false);
handler.destroy();
expect(handler.isDestroyed()).toEqual(true);
});
it("unregisters event listeners when destroyed", function () {
handler = handler.destroy();
spyOn(element, "addEventListener").and.callThrough();
spyOn(element, "removeEventListener").and.callThrough();
handler = new ScreenSpaceEventHandler(element);
expect(element.addEventListener.calls.count()).not.toEqual(0);
expect(element.removeEventListener.calls.count()).toEqual(0);
handler.destroy();
expect(element.removeEventListener.calls.count()).toEqual(
element.addEventListener.calls.count()
);
});
});