Merge pull request #1840 from tsauerwein/pointerevents

Use pointer events
This commit is contained in:
Éric Lemoine
2014-03-14 11:13:24 +01:00
39 changed files with 3220 additions and 1158 deletions

View File

@@ -51,8 +51,8 @@ var map = new ol.Map({
layers: [vector],
interactions: ol.interaction.defaults({
altShiftDragRotate: false,
dragPan: false,
touchRotate: false
pan: false,
rotate: false
}).extend([new ol.interaction.DragPan({kinetic: false})]),
renderer: 'canvas',
target: olMapDiv,

View File

@@ -1,3 +1,4 @@
goog.require('ol.Collection');
goog.require('ol.Map');
goog.require('ol.View2D');
goog.require('ol.interaction');

View File

@@ -369,6 +369,30 @@
* supported.
*/
/**
* Interactions for the map. Default is `true` for all options.
* @typedef {Object} olx.interaction.DefaultsOptions
* @property {boolean|undefined} altShiftDragRotate Whether Alt-Shift-drag
* rotate is desired. Default is `true`.
* @property {boolean|undefined} doubleClickZoom Whether double click zoom is
* desired. Default is `true`.
* @property {boolean|undefined} keyboard Whether keyboard interaction is
* desired. Default is `true`.
* @property {boolean|undefined} mouseWheelZoom Whether mousewheel zoom is
* desired. Default is `true`.
* @property {boolean|undefined} shiftDragZoom Whether Shift-drag zoom is
* desired. Default is `true`.
* @property {boolean|undefined} dragPan Whether drag pan is
* desired. Default is `true`.
* @property {boolean|undefined} pinchRotate Whether pinch rotate is
* desired. Default is `true`.
* @property {boolean|undefined} pinchZoom Whether pinch zoom is
* desired. Default is `true`.
* @property {number|undefined} zoomDelta Zoom delta.
* @property {number|undefined} zoomDuration Zoom duration.
* @todo stability experimental
*/
/**
* @typedef {Object} olx.interaction.DoubleClickZoomOptions
* @property {number|undefined} duration Animation duration in milliseconds. Default is `250`.
@@ -386,19 +410,18 @@
*/
/**
* @typedef {Object} olx.interaction.DragPanOptions
* @property {ol.Kinetic|undefined} kinetic Kinetic inertia to apply to the pan.
* @typedef {Object} olx.interaction.DragBoxOptions
* @property {ol.events.ConditionType|undefined} condition A conditional
* modifier (i.e. Shift key) that determines if the interaction is active
* or not, default is no modifiers.
* or not, default is always.
* @property {ol.style.Style} style Style for the box.
* @todo stability experimental
*/
/**
* @typedef {Object} olx.interaction.DragRotateOptions
* @property {ol.events.ConditionType|undefined} condition A conditional
* modifier (i.e. Shift key) that determines if the interaction is active
* or not, default is both shift and alt keys.
* @typedef {Object} olx.interaction.DragPanOptions
* @property {ol.Kinetic|undefined} kinetic Kinetic inertia to apply to the
* pan.
* @todo stability experimental
*/
@@ -411,11 +434,10 @@
*/
/**
* @typedef {Object} olx.interaction.DragBoxOptions
* @typedef {Object} olx.interaction.DragRotateOptions
* @property {ol.events.ConditionType|undefined} condition A conditional
* modifier (i.e. Shift key) that determines if the interaction is active
* or not, default is always.
* @property {ol.style.Style} style Style for the box.
* or not, default is both shift and alt keys.
* @todo stability experimental
*/
@@ -428,29 +450,6 @@
* @todo stability experimental
*/
/**
* Interactions for the map. Default is `true` for all options.
* @typedef {Object} olx.interaction.DefaultsOptions
* @property {boolean|undefined} altShiftDragRotate Whether Alt-Shift-drag
* rotate is desired. Default is `true`.
* @property {boolean|undefined} doubleClickZoom Whether double click zoom is
* desired. Default is `true`.
* @property {boolean|undefined} dragPan Whether drag-pan is desired. Default is `true`.
* @property {boolean|undefined} keyboard Whether keyboard interaction is
* desired. Default is `true`.
* @property {boolean|undefined} mouseWheelZoom Whether mousewheel zoom is
* desired. Default is `true`.
* @property {boolean|undefined} shiftDragZoom Whether Shift-drag zoom is
* desired. Default is `true`.
* @property {boolean|undefined} touchPan Whether touch pan is
* desired. Default is `true`.
* @property {boolean|undefined} touchRotate Whether touch rotate is desired. Default is `true`.
* @property {boolean|undefined} touchZoom Whether touch zoom is desired. Default is `true`.
* @property {number|undefined} zoomDelta Zoom delta.
* @property {number|undefined} zoomDuration Zoom duration.
* @todo stability experimental
*/
/**
* @typedef {Object} olx.interaction.DrawOptions
* @property {ol.Collection|undefined} features Destination collection for the drawn features.
@@ -486,12 +485,33 @@
* @todo stability experimental
*/
/**
* @typedef {Object} olx.interaction.ModifyOptions
* @property {number|undefined} pixelTolerance Pixel tolerance for considering
* the pointer close enough to a vertex for editing. Default is 20 pixels.
* @property {ol.style.Style|Array.<ol.style.Style>|ol.feature.StyleFunction|undefined} style FeatureOverlay style.
* @property {ol.Collection} features The features the interaction works on.
*/
/**
* @typedef {Object} olx.interaction.MouseWheelZoomOptions
* @property {number|undefined} duration Animation duration in milliseconds. Default is `250`.
* @todo stability experimental
*/
/**
* @typedef {Object} olx.interaction.PinchRotateOptions
* @property {number|undefined} threshold Minimal angle in radians to start a rotation.
* Default is `0.3`.
* @todo stability experimental
*/
/**
* @typedef {Object} olx.interaction.PinchZoomOptions
* @property {number|undefined} duration Animation duration in milliseconds. Default is `400`.
* @todo stability experimental
*/
/**
* @typedef {Object} olx.interaction.SelectOptions
* @property {ol.events.ConditionType|undefined} addCondition A conditional
@@ -511,34 +531,6 @@
* @property {ol.style.Style|Array.<ol.style.Style>|ol.feature.StyleFunction|undefined} style FeatureOverlay style.
*/
/**
* @typedef {Object} olx.interaction.TouchPanOptions
* @property {ol.Kinetic|undefined} kinetic Kinetic inertia to apply to the
* pan.
* @todo stability experimental
*/
/**
* @typedef {Object} olx.interaction.ModifyOptions
* @property {number|undefined} pixelTolerance Pixel tolerance for considering
* the pointer close enough to a vertex for editing. Default is 20 pixels.
* @property {ol.style.Style|Array.<ol.style.Style>|ol.feature.StyleFunction|undefined} style FeatureOverlay style.
* @property {ol.Collection} features The features the interaction works on.
*/
/**
* @typedef {Object} olx.interaction.TouchRotateOptions
* @property {number|undefined} threshold Minimal angle in radians to start a rotation.
* Default is `0.3`.
* @todo stability experimental
*/
/**
* @typedef {Object} olx.interaction.TouchZoomOptions
* @property {number|undefined} duration Animation duration in milliseconds. Default is `400`.
* @todo stability experimental
*/
/**
* @typedef {Object} olx.layer.BaseOptions
* @property {number|undefined} brightness Brightness. Default is `0`.

View File

@@ -159,7 +159,25 @@ ol.BrowserFeature.HAS_GEOLOCATION = 'geolocation' in goog.global.navigator;
*/
ol.BrowserFeature.HAS_TOUCH = ol.ASSUME_TOUCH ||
(goog.global.document &&
'ontouchstart' in goog.global.document.documentElement) ||
'ontouchstart' in goog.global.document.documentElement);
/**
* True if browser supports pointer events.
* @const
* @type {boolean}
* @todo stability experimental
*/
ol.BrowserFeature.HAS_POINTER = 'PointerEvent' in goog.global;
/**
* True if browser supports ms pointer events (IE 10).
* @const
* @type {boolean}
* @todo stability experimental
*/
ol.BrowserFeature.HAS_MSPOINTER =
!!(goog.global.navigator.msPointerEnabled);

View File

@@ -5,6 +5,7 @@ goog.require('goog.asserts');
goog.require('goog.dom.TagName');
goog.require('goog.functions');
goog.require('ol.MapBrowserEvent.EventType');
goog.require('ol.MapBrowserPointerEvent');
/**
@@ -120,3 +121,17 @@ ol.events.condition.targetNotEditable = function(mapBrowserEvent) {
tagName !== goog.dom.TagName.SELECT &&
tagName !== goog.dom.TagName.TEXTAREA);
};
/**
* @param {ol.MapBrowserEvent} mapBrowserEvent Map browser event.
* @return {boolean} True if the event originates from a mouse device.
* @todo stability experimental
*/
ol.events.condition.mouseOnly = function(mapBrowserEvent) {
goog.asserts.assertInstanceof(mapBrowserEvent, ol.MapBrowserPointerEvent);
/* pointerId must be 1 for mouse devices,
* see: http://www.w3.org/Submission/pointer-events/#pointerevent-interface
*/
return mapBrowserEvent.pointerEvent.pointerId == 1;
};

View File

@@ -8,7 +8,7 @@ goog.require('goog.asserts');
goog.require('goog.events.Event');
goog.require('ol.events.ConditionType');
goog.require('ol.events.condition');
goog.require('ol.interaction.Drag');
goog.require('ol.interaction.Pointer');
goog.require('ol.render.Box');
@@ -62,8 +62,14 @@ goog.inherits(ol.DragBoxEvent, goog.events.Event);
/**
* Allows the user to zoom the map by clicking and dragging on the map,
* normally combined with an {@link ol.events.condition} that limits
* it to when the shift key is held down.
*
* This interaction is only supported for mouse devices.
*
* @constructor
* @extends {ol.interaction.Drag}
* @extends {ol.interaction.Pointer}
* @param {olx.interaction.DragBoxOptions=} opt_options Options.
* @todo stability experimental
*/
@@ -99,13 +105,17 @@ ol.interaction.DragBox = function(opt_options) {
options.condition : ol.events.condition.always;
};
goog.inherits(ol.interaction.DragBox, ol.interaction.Drag);
goog.inherits(ol.interaction.DragBox, ol.interaction.Pointer);
/**
* @inheritDoc
*/
ol.interaction.DragBox.prototype.handleDrag = function(mapBrowserEvent) {
ol.interaction.DragBox.prototype.handlePointerDrag = function(mapBrowserEvent) {
if (!ol.events.condition.mouseOnly(mapBrowserEvent)) {
return;
}
this.box_.setPixels(this.startPixel_, mapBrowserEvent.pixel);
};
@@ -129,23 +139,36 @@ ol.interaction.DragBox.prototype.onBoxEnd = goog.nullFunction;
/**
* @inheritDoc
*/
ol.interaction.DragBox.prototype.handleDragEnd =
ol.interaction.DragBox.prototype.handlePointerUp =
function(mapBrowserEvent) {
if (!ol.events.condition.mouseOnly(mapBrowserEvent)) {
return true;
}
this.box_.setMap(null);
if (this.deltaX * this.deltaX + this.deltaY * this.deltaY >=
var deltaX = mapBrowserEvent.pixel[0] - this.startPixel_[0];
var deltaY = mapBrowserEvent.pixel[1] - this.startPixel_[1];
if (deltaX * deltaX + deltaY * deltaY >=
ol.DRAG_BOX_HYSTERESIS_PIXELS_SQUARED) {
this.onBoxEnd(mapBrowserEvent);
this.dispatchEvent(new ol.DragBoxEvent(ol.DragBoxEventType.BOXEND,
mapBrowserEvent.coordinate));
}
return false;
};
/**
* @inheritDoc
*/
ol.interaction.DragBox.prototype.handleDragStart =
ol.interaction.DragBox.prototype.handlePointerDown =
function(mapBrowserEvent) {
if (!ol.events.condition.mouseOnly(mapBrowserEvent)) {
return false;
}
var browserEvent = mapBrowserEvent.browserEvent;
if (browserEvent.isMouseActionButton() && this.condition_(mapBrowserEvent)) {
this.startPixel_ = mapBrowserEvent.pixel;

View File

@@ -1,145 +0,0 @@
goog.provide('ol.interaction.Drag');
goog.require('goog.asserts');
goog.require('goog.events.BrowserEvent');
goog.require('goog.functions');
goog.require('ol.Coordinate');
goog.require('ol.MapBrowserEvent');
goog.require('ol.MapBrowserEvent.EventType');
goog.require('ol.interaction.Interaction');
/**
* Base class for interactions that drag the map.
* @constructor
* @extends {ol.interaction.Interaction}
*/
ol.interaction.Drag = function() {
goog.base(this);
/**
* @private
* @type {boolean}
*/
this.dragging_ = false;
/**
* @type {number}
*/
this.startX = 0;
/**
* @type {number}
*/
this.startY = 0;
/**
* @type {number}
*/
this.deltaX = 0;
/**
* @type {number}
*/
this.deltaY = 0;
/**
* @type {ol.Coordinate}
*/
this.startCenter = null;
/**
* @type {ol.Coordinate}
*/
this.startCoordinate = null;
};
goog.inherits(ol.interaction.Drag, ol.interaction.Interaction);
/**
* @return {boolean} Whether we're dragging.
*/
ol.interaction.Drag.prototype.getDragging = function() {
return this.dragging_;
};
/**
* @param {ol.MapBrowserEvent} mapBrowserEvent Event.
* @protected
*/
ol.interaction.Drag.prototype.handleDrag = goog.nullFunction;
/**
* @param {ol.MapBrowserEvent} mapBrowserEvent Event.
* @protected
*/
ol.interaction.Drag.prototype.handleDragEnd = goog.nullFunction;
/**
* @param {ol.MapBrowserEvent} mapBrowserEvent Event.
* @protected
* @return {boolean} Capture dragging.
*/
ol.interaction.Drag.prototype.handleDragStart = goog.functions.FALSE;
/**
* @param {ol.MapBrowserEvent} mapBrowserEvent Event.
* @protected
*/
ol.interaction.Drag.prototype.handleDown = goog.nullFunction;
/**
* @inheritDoc
*/
ol.interaction.Drag.prototype.handleMapBrowserEvent =
function(mapBrowserEvent) {
var map = mapBrowserEvent.map;
if (!map.isDef()) {
return true;
}
var stopEvent = false;
var view = map.getView();
var browserEvent = mapBrowserEvent.browserEvent;
if (mapBrowserEvent.type == ol.MapBrowserEvent.EventType.DOWN) {
goog.asserts.assertInstanceof(browserEvent, goog.events.BrowserEvent);
this.handleDown(mapBrowserEvent);
}
if (this.dragging_) {
if (mapBrowserEvent.type == ol.MapBrowserEvent.EventType.DRAG) {
goog.asserts.assertInstanceof(browserEvent, goog.events.BrowserEvent);
this.deltaX = browserEvent.clientX - this.startX;
this.deltaY = browserEvent.clientY - this.startY;
this.handleDrag(mapBrowserEvent);
} else if (mapBrowserEvent.type == ol.MapBrowserEvent.EventType.DRAGEND) {
goog.asserts.assertInstanceof(browserEvent, goog.events.BrowserEvent);
this.deltaX = browserEvent.clientX - this.startX;
this.deltaY = browserEvent.clientY - this.startY;
this.dragging_ = false;
this.handleDragEnd(mapBrowserEvent);
}
} else if (mapBrowserEvent.type == ol.MapBrowserEvent.EventType.DRAGSTART) {
goog.asserts.assertInstanceof(browserEvent, goog.events.BrowserEvent);
var view2DState = view.getView2D().getView2DState();
this.startX = browserEvent.clientX;
this.startY = browserEvent.clientY;
this.deltaX = 0;
this.deltaY = 0;
this.startCenter = view2DState.center;
this.startCoordinate = mapBrowserEvent.coordinate;
var handled = this.handleDragStart(mapBrowserEvent);
if (handled) {
this.dragging_ = true;
mapBrowserEvent.preventDefault();
stopEvent = true;
}
}
return !stopEvent;
};

View File

@@ -1,23 +1,22 @@
// FIXME works for View2D only
goog.provide('ol.interaction.DragPan');
goog.require('goog.asserts');
goog.require('ol.Kinetic');
goog.require('ol.Pixel');
goog.require('ol.PreRenderFunction');
goog.require('ol.View2D');
goog.require('ol.ViewHint');
goog.require('ol.coordinate');
goog.require('ol.events.ConditionType');
goog.require('ol.events.condition');
goog.require('ol.interaction.Drag');
goog.require('ol.interaction.Pointer');
/**
* Allows the user to pan the map by clickng and dragging.
* Allows the user to pan the map by dragging the map.
* @constructor
* @extends {ol.interaction.Drag}
* @extends {ol.interaction.Pointer}
* @param {olx.interaction.DragPanOptions=} opt_options Options.
* @todo stability experimental
*/
@@ -27,13 +26,6 @@ ol.interaction.DragPan = function(opt_options) {
var options = goog.isDef(opt_options) ? opt_options : {};
/**
* @private
* @type {ol.events.ConditionType}
*/
this.condition_ = goog.isDef(options.condition) ?
options.condition : ol.events.condition.noModifierKeys;
/**
* @private
* @type {ol.Kinetic|undefined}
@@ -46,101 +38,118 @@ ol.interaction.DragPan = function(opt_options) {
*/
this.kineticPreRenderFn_ = null;
/**
* @type {ol.Pixel}
*/
this.lastCentroid = null;
/**
* @private
* @type {ol.events.ConditionType}
*/
this.condition_ = goog.isDef(opt_options.condition) ?
opt_options.condition : ol.events.condition.noModifierKeys;
/**
* @private
* @type {boolean}
*/
this.noKinetic_ = false;
};
goog.inherits(ol.interaction.DragPan, ol.interaction.Drag);
goog.inherits(ol.interaction.DragPan, ol.interaction.Pointer);
/**
* @inheritDoc
*/
ol.interaction.DragPan.prototype.handleDrag = function(mapBrowserEvent) {
if (this.kinetic_) {
this.kinetic_.update(
mapBrowserEvent.browserEvent.clientX,
mapBrowserEvent.browserEvent.clientY);
}
var map = mapBrowserEvent.map;
// FIXME works for View2D only
var view = map.getView();
goog.asserts.assertInstanceof(view, ol.View2D);
var view2DState = view.getView2DState();
var newCenter = [
-view2DState.resolution * this.deltaX,
view2DState.resolution * this.deltaY
];
ol.coordinate.rotate(newCenter, view2DState.rotation);
ol.coordinate.add(newCenter, this.startCenter);
newCenter = view.constrainCenter(newCenter);
map.render();
view.setCenter(newCenter);
};
/**
* @inheritDoc
*/
ol.interaction.DragPan.prototype.handleDragEnd = function(mapBrowserEvent) {
// FIXME works for View2D only
var map = mapBrowserEvent.map;
var view = map.getView();
view.setHint(ol.ViewHint.INTERACTING, -1);
if (this.kinetic_ && this.kinetic_.end()) {
var view2D = view.getView2D();
ol.interaction.DragPan.prototype.handlePointerDrag = function(mapBrowserEvent) {
goog.asserts.assert(this.targetPointers.length >= 1);
var centroid =
ol.interaction.Pointer.centroid(this.targetPointers);
if (!goog.isNull(this.lastCentroid)) {
if (this.kinetic_) {
this.kinetic_.update(centroid[0], centroid[1]);
}
var deltaX = this.lastCentroid[0] - centroid[0];
var deltaY = centroid[1] - this.lastCentroid[1];
var map = mapBrowserEvent.map;
var view2D = map.getView().getView2D();
goog.asserts.assertInstanceof(view2D, ol.View2D);
var view2DState = view2D.getView2DState();
var distance = this.kinetic_.getDistance();
var angle = this.kinetic_.getAngle();
this.kineticPreRenderFn_ = this.kinetic_.pan(view2DState.center);
map.beforeRender(this.kineticPreRenderFn_);
var centerpx = map.getPixelFromCoordinate(view2DState.center);
var dest = map.getCoordinateFromPixel([
centerpx[0] - distance * Math.cos(angle),
centerpx[1] - distance * Math.sin(angle)
]);
dest = view2D.constrainCenter(dest);
view2D.setCenter(dest);
var center = [deltaX, deltaY];
ol.coordinate.scale(center, view2DState.resolution);
ol.coordinate.rotate(center, view2DState.rotation);
ol.coordinate.add(center, view2DState.center);
center = view2D.constrainCenter(center);
map.render();
view2D.setCenter(center);
}
map.render();
this.lastCentroid = centroid;
};
/**
* @inheritDoc
*/
ol.interaction.DragPan.prototype.handleDragStart = function(mapBrowserEvent) {
var browserEvent = mapBrowserEvent.browserEvent;
if (browserEvent.isMouseActionButton() && this.condition_(mapBrowserEvent)) {
ol.interaction.DragPan.prototype.handlePointerUp =
function(mapBrowserEvent) {
var map = mapBrowserEvent.map;
var view2D = map.getView().getView2D();
goog.asserts.assertInstanceof(view2D, ol.View2D);
if (this.targetPointers.length === 0) {
if (!this.noKinetic_ && this.kinetic_ && this.kinetic_.end()) {
var distance = this.kinetic_.getDistance();
var angle = this.kinetic_.getAngle();
var center = view2D.getCenter();
goog.asserts.assert(goog.isDef(center));
this.kineticPreRenderFn_ = this.kinetic_.pan(center);
map.beforeRender(this.kineticPreRenderFn_);
var centerpx = map.getPixelFromCoordinate(center);
var dest = map.getCoordinateFromPixel([
centerpx[0] - distance * Math.cos(angle),
centerpx[1] - distance * Math.sin(angle)
]);
dest = view2D.constrainCenter(dest);
view2D.setCenter(dest);
}
view2D.setHint(ol.ViewHint.INTERACTING, -1);
map.render();
return false;
} else {
this.lastCentroid = null;
return true;
}
};
/**
* @inheritDoc
*/
ol.interaction.DragPan.prototype.handlePointerDown =
function(mapBrowserEvent) {
if (this.targetPointers.length > 0 && this.condition_(mapBrowserEvent)) {
var map = mapBrowserEvent.map;
var view2D = map.getView().getView2D();
goog.asserts.assertInstanceof(view2D, ol.View2D);
this.lastCentroid = null;
if (!this.handlingDownUpSequence) {
view2D.setHint(ol.ViewHint.INTERACTING, 1);
}
map.render();
if (!goog.isNull(this.kineticPreRenderFn_) &&
map.removePreRenderFunction(this.kineticPreRenderFn_)) {
view2D.setCenter(mapBrowserEvent.frameState.view2DState.center);
this.kineticPreRenderFn_ = null;
}
if (this.kinetic_) {
this.kinetic_.begin();
this.kinetic_.update(browserEvent.clientX, browserEvent.clientY);
}
var map = mapBrowserEvent.map;
map.getView().setHint(ol.ViewHint.INTERACTING, 1);
map.render();
// No kinetic as soon as more than one pointer on the screen is
// detected. This is to prevent nasty pans after pinch.
this.noKinetic_ = this.targetPointers.length > 1;
return true;
} else {
return false;
}
};
/**
* @inheritDoc
*/
ol.interaction.DragPan.prototype.handleDown = function(mapBrowserEvent) {
var map = mapBrowserEvent.map;
// FIXME works for View2D only
var view = map.getView();
goog.asserts.assertInstanceof(view, ol.View2D);
goog.asserts.assert(!goog.isNull(mapBrowserEvent.frameState));
if (!goog.isNull(this.kineticPreRenderFn_) &&
map.removePreRenderFunction(this.kineticPreRenderFn_)) {
map.render();
view.setCenter(mapBrowserEvent.frameState.view2DState.center);
this.kineticPreRenderFn_ = null;
}
};

View File

@@ -7,8 +7,8 @@ goog.require('goog.math.Vec2');
goog.require('ol.ViewHint');
goog.require('ol.events.ConditionType');
goog.require('ol.events.condition');
goog.require('ol.interaction.Drag');
goog.require('ol.interaction.Interaction');
goog.require('ol.interaction.Pointer');
/**
@@ -23,9 +23,12 @@ ol.interaction.DRAGROTATEANDZOOM_ANIMATION_DURATION = 400;
* on the map. By default, this interaction is limited to when the shift
* key is held down.
*
* This interaction is not included in the default interactions.
* This interaction is only supported for mouse devices.
*
* And this interaction is not included in the default interactions.
*
* @constructor
* @extends {ol.interaction.Drag}
* @extends {ol.interaction.Pointer}
* @param {olx.interaction.DragRotateAndZoomOptions=} opt_options Options.
* @todo stability experimental
*/
@@ -61,14 +64,19 @@ ol.interaction.DragRotateAndZoom = function(opt_options) {
this.lastScaleDelta_ = 0;
};
goog.inherits(ol.interaction.DragRotateAndZoom, ol.interaction.Drag);
goog.inherits(ol.interaction.DragRotateAndZoom,
ol.interaction.Pointer);
/**
* @inheritDoc
*/
ol.interaction.DragRotateAndZoom.prototype.handleDrag =
ol.interaction.DragRotateAndZoom.prototype.handlePointerDrag =
function(mapBrowserEvent) {
if (!ol.events.condition.mouseOnly(mapBrowserEvent)) {
return;
}
var map = mapBrowserEvent.map;
var size = map.getSize();
var offset = mapBrowserEvent.pixel;
@@ -101,8 +109,12 @@ ol.interaction.DragRotateAndZoom.prototype.handleDrag =
/**
* @inheritDoc
*/
ol.interaction.DragRotateAndZoom.prototype.handleDragEnd =
ol.interaction.DragRotateAndZoom.prototype.handlePointerUp =
function(mapBrowserEvent) {
if (!ol.events.condition.mouseOnly(mapBrowserEvent)) {
return true;
}
var map = mapBrowserEvent.map;
// FIXME works for View2D only
var view = map.getView();
@@ -115,15 +127,19 @@ ol.interaction.DragRotateAndZoom.prototype.handleDragEnd =
undefined, ol.interaction.DRAGROTATEANDZOOM_ANIMATION_DURATION,
direction);
this.lastScaleDelta_ = 0;
return true;
return false;
};
/**
* @inheritDoc
*/
ol.interaction.DragRotateAndZoom.prototype.handleDragStart =
ol.interaction.DragRotateAndZoom.prototype.handlePointerDown =
function(mapBrowserEvent) {
if (!ol.events.condition.mouseOnly(mapBrowserEvent)) {
return false;
}
if (this.condition_(mapBrowserEvent)) {
mapBrowserEvent.map.getView().setHint(ol.ViewHint.INTERACTING, 1);
this.lastAngle_ = undefined;
@@ -133,3 +149,15 @@ ol.interaction.DragRotateAndZoom.prototype.handleDragStart =
return false;
}
};
/**
* @inheritDoc
*/
ol.interaction.DragRotateAndZoom.prototype.shouldStopEvent =
function(hasHandledEvent) {
/* Stop the event if it was handled, so that interaction `DragZoom`
* does not interfere.
*/
return hasHandledEvent;
};

View File

@@ -4,8 +4,8 @@ goog.require('goog.asserts');
goog.require('ol.ViewHint');
goog.require('ol.events.ConditionType');
goog.require('ol.events.condition');
goog.require('ol.interaction.Drag');
goog.require('ol.interaction.Interaction');
goog.require('ol.interaction.Pointer');
/**
@@ -19,8 +19,11 @@ ol.interaction.DRAGROTATE_ANIMATION_DURATION = 250;
* Allows the user to rotate the map by clicking and dragging on the map,
* normally combined with an {@link ol.events.condition} that limits
* it to when the alt and shift keys are held down.
*
* This interaction is only supported for mouse devices.
*
* @constructor
* @extends {ol.interaction.Drag}
* @extends {ol.interaction.Pointer}
* @param {olx.interaction.DragRotateOptions=} opt_options Options.
*/
ol.interaction.DragRotate = function(opt_options) {
@@ -43,13 +46,18 @@ ol.interaction.DragRotate = function(opt_options) {
this.lastAngle_ = undefined;
};
goog.inherits(ol.interaction.DragRotate, ol.interaction.Drag);
goog.inherits(ol.interaction.DragRotate, ol.interaction.Pointer);
/**
* @inheritDoc
*/
ol.interaction.DragRotate.prototype.handleDrag = function(mapBrowserEvent) {
ol.interaction.DragRotate.prototype.handlePointerDrag =
function(mapBrowserEvent) {
if (!ol.events.condition.mouseOnly(mapBrowserEvent)) {
return;
}
var map = mapBrowserEvent.map;
var size = map.getSize();
var offset = mapBrowserEvent.pixel;
@@ -71,7 +79,12 @@ ol.interaction.DragRotate.prototype.handleDrag = function(mapBrowserEvent) {
/**
* @inheritDoc
*/
ol.interaction.DragRotate.prototype.handleDragEnd = function(mapBrowserEvent) {
ol.interaction.DragRotate.prototype.handlePointerUp =
function(mapBrowserEvent) {
if (!ol.events.condition.mouseOnly(mapBrowserEvent)) {
return true;
}
var map = mapBrowserEvent.map;
// FIXME works for View2D only
var view = map.getView();
@@ -80,14 +93,19 @@ ol.interaction.DragRotate.prototype.handleDragEnd = function(mapBrowserEvent) {
var view2DState = view2D.getView2DState();
ol.interaction.Interaction.rotate(map, view2D, view2DState.rotation,
undefined, ol.interaction.DRAGROTATE_ANIMATION_DURATION);
return false;
};
/**
* @inheritDoc
*/
ol.interaction.DragRotate.prototype.handleDragStart =
ol.interaction.DragRotate.prototype.handlePointerDown =
function(mapBrowserEvent) {
if (!ol.events.condition.mouseOnly(mapBrowserEvent)) {
return false;
}
var browserEvent = mapBrowserEvent.browserEvent;
if (browserEvent.isMouseActionButton() && this.condition_(mapBrowserEvent)) {
var map = mapBrowserEvent.map;

View File

@@ -17,7 +17,7 @@ goog.require('ol.geom.MultiPoint');
goog.require('ol.geom.MultiPolygon');
goog.require('ol.geom.Point');
goog.require('ol.geom.Polygon');
goog.require('ol.interaction.Interaction');
goog.require('ol.interaction.Pointer');
goog.require('ol.source.Vector');
goog.require('ol.style.Circle');
goog.require('ol.style.Fill');
@@ -59,7 +59,7 @@ goog.inherits(ol.DrawEvent, goog.events.Event);
/**
* Interaction that allows drawing geometries
* @constructor
* @extends {ol.interaction.Interaction}
* @extends {ol.interaction.Pointer}
* @param {olx.interaction.DrawOptions} options Options.
* @todo stability experimental
*/
@@ -67,6 +67,12 @@ ol.interaction.Draw = function(options) {
goog.base(this);
/**
* @type {ol.Pixel}
* @private
*/
this.downPx_ = null;
/**
* Target source for drawn features.
* @type {ol.source.Vector}
@@ -149,8 +155,8 @@ ol.interaction.Draw = function(options) {
this.sketchRawPolygon_ = null;
/**
* Squared tolerance for handling click events. If the squared distance
* between a down and click event is greater than this tolerance, click events
* Squared tolerance for handling up events. If the squared distance
* between a down and up event is greater than this tolerance, up events
* will not be handled.
* @type {number}
* @private
@@ -168,7 +174,7 @@ ol.interaction.Draw = function(options) {
});
};
goog.inherits(ol.interaction.Draw, ol.interaction.Interaction);
goog.inherits(ol.interaction.Draw, ol.interaction.Pointer);
/**
@@ -250,31 +256,40 @@ ol.interaction.Draw.prototype.handleMapBrowserEvent = function(event) {
return true;
}
var pass = true;
if (event.type === ol.MapBrowserEvent.EventType.CLICK) {
pass = this.handleClick_(event);
} else if (event.type === ol.MapBrowserEvent.EventType.MOUSEMOVE) {
pass = this.handleMove_(event);
if (event.type === ol.MapBrowserEvent.EventType.POINTERMOVE) {
pass = this.handlePointerMove_(event);
} else if (event.type === ol.MapBrowserEvent.EventType.DBLCLICK) {
pass = false;
}
return pass;
return (goog.base(this, 'handleMapBrowserEvent', event) && pass);
};
/**
* Handle click events.
* @param {ol.MapBrowserEvent} event A click event.
* Handle down events.
* @param {ol.MapBrowserEvent} event A down event.
* @return {boolean} Pass the event to other interactions.
* @private
*/
ol.interaction.Draw.prototype.handleClick_ = function(event) {
var downPx = event.map.getEventPixel(event.target.getDown());
ol.interaction.Draw.prototype.handlePointerDown = function(event) {
this.downPx_ = event.pixel;
return true;
};
/**
* Handle up events.
* @param {ol.MapBrowserEvent} event An up event.
* @return {boolean} Pass the event to other interactions.
*/
ol.interaction.Draw.prototype.handlePointerUp = function(event) {
var downPx = this.downPx_;
var clickPx = event.pixel;
var dx = downPx[0] - clickPx[0];
var dy = downPx[1] - clickPx[1];
var squaredDistance = dx * dx + dy * dy;
var pass = true;
if (squaredDistance <= this.squaredClickTolerance_) {
this.handlePointerMove_(event);
if (goog.isNull(this.finishCoordinate_)) {
this.startDrawing_(event);
} else if (this.mode_ === ol.interaction.DrawMode.POINT ||
@@ -290,12 +305,12 @@ ol.interaction.Draw.prototype.handleClick_ = function(event) {
/**
* Handle mousemove events.
* @param {ol.MapBrowserEvent} event A mousemove event.
* Handle move events.
* @param {ol.MapBrowserEvent} event A move event.
* @return {boolean} Pass the event to other interactions.
* @private
*/
ol.interaction.Draw.prototype.handleMove_ = function(event) {
ol.interaction.Draw.prototype.handlePointerMove_ = function(event) {
if (this.mode_ === ol.interaction.DrawMode.POINT &&
goog.isNull(this.finishCoordinate_)) {
this.startDrawing_(event);

View File

@@ -9,9 +9,8 @@ goog.require('ol.interaction.DragZoom');
goog.require('ol.interaction.KeyboardPan');
goog.require('ol.interaction.KeyboardZoom');
goog.require('ol.interaction.MouseWheelZoom');
goog.require('ol.interaction.TouchPan');
goog.require('ol.interaction.TouchRotate');
goog.require('ol.interaction.TouchZoom');
goog.require('ol.interaction.PinchRotate');
goog.require('ol.interaction.PinchZoom');
/**
@@ -51,28 +50,6 @@ ol.interaction.defaults = function(opt_options) {
}));
}
var touchPan = goog.isDef(options.touchPan) ?
options.touchPan : true;
if (touchPan) {
interactions.push(new ol.interaction.TouchPan({
kinetic: kinetic
}));
}
var touchRotate = goog.isDef(options.touchRotate) ?
options.touchRotate : true;
if (touchRotate) {
interactions.push(new ol.interaction.TouchRotate());
}
var touchZoom = goog.isDef(options.touchZoom) ?
options.touchZoom : true;
if (touchZoom) {
interactions.push(new ol.interaction.TouchZoom({
duration: options.zoomDuration
}));
}
var dragPan = goog.isDef(options.dragPan) ?
options.dragPan : true;
if (dragPan) {
@@ -81,6 +58,20 @@ ol.interaction.defaults = function(opt_options) {
}));
}
var pinchRotate = goog.isDef(options.pinchRotate) ?
options.pinchRotate : true;
if (pinchRotate) {
interactions.push(new ol.interaction.PinchRotate());
}
var pinchZoom = goog.isDef(options.pinchZoom) ?
options.pinchZoom : true;
if (pinchZoom) {
interactions.push(new ol.interaction.PinchZoom({
duration: options.zoomDuration
}));
}
var keyboard = goog.isDef(options.keyboard) ?
options.keyboard : true;
if (keyboard) {

View File

@@ -18,7 +18,7 @@ goog.require('ol.geom.MultiPoint');
goog.require('ol.geom.MultiPolygon');
goog.require('ol.geom.Point');
goog.require('ol.geom.Polygon');
goog.require('ol.interaction.Drag');
goog.require('ol.interaction.Pointer');
goog.require('ol.structs.RBush');
@@ -35,7 +35,7 @@ ol.interaction.SegmentDataType;
/**
* @constructor
* @extends {ol.interaction.Drag}
* @extends {ol.interaction.Pointer}
* @param {olx.interaction.ModifyOptions} options Options.
*/
ol.interaction.Modify = function(options) {
@@ -126,7 +126,7 @@ ol.interaction.Modify = function(options) {
};
};
goog.inherits(ol.interaction.Modify, ol.interaction.Drag);
goog.inherits(ol.interaction.Modify, ol.interaction.Pointer);
/**
@@ -158,7 +158,7 @@ ol.interaction.Modify.prototype.addFeature_ = function(evt) {
if (goog.isDef(this.SEGMENT_WRITERS_[geometry.getType()])) {
this.SEGMENT_WRITERS_[geometry.getType()].call(this, feature, geometry);
}
this.handleMouseAtPixel_(this.lastPixel_, this.getMap());
this.handlePointerAtPixel_(this.lastPixel_, this.getMap());
};
@@ -368,7 +368,8 @@ ol.interaction.Modify.prototype.createOrUpdateVertexFeature_ =
/**
* @inheritDoc
*/
ol.interaction.Modify.prototype.handleDragStart = function(evt) {
ol.interaction.Modify.prototype.handlePointerDown = function(evt) {
this.handlePointerAtPixel_(evt.pixel, evt.map);
this.dragSegments_ = [];
var vertexFeature = this.vertexFeature_;
if (!goog.isNull(vertexFeature)) {
@@ -408,7 +409,7 @@ ol.interaction.Modify.prototype.handleDragStart = function(evt) {
/**
* @inheritDoc
*/
ol.interaction.Modify.prototype.handleDrag = function(evt) {
ol.interaction.Modify.prototype.handlePointerDrag = function(evt) {
var vertex = evt.coordinate;
for (var i = 0, ii = this.dragSegments_.length; i < ii; ++i) {
var dragSegment = this.dragSegments_[i];
@@ -458,13 +459,14 @@ ol.interaction.Modify.prototype.handleDrag = function(evt) {
/**
* @inheritDoc
*/
ol.interaction.Modify.prototype.handleDragEnd = function(evt) {
ol.interaction.Modify.prototype.handlePointerUp = function(evt) {
var segmentData;
for (var i = this.dragSegments_.length - 1; i >= 0; --i) {
segmentData = this.dragSegments_[i][0];
this.rBush_.update(ol.extent.boundingExtent(segmentData.segment),
segmentData);
}
return false;
};
@@ -474,12 +476,11 @@ ol.interaction.Modify.prototype.handleDragEnd = function(evt) {
ol.interaction.Modify.prototype.handleMapBrowserEvent =
function(mapBrowserEvent) {
if (!mapBrowserEvent.map.getView().getHints()[ol.ViewHint.INTERACTING] &&
!this.getDragging() &&
mapBrowserEvent.type == ol.MapBrowserEvent.EventType.MOUSEMOVE) {
this.handleMouseMove_(mapBrowserEvent);
mapBrowserEvent.type == ol.MapBrowserEvent.EventType.POINTERMOVE) {
this.handlePointerMove_(mapBrowserEvent);
}
goog.base(this, 'handleMapBrowserEvent', mapBrowserEvent);
return !this.modifiable_;
return (goog.base(this, 'handleMapBrowserEvent', mapBrowserEvent) &&
!this.modifiable_);
};
@@ -487,9 +488,9 @@ ol.interaction.Modify.prototype.handleMapBrowserEvent =
* @param {ol.MapBrowserEvent} evt Event.
* @private
*/
ol.interaction.Modify.prototype.handleMouseMove_ = function(evt) {
ol.interaction.Modify.prototype.handlePointerMove_ = function(evt) {
this.lastPixel_ = evt.pixel;
this.handleMouseAtPixel_(evt.pixel, evt.map);
this.handlePointerAtPixel_(evt.pixel, evt.map);
};
@@ -498,7 +499,7 @@ ol.interaction.Modify.prototype.handleMouseMove_ = function(evt) {
* @param {ol.Map} map Map.
* @private
*/
ol.interaction.Modify.prototype.handleMouseAtPixel_ = function(pixel, map) {
ol.interaction.Modify.prototype.handlePointerAtPixel_ = function(pixel, map) {
var pixelCoordinate = map.getCoordinateFromPixel(pixel);
var sortByDistance = function(a, b) {
return ol.coordinate.squaredDistanceToSegment(pixelCoordinate, a.segment) -

View File

@@ -0,0 +1 @@
@exportSymbol ol.interaction.PinchRotate

View File

@@ -1,18 +1,19 @@
// FIXME works for View2D only
goog.provide('ol.interaction.TouchRotate');
goog.provide('ol.interaction.PinchRotate');
goog.require('goog.asserts');
goog.require('goog.style');
goog.require('ol.Coordinate');
goog.require('ol.ViewHint');
goog.require('ol.interaction.Interaction');
goog.require('ol.interaction.Touch');
goog.require('ol.interaction.Pointer');
/**
* @define {number} Animation duration.
*/
ol.interaction.TOUCHROTATE_ANIMATION_DURATION = 250;
ol.interaction.ROTATE_ANIMATION_DURATION = 250;
@@ -20,11 +21,11 @@ ol.interaction.TOUCHROTATE_ANIMATION_DURATION = 250;
* Allows the user to rotate the map by twisting with two fingers
* on a touch screen.
* @constructor
* @extends {ol.interaction.Touch}
* @param {olx.interaction.TouchRotateOptions=} opt_options Options.
* @extends {ol.interaction.Pointer}
* @param {olx.interaction.PinchRotateOptions=} opt_options Options.
* @todo stability experimental
*/
ol.interaction.TouchRotate = function(opt_options) {
ol.interaction.PinchRotate = function(opt_options) {
goog.base(this);
@@ -61,19 +62,19 @@ ol.interaction.TouchRotate = function(opt_options) {
this.threshold_ = goog.isDef(options.threshold) ? options.threshold : 0.3;
};
goog.inherits(ol.interaction.TouchRotate, ol.interaction.Touch);
goog.inherits(ol.interaction.PinchRotate, ol.interaction.Pointer);
/**
* @inheritDoc
*/
ol.interaction.TouchRotate.prototype.handleTouchMove =
ol.interaction.PinchRotate.prototype.handlePointerDrag =
function(mapBrowserEvent) {
goog.asserts.assert(this.targetTouches.length >= 2);
goog.asserts.assert(this.targetPointers.length >= 2);
var rotationDelta = 0.0;
var touch0 = this.targetTouches[0];
var touch1 = this.targetTouches[1];
var touch0 = this.targetPointers[0];
var touch1 = this.targetPointers[1];
// angle between touches
var angle = Math.atan2(
@@ -97,7 +98,8 @@ ol.interaction.TouchRotate.prototype.handleTouchMove =
// FIXME: should be the intersection point between the lines:
// touch0,touch1 and previousTouch0,previousTouch1
var viewportPosition = goog.style.getClientPosition(map.getViewport());
var centroid = ol.interaction.Touch.centroid(this.targetTouches);
var centroid =
ol.interaction.Pointer.centroid(this.targetPointers);
centroid[0] -= viewportPosition.x;
centroid[1] -= viewportPosition.y;
this.anchor_ = map.getCoordinateFromPixel(centroid);
@@ -117,17 +119,19 @@ ol.interaction.TouchRotate.prototype.handleTouchMove =
/**
* @inheritDoc
*/
ol.interaction.TouchRotate.prototype.handleTouchEnd =
ol.interaction.PinchRotate.prototype.handlePointerUp =
function(mapBrowserEvent) {
if (this.targetTouches.length < 2) {
if (this.targetPointers.length < 2) {
var map = mapBrowserEvent.map;
// FIXME works for View2D only
var view = map.getView().getView2D();
var view2DState = view.getView2DState();
var view = map.getView();
view.setHint(ol.ViewHint.INTERACTING, -1);
if (this.rotating_) {
// FIXME works for View2D only
var view2D = view.getView2D();
var view2DState = view2D.getView2DState();
ol.interaction.Interaction.rotate(
map, view, view2DState.rotation, this.anchor_,
ol.interaction.TOUCHROTATE_ANIMATION_DURATION);
map, view2D, view2DState.rotation, this.anchor_,
ol.interaction.ROTATE_ANIMATION_DURATION);
}
return false;
} else {
@@ -139,14 +143,17 @@ ol.interaction.TouchRotate.prototype.handleTouchEnd =
/**
* @inheritDoc
*/
ol.interaction.TouchRotate.prototype.handleTouchStart =
ol.interaction.PinchRotate.prototype.handlePointerDown =
function(mapBrowserEvent) {
if (this.targetTouches.length >= 2) {
if (this.targetPointers.length >= 2) {
var map = mapBrowserEvent.map;
this.anchor_ = null;
this.lastAngle_ = undefined;
this.rotating_ = false;
this.rotationDelta_ = 0.0;
if (!this.handlingDownUpSequence) {
map.getView().setHint(ol.ViewHint.INTERACTING, 1);
}
map.render();
return true;
} else {

View File

@@ -0,0 +1 @@
@exportSymbol ol.interaction.PinchZoom

View File

@@ -1,12 +1,13 @@
// FIXME works for View2D only
goog.provide('ol.interaction.TouchZoom');
goog.provide('ol.interaction.PinchZoom');
goog.require('goog.asserts');
goog.require('goog.style');
goog.require('ol.Coordinate');
goog.require('ol.ViewHint');
goog.require('ol.interaction.Interaction');
goog.require('ol.interaction.Touch');
goog.require('ol.interaction.Pointer');
@@ -14,11 +15,11 @@ goog.require('ol.interaction.Touch');
* Allows the user to zoom the map by pinching with two fingers
* on a touch screen.
* @constructor
* @extends {ol.interaction.Touch}
* @param {olx.interaction.TouchZoomOptions=} opt_options Options.
* @extends {ol.interaction.Pointer}
* @param {olx.interaction.PinchZoomOptions=} opt_options Options.
* @todo stability experimental
*/
ol.interaction.TouchZoom = function(opt_options) {
ol.interaction.PinchZoom = function(opt_options) {
var options = goog.isDef(opt_options) ? opt_options : {};
@@ -49,19 +50,19 @@ ol.interaction.TouchZoom = function(opt_options) {
this.lastScaleDelta_ = 1;
};
goog.inherits(ol.interaction.TouchZoom, ol.interaction.Touch);
goog.inherits(ol.interaction.PinchZoom, ol.interaction.Pointer);
/**
* @inheritDoc
*/
ol.interaction.TouchZoom.prototype.handleTouchMove =
ol.interaction.PinchZoom.prototype.handlePointerDrag =
function(mapBrowserEvent) {
goog.asserts.assert(this.targetTouches.length >= 2);
goog.asserts.assert(this.targetPointers.length >= 2);
var scaleDelta = 1.0;
var touch0 = this.targetTouches[0];
var touch1 = this.targetTouches[1];
var touch0 = this.targetPointers[0];
var touch1 = this.targetPointers[1];
var dx = touch0.clientX - touch1.clientX;
var dy = touch0.clientY - touch1.clientY;
@@ -83,7 +84,8 @@ ol.interaction.TouchZoom.prototype.handleTouchMove =
// scale anchor point.
var viewportPosition = goog.style.getClientPosition(map.getViewport());
var centroid = ol.interaction.Touch.centroid(this.targetTouches);
var centroid =
ol.interaction.Pointer.centroid(this.targetPointers);
centroid[0] -= viewportPosition.x;
centroid[1] -= viewportPosition.y;
this.anchor_ = map.getCoordinateFromPixel(centroid);
@@ -99,18 +101,20 @@ ol.interaction.TouchZoom.prototype.handleTouchMove =
/**
* @inheritDoc
*/
ol.interaction.TouchZoom.prototype.handleTouchEnd =
ol.interaction.PinchZoom.prototype.handlePointerUp =
function(mapBrowserEvent) {
if (this.targetTouches.length < 2) {
if (this.targetPointers.length < 2) {
var map = mapBrowserEvent.map;
var view = map.getView();
view.setHint(ol.ViewHint.INTERACTING, -1);
// FIXME works for View2D only
var view = map.getView().getView2D();
var view2DState = view.getView2DState();
var view2D = view.getView2D();
var view2DState = view2D.getView2DState();
// Zoom to final resolution, with an animation, and provide a
// direction not to zoom out/in if user was pinching in/out.
// Direction is > 0 if pinching out, and < 0 if pinching in.
var direction = this.lastScaleDelta_ - 1;
ol.interaction.Interaction.zoom(map, view, view2DState.resolution,
ol.interaction.Interaction.zoom(map, view2D, view2DState.resolution,
this.anchor_, this.duration_, direction);
return false;
} else {
@@ -122,13 +126,16 @@ ol.interaction.TouchZoom.prototype.handleTouchEnd =
/**
* @inheritDoc
*/
ol.interaction.TouchZoom.prototype.handleTouchStart =
ol.interaction.PinchZoom.prototype.handlePointerDown =
function(mapBrowserEvent) {
if (this.targetTouches.length >= 2) {
if (this.targetPointers.length >= 2) {
var map = mapBrowserEvent.map;
this.anchor_ = null;
this.lastDistance_ = undefined;
this.lastScaleDelta_ = 1;
if (!this.handlingDownUpSequence) {
map.getView().setHint(ol.ViewHint.INTERACTING, 1);
}
map.render();
return true;
} else {

View File

@@ -0,0 +1,166 @@
goog.provide('ol.interaction.Pointer');
goog.require('goog.asserts');
goog.require('goog.functions');
goog.require('goog.object');
goog.require('ol.MapBrowserEvent');
goog.require('ol.MapBrowserEvent.EventType');
goog.require('ol.MapBrowserPointerEvent');
goog.require('ol.Pixel');
goog.require('ol.interaction.Interaction');
/**
* Base class for pointer interactions.
* @constructor
* @extends {ol.interaction.Interaction}
*/
ol.interaction.Pointer = function() {
goog.base(this);
/**
* @type {boolean}
* @protected
*/
this.handlingDownUpSequence = false;
/**
* @type {Object.<number, ol.pointer.PointerEvent>}
* @private
*/
this.trackedPointers_ = {};
/**
* @type {Array.<ol.pointer.PointerEvent>}
* @protected
*/
this.targetPointers = [];
};
goog.inherits(ol.interaction.Pointer, ol.interaction.Interaction);
/**
* @param {Array.<ol.pointer.PointerEvent>} pointerEvents
* @return {ol.Pixel} Centroid pixel.
*/
ol.interaction.Pointer.centroid = function(pointerEvents) {
var length = pointerEvents.length;
var clientX = 0;
var clientY = 0;
for (var i = 0; i < length; i++) {
clientX += pointerEvents[i].clientX;
clientY += pointerEvents[i].clientY;
}
return [clientX / length, clientY / length];
};
/**
* @param {ol.MapBrowserPointerEvent} mapBrowserEvent Event.
* @return {boolean} Whether the event is a pointerdown, pointerdrag
* or pointerup event.
* @private
*/
ol.interaction.Pointer.prototype.isPointerDraggingEvent_ =
function(mapBrowserEvent) {
var type = mapBrowserEvent.type;
return (
type === ol.MapBrowserEvent.EventType.POINTERDOWN ||
type === ol.MapBrowserEvent.EventType.POINTERDRAG ||
type === ol.MapBrowserEvent.EventType.POINTERUP);
};
/**
* @param {ol.MapBrowserPointerEvent} mapBrowserEvent Event.
* @private
*/
ol.interaction.Pointer.prototype.updateTrackedPointers_ =
function(mapBrowserEvent) {
if (this.isPointerDraggingEvent_(mapBrowserEvent)) {
var event = mapBrowserEvent.pointerEvent;
if (mapBrowserEvent.type == ol.MapBrowserEvent.EventType.POINTERUP) {
delete this.trackedPointers_[event.pointerId];
} else if (mapBrowserEvent.type ==
ol.MapBrowserEvent.EventType.POINTERDOWN) {
this.trackedPointers_[event.pointerId] = event;
} else if (event.pointerId in this.trackedPointers_) {
// update only when there was a pointerdown event for this pointer
this.trackedPointers_[event.pointerId] = event;
}
this.targetPointers = goog.object.getValues(this.trackedPointers_);
}
};
/**
* @param {ol.MapBrowserPointerEvent} mapBrowserEvent Event.
* @protected
*/
ol.interaction.Pointer.prototype.handlePointerDrag =
goog.nullFunction;
/**
* @param {ol.MapBrowserPointerEvent} mapBrowserEvent Event.
* @protected
* @return {boolean} Capture dragging.
*/
ol.interaction.Pointer.prototype.handlePointerUp =
goog.functions.FALSE;
/**
* @param {ol.MapBrowserPointerEvent} mapBrowserEvent Event.
* @protected
* @return {boolean} Capture dragging.
*/
ol.interaction.Pointer.prototype.handlePointerDown =
goog.functions.FALSE;
/**
* @inheritDoc
*/
ol.interaction.Pointer.prototype.handleMapBrowserEvent =
function(mapBrowserEvent) {
if (!(mapBrowserEvent instanceof ol.MapBrowserPointerEvent)) {
return true;
}
var stopEvent = false;
this.updateTrackedPointers_(mapBrowserEvent);
if (this.handlingDownUpSequence) {
if (mapBrowserEvent.type ==
ol.MapBrowserEvent.EventType.POINTERDRAG) {
this.handlePointerDrag(mapBrowserEvent);
} else if (mapBrowserEvent.type ==
ol.MapBrowserEvent.EventType.POINTERUP) {
this.handlingDownUpSequence =
this.handlePointerUp(mapBrowserEvent);
}
}
if (mapBrowserEvent.type == ol.MapBrowserEvent.EventType.POINTERDOWN) {
var handled = this.handlePointerDown(mapBrowserEvent);
this.handlingDownUpSequence = handled;
stopEvent = this.shouldStopEvent(handled);
}
return !stopEvent;
};
/**
* This method allows inheriting classes to stop the event from being
* passed to further interactions. For example, this is required for
* interaction `DragRotateAndZoom`.
*
* @protected
* @param {boolean} handled Was the event handled by the interaction?
* @return {boolean} Should the event be stopped?
*/
ol.interaction.Pointer.prototype.shouldStopEvent =
goog.functions.FALSE;

View File

@@ -1,150 +0,0 @@
goog.provide('ol.interaction.Touch');
goog.require('goog.asserts');
goog.require('goog.functions');
goog.require('goog.object');
goog.require('ol.MapBrowserEvent');
goog.require('ol.MapBrowserEvent.EventType');
goog.require('ol.Pixel');
goog.require('ol.ViewHint');
goog.require('ol.interaction.Interaction');
/**
* Base class for touch interactions.
* @constructor
* @extends {ol.interaction.Interaction}
*/
ol.interaction.Touch = function() {
goog.base(this);
/**
* @type {boolean}
* @private
*/
this.handled_ = false;
/**
* @type {Object}
* @private
*/
this.trackedTouches_ = {};
/**
* @type {Array.<Object>}
* @protected
*/
this.targetTouches = [];
};
goog.inherits(ol.interaction.Touch, ol.interaction.Interaction);
/**
* @param {Array.<Object>} touches TouchEvents.
* @return {ol.Pixel} Centroid pixel.
*/
ol.interaction.Touch.centroid = function(touches) {
var length = touches.length;
var clientX = 0;
var clientY = 0;
for (var i = 0; i < length; i++) {
clientX += touches[i].clientX;
clientY += touches[i].clientY;
}
return [clientX / length, clientY / length];
};
/**
* @param {ol.MapBrowserEvent} mapBrowserEvent Event.
* @return {boolean} Whether the event is a touchstart, touchmove
* or touchend event.
* @private
*/
ol.interaction.Touch.isTouchEvent_ = function(mapBrowserEvent) {
var type = mapBrowserEvent.type;
return (
type === ol.MapBrowserEvent.EventType.TOUCHSTART ||
type === ol.MapBrowserEvent.EventType.TOUCHMOVE ||
type === ol.MapBrowserEvent.EventType.TOUCHEND);
};
/**
* @param {ol.MapBrowserEvent} mapBrowserEvent Event.
* @private
*/
ol.interaction.Touch.prototype.updateTrackedTouches_ =
function(mapBrowserEvent) {
if (ol.interaction.Touch.isTouchEvent_(mapBrowserEvent)) {
var event = mapBrowserEvent.originalEvent;
if (goog.isDef(event.targetTouches)) {
// W3C touch events
this.targetTouches = event.targetTouches;
} else if (goog.isDef(event.pointerId)) {
// IE pointer event
if (mapBrowserEvent.type == ol.MapBrowserEvent.EventType.TOUCHEND) {
delete this.trackedTouches_[event.pointerId];
} else {
this.trackedTouches_[event.pointerId] = event;
}
this.targetTouches = goog.object.getValues(this.trackedTouches_);
} else {
goog.asserts.fail('unknown touch event model');
}
}
};
/**
* @param {ol.MapBrowserEvent} mapBrowserEvent Event.
* @protected
*/
ol.interaction.Touch.prototype.handleTouchMove = goog.nullFunction;
/**
* @param {ol.MapBrowserEvent} mapBrowserEvent Event.
* @protected
* @return {boolean} Capture dragging.
*/
ol.interaction.Touch.prototype.handleTouchEnd = goog.functions.FALSE;
/**
* @param {ol.MapBrowserEvent} mapBrowserEvent Event.
* @protected
* @return {boolean} Capture dragging.
*/
ol.interaction.Touch.prototype.handleTouchStart = goog.functions.FALSE;
/**
* @inheritDoc
*/
ol.interaction.Touch.prototype.handleMapBrowserEvent =
function(mapBrowserEvent) {
var view = mapBrowserEvent.map.getView();
this.updateTrackedTouches_(mapBrowserEvent);
if (this.handled_) {
if (mapBrowserEvent.type == ol.MapBrowserEvent.EventType.TOUCHMOVE) {
this.handleTouchMove(mapBrowserEvent);
} else if (mapBrowserEvent.type == ol.MapBrowserEvent.EventType.TOUCHEND) {
this.handled_ = this.handleTouchEnd(mapBrowserEvent);
if (!this.handled_) {
view.setHint(ol.ViewHint.INTERACTING, -1);
}
}
}
if (mapBrowserEvent.type == ol.MapBrowserEvent.EventType.TOUCHSTART) {
var handled = this.handleTouchStart(mapBrowserEvent);
if (!this.handled_ && handled) {
view.setHint(ol.ViewHint.INTERACTING, 1);
}
this.handled_ = handled;
}
return true;
};

View File

@@ -1 +0,0 @@
@exportSymbol ol.interaction.TouchPan

View File

@@ -1,142 +0,0 @@
// FIXME works for View2D only
goog.provide('ol.interaction.TouchPan');
goog.require('goog.asserts');
goog.require('ol.Kinetic');
goog.require('ol.Pixel');
goog.require('ol.PreRenderFunction');
goog.require('ol.View2D');
goog.require('ol.coordinate');
goog.require('ol.interaction.Touch');
/**
* Allows the user to pan the map by touching and dragging
* on a touch screen.
* @constructor
* @extends {ol.interaction.Touch}
* @param {olx.interaction.TouchPanOptions=} opt_options Options.
* @todo stability experimental
*/
ol.interaction.TouchPan = function(opt_options) {
goog.base(this);
var options = goog.isDef(opt_options) ? opt_options : {};
/**
* @private
* @type {ol.Kinetic|undefined}
*/
this.kinetic_ = options.kinetic;
/**
* @private
* @type {?ol.PreRenderFunction}
*/
this.kineticPreRenderFn_ = null;
/**
* @type {ol.Pixel}
*/
this.lastCentroid = null;
/**
* @private
* @type {boolean}
*/
this.noKinetic_ = false;
};
goog.inherits(ol.interaction.TouchPan, ol.interaction.Touch);
/**
* @inheritDoc
*/
ol.interaction.TouchPan.prototype.handleTouchMove = function(mapBrowserEvent) {
goog.asserts.assert(this.targetTouches.length >= 1);
var centroid = ol.interaction.Touch.centroid(this.targetTouches);
if (!goog.isNull(this.lastCentroid)) {
if (this.kinetic_) {
this.kinetic_.update(centroid[0], centroid[1]);
}
var deltaX = this.lastCentroid[0] - centroid[0];
var deltaY = centroid[1] - this.lastCentroid[1];
var map = mapBrowserEvent.map;
var view2D = map.getView().getView2D();
goog.asserts.assertInstanceof(view2D, ol.View2D);
var view2DState = view2D.getView2DState();
var center = [deltaX, deltaY];
ol.coordinate.scale(center, view2DState.resolution);
ol.coordinate.rotate(center, view2DState.rotation);
ol.coordinate.add(center, view2DState.center);
center = view2D.constrainCenter(center);
map.render();
view2D.setCenter(center);
}
this.lastCentroid = centroid;
};
/**
* @inheritDoc
*/
ol.interaction.TouchPan.prototype.handleTouchEnd =
function(mapBrowserEvent) {
var map = mapBrowserEvent.map;
var view2D = map.getView().getView2D();
goog.asserts.assertInstanceof(view2D, ol.View2D);
if (this.targetTouches.length === 0) {
if (!this.noKinetic_ && this.kinetic_ && this.kinetic_.end()) {
var distance = this.kinetic_.getDistance();
var angle = this.kinetic_.getAngle();
var center = view2D.getCenter();
goog.asserts.assert(goog.isDef(center));
this.kineticPreRenderFn_ = this.kinetic_.pan(center);
map.beforeRender(this.kineticPreRenderFn_);
var centerpx = map.getPixelFromCoordinate(center);
var dest = map.getCoordinateFromPixel([
centerpx[0] - distance * Math.cos(angle),
centerpx[1] - distance * Math.sin(angle)
]);
dest = view2D.constrainCenter(dest);
view2D.setCenter(dest);
}
map.render();
return false;
} else {
this.lastCentroid = null;
return true;
}
};
/**
* @inheritDoc
*/
ol.interaction.TouchPan.prototype.handleTouchStart =
function(mapBrowserEvent) {
if (this.targetTouches.length > 0) {
var map = mapBrowserEvent.map;
var view2D = map.getView().getView2D();
goog.asserts.assertInstanceof(view2D, ol.View2D);
this.lastCentroid = null;
map.render();
if (!goog.isNull(this.kineticPreRenderFn_) &&
map.removePreRenderFunction(this.kineticPreRenderFn_)) {
view2D.setCenter(mapBrowserEvent.frameState.view2DState.center);
this.kineticPreRenderFn_ = null;
}
if (this.kinetic_) {
this.kinetic_.begin();
}
// No kinetic as soon as more than one fingers on the screen is
// detected. This is to prevent nasty pans after pinch.
this.noKinetic_ = this.targetTouches.length > 1;
return true;
} else {
return false;
}
};

View File

@@ -1 +0,0 @@
@exportSymbol ol.interaction.TouchRotate

View File

@@ -1 +0,0 @@
@exportSymbol ol.interaction.TouchZoom

View File

@@ -261,7 +261,8 @@ ol.Map = function(options) {
goog.events.EventType.DBLCLICK,
goog.events.EventType.MOUSEDOWN,
goog.events.EventType.TOUCHSTART,
goog.events.EventType.MSPOINTERDOWN
goog.events.EventType.MSPOINTERDOWN,
'pointerdown'
], goog.events.Event.stopPropagation);
goog.dom.appendChild(this.viewport_, this.overlayContainerStopEvent_);

View File

@@ -1,6 +1,7 @@
goog.provide('ol.MapBrowserEvent');
goog.provide('ol.MapBrowserEvent.EventType');
goog.provide('ol.MapBrowserEventHandler');
goog.provide('ol.MapBrowserPointerEvent');
goog.require('goog.array');
goog.require('goog.asserts');
@@ -12,6 +13,8 @@ goog.require('goog.object');
goog.require('ol.Coordinate');
goog.require('ol.MapEvent');
goog.require('ol.Pixel');
goog.require('ol.pointer.PointerEvent');
goog.require('ol.pointer.PointerEventHandler');
@@ -80,6 +83,30 @@ ol.MapBrowserEvent.prototype.stopPropagation = function() {
/**
* @constructor
* @extends {ol.MapBrowserEvent}
* @param {string} type Event type.
* @param {ol.Map} map Map.
* @param {ol.pointer.PointerEvent} pointerEvent Pointer event.
* @param {?oli.FrameState=} opt_frameState Frame state.
* @todo stability experimental
*/
ol.MapBrowserPointerEvent = function(type, map, pointerEvent, opt_frameState) {
goog.base(this, type, map, pointerEvent.browserEvent, opt_frameState);
/**
* @const
* @type {ol.pointer.PointerEvent}
*/
this.pointerEvent = pointerEvent;
};
goog.inherits(ol.MapBrowserPointerEvent, ol.MapBrowserEvent);
/**
* @param {ol.Map} map The map with the viewport to listen to events on.
* @constructor
@@ -114,24 +141,12 @@ ol.MapBrowserEventHandler = function(map) {
*/
this.dragListenerKeys_ = null;
/**
* @type {goog.events.Key}
* @private
*/
this.mousedownListenerKey_ = null;
/**
* @type {goog.events.Key}
* @private
*/
this.pointerdownListenerKey_ = null;
/**
* @type {goog.events.Key}
* @private
*/
this.touchstartListenerKey_ = null;
if (ol.LEGACY_IE_SUPPORT && ol.IS_LEGACY_IE) {
/**
* @type {goog.events.Key}
@@ -141,46 +156,69 @@ ol.MapBrowserEventHandler = function(map) {
}
/**
* @type {goog.events.BrowserEvent}
* @type {ol.pointer.PointerEvent}
* @private
*/
this.down_ = null;
var element = this.map_.getViewport();
this.relayedListenerKeys_ = [
goog.events.listen(element,
goog.events.EventType.MOUSEMOVE,
this.relayEvent_, false, this),
goog.events.listen(element,
goog.events.EventType.CLICK,
this.relayEvent_, false, this)
];
/**
* @type {number}
* @private
*/
this.activePointers_ = 0;
this.mousedownListenerKey_ = goog.events.listen(element,
goog.events.EventType.MOUSEDOWN,
this.handleMouseDown_, false, this);
/**
* @type {Object.<number, boolean>}
* @private
*/
this.trackedTouches_ = {};
this.pointerdownListenerKey_ = goog.events.listen(element,
goog.events.EventType.MSPOINTERDOWN,
/**
* Event handler which generates pointer events for
* the viewport element.
*
* @type {ol.pointer.PointerEventHandler}
* @private
*/
this.pointerEventHandler_ = new ol.pointer.PointerEventHandler(element);
/**
* Event handler which generates pointer events for
* the document (used when dragging).
*
* @type {ol.pointer.PointerEventHandler}
* @private
*/
this.documentPointerEventHandler_ = null;
this.pointerdownListenerKey_ = goog.events.listen(this.pointerEventHandler_,
ol.pointer.EventType.POINTERDOWN,
this.handlePointerDown_, false, this);
this.touchstartListenerKey_ = goog.events.listen(element,
goog.events.EventType.TOUCHSTART,
this.handleTouchStart_, false, this);
this.relayedListenerKey_ = goog.events.listen(this.pointerEventHandler_,
ol.pointer.EventType.POINTERMOVE,
this.relayEvent_, false, this);
if (ol.LEGACY_IE_SUPPORT && ol.IS_LEGACY_IE) {
/*
* On legacy IE, double clicks do not produce two mousedown and
* mouseup events. That is why a separate DBLCLICK event listener
* is used.
*/
this.ieDblclickListenerKey_ = goog.events.listen(element,
goog.events.EventType.DBLCLICK, this.emulateClick_, false, this);
goog.events.EventType.DBLCLICK,
this.emulateClickLegacyIE_, false, this);
}
};
goog.inherits(ol.MapBrowserEventHandler, goog.events.EventTarget);
/**
* Get the last "down" type event. This will be set on mousedown,
* touchstart, and pointerdown.
* @return {goog.events.BrowserEvent} The most recent "down" type event (or null
* Get the last "down" type event. This will be set on pointerdown.
* @return {ol.pointer.PointerEvent} The most recent "down" type event (or null
* if none have occurred).
*/
ol.MapBrowserEventHandler.prototype.getDown = function() {
@@ -189,291 +227,224 @@ ol.MapBrowserEventHandler.prototype.getDown = function() {
/**
* @param {goog.events.BrowserEvent} browserEvent Browser event.
* @param {goog.events.BrowserEvent} browserEvent Pointer event.
* @private
*/
ol.MapBrowserEventHandler.prototype.emulateClick_ = function(browserEvent) {
ol.MapBrowserEventHandler.prototype.emulateClickLegacyIE_ =
function(browserEvent) {
var pointerEvent = this.pointerEventHandler_.wrapMouseEvent(
ol.MapBrowserEvent.EventType.POINTERUP,
browserEvent
);
this.emulateClick_(pointerEvent);
};
/**
* @param {ol.pointer.PointerEvent} pointerEvent Pointer event.
* @private
*/
ol.MapBrowserEventHandler.prototype.emulateClick_ = function(pointerEvent) {
if (this.clickTimeoutId_ !== 0) {
// double-click
goog.global.clearTimeout(this.clickTimeoutId_);
this.clickTimeoutId_ = 0;
var newEvent = new ol.MapBrowserEvent(
ol.MapBrowserEvent.EventType.DBLCLICK, this.map_, browserEvent);
var newEvent = new ol.MapBrowserPointerEvent(
ol.MapBrowserEvent.EventType.DBLCLICK, this.map_, pointerEvent);
this.dispatchEvent(newEvent);
} else {
// click
if (ol.LEGACY_IE_SUPPORT && ol.IS_LEGACY_IE) {
// In IE 7-8, referring to the original event object after the current
// call stack causes "member not found" exceptions, such as in the timeout
// we use here.
var ev = /** @type {Event} */ (
goog.object.clone(browserEvent.getBrowserEvent()));
this.clickTimeoutId_ = goog.global.setTimeout(goog.bind(function() {
this.clickTimeoutId_ = 0;
var newEvent = new ol.MapBrowserEvent(
ol.MapBrowserEvent.EventType.SINGLECLICK, this.map_,
new goog.events.BrowserEvent(ev, browserEvent.currentTarget));
this.dispatchEvent(newEvent);
}, this), 250);
} else {
this.clickTimeoutId_ = goog.global.setTimeout(goog.bind(function() {
this.clickTimeoutId_ = 0;
var newEvent = new ol.MapBrowserEvent(
ol.MapBrowserEvent.EventType.SINGLECLICK, this.map_, browserEvent);
this.dispatchEvent(newEvent);
}, this), 250);
}
this.clickTimeoutId_ = goog.global.setTimeout(goog.bind(function() {
this.clickTimeoutId_ = 0;
var newEvent = new ol.MapBrowserPointerEvent(
ol.MapBrowserEvent.EventType.SINGLECLICK, this.map_, pointerEvent);
this.dispatchEvent(newEvent);
}, this), 250);
}
};
/**
* @param {goog.events.BrowserEvent} browserEvent Browser event.
* Keeps track on how many pointers are currently active.
*
* @param {ol.pointer.PointerEvent} pointerEvent Pointer event.
* @private
*/
ol.MapBrowserEventHandler.prototype.handleMouseUp_ = function(browserEvent) {
if (this.down_) {
ol.MapBrowserEventHandler.prototype.updateActivePointers_ =
function(pointerEvent) {
var event = pointerEvent;
if (event.type == ol.MapBrowserEvent.EventType.POINTERUP ||
event.type == ol.MapBrowserEvent.EventType.POINTERCANCEL) {
delete this.trackedTouches_[event.pointerId];
} else if (event.type == ol.MapBrowserEvent.EventType.POINTERDOWN) {
this.trackedTouches_[event.pointerId] = true;
}
this.activePointers_ = goog.object.getCount(this.trackedTouches_);
};
/**
* @param {ol.pointer.PointerEvent} pointerEvent Pointer event.
* @private
*/
ol.MapBrowserEventHandler.prototype.handlePointerUp_ = function(pointerEvent) {
this.updateActivePointers_(pointerEvent);
var newEvent = new ol.MapBrowserPointerEvent(
ol.MapBrowserEvent.EventType.POINTERUP, this.map_, pointerEvent);
this.dispatchEvent(newEvent);
goog.asserts.assert(this.activePointers_ >= 0);
if (this.activePointers_ === 0) {
goog.array.forEach(this.dragListenerKeys_, goog.events.unlistenByKey);
this.dragListenerKeys_ = null;
if (this.dragged_) {
var newEvent = new ol.MapBrowserEvent(
ol.MapBrowserEvent.EventType.DRAGEND, this.map_, browserEvent);
this.dispatchEvent(newEvent);
} else if (browserEvent.isMouseActionButton()) {
this.emulateClick_(browserEvent);
}
goog.dispose(this.documentPointerEventHandler_);
this.documentPointerEventHandler_ = null;
}
// We emulate click event on left mouse button click, touch contact, and pen
// contact. isMouseActionButton returns true in these cases (evt.button is set
// to 0).
// See http://www.w3.org/TR/pointerevents/#button-states
if (!this.dragged_ && this.isMouseActionButton_(pointerEvent)) {
goog.asserts.assert(!goog.isNull(this.down_));
this.emulateClick_(this.down_);
}
};
/**
* @param {goog.events.BrowserEvent} browserEvent Browser event.
* @param {ol.pointer.PointerEvent} pointerEvent Pointer event.
* @return {boolean} If the left mouse button was pressed.
* @private
*/
ol.MapBrowserEventHandler.prototype.handleMouseDown_ = function(browserEvent) {
if (!goog.isNull(this.pointerdownListenerKey_)) {
// mouse device detected - unregister the pointerdown and touchstart
// listeners
goog.events.unlistenByKey(this.pointerdownListenerKey_);
this.pointerdownListenerKey_ = null;
goog.asserts.assert(!goog.isNull(this.touchstartListenerKey_));
goog.events.unlistenByKey(this.touchstartListenerKey_);
this.touchstartListenerKey_ = null;
ol.MapBrowserEventHandler.prototype.isMouseActionButton_ =
function(pointerEvent) {
if (ol.LEGACY_IE_SUPPORT && ol.IS_LEGACY_IE) {
return pointerEvent.button == 1;
} else {
return pointerEvent.button == 0;
}
var newEvent = new ol.MapBrowserEvent(
ol.MapBrowserEvent.EventType.DOWN, this.map_, browserEvent);
this.dispatchEvent(newEvent);
this.down_ = browserEvent;
this.dragged_ = false;
this.dragListenerKeys_ = [
goog.events.listen(goog.global.document, goog.events.EventType.MOUSEMOVE,
this.handleMouseMove_, false, this),
goog.events.listen(goog.global.document, goog.events.EventType.MOUSEUP,
this.handleMouseUp_, false, this)
];
// prevent browser image dragging with the dom renderer
browserEvent.preventDefault();
};
/**
* @param {goog.events.BrowserEvent} browserEvent Browser event.
* @private
*/
ol.MapBrowserEventHandler.prototype.handleMouseMove_ = function(browserEvent) {
var newEvent;
if (!this.dragged_) {
this.dragged_ = true;
newEvent = new ol.MapBrowserEvent(
ol.MapBrowserEvent.EventType.DRAGSTART, this.map_, this.down_);
this.dispatchEvent(newEvent);
}
newEvent = new ol.MapBrowserEvent(
ol.MapBrowserEvent.EventType.DRAG, this.map_, browserEvent);
this.dispatchEvent(newEvent);
};
/**
* @param {goog.events.BrowserEvent} browserEvent Browser event.
* @param {ol.pointer.PointerEvent} pointerEvent Pointer event.
* @private
*/
ol.MapBrowserEventHandler.prototype.handlePointerDown_ =
function(browserEvent) {
if (!goog.isNull(this.mousedownListenerKey_)) {
// pointer device detected - unregister the mousedown and touchstart
// listeners
goog.events.unlistenByKey(this.mousedownListenerKey_);
this.mousedownListenerKey_ = null;
goog.asserts.assert(!goog.isNull(this.touchstartListenerKey_));
goog.events.unlistenByKey(this.touchstartListenerKey_);
this.touchstartListenerKey_ = null;
}
var newEvent = new ol.MapBrowserEvent(
ol.MapBrowserEvent.EventType.TOUCHSTART, this.map_, browserEvent);
function(pointerEvent) {
this.updateActivePointers_(pointerEvent);
var newEvent = new ol.MapBrowserPointerEvent(
ol.MapBrowserEvent.EventType.POINTERDOWN, this.map_, pointerEvent);
this.dispatchEvent(newEvent);
this.down_ = browserEvent;
this.down_ = pointerEvent;
this.dragged_ = false;
this.dragListenerKeys_ = [
goog.events.listen(goog.global.document,
goog.events.EventType.MSPOINTERMOVE,
this.handlePointerMove_, false, this),
goog.events.listen(goog.global.document, goog.events.EventType.MSPOINTERUP,
this.handlePointerUp_, false, this)
];
if (goog.isNull(this.dragListenerKeys_)) {
/* Set up a pointer event handler on the `document`,
* which is required when the pointer is moved outside
* the viewport when dragging.
*/
this.documentPointerEventHandler_ =
new ol.pointer.PointerEventHandler(document);
this.dragListenerKeys_ = [
goog.events.listen(this.documentPointerEventHandler_,
ol.MapBrowserEvent.EventType.POINTERMOVE,
this.handlePointerMove_, false, this),
goog.events.listen(this.documentPointerEventHandler_,
ol.MapBrowserEvent.EventType.POINTERUP,
this.handlePointerUp_, false, this),
/* Note that the listener for `pointercancel is set up on
* `pointerEventHandler_` and not `documentPointerEventHandler_` like
* the `pointerup` and `pointermove` listeners.
*
* The reason for this is the following: `TouchSource.vacuumTouches_()`
* issues `pointercancel` events, when there was no `touchend` for a
* `touchstart`. Now, let's say a first `touchstart` is registered on
* `pointerEventHandler_`. The `documentPointerEventHandler_` is set up.
* But `documentPointerEventHandler_` doesn't know about the first
* `touchstart`. If there is no `touchend` for the `touchstart`, we can
* only receive a `touchcancel` from `pointerEventHandler_`, because it is
* only registered there.
*/
goog.events.listen(this.pointerEventHandler_,
ol.MapBrowserEvent.EventType.POINTERCANCEL,
this.handlePointerUp_, false, this)
];
}
// FIXME check if/when this is necessary
// prevent context menu
browserEvent.preventDefault();
pointerEvent.preventDefault();
};
/**
* @param {goog.events.BrowserEvent} browserEvent Browser event.
* @param {ol.pointer.PointerEvent} pointerEvent Pointer event.
* @private
*/
ol.MapBrowserEventHandler.prototype.handlePointerMove_ =
function(browserEvent) {
function(pointerEvent) {
// Fix IE10 on windows Surface : When you tap the tablet, it triggers
// multiple pointermove events between pointerdown and pointerup with
// the exact same coordinates of the pointerdown event. To avoid a
// 'false' touchmove event to be dispatched , we test if the pointer
// effectively moved.
if (browserEvent.clientX != this.down_.clientX ||
browserEvent.clientY != this.down_.clientY) {
if (pointerEvent.clientX != this.down_.clientX ||
pointerEvent.clientY != this.down_.clientY) {
this.dragged_ = true;
var newEvent = new ol.MapBrowserEvent(
ol.MapBrowserEvent.EventType.TOUCHMOVE, this.map_, browserEvent);
var newEvent = new ol.MapBrowserPointerEvent(
ol.MapBrowserEvent.EventType.POINTERDRAG, this.map_, pointerEvent);
this.dispatchEvent(newEvent);
}
};
/**
* @param {goog.events.BrowserEvent} browserEvent Browser event.
* @private
*/
ol.MapBrowserEventHandler.prototype.handlePointerUp_ = function(browserEvent) {
var newEvent = new ol.MapBrowserEvent(
ol.MapBrowserEvent.EventType.TOUCHEND, this.map_, browserEvent);
this.dispatchEvent(newEvent);
goog.array.forEach(this.dragListenerKeys_, goog.events.unlistenByKey);
// We emulate click event on left mouse button click, touch contact, and pen
// contact. isMouseActionButton returns true in these cases (evt.button is set
// to 0).
// See http://www.w3.org/TR/pointerevents/#button-states .
if (!this.dragged_ && browserEvent.isMouseActionButton()) {
goog.asserts.assert(!goog.isNull(this.down_));
this.emulateClick_(this.down_);
}
};
/**
* @param {goog.events.BrowserEvent} browserEvent Browser event.
* @private
*/
ol.MapBrowserEventHandler.prototype.handleTouchStart_ = function(browserEvent) {
if (!goog.isNull(this.mousedownListenerKey_)) {
// touch device detected - unregister the mousedown and pointerdown
// listeners
goog.events.unlistenByKey(this.mousedownListenerKey_);
this.mousedownListenerKey_ = null;
goog.asserts.assert(!goog.isNull(this.pointerdownListenerKey_));
goog.events.unlistenByKey(this.pointerdownListenerKey_);
this.pointerdownListenerKey_ = null;
}
var newEvent = new ol.MapBrowserEvent(
ol.MapBrowserEvent.EventType.TOUCHSTART, this.map_, browserEvent);
this.dispatchEvent(newEvent);
this.down_ = browserEvent;
this.dragged_ = false;
if (goog.isNull(this.dragListenerKeys_)) {
this.dragListenerKeys_ = [
goog.events.listen(goog.global.document, goog.events.EventType.TOUCHMOVE,
this.handleTouchMove_, false, this),
goog.events.listen(goog.global.document, goog.events.EventType.TOUCHEND,
this.handleTouchEnd_, false, this)
];
}
// FIXME check if/when this is necessary
browserEvent.preventDefault();
};
/**
* @param {goog.events.BrowserEvent} browserEvent Browser event.
* @private
*/
ol.MapBrowserEventHandler.prototype.handleTouchMove_ = function(browserEvent) {
this.dragged_ = true;
var newEvent = new ol.MapBrowserEvent(
ol.MapBrowserEvent.EventType.TOUCHMOVE, this.map_, browserEvent);
this.dispatchEvent(newEvent);
// Some native android browser triggers mousemove events during small period
// of time. See: https://code.google.com/p/android/issues/detail?id=5491 or
// https://code.google.com/p/android/issues/detail?id=19827
// ex: Galaxy Tab P3110 + Android 4.1.1
browserEvent.preventDefault();
pointerEvent.preventDefault();
};
/**
* @param {goog.events.BrowserEvent} browserEvent Browser event.
* Wrap and relay a pointer event. Note that this requires that the type
* string for the MapBrowserPointerEvent matches the PointerEvent type.
* @param {ol.pointer.PointerEvent} pointerEvent Pointer event.
* @private
*/
ol.MapBrowserEventHandler.prototype.handleTouchEnd_ = function(browserEvent) {
var newEvent = new ol.MapBrowserEvent(
ol.MapBrowserEvent.EventType.TOUCHEND, this.map_, browserEvent);
this.dispatchEvent(newEvent);
if (browserEvent.getBrowserEvent().targetTouches.length === 0) {
goog.array.forEach(this.dragListenerKeys_, goog.events.unlistenByKey);
this.dragListenerKeys_ = null;
}
if (!this.dragged_) {
goog.asserts.assert(!goog.isNull(this.down_));
this.emulateClick_(this.down_);
}
ol.MapBrowserEventHandler.prototype.relayEvent_ = function(pointerEvent) {
this.dispatchEvent(new ol.MapBrowserPointerEvent(
pointerEvent.type, this.map_, pointerEvent));
};
/**
* FIXME empty description for jsdoc
* @inheritDoc
*/
ol.MapBrowserEventHandler.prototype.disposeInternal = function() {
if (!goog.isNull(this.relayedListenerKeys_)) {
goog.array.forEach(this.relayedListenerKeys_, goog.events.unlistenByKey);
this.relayedListenerKeys_ = null;
}
if (!goog.isNull(this.mousedownListenerKey_)) {
goog.events.unlistenByKey(this.mousedownListenerKey_);
this.mousedownListenerKey_ = null;
if (!goog.isNull(this.relayedListenerKey_)) {
goog.events.unlistenByKey(this.relayedListenerKey_);
this.relayedListenerKey_ = null;
}
if (!goog.isNull(this.pointerdownListenerKey_)) {
goog.events.unlistenByKey(this.pointerdownListenerKey_);
this.pointerdownListenerKey_ = null;
}
if (!goog.isNull(this.touchstartListenerKey_)) {
goog.events.unlistenByKey(this.touchstartListenerKey_);
this.touchstartListenerKey_ = null;
}
if (!goog.isNull(this.dragListenerKeys_)) {
goog.array.forEach(this.dragListenerKeys_, goog.events.unlistenByKey);
this.dragListenerKeys_ = null;
}
if (!goog.isNull(this.documentPointerEventHandler_)) {
goog.dispose(this.documentPointerEventHandler_);
this.documentPointerEventHandler_ = null;
}
if (!goog.isNull(this.pointerEventHandler_)) {
goog.dispose(this.pointerEventHandler_);
this.pointerEventHandler_ = null;
}
if (ol.LEGACY_IE_SUPPORT && ol.IS_LEGACY_IE &&
!goog.isNull(this.ieDblclickListenerKey_)) {
goog.events.unlistenByKey(this.ieDblclickListenerKey_);
@@ -483,32 +454,23 @@ ol.MapBrowserEventHandler.prototype.disposeInternal = function() {
};
/**
* Wrap and relay a browser event. Note that this requires that the type
* string for the MapBrowserEvent matches the BrowserEvent type.
* @param {goog.events.BrowserEvent} browserEvent Browser event.
* @private
*/
ol.MapBrowserEventHandler.prototype.relayEvent_ = function(browserEvent) {
this.dispatchEvent(new ol.MapBrowserEvent(
browserEvent.type, this.map_, browserEvent));
};
/**
* Constants for event names.
* @enum {string}
*/
ol.MapBrowserEvent.EventType = {
CLICK: goog.events.EventType.CLICK,
DBLCLICK: goog.events.EventType.DBLCLICK,
MOUSEMOVE: goog.events.EventType.MOUSEMOVE,
DOWN: 'down',
DRAGSTART: 'dragstart',
DRAG: 'drag',
DRAGEND: 'dragend',
// derived event types
SINGLECLICK: 'singleclick',
TOUCHSTART: goog.events.EventType.TOUCHSTART,
TOUCHMOVE: goog.events.EventType.TOUCHMOVE,
TOUCHEND: goog.events.EventType.TOUCHEND
DBLCLICK: goog.events.EventType.DBLCLICK,
POINTERDRAG: 'pointerdrag',
// original pointer event types
POINTERMOVE: 'pointermove',
POINTERDOWN: 'pointerdown',
POINTERUP: 'pointerup',
POINTEROVER: 'pointerover',
POINTEROUT: 'pointerout',
POINTERENTER: 'pointerenter',
POINTERLEAVE: 'pointerleave',
POINTERCANCEL: 'pointercancel'
};

View File

@@ -0,0 +1,55 @@
goog.provide('ol.pointer.EventSource');
goog.require('goog.events.BrowserEvent');
goog.require('goog.object');
/**
* @param {ol.pointer.PointerEventHandler} dispatcher
* @param {Object.<string, function(goog.events.BrowserEvent)>} mapping
* @constructor
*/
ol.pointer.EventSource = function(dispatcher, mapping) {
/**
* @type {ol.pointer.PointerEventHandler}
*/
this.dispatcher = dispatcher;
/**
* @private
* @const
* @type {Object.<string, function(goog.events.BrowserEvent)>}
*/
this.mapping_ = mapping;
};
/**
* List of events supported by this source.
* @return {Array.<string>} Event names
*/
ol.pointer.EventSource.prototype.getEvents = function() {
return goog.object.getKeys(this.mapping_);
};
/**
* Returns a mapping between the supported event types and
* the handlers that should handle an event.
* @return {Object.<string, function(goog.events.BrowserEvent)>}
* Event/Handler mapping
*/
ol.pointer.EventSource.prototype.getMapping = function() {
return this.mapping_;
};
/**
* Returns the handler that should handle a given event type.
* @param {string} eventType
* @return {function(goog.events.BrowserEvent)} Handler
*/
ol.pointer.EventSource.prototype.getHandlerForEvent = function(eventType) {
return this.mapping_[eventType];
};

View File

@@ -0,0 +1,255 @@
// Based on https://github.com/Polymer/PointerEvents
// Copyright (c) 2013 The Polymer Authors. All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
goog.provide('ol.pointer.MouseSource');
goog.require('goog.object');
goog.require('ol.pointer.EventSource');
/**
* @param {ol.pointer.PointerEventHandler} dispatcher
* @constructor
* @extends {ol.pointer.EventSource}
*/
ol.pointer.MouseSource = function(dispatcher) {
var mapping = {
'mousedown': this.mousedown,
'mousemove': this.mousemove,
'mouseup': this.mouseup,
'mouseover': this.mouseover,
'mouseout': this.mouseout
};
goog.base(this, dispatcher, mapping);
/**
* @const
* @type {Object.<string, goog.events.BrowserEvent|Object>}
*/
this.pointerMap = dispatcher.pointerMap;
/**
* @const
* @type {Array.<goog.math.Coordinate>}
*/
this.lastTouches = [];
};
goog.inherits(ol.pointer.MouseSource, ol.pointer.EventSource);
/**
* @const
* @type {number}
*/
ol.pointer.MouseSource.POINTER_ID = 1;
/**
* @const
* @type {string}
*/
ol.pointer.MouseSource.POINTER_TYPE = 'mouse';
/**
* Radius around touchend that swallows mouse events.
*
* @const
* @type {number}
*/
ol.pointer.MouseSource.DEDUP_DIST = 25;
/**
* Detect if a mouse event was simulated from a touch by
* checking if previously there was a touch event at the
* same position.
*
* FIXME - Known problem with the native Android browser on
* Samsung GT-I9100 (Android 4.1.2):
* In case the page is scrolled, this function does not work
* correctly when a canvas is used (WebGL or canvas renderer).
* Mouse listeners on canvas elements (for this browser), create
* two mouse events: One 'good' and one 'bad' one (on other browsers or
* when a div is used, there is only one event). For the 'bad' one,
* clientX/clientY and also pageX/pageY are wrong when the page
* is scrolled. Because of that, this function can not detect if
* the events were simulated from a touch event. As result, a
* pointer event at a wrong position is dispatched, which confuses
* the map interactions.
* It is unclear, how one can get the correct position for the event
* or detect that the positions are invalid.
*
* @private
* @param {goog.events.BrowserEvent} inEvent
* @return {boolean} True, if the event was generated by a touch.
*/
ol.pointer.MouseSource.prototype.isEventSimulatedFromTouch_ =
function(inEvent) {
var lts = this.lastTouches;
var x = inEvent.clientX, y = inEvent.clientY;
for (var i = 0, l = lts.length, t; i < l && (t = lts[i]); i++) {
// simulated mouse events will be swallowed near a primary touchend
var dx = Math.abs(x - t.x), dy = Math.abs(y - t.y);
if (dx <= ol.pointer.MouseSource.DEDUP_DIST &&
dy <= ol.pointer.MouseSource.DEDUP_DIST) {
return true;
}
}
return false;
};
/**
* Creates a copy of the original event that will be used
* for the fake pointer event.
*
* @param {goog.events.BrowserEvent} inEvent
* @param {ol.pointer.PointerEventHandler} dispatcher
* @return {Object}
*/
ol.pointer.MouseSource.prepareEvent = function(inEvent, dispatcher) {
var e = dispatcher.cloneEvent(inEvent, inEvent.getBrowserEvent());
// forward mouse preventDefault
var pd = e.preventDefault;
e.preventDefault = function() {
inEvent.preventDefault();
pd();
};
e.pointerId = ol.pointer.MouseSource.POINTER_ID;
e.isPrimary = true;
e.pointerType = ol.pointer.MouseSource.POINTER_TYPE;
return e;
};
/**
* Handler for `mousedown`.
*
* @param {goog.events.BrowserEvent} inEvent
*/
ol.pointer.MouseSource.prototype.mousedown = function(inEvent) {
if (!this.isEventSimulatedFromTouch_(inEvent)) {
var p = goog.object.containsKey(this.pointerMap,
ol.pointer.MouseSource.POINTER_ID.toString());
// TODO(dfreedman) workaround for some elements not sending mouseup
// http://crbug/149091
if (p) {
this.cancel(inEvent);
}
var e = ol.pointer.MouseSource.prepareEvent(inEvent, this.dispatcher);
goog.object.set(this.pointerMap,
ol.pointer.MouseSource.POINTER_ID.toString(), inEvent);
this.dispatcher.down(e, inEvent);
}
};
/**
* Handler for `mousemove`.
*
* @param {goog.events.BrowserEvent} inEvent
*/
ol.pointer.MouseSource.prototype.mousemove = function(inEvent) {
if (!this.isEventSimulatedFromTouch_(inEvent)) {
var e = ol.pointer.MouseSource.prepareEvent(inEvent, this.dispatcher);
this.dispatcher.move(e, inEvent);
}
};
/**
* Handler for `mouseup`.
*
* @param {goog.events.BrowserEvent} inEvent
*/
ol.pointer.MouseSource.prototype.mouseup = function(inEvent) {
if (!this.isEventSimulatedFromTouch_(inEvent)) {
var p = goog.object.get(this.pointerMap,
ol.pointer.MouseSource.POINTER_ID.toString());
if (p && p.button === inEvent.button) {
var e = ol.pointer.MouseSource.prepareEvent(inEvent, this.dispatcher);
this.dispatcher.up(e, inEvent);
this.cleanupMouse();
}
}
};
/**
* Handler for `mouseover`.
*
* @param {goog.events.BrowserEvent} inEvent
*/
ol.pointer.MouseSource.prototype.mouseover = function(inEvent) {
if (!this.isEventSimulatedFromTouch_(inEvent)) {
var e = ol.pointer.MouseSource.prepareEvent(inEvent, this.dispatcher);
this.dispatcher.enterOver(e, inEvent);
}
};
/**
* Handler for `mouseout`.
*
* @param {goog.events.BrowserEvent} inEvent
*/
ol.pointer.MouseSource.prototype.mouseout = function(inEvent) {
if (!this.isEventSimulatedFromTouch_(inEvent)) {
var e = ol.pointer.MouseSource.prepareEvent(inEvent, this.dispatcher);
this.dispatcher.leaveOut(e, inEvent);
}
};
/**
* Dispatches a `pointercancel` event.
*
* @param {goog.events.BrowserEvent} inEvent
*/
ol.pointer.MouseSource.prototype.cancel = function(inEvent) {
var e = ol.pointer.MouseSource.prepareEvent(inEvent, this.dispatcher);
this.dispatcher.cancel(e, inEvent);
this.cleanupMouse();
};
/**
* Remove the mouse from the list of active pointers.
*/
ol.pointer.MouseSource.prototype.cleanupMouse = function() {
goog.object.remove(this.pointerMap,
ol.pointer.MouseSource.POINTER_ID.toString());
};

196
src/ol/pointer/mssource.js Normal file
View File

@@ -0,0 +1,196 @@
// Based on https://github.com/Polymer/PointerEvents
// Copyright (c) 2013 The Polymer Authors. All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
goog.provide('ol.pointer.MsSource');
goog.require('goog.object');
goog.require('ol.pointer.EventSource');
/**
* @param {ol.pointer.PointerEventHandler} dispatcher
* @constructor
* @extends {ol.pointer.EventSource}
*/
ol.pointer.MsSource = function(dispatcher) {
var mapping = {
'MSPointerDown': this.msPointerDown,
'MSPointerMove': this.msPointerMove,
'MSPointerUp': this.msPointerUp,
'MSPointerOut': this.msPointerOut,
'MSPointerOver': this.msPointerOver,
'MSPointerCancel': this.msPointerCancel,
'MSGotPointerCapture': this.msGotPointerCapture,
'MSLostPointerCapture': this.msLostPointerCapture
};
goog.base(this, dispatcher, mapping);
/**
* @const
* @type {Object.<string, goog.events.BrowserEvent|Object>}
*/
this.pointerMap = dispatcher.pointerMap;
/**
* @const
* @type {Array.<string>}
*/
this.POINTER_TYPES = [
'',
'unavailable',
'touch',
'pen',
'mouse'
];
};
goog.inherits(ol.pointer.MsSource, ol.pointer.EventSource);
/**
* Creates a copy of the original event that will be used
* for the fake pointer event.
*
* @private
* @param {goog.events.BrowserEvent} inEvent
* @return {Object}
*/
ol.pointer.MsSource.prototype.prepareEvent_ = function(inEvent) {
var e = inEvent;
if (goog.isNumber(inEvent.getBrowserEvent().pointerType)) {
e = this.dispatcher.cloneEvent(inEvent, inEvent.getBrowserEvent());
e.pointerType = this.POINTER_TYPES[inEvent.getBrowserEvent().pointerType];
}
return e;
};
/**
* Remove this pointer from the list of active pointers.
* @param {number} pointerId
*/
ol.pointer.MsSource.prototype.cleanup = function(pointerId) {
goog.object.remove(this.pointerMap, pointerId);
};
/**
* Handler for `msPointerDown`.
*
* @param {goog.events.BrowserEvent} inEvent
*/
ol.pointer.MsSource.prototype.msPointerDown = function(inEvent) {
goog.object.set(this.pointerMap,
inEvent.getBrowserEvent().pointerId, inEvent);
var e = this.prepareEvent_(inEvent);
this.dispatcher.down(e, inEvent);
};
/**
* Handler for `msPointerMove`.
*
* @param {goog.events.BrowserEvent} inEvent
*/
ol.pointer.MsSource.prototype.msPointerMove = function(inEvent) {
var e = this.prepareEvent_(inEvent);
this.dispatcher.move(e, inEvent);
};
/**
* Handler for `msPointerUp`.
*
* @param {goog.events.BrowserEvent} inEvent
*/
ol.pointer.MsSource.prototype.msPointerUp = function(inEvent) {
var e = this.prepareEvent_(inEvent);
this.dispatcher.up(e, inEvent);
this.cleanup(inEvent.getBrowserEvent().pointerId);
};
/**
* Handler for `msPointerOut`.
*
* @param {goog.events.BrowserEvent} inEvent
*/
ol.pointer.MsSource.prototype.msPointerOut = function(inEvent) {
var e = this.prepareEvent_(inEvent);
this.dispatcher.leaveOut(e, inEvent);
};
/**
* Handler for `msPointerOver`.
*
* @param {goog.events.BrowserEvent} inEvent
*/
ol.pointer.MsSource.prototype.msPointerOver = function(inEvent) {
var e = this.prepareEvent_(inEvent);
this.dispatcher.enterOver(e, inEvent);
};
/**
* Handler for `msPointerCancel`.
*
* @param {goog.events.BrowserEvent} inEvent
*/
ol.pointer.MsSource.prototype.msPointerCancel = function(inEvent) {
var e = this.prepareEvent_(inEvent);
this.dispatcher.cancel(e, inEvent);
this.cleanup(inEvent.getBrowserEvent().pointerId);
};
/**
* Handler for `msLostPointerCapture`.
*
* @param {goog.events.BrowserEvent} inEvent
*/
ol.pointer.MsSource.prototype.msLostPointerCapture = function(inEvent) {
var e = this.dispatcher.makeEvent('lostpointercapture',
inEvent.getBrowserEvent(), inEvent);
this.dispatcher.dispatchEvent(e);
};
/**
* Handler for `msGotPointerCapture`.
*
* @param {goog.events.BrowserEvent} inEvent
*/
ol.pointer.MsSource.prototype.msGotPointerCapture = function(inEvent) {
var e = this.dispatcher.makeEvent('gotpointercapture',
inEvent.getBrowserEvent(), inEvent);
this.dispatcher.dispatchEvent(e);
};

View File

@@ -0,0 +1,136 @@
// Based on https://github.com/Polymer/PointerEvents
// Copyright (c) 2013 The Polymer Authors. All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
goog.provide('ol.pointer.NativeSource');
goog.require('goog.object');
goog.require('ol.pointer.EventSource');
/**
* @param {ol.pointer.PointerEventHandler} dispatcher
* @constructor
* @extends {ol.pointer.EventSource}
*/
ol.pointer.NativeSource = function(dispatcher) {
var mapping = {
'pointerdown': this.pointerDown,
'pointermove': this.pointerMove,
'pointerup': this.pointerUp,
'pointerout': this.pointerOut,
'pointerover': this.pointerOver,
'pointercancel': this.pointerCancel,
'gotpointercapture': this.gotPointerCapture,
'lostpointercapture': this.lostPointerCapture
};
goog.base(this, dispatcher, mapping);
};
goog.inherits(ol.pointer.NativeSource, ol.pointer.EventSource);
/**
* Handler for `pointerdown`.
*
* @param {goog.events.BrowserEvent} inEvent
*/
ol.pointer.NativeSource.prototype.pointerDown = function(inEvent) {
this.dispatcher.fireNativeEvent(inEvent);
};
/**
* Handler for `pointermove`.
*
* @param {goog.events.BrowserEvent} inEvent
*/
ol.pointer.NativeSource.prototype.pointerMove = function(inEvent) {
this.dispatcher.fireNativeEvent(inEvent);
};
/**
* Handler for `pointerup`.
*
* @param {goog.events.BrowserEvent} inEvent
*/
ol.pointer.NativeSource.prototype.pointerUp = function(inEvent) {
this.dispatcher.fireNativeEvent(inEvent);
};
/**
* Handler for `pointerout`.
*
* @param {goog.events.BrowserEvent} inEvent
*/
ol.pointer.NativeSource.prototype.pointerOut = function(inEvent) {
this.dispatcher.fireNativeEvent(inEvent);
};
/**
* Handler for `pointerover`.
*
* @param {goog.events.BrowserEvent} inEvent
*/
ol.pointer.NativeSource.prototype.pointerOver = function(inEvent) {
this.dispatcher.fireNativeEvent(inEvent);
};
/**
* Handler for `pointercancel`.
*
* @param {goog.events.BrowserEvent} inEvent
*/
ol.pointer.NativeSource.prototype.pointerCancel = function(inEvent) {
this.dispatcher.fireNativeEvent(inEvent);
};
/**
* Handler for `lostpointercapture`.
*
* @param {goog.events.BrowserEvent} inEvent
*/
ol.pointer.NativeSource.prototype.lostPointerCapture = function(inEvent) {
this.dispatcher.fireNativeEvent(inEvent);
};
/**
* Handler for `gotpointercapture`.
*
* @param {goog.events.BrowserEvent} inEvent
*/
ol.pointer.NativeSource.prototype.gotPointerCapture = function(inEvent) {
this.dispatcher.fireNativeEvent(inEvent);
};

View File

@@ -0,0 +1,295 @@
// Based on https://github.com/Polymer/PointerEvents
// Copyright (c) 2013 The Polymer Authors. All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
goog.provide('ol.pointer.PointerEvent');
goog.require('goog.events');
goog.require('goog.events.Event');
goog.require('goog.object');
/**
* A class for pointer events.
*
* This class is used as an abstraction for mouse events,
* touch events and even native pointer events.
*
* @constructor
* @extends {goog.events.Event}
* @param {string} type The type of the event to create.
* @param {goog.events.BrowserEvent} browserEvent
* @param {Object.<string, ?>=} opt_eventDict An optional dictionary of
* initial event properties.
*/
ol.pointer.PointerEvent = function(type, browserEvent, opt_eventDict) {
goog.base(this, type);
/**
* @const
* @type {goog.events.BrowserEvent}
*/
this.browserEvent = browserEvent;
/**
* @const
* @type {Event}
*/
this.originalEvent = browserEvent.getBrowserEvent();
var eventDict = goog.isDef(opt_eventDict) ? opt_eventDict : {};
/**
* @type {number}
*/
this.buttons = this.getButtons_(eventDict);
/**
* @type {number}
*/
this.pressure = this.getPressure_(eventDict, this.buttons);
// MouseEvent related properties
/**
* @type {boolean}
*/
this.bubbles = goog.object.get(eventDict, 'bubbles', false);
/**
* @type {boolean}
*/
this.cancelable = goog.object.get(eventDict, 'cancelable', false);
/**
* @type {Object}
*/
this.view = goog.object.get(eventDict, 'view', null);
/**
* @type {number}
*/
this.detail = goog.object.get(eventDict, 'detail', null);
/**
* @type {number}
*/
this.screenX = goog.object.get(eventDict, 'screenX', 0);
/**
* @type {number}
*/
this.screenY = goog.object.get(eventDict, 'screenY', 0);
/**
* @type {number}
*/
this.clientX = goog.object.get(eventDict, 'clientX', 0);
/**
* @type {number}
*/
this.clientY = goog.object.get(eventDict, 'clientY', 0);
/**
* @type {boolean}
*/
this.ctrlKey = goog.object.get(eventDict, 'ctrlKey', false);
/**
* @type {boolean}
*/
this.altKey = goog.object.get(eventDict, 'altKey', false);
/**
* @type {boolean}
*/
this.shiftKey = goog.object.get(eventDict, 'shiftKey', false);
/**
* @type {boolean}
*/
this.metaKey = goog.object.get(eventDict, 'metaKey', false);
/**
* @type {number}
*/
this.button = goog.object.get(eventDict, 'button', 0);
/**
* @type {Node}
*/
this.relatedTarget = goog.object.get(eventDict, 'relatedTarget', null);
// PointerEvent related properties
/**
* @const
* @type {number}
*/
this.pointerId = goog.object.get(eventDict, 'pointerId', 0);
/**
* @type {number}
*/
this.width = goog.object.get(eventDict, 'width', 0);
/**
* @type {number}
*/
this.height = goog.object.get(eventDict, 'height', 0);
/**
* @type {number}
*/
this.tiltX = goog.object.get(eventDict, 'tiltX', 0);
/**
* @type {number}
*/
this.tiltY = goog.object.get(eventDict, 'tiltY', 0);
/**
* @type {string}
*/
this.pointerType = goog.object.get(eventDict, 'pointerType', '');
/**
* @type {number}
*/
this.hwTimestamp = goog.object.get(eventDict, 'hwTimestamp', 0);
/**
* @type {boolean}
*/
this.isPrimary = goog.object.get(eventDict, 'isPrimary', false);
// keep the semantics of preventDefault
if (browserEvent.preventDefault) {
this.preventDefault = function() {
browserEvent.preventDefault();
};
}
};
goog.inherits(ol.pointer.PointerEvent, goog.events.Event);
/**
* @private
* @param {Object.<string, ?>} eventDict
* @return {number}
*/
ol.pointer.PointerEvent.prototype.getButtons_ = function(eventDict) {
// 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;
if (eventDict.buttons || ol.pointer.PointerEvent.HAS_BUTTONS) {
buttons = eventDict.buttons;
} else {
switch (eventDict.which) {
case 1: buttons = 1; break;
case 2: buttons = 4; break;
case 3: buttons = 2; break;
default: buttons = 0;
}
}
return buttons;
};
/**
* @private
* @param {Object.<string, ?>} eventDict
* @param {number} buttons
* @return {number}
*/
ol.pointer.PointerEvent.prototype.getPressure_ = function(eventDict, buttons) {
// Spec requires that pointers without pressure specified use 0.5 for down
// state and 0 for up state.
var pressure = 0;
if (eventDict.pressure) {
pressure = eventDict.pressure;
} else {
pressure = buttons ? 0.5 : 0;
}
return pressure;
};
/**
* Is the `buttons` property supported?
* @type {boolean}
*/
ol.pointer.PointerEvent.HAS_BUTTONS = false;
/**
* Checks if the `buttons` property is supported.
*/
(function() {
try {
var ev = ol.pointer.PointerEvent.createMouseEvent('click', {buttons: 1});
ol.pointer.PointerEvent.HAS_BUTTONS = ev.buttons === 1;
} catch (e) {
}
})();
/**
* Warning is suppressed because Closure thinks the MouseEvent
* constructor takes no arguments.
* @param {string} inType The type of the event to create.
* @param {Object} inDict An dictionary of initial event properties.
* @return {MouseEvent}
* @suppress {checkTypes}
*/
ol.pointer.PointerEvent.createMouseEvent = function(inType, inDict) {
return new MouseEvent(inType, inDict);
};

View File

@@ -0,0 +1,496 @@
// Based on https://github.com/Polymer/PointerEvents
// Copyright (c) 2013 The Polymer Authors. All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
goog.provide('ol.pointer.PointerEventHandler');
goog.require('goog.array');
goog.require('goog.events');
goog.require('goog.events.BrowserEvent');
goog.require('goog.events.Event');
goog.require('goog.events.EventTarget');
goog.require('ol.BrowserFeature');
goog.require('ol.pointer.MouseSource');
goog.require('ol.pointer.MsSource');
goog.require('ol.pointer.NativeSource');
goog.require('ol.pointer.PointerEvent');
goog.require('ol.pointer.TouchSource');
/**
* @constructor
* @extends {goog.events.EventTarget}
* @param {Element|HTMLDocument} element Viewport element.
*/
ol.pointer.PointerEventHandler = function(element) {
goog.base(this);
/**
* @const
* @private
* @type {Element|HTMLDocument}
*/
this.element_ = element;
/**
* @const
* @type {Object.<string, goog.events.BrowserEvent|Object>}
*/
this.pointerMap = {};
/**
* @type {Object.<string, function(goog.events.BrowserEvent)>}
* @private
*/
this.eventMap_ = {};
/**
* @type {Array.<ol.pointer.EventSource>}
* @private
*/
this.eventSourceList_ = [];
this.registerSources();
};
goog.inherits(ol.pointer.PointerEventHandler, goog.events.EventTarget);
/**
* Set up the event sources (mouse, touch and native pointers)
* that generate pointer events.
*/
ol.pointer.PointerEventHandler.prototype.registerSources = function() {
if (ol.BrowserFeature.HAS_POINTER) {
this.registerSource('native', new ol.pointer.NativeSource(this));
} else if (ol.BrowserFeature.HAS_MSPOINTER) {
this.registerSource('ms', new ol.pointer.MsSource(this));
} else {
var mouseSource = new ol.pointer.MouseSource(this);
this.registerSource('mouse', mouseSource);
if (ol.BrowserFeature.HAS_TOUCH) {
this.registerSource('touch',
new ol.pointer.TouchSource(this, mouseSource));
}
}
// register events on the viewport element
this.register_();
};
/**
* Add a new event source that will generate pointer events.
*
* @param {string} name A name for the event source
* @param {ol.pointer.EventSource} source
*/
ol.pointer.PointerEventHandler.prototype.registerSource =
function(name, source) {
var s = source;
var newEvents = s.getEvents();
if (newEvents) {
goog.array.forEach(newEvents, function(e) {
var handler = s.getHandlerForEvent(e);
if (handler) {
this.eventMap_[e] = goog.bind(handler, s);
}
}, this);
this.eventSourceList_.push(s);
}
};
/**
* Set up the events for all registered event sources.
* @private
*/
ol.pointer.PointerEventHandler.prototype.register_ = function() {
var l = this.eventSourceList_.length;
var eventSource;
for (var i = 0; i < l; i++) {
eventSource = this.eventSourceList_[i];
this.addEvents_(eventSource.getEvents());
}
};
/**
* Remove all registered events.
* @private
*/
ol.pointer.PointerEventHandler.prototype.unregister_ = function() {
var l = this.eventSourceList_.length;
var eventSource;
for (var i = 0; i < l; i++) {
eventSource = this.eventSourceList_[i];
this.removeEvents_(eventSource.getEvents());
}
};
/**
* Calls the right handler for a new event.
* @private
* @param {goog.events.BrowserEvent} inEvent Browser event.
*/
ol.pointer.PointerEventHandler.prototype.eventHandler_ = function(inEvent) {
var type = inEvent.type;
var handler = this.eventMap_[type];
if (handler) {
handler(inEvent);
}
};
/**
* Setup listeners for the given events.
* @private
* @param {Array.<string>} events List of events.
*/
ol.pointer.PointerEventHandler.prototype.addEvents_ = function(events) {
goog.array.forEach(events, function(eventName) {
goog.events.listen(this.element_, eventName,
this.eventHandler_, false, this);
}, this);
};
/**
* Unregister listeners for the given events.
* @private
* @param {Array.<string>} events List of events.
*/
ol.pointer.PointerEventHandler.prototype.removeEvents_ = function(events) {
goog.array.forEach(events, function(e) {
goog.events.unlisten(this.element_, e,
this.eventHandler_, false, this);
}, this);
};
/**
* Returns a snapshot of inEvent, with writable properties.
*
* @param {goog.events.BrowserEvent} browserEvent Browser event.
* @param {Event|Touch} inEvent An event that contains
* properties to copy.
* @return {Object} An object containing shallow copies of
* `inEvent`'s properties.
*/
ol.pointer.PointerEventHandler.prototype.cloneEvent =
function(browserEvent, inEvent) {
var eventCopy = {}, p;
for (var i = 0, ii = ol.pointer.CLONE_PROPS.length; i < ii; i++) {
p = ol.pointer.CLONE_PROPS[i][0];
eventCopy[p] =
browserEvent[p] ||
inEvent[p] ||
ol.pointer.CLONE_PROPS[i][1];
}
return eventCopy;
};
// EVENTS
/**
* Triggers a 'pointerdown' event.
* @param {Object} pointerEventData
* @param {goog.events.BrowserEvent } browserEvent
*/
ol.pointer.PointerEventHandler.prototype.down =
function(pointerEventData, browserEvent) {
this.fireEvent(ol.pointer.EventType.POINTERDOWN,
pointerEventData, browserEvent);
};
/**
* Triggers a 'pointermove' event.
* @param {Object} pointerEventData
* @param {goog.events.BrowserEvent } browserEvent
*/
ol.pointer.PointerEventHandler.prototype.move =
function(pointerEventData, browserEvent) {
this.fireEvent(ol.pointer.EventType.POINTERMOVE,
pointerEventData, browserEvent);
};
/**
* Triggers a 'pointerup' event.
* @param {Object} pointerEventData
* @param {goog.events.BrowserEvent } browserEvent
*/
ol.pointer.PointerEventHandler.prototype.up =
function(pointerEventData, browserEvent) {
this.fireEvent(ol.pointer.EventType.POINTERUP,
pointerEventData, browserEvent);
};
/**
* Triggers a 'pointerenter' event.
* @param {Object} pointerEventData
* @param {goog.events.BrowserEvent } browserEvent
*/
ol.pointer.PointerEventHandler.prototype.enter =
function(pointerEventData, browserEvent) {
pointerEventData.bubbles = false;
this.fireEvent(ol.pointer.EventType.POINTERENTER,
pointerEventData, browserEvent);
};
/**
* Triggers a 'pointerleave' event.
* @param {Object} pointerEventData
* @param {goog.events.BrowserEvent } browserEvent
*/
ol.pointer.PointerEventHandler.prototype.leave =
function(pointerEventData, browserEvent) {
pointerEventData.bubbles = false;
this.fireEvent(ol.pointer.EventType.POINTERLEAVE,
pointerEventData, browserEvent);
};
/**
* Triggers a 'pointerover' event.
* @param {Object} pointerEventData
* @param {goog.events.BrowserEvent } browserEvent
*/
ol.pointer.PointerEventHandler.prototype.over =
function(pointerEventData, browserEvent) {
pointerEventData.bubbles = true;
this.fireEvent(ol.pointer.EventType.POINTEROVER,
pointerEventData, browserEvent);
};
/**
* Triggers a 'pointerout' event.
* @param {Object} pointerEventData
* @param {goog.events.BrowserEvent } browserEvent
*/
ol.pointer.PointerEventHandler.prototype.out =
function(pointerEventData, browserEvent) {
pointerEventData.bubbles = true;
this.fireEvent(ol.pointer.EventType.POINTEROUT,
pointerEventData, browserEvent);
};
/**
* Triggers a 'pointercancel' event.
* @param {Object} pointerEventData
* @param {goog.events.BrowserEvent } browserEvent
*/
ol.pointer.PointerEventHandler.prototype.cancel =
function(pointerEventData, browserEvent) {
this.fireEvent(ol.pointer.EventType.POINTERCANCEL,
pointerEventData, browserEvent);
};
/**
* Triggers a combination of 'pointerout' and 'pointerleave' events.
* @param {Object} pointerEventData
* @param {goog.events.BrowserEvent } browserEvent
*/
ol.pointer.PointerEventHandler.prototype.leaveOut =
function(pointerEventData, browserEvent) {
this.out(pointerEventData, browserEvent);
if (!this.contains_(
pointerEventData.target,
pointerEventData.relatedTarget)) {
this.leave(pointerEventData, browserEvent);
}
};
/**
* Triggers a combination of 'pointerover' and 'pointerevents' events.
* @param {Object} pointerEventData
* @param {goog.events.BrowserEvent } browserEvent
*/
ol.pointer.PointerEventHandler.prototype.enterOver =
function(pointerEventData, browserEvent) {
this.over(pointerEventData, browserEvent);
if (!this.contains_(
pointerEventData.target,
pointerEventData.relatedTarget)) {
this.enter(pointerEventData, browserEvent);
}
};
/**
* @private
* @param {Element} container
* @param {Element} contained
* @return {boolean} Returns true if the container element
* contains the other element.
*/
ol.pointer.PointerEventHandler.prototype.contains_ =
function(container, contained) {
return container.contains(contained);
};
// EVENT CREATION AND TRACKING
/**
* Creates a new Event of type `inType`, based on the information in
* `pointerEventData`.
*
* @param {string} inType A string representing the type of event to create.
* @param {Object} pointerEventData
* @param {goog.events.BrowserEvent } browserEvent
* @return {ol.pointer.PointerEvent} A PointerEvent of type `inType`.
*/
ol.pointer.PointerEventHandler.prototype.makeEvent =
function(inType, pointerEventData, browserEvent) {
return new ol.pointer.PointerEvent(inType, browserEvent, pointerEventData);
};
/**
* Make and dispatch an event in one call.
* @param {string} inType A string representing the type of event.
* @param {Object} pointerEventData
* @param {goog.events.BrowserEvent } browserEvent
*/
ol.pointer.PointerEventHandler.prototype.fireEvent =
function(inType, pointerEventData, browserEvent) {
var e = this.makeEvent(inType, pointerEventData, browserEvent);
this.dispatchEvent(e);
};
/**
* Creates a pointer event from a native pointer event
* and dispatches this event.
* @param {goog.events.BrowserEvent} nativeEvent A platform event with a target.
*/
ol.pointer.PointerEventHandler.prototype.fireNativeEvent =
function(nativeEvent) {
var e = this.makeEvent(nativeEvent.type, nativeEvent.getBrowserEvent(),
nativeEvent);
this.dispatchEvent(e);
};
/**
* Wrap a native mouse event into a pointer event.
* This proxy method is required for the legacy IE support.
* @param {string} eventType The pointer event type.
* @param {goog.events.BrowserEvent} browserEvent
* @return {ol.pointer.PointerEvent}
*/
ol.pointer.PointerEventHandler.prototype.wrapMouseEvent =
function(eventType, browserEvent) {
var pointerEvent = this.makeEvent(
eventType,
ol.pointer.MouseSource.prepareEvent(browserEvent, this),
browserEvent
);
return pointerEvent;
};
/**
* @inheritDoc
*/
ol.pointer.PointerEventHandler.prototype.disposeInternal = function() {
this.unregister_();
goog.base(this, 'disposeInternal');
};
/**
* Constants for event names.
* @enum {string}
*/
ol.pointer.EventType = {
POINTERMOVE: 'pointermove',
POINTERDOWN: 'pointerdown',
POINTERUP: 'pointerup',
POINTEROVER: 'pointerover',
POINTEROUT: 'pointerout',
POINTERENTER: 'pointerenter',
POINTERLEAVE: 'pointerleave',
POINTERCANCEL: 'pointercancel'
};
/**
* Properties to copy when cloning an event, with default values.
* @type {Array.<Array>}
*/
ol.pointer.CLONE_PROPS = [
// MouseEvent
['bubbles', false],
['cancelable', false],
['view', null],
['detail', null],
['screenX', 0],
['screenY', 0],
['clientX', 0],
['clientY', 0],
['ctrlKey', false],
['altKey', false],
['shiftKey', false],
['metaKey', false],
['button', 0],
['relatedTarget', null],
// DOM Level 3
['buttons', 0],
// PointerEvent
['pointerId', 0],
['width', 0],
['height', 0],
['pressure', 0],
['tiltX', 0],
['tiltY', 0],
['pointerType', ''],
['hwTimestamp', 0],
['isPrimary', false],
// event instance
['type', ''],
['target', null],
['currentTarget', null],
['which', 0]
];

View File

@@ -0,0 +1,445 @@
// Based on https://github.com/Polymer/PointerEvents
// Copyright (c) 2013 The Polymer Authors. All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
goog.provide('ol.pointer.TouchSource');
goog.require('goog.array');
goog.require('goog.math.Coordinate');
goog.require('goog.object');
goog.require('ol.pointer.EventSource');
goog.require('ol.pointer.MouseSource');
/**
* @constructor
* @param {ol.pointer.PointerEventHandler} dispatcher
* @param {ol.pointer.MouseSource} mouseSource
* @extends {ol.pointer.EventSource}
*/
ol.pointer.TouchSource = function(dispatcher, mouseSource) {
var mapping = {
'touchstart': this.touchstart,
'touchmove': this.touchmove,
'touchend': this.touchend,
'touchcancel': this.touchcancel
};
goog.base(this, dispatcher, mapping);
/**
* @const
* @type {Object.<string, goog.events.BrowserEvent|Object>}
*/
this.pointerMap = dispatcher.pointerMap;
/**
* @const
* @type {ol.pointer.MouseSource}
*/
this.mouseSource = mouseSource;
/**
* @private
* @type {number|undefined}
*/
this.firstTouchId_ = undefined;
/**
* @private
* @type {number}
*/
this.clickCount_ = 0;
/**
* @private
* @type {number|undefined}
*/
this.resetId_ = undefined;
/**
* @private
* @type {function()}
*/
this.resetClickCountHandler_ = goog.bind(function() {
this.clickCount_ = 0;
this.resetId_ = undefined;
}, this);
};
goog.inherits(ol.pointer.TouchSource, ol.pointer.EventSource);
/**
* Mouse event timeout: This should be long enough to
* ignore compat mouse events made by touch.
* @const
* @type {number}
*/
ol.pointer.TouchSource.DEDUP_TIMEOUT = 2500;
/**
* @const
* @type {number}
*/
ol.pointer.TouchSource.CLICK_COUNT_TIMEOUT = 200;
/**
* @const
* @type {string}
*/
ol.pointer.TouchSource.POINTER_TYPE = 'touch';
/**
* @private
* @param {Touch} inTouch
* @return {boolean} True, if this is the primary touch.
*/
ol.pointer.TouchSource.prototype.isPrimaryTouch_ = function(inTouch) {
return this.firstTouchId_ === inTouch.identifier;
};
/**
* Set primary touch if there are no pointers, or the only pointer is the mouse.
* @param {Touch} inTouch
* @private
*/
ol.pointer.TouchSource.prototype.setPrimaryTouch_ = function(inTouch) {
if (goog.object.getCount(this.pointerMap) === 0 ||
(goog.object.getCount(this.pointerMap) === 1 &&
goog.object.containsKey(this.pointerMap,
ol.pointer.MouseSource.POINTER_ID.toString()))) {
this.firstTouchId_ = inTouch.identifier;
this.cancelResetClickCount_();
}
};
/**
* @private
* @param {Object} inPointer
*/
ol.pointer.TouchSource.prototype.removePrimaryPointer_ = function(inPointer) {
if (inPointer.isPrimary) {
this.firstTouchId_ = undefined;
this.resetClickCount_();
}
};
/**
* @private
*/
ol.pointer.TouchSource.prototype.resetClickCount_ = function() {
this.resetId_ = goog.global.setTimeout(this.resetClickCountHandler_,
ol.pointer.TouchSource.CLICK_COUNT_TIMEOUT);
};
/**
* @private
*/
ol.pointer.TouchSource.prototype.cancelResetClickCount_ = function() {
if (goog.isDef(this.resetId_)) {
goog.global.clearTimeout(this.resetId_);
}
};
/**
* @private
* @param {goog.events.BrowserEvent} browserEvent Browser event
* @param {Touch} inTouch Touch event
* @return {Object}
*/
ol.pointer.TouchSource.prototype.touchToPointer_ =
function(browserEvent, inTouch) {
var e = this.dispatcher.cloneEvent(browserEvent, inTouch);
// Spec specifies that pointerId 1 is reserved for Mouse.
// Touch identifiers can start at 0.
// Add 2 to the touch identifier for compatibility.
e.pointerId = inTouch.identifier + 2;
// TODO: check if this is neccessary?
//e.target = findTarget(e);
e.bubbles = true;
e.cancelable = true;
e.detail = this.clickCount_;
e.button = 0;
e.buttons = 1;
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 = ol.pointer.TouchSource.POINTER_TYPE;
// make sure that the properties that are different for
// each `Touch` object are not copied from the BrowserEvent object
e.clientX = inTouch.clientX;
e.clientY = inTouch.clientY;
e.screenX = inTouch.screenX;
e.screenY = inTouch.screenY;
return e;
};
/**
* @private
* @param {goog.events.BrowserEvent} inEvent Touch event
* @param {function(goog.events.BrowserEvent, Object)} inFunction
*/
ol.pointer.TouchSource.prototype.processTouches_ =
function(inEvent, inFunction) {
var tl = inEvent.getBrowserEvent().changedTouches;
var touchesCopy = goog.array.clone(tl);
var pointers = goog.array.map(touchesCopy,
goog.partial(this.touchToPointer_, inEvent), this);
// forward touch preventDefaults
goog.array.forEach(pointers, function(p) {
p.preventDefault = function() {
inEvent.preventDefault();
};
}, this);
goog.array.forEach(pointers, goog.partial(inFunction, inEvent), this);
};
/**
* @private
* @param {TouchList} touchList
* @param {number} searchId
* @return {boolean} True, if the `Touch` with the given id is in the list.
*/
ol.pointer.TouchSource.prototype.findTouch_ = function(touchList, searchId) {
var l = touchList.length;
var touch;
for (var i = 0; i < l; i++) {
touch = touchList[i];
if (touch.identifier === searchId) {
return true;
}
}
return false;
};
/**
* In some instances, a touchstart can happen without a touchend. This
* leaves the pointermap in a broken state.
* Therefore, on every touchstart, we remove the touches that did not fire a
* touchend event.
* To keep state globally consistent, we fire a pointercancel for
* this "abandoned" touch
*
* @private
* @param {goog.events.BrowserEvent} inEvent
*/
ol.pointer.TouchSource.prototype.vacuumTouches_ = function(inEvent) {
var touchList = inEvent.getBrowserEvent().touches;
// pointerMap.getCount() should be < touchList.length here,
// as the touchstart has not been processed yet.
if (goog.object.getCount(this.pointerMap) >= touchList.length) {
var d = [];
goog.object.forEach(this.pointerMap, function(value, key) {
// Never remove pointerId == 1, which is mouse.
// Touch identifiers are 2 smaller than their pointerId, which is the
// index in pointermap.
if (key != ol.pointer.MouseSource.POINTER_ID &&
!this.findTouch_(touchList, key - 2)) {
d.push(value.out);
}
}, this);
goog.array.forEach(d, goog.partial(this.cancelOut_, inEvent), this);
}
};
/**
* Handler for `touchstart`, triggers `pointerover`,
* `pointerenter` and `pointerdown` events.
*
* @param {goog.events.BrowserEvent} inEvent
*/
ol.pointer.TouchSource.prototype.touchstart = function(inEvent) {
this.vacuumTouches_(inEvent);
this.setPrimaryTouch_(inEvent.getBrowserEvent().changedTouches[0]);
this.dedupSynthMouse_(inEvent);
this.clickCount_++;
this.processTouches_(inEvent, this.overDown_);
};
/**
* @private
* @param {goog.events.BrowserEvent} browserEvent
* @param {Object} inPointer
*/
ol.pointer.TouchSource.prototype.overDown_ = function(browserEvent, inPointer) {
goog.object.set(this.pointerMap, inPointer.pointerId, {
target: inPointer.target,
out: inPointer,
outTarget: inPointer.target
});
this.dispatcher.over(inPointer, browserEvent);
this.dispatcher.enter(inPointer, browserEvent);
this.dispatcher.down(inPointer, browserEvent);
};
/**
* Handler for `touchmove`.
*
* @param {goog.events.BrowserEvent} inEvent
*/
ol.pointer.TouchSource.prototype.touchmove = function(inEvent) {
inEvent.preventDefault();
this.processTouches_(inEvent, this.moveOverOut_);
};
/**
* @private
* @param {goog.events.BrowserEvent} browserEvent
* @param {Object} inPointer
*/
ol.pointer.TouchSource.prototype.moveOverOut_ =
function(browserEvent, inPointer) {
var event = inPointer;
var pointer = goog.object.get(this.pointerMap, event.pointerId);
// a finger drifted off the screen, ignore it
if (!pointer) {
return;
}
var outEvent = pointer.out;
var outTarget = pointer.outTarget;
this.dispatcher.move(event, browserEvent);
if (outEvent && outTarget !== event.target) {
outEvent.relatedTarget = event.target;
event.relatedTarget = outTarget;
// recover from retargeting by shadow
outEvent.target = outTarget;
if (event.target) {
this.dispatcher.leaveOut(outEvent, browserEvent);
this.dispatcher.enterOver(event, browserEvent);
} else {
// clean up case when finger leaves the screen
event.target = outTarget;
event.relatedTarget = null;
this.cancelOut_(browserEvent, event);
}
}
pointer.out = event;
pointer.outTarget = event.target;
};
/**
* Handler for `touchend`, triggers `pointerup`,
* `pointerout` and `pointerleave` events.
*
* @param {goog.events.BrowserEvent} inEvent
*/
ol.pointer.TouchSource.prototype.touchend = function(inEvent) {
this.dedupSynthMouse_(inEvent);
this.processTouches_(inEvent, this.upOut_);
};
/**
* @private
* @param {goog.events.BrowserEvent} browserEvent
* @param {Object} inPointer
*/
ol.pointer.TouchSource.prototype.upOut_ = function(browserEvent, inPointer) {
this.dispatcher.up(inPointer, browserEvent);
this.dispatcher.out(inPointer, browserEvent);
this.dispatcher.leave(inPointer, browserEvent);
this.cleanUpPointer_(inPointer);
};
/**
* Handler for `touchcancel`, triggers `pointercancel`,
* `pointerout` and `pointerleave` events.
*
* @param {goog.events.BrowserEvent} inEvent
*/
ol.pointer.TouchSource.prototype.touchcancel = function(inEvent) {
this.processTouches_(inEvent, this.cancelOut_);
};
/**
* @private
* @param {goog.events.BrowserEvent} browserEvent
* @param {Object} inPointer
*/
ol.pointer.TouchSource.prototype.cancelOut_ =
function(browserEvent, inPointer) {
this.dispatcher.cancel(inPointer, browserEvent);
this.dispatcher.out(inPointer, browserEvent);
this.dispatcher.leave(inPointer, browserEvent);
this.cleanUpPointer_(inPointer);
};
/**
* @private
* @param {Object} inPointer
*/
ol.pointer.TouchSource.prototype.cleanUpPointer_ = function(inPointer) {
goog.object.remove(this.pointerMap, inPointer.pointerId);
this.removePrimaryPointer_(inPointer);
};
/**
* Prevent synth mouse events from creating pointer events.
*
* @private
* @param {goog.events.BrowserEvent} inEvent
*/
ol.pointer.TouchSource.prototype.dedupSynthMouse_ = function(inEvent) {
var lts = this.mouseSource.lastTouches;
var t = inEvent.getBrowserEvent().changedTouches[0];
// only the primary finger will synth mouse events
if (this.isPrimaryTouch_(t)) {
// remember x/y of last touch
var lt = new goog.math.Coordinate(t.clientX, t.clientY);
lts.push(lt);
goog.global.setTimeout(function() {
// remove touch after timeout
goog.array.remove(lts, lt);
}, ol.pointer.TouchSource.DEDUP_TIMEOUT);
}
};

View File

@@ -48,12 +48,13 @@ describe('ol.interaction.Draw', function() {
var viewport = map.getViewport();
// calculated in case body has top < 0 (test runner with small window)
var position = goog.style.getClientPosition(viewport);
var event = new goog.events.BrowserEvent({
type: type,
clientX: position.x + x + width / 2,
clientY: position.y + y + height / 2
});
goog.events.fireListeners(viewport, type, false, event);
var event = new ol.MapBrowserPointerEvent(type, map,
new ol.pointer.PointerEvent(type,
new goog.events.BrowserEvent({
clientX: position.x + x + width / 2,
clientY: position.y + y + height / 2
})));
map.handleMapBrowserEvent(event);
}
describe('constructor', function() {
@@ -81,10 +82,9 @@ describe('ol.interaction.Draw', function() {
});
it('draws a point on click', function() {
simulateEvent('mousemove', 10, 20);
simulateEvent('mousedown', 10, 20);
simulateEvent('mouseup', 10, 20);
simulateEvent('click', 10, 20);
simulateEvent('pointermove', 10, 20);
simulateEvent('pointerdown', 10, 20);
simulateEvent('pointerup', 10, 20);
var features = source.getFeatures();
expect(features).to.have.length(1);
var geometry = features[0].getGeometry();
@@ -93,11 +93,10 @@ describe('ol.interaction.Draw', function() {
});
it('does not draw a point with a significant drag', function() {
simulateEvent('mousemove', 10, 20);
simulateEvent('mousedown', 10, 20);
simulateEvent('mousemove', 15, 20);
simulateEvent('mouseup', 15, 20);
simulateEvent('click', 15, 20);
simulateEvent('pointermove', 10, 20);
simulateEvent('pointerdown', 10, 20);
simulateEvent('pointermove', 15, 20);
simulateEvent('pointerup', 15, 20);
var features = source.getFeatures();
expect(features).to.have.length(0);
});
@@ -107,11 +106,10 @@ describe('ol.interaction.Draw', function() {
var de = sinon.spy();
goog.events.listen(draw, ol.DrawEventType.DRAWSTART, ds);
goog.events.listen(draw, ol.DrawEventType.DRAWEND, de);
simulateEvent('mousemove', 10, 20);
simulateEvent('mousedown', 10, 20);
simulateEvent('mouseup', 10, 20);
simulateEvent('click', 10, 20);
simulateEvent('mousemove', 20, 20);
simulateEvent('pointermove', 10, 20);
simulateEvent('pointerdown', 10, 20);
simulateEvent('pointerup', 10, 20);
simulateEvent('pointermove', 20, 20);
expect(ds).to.be.called(2);
expect(de).to.be.called(1);
});
@@ -128,10 +126,9 @@ describe('ol.interaction.Draw', function() {
});
it('draws multipoint on click', function() {
simulateEvent('mousemove', 30, 15);
simulateEvent('mousedown', 30, 15);
simulateEvent('mouseup', 30, 15);
simulateEvent('click', 30, 15);
simulateEvent('pointermove', 30, 15);
simulateEvent('pointerdown', 30, 15);
simulateEvent('pointerup', 30, 15);
var features = source.getFeatures();
expect(features).to.have.length(1);
var geometry = features[0].getGeometry();
@@ -154,21 +151,18 @@ describe('ol.interaction.Draw', function() {
it('draws linestring with clicks, finishing on last point', function() {
// first point
simulateEvent('mousemove', 10, 20);
simulateEvent('mousedown', 10, 20);
simulateEvent('mouseup', 10, 20);
simulateEvent('click', 10, 20);
simulateEvent('pointermove', 10, 20);
simulateEvent('pointerdown', 10, 20);
simulateEvent('pointerup', 10, 20);
// second point
simulateEvent('mousemove', 30, 20);
simulateEvent('mousedown', 30, 20);
simulateEvent('mouseup', 30, 20);
simulateEvent('click', 30, 20);
simulateEvent('pointermove', 30, 20);
simulateEvent('pointerdown', 30, 20);
simulateEvent('pointerup', 30, 20);
// finish on second point
simulateEvent('mousedown', 30, 20);
simulateEvent('mouseup', 30, 20);
simulateEvent('click', 30, 20);
simulateEvent('pointerdown', 30, 20);
simulateEvent('pointerup', 30, 20);
var features = source.getFeatures();
expect(features).to.have.length(1);
@@ -179,29 +173,24 @@ describe('ol.interaction.Draw', function() {
it('does not add a point with a significant drag', function() {
// first point
simulateEvent('mousemove', 10, 20);
simulateEvent('mousedown', 10, 20);
simulateEvent('mouseup', 10, 20);
simulateEvent('click', 10, 20);
simulateEvent('pointermove', 10, 20);
simulateEvent('pointerdown', 10, 20);
simulateEvent('pointerup', 10, 20);
// drag map
simulateEvent('mousemove', 15, 20);
simulateEvent('mousedown', 15, 20);
simulateEvent('mousemove', 20, 20);
simulateEvent('mouseup', 20, 20);
simulateEvent('click', 20, 20);
simulateEvent('pointermove', 15, 20);
simulateEvent('pointerdown', 15, 20);
simulateEvent('pointermove', 20, 20);
simulateEvent('pointerup', 20, 20);
// second point
simulateEvent('mousemove', 30, 20);
simulateEvent('mousedown', 30, 20);
simulateEvent('mouseup', 30, 20);
simulateEvent('click', 30, 20);
simulateEvent('pointermove', 30, 20);
simulateEvent('pointerdown', 30, 20);
simulateEvent('pointerup', 30, 20);
// finish on second point
simulateEvent('mousedown', 30, 20);
simulateEvent('mouseup', 30, 20);
simulateEvent('click', 30, 20);
simulateEvent('pointerdown', 30, 20);
simulateEvent('pointerup', 30, 20);
var features = source.getFeatures();
expect(features).to.have.length(1);
@@ -217,22 +206,19 @@ describe('ol.interaction.Draw', function() {
goog.events.listen(draw, ol.DrawEventType.DRAWEND, de);
// first point
simulateEvent('mousemove', 10, 20);
simulateEvent('mousedown', 10, 20);
simulateEvent('mouseup', 10, 20);
simulateEvent('click', 10, 20);
simulateEvent('pointermove', 10, 20);
simulateEvent('pointerdown', 10, 20);
simulateEvent('pointerup', 10, 20);
// second point
simulateEvent('mousemove', 30, 20);
simulateEvent('mousedown', 30, 20);
simulateEvent('mouseup', 30, 20);
simulateEvent('click', 30, 20);
simulateEvent('pointermove', 30, 20);
simulateEvent('pointerdown', 30, 20);
simulateEvent('pointerup', 30, 20);
// finish on second point
simulateEvent('mousedown', 30, 20);
simulateEvent('mouseup', 30, 20);
simulateEvent('click', 30, 20);
simulateEvent('mousemove', 10, 20);
simulateEvent('pointerdown', 30, 20);
simulateEvent('pointerup', 30, 20);
simulateEvent('pointermove', 10, 20);
expect(ds).to.be.called(1);
expect(de).to.be.called(1);
@@ -251,21 +237,18 @@ describe('ol.interaction.Draw', function() {
it('draws multi with clicks, finishing on last point', function() {
// first point
simulateEvent('mousemove', 10, 20);
simulateEvent('mousedown', 10, 20);
simulateEvent('mouseup', 10, 20);
simulateEvent('click', 10, 20);
simulateEvent('pointermove', 10, 20);
simulateEvent('pointerdown', 10, 20);
simulateEvent('pointerup', 10, 20);
// second point
simulateEvent('mousemove', 30, 20);
simulateEvent('mousedown', 30, 20);
simulateEvent('mouseup', 30, 20);
simulateEvent('click', 30, 20);
simulateEvent('pointermove', 30, 20);
simulateEvent('pointerdown', 30, 20);
simulateEvent('pointerup', 30, 20);
// finish on second point
simulateEvent('mousedown', 30, 20);
simulateEvent('mouseup', 30, 20);
simulateEvent('click', 30, 20);
simulateEvent('pointerdown', 30, 20);
simulateEvent('pointerup', 30, 20);
var features = source.getFeatures();
expect(features).to.have.length(1);
@@ -289,28 +272,24 @@ describe('ol.interaction.Draw', function() {
it('draws polygon with clicks, finishing on first point', function() {
// first point
simulateEvent('mousemove', 10, 20);
simulateEvent('mousedown', 10, 20);
simulateEvent('mouseup', 10, 20);
simulateEvent('click', 10, 20);
simulateEvent('pointermove', 10, 20);
simulateEvent('pointerdown', 10, 20);
simulateEvent('pointerup', 10, 20);
// second point
simulateEvent('mousemove', 30, 20);
simulateEvent('mousedown', 30, 20);
simulateEvent('mouseup', 30, 20);
simulateEvent('click', 30, 20);
simulateEvent('pointermove', 30, 20);
simulateEvent('pointerdown', 30, 20);
simulateEvent('pointerup', 30, 20);
// third point
simulateEvent('mousemove', 40, 10);
simulateEvent('mousedown', 40, 10);
simulateEvent('mouseup', 40, 10);
simulateEvent('click', 40, 10);
simulateEvent('pointermove', 40, 10);
simulateEvent('pointerdown', 40, 10);
simulateEvent('pointerup', 40, 10);
// finish on first point
simulateEvent('mousemove', 10, 20);
simulateEvent('mousedown', 10, 20);
simulateEvent('mouseup', 10, 20);
simulateEvent('click', 10, 20);
simulateEvent('pointermove', 10, 20);
simulateEvent('pointerdown', 10, 20);
simulateEvent('pointerup', 10, 20);
var features = source.getFeatures();
expect(features).to.have.length(1);
@@ -324,27 +303,23 @@ describe('ol.interaction.Draw', function() {
it('draws polygon with clicks, finishing on last point', function() {
// first point
simulateEvent('mousemove', 10, 20);
simulateEvent('mousedown', 10, 20);
simulateEvent('mouseup', 10, 20);
simulateEvent('click', 10, 20);
simulateEvent('pointermove', 10, 20);
simulateEvent('pointerdown', 10, 20);
simulateEvent('pointerup', 10, 20);
// second point
simulateEvent('mousemove', 30, 20);
simulateEvent('mousedown', 30, 20);
simulateEvent('mouseup', 30, 20);
simulateEvent('click', 30, 20);
simulateEvent('pointermove', 30, 20);
simulateEvent('pointerdown', 30, 20);
simulateEvent('pointerup', 30, 20);
// third point
simulateEvent('mousemove', 40, 10);
simulateEvent('mousedown', 40, 10);
simulateEvent('mouseup', 40, 10);
simulateEvent('click', 40, 10);
simulateEvent('pointermove', 40, 10);
simulateEvent('pointerdown', 40, 10);
simulateEvent('pointerup', 40, 10);
// finish on last point
simulateEvent('mousedown', 40, 10);
simulateEvent('mouseup', 40, 10);
simulateEvent('click', 40, 11);
simulateEvent('pointerdown', 40, 10);
simulateEvent('pointerup', 40, 10);
var features = source.getFeatures();
expect(features).to.have.length(1);
@@ -363,28 +338,24 @@ describe('ol.interaction.Draw', function() {
goog.events.listen(draw, ol.DrawEventType.DRAWEND, de);
// first point
simulateEvent('mousemove', 10, 20);
simulateEvent('mousedown', 10, 20);
simulateEvent('mouseup', 10, 20);
simulateEvent('click', 10, 20);
simulateEvent('pointermove', 10, 20);
simulateEvent('pointerdown', 10, 20);
simulateEvent('pointerup', 10, 20);
// second point
simulateEvent('mousemove', 30, 20);
simulateEvent('mousedown', 30, 20);
simulateEvent('mouseup', 30, 20);
simulateEvent('click', 30, 20);
simulateEvent('pointermove', 30, 20);
simulateEvent('pointerdown', 30, 20);
simulateEvent('pointerup', 30, 20);
// third point
simulateEvent('mousemove', 30, 10);
simulateEvent('mousedown', 30, 10);
simulateEvent('mouseup', 30, 10);
simulateEvent('click', 30, 10);
simulateEvent('pointermove', 30, 10);
simulateEvent('pointerdown', 30, 10);
simulateEvent('pointerup', 30, 10);
// finish on first point
simulateEvent('mousemove', 10, 20);
simulateEvent('mousedown', 10, 20);
simulateEvent('mouseup', 10, 20);
simulateEvent('click', 10, 20);
simulateEvent('pointermove', 10, 20);
simulateEvent('pointerdown', 10, 20);
simulateEvent('pointerup', 10, 20);
expect(ds).to.be.called(1);
expect(de).to.be.called(1);
@@ -403,28 +374,24 @@ describe('ol.interaction.Draw', function() {
it('draws multi with clicks, finishing on first point', function() {
// first point
simulateEvent('mousemove', 10, 20);
simulateEvent('mousedown', 10, 20);
simulateEvent('mouseup', 10, 20);
simulateEvent('click', 10, 20);
simulateEvent('pointermove', 10, 20);
simulateEvent('pointerdown', 10, 20);
simulateEvent('pointerup', 10, 20);
// second point
simulateEvent('mousemove', 30, 20);
simulateEvent('mousedown', 30, 20);
simulateEvent('mouseup', 30, 20);
simulateEvent('click', 30, 20);
simulateEvent('pointermove', 30, 20);
simulateEvent('pointerdown', 30, 20);
simulateEvent('pointerup', 30, 20);
// third point
simulateEvent('mousemove', 40, 10);
simulateEvent('mousedown', 40, 10);
simulateEvent('mouseup', 40, 10);
simulateEvent('click', 40, 10);
simulateEvent('pointermove', 40, 10);
simulateEvent('pointerdown', 40, 10);
simulateEvent('pointerup', 40, 10);
// finish on first point
simulateEvent('mousemove', 10, 20);
simulateEvent('mousedown', 10, 20);
simulateEvent('mouseup', 10, 20);
simulateEvent('click', 10, 20);
simulateEvent('pointermove', 10, 20);
simulateEvent('pointerdown', 10, 20);
simulateEvent('pointerup', 10, 20);
var features = source.getFeatures();
expect(features).to.have.length(1);
@@ -440,27 +407,23 @@ describe('ol.interaction.Draw', function() {
it('draws multi with clicks, finishing on last point', function() {
// first point
simulateEvent('mousemove', 10, 20);
simulateEvent('mousedown', 10, 20);
simulateEvent('mouseup', 10, 20);
simulateEvent('click', 10, 20);
simulateEvent('pointermove', 10, 20);
simulateEvent('pointerdown', 10, 20);
simulateEvent('pointerup', 10, 20);
// second point
simulateEvent('mousemove', 30, 20);
simulateEvent('mousedown', 30, 20);
simulateEvent('mouseup', 30, 20);
simulateEvent('click', 30, 20);
simulateEvent('pointermove', 30, 20);
simulateEvent('pointerdown', 30, 20);
simulateEvent('pointerup', 30, 20);
// third point
simulateEvent('mousemove', 40, 10);
simulateEvent('mousedown', 40, 10);
simulateEvent('mouseup', 40, 10);
simulateEvent('click', 40, 10);
simulateEvent('pointermove', 40, 10);
simulateEvent('pointerdown', 40, 10);
simulateEvent('pointerup', 40, 10);
// finish on last point
simulateEvent('mousedown', 40, 10);
simulateEvent('mouseup', 40, 10);
simulateEvent('click', 40, 10);
simulateEvent('pointerdown', 40, 10);
simulateEvent('pointerup', 40, 10);
var features = source.getFeatures();
expect(features).to.have.length(1);
@@ -483,6 +446,7 @@ goog.require('goog.events');
goog.require('goog.events.BrowserEvent');
goog.require('goog.style');
goog.require('ol.Map');
goog.require('ol.MapBrowserPointerEvent');
goog.require('ol.RendererHint');
goog.require('ol.View2D');
goog.require('ol.geom.GeometryType');
@@ -495,4 +459,5 @@ goog.require('ol.geom.Polygon');
goog.require('ol.interaction.Draw');
goog.require('ol.interaction.Interaction');
goog.require('ol.layer.Vector');
goog.require('ol.pointer.PointerEvent');
goog.require('ol.source.Vector');

View File

@@ -141,13 +141,12 @@ describe('ol.Map', function() {
options = {
altShiftDragRotate: false,
doubleClickZoom: false,
dragPan: false,
keyboard: false,
mouseWheelZoom: false,
shiftDragZoom: false,
touchPan: false,
touchRotate: false,
touchZoom: false
dragPan: false,
pinchRotate: false,
pinchZoom: false
};
});

View File

@@ -28,12 +28,13 @@ describe('ol.MapBrowserEventHandler', function() {
});
it('emulates click', function() {
handler.emulateClick_(new goog.events.BrowserEvent({
type: 'mousedown',
target: target,
clientX: 0,
clientY: 0
}));
handler.emulateClick_(new ol.pointer.PointerEvent('pointerdown',
new goog.events.BrowserEvent({
type: 'mousedown',
target: target,
clientX: 0,
clientY: 0
})));
expect(singleclickSpy.called).to.not.be.ok();
expect(dblclickSpy.called).to.not.be.ok();
@@ -41,12 +42,13 @@ describe('ol.MapBrowserEventHandler', function() {
expect(singleclickSpy.calledOnce).to.be.ok();
expect(dblclickSpy.called).to.not.be.ok();
handler.emulateClick_(new goog.events.BrowserEvent({
type: 'mousedown',
target: target,
clientX: 0,
clientY: 0
}));
handler.emulateClick_(new ol.pointer.PointerEvent('pointerdown',
new goog.events.BrowserEvent({
type: 'mousedown',
target: target,
clientX: 0,
clientY: 0
})));
expect(singleclickSpy.calledOnce).to.be.ok();
expect(dblclickSpy.called).to.not.be.ok();
});
@@ -61,12 +63,13 @@ describe('ol.MapBrowserEventHandler', function() {
expect(singleclickSpy.called).to.not.be.ok();
expect(dblclickSpy.called).to.not.be.ok();
handler.emulateClick_(new goog.events.BrowserEvent({
type: 'mousedown',
target: target,
clientX: 0,
clientY: 0
}));
handler.emulateClick_(new ol.pointer.PointerEvent('pointerdown',
new goog.events.BrowserEvent({
type: 'mousedown',
target: target,
clientX: 0,
clientY: 0
})));
expect(singleclickSpy.called).to.not.be.ok();
expect(dblclickSpy.calledOnce).to.be.ok();
@@ -88,24 +91,13 @@ describe('ol.MapBrowserEventHandler', function() {
expect(handler.getDown()).to.be(null);
});
it('returns an event after handleMouseDown_ has been called', function() {
var event = new goog.events.BrowserEvent({});
handler.handleMouseDown_(event);
expect(handler.getDown()).to.be(event);
});
it('returns an event after handlePointerDown_ has been called', function() {
var event = new goog.events.BrowserEvent({});
var event = new ol.pointer.PointerEvent('pointerdown',
new goog.events.BrowserEvent({}));
handler.handlePointerDown_(event);
expect(handler.getDown()).to.be(event);
});
it('returns an event after handleTouchStart_ has been called', function() {
var event = new goog.events.BrowserEvent({});
handler.handleTouchStart_(event);
expect(handler.getDown()).to.be(event);
});
});
});
@@ -115,3 +107,4 @@ goog.require('goog.events');
goog.require('goog.events.BrowserEvent');
goog.require('ol.Map');
goog.require('ol.MapBrowserEventHandler');
goog.require('ol.pointer.PointerEvent');

View File

@@ -0,0 +1,101 @@
goog.provide('ol.test.pointer.MouseSource');
describe('ol.pointer.MouseSource', function() {
var handler;
var target;
var eventSpy;
var clock;
beforeEach(function() {
clock = sinon.useFakeTimers();
target = goog.dom.createElement(goog.dom.TagName.DIV);
// make sure that a mouse and touch event source is used
ol.BrowserFeature.HAS_POINTER = false;
ol.BrowserFeature.HAS_MSPOINTER = false;
ol.BrowserFeature.HAS_TOUCH = true;
handler = new ol.pointer.PointerEventHandler(target);
eventSpy = sinon.spy();
});
afterEach(function() {
handler.dispose();
});
describe('simulated mouse events', function() {
it('prevents simulated mouse events', function() {
goog.events.listen(handler, 'pointerdown', eventSpy);
// simulates that a mouse event is triggered from a touch
simulateTouchEvent('touchstart', 10, 20);
simulateEvent('mousedown', 10, 20);
expect(eventSpy.calledOnce).to.be.ok();
});
it('dispatches real mouse events', function() {
goog.events.listen(handler, 'pointerdown', eventSpy);
// the two events are at different positions
simulateTouchEvent('touchstart', 10, 20);
simulateEvent('mousedown', 10, 40);
expect(eventSpy.calledTwice).to.be.ok();
});
it('dispatches real mouse events after timeout', function() {
// set the timeout to a lower value, to speed up the tests
ol.pointer.TouchSource.DEDUP_TIMEOUT = 100;
goog.events.listen(handler, 'pointerdown', eventSpy);
// first simulate a touch event, then a mouse event
// at the same position after a timeout
simulateTouchEvent('touchstart', 10, 20);
clock.tick(150);
simulateEvent('mousedown', 10, 20);
expect(eventSpy.calledTwice).to.be.ok();
});
});
function simulateTouchEvent(type, x, y) {
var touches = [
{
identifier: 4,
clientX: x,
clientX: y,
target: target
}
];
var event = new goog.events.BrowserEvent({
type: type,
touches: touches,
changedTouches: touches
});
goog.events.fireListeners(target, type, false, event);
}
function simulateEvent(type, x, y) {
var event = new goog.events.BrowserEvent({
type: type,
clientX: x,
clientY: y,
target: target
});
goog.events.fireListeners(target, type, false, event);
}
});
goog.require('goog.dom');
goog.require('goog.dom.TagName');
goog.require('goog.events');
goog.require('goog.events.BrowserEvent');
goog.require('ol.BrowserFeature');
goog.require('ol.pointer.MouseSource');
goog.require('ol.pointer.PointerEvent');
goog.require('ol.pointer.PointerEventHandler');
goog.require('ol.pointer.TouchSource');

View File

@@ -0,0 +1,170 @@
goog.provide('ol.test.pointer.PointerEventHandler');
describe('ol.pointer.PointerEventHandler', function() {
var handler;
var target;
var eventSpy;
beforeEach(function() {
target = goog.dom.createElement(goog.dom.TagName.DIV);
// make sure that a mouse event source is used
ol.BrowserFeature.HAS_POINTER = false;
ol.BrowserFeature.HAS_MSPOINTER = false;
handler = new ol.pointer.PointerEventHandler(target);
eventSpy = sinon.spy();
});
afterEach(function() {
handler.dispose();
});
describe('constructor', function() {
it('registers a least one event source', function() {
expect(handler.eventSourceList_.length).to.be.greaterThan(0);
expect(handler.eventSourceList_[0]).to.be.a(ol.pointer.MouseSource);
});
});
function simulateEvent(type, x, y) {
var event = new goog.events.BrowserEvent({
type: type,
clientX: x,
clientY: y,
target: target
});
goog.events.fireListeners(target, type, false, event);
}
describe('pointer down', function() {
it('fires pointerdown events', function() {
goog.events.listen(handler, 'pointerdown', eventSpy);
simulateEvent('mousedown', 0, 0);
expect(eventSpy.calledOnce).to.be.ok();
var pointerEvent = eventSpy.firstCall.args[0];
expect(pointerEvent).to.be.a(ol.pointer.PointerEvent);
expect(pointerEvent.type).to.be('pointerdown');
expect(pointerEvent.pointerId).to.be(1);
expect(pointerEvent.pointerType).to.be('mouse');
});
});
describe('pointer up', function() {
it('fires pointerup events', function() {
goog.events.listen(handler, 'pointerup', eventSpy);
simulateEvent('mousedown', 0, 0);
simulateEvent('mouseup', 0, 0);
expect(eventSpy.calledOnce).to.be.ok();
});
});
describe('pointer move', function() {
it('fires pointermove events', function() {
goog.events.listen(handler, 'pointermove', eventSpy);
simulateEvent('mousemove', 0, 0);
expect(eventSpy.calledOnce).to.be.ok();
});
});
describe('pointer enter and over', function() {
it('fires pointerenter and pointerover events', function() {
var enterEventSpy = sinon.spy();
var overEventSpy = sinon.spy();
goog.events.listen(handler, 'pointerenter', enterEventSpy);
goog.events.listen(handler, 'pointerover', overEventSpy);
simulateEvent('mouseover', 0, 0);
expect(enterEventSpy.calledOnce).to.be.ok();
expect(overEventSpy.calledOnce).to.be.ok();
});
});
describe('pointer leave and out', function() {
it('fires pointerleave and pointerout events', function() {
var leaveEventSpy = sinon.spy();
var outEventSpy = sinon.spy();
goog.events.listen(handler, 'pointerleave', leaveEventSpy);
goog.events.listen(handler, 'pointerout', outEventSpy);
simulateEvent('mouseout', 0, 0);
expect(leaveEventSpy.calledOnce).to.be.ok();
expect(outEventSpy.calledOnce).to.be.ok();
});
});
describe('#cloneEvent', function() {
it('copies the properties of an event', function() {
var event = {
type: 'mousedown',
target: target,
clientX: 1,
clientY: 2
};
var browserEvent = new goog.events.BrowserEvent(event);
var eventClone = handler.cloneEvent(browserEvent, event);
// properties are copied from `event`
expect(eventClone.type).to.be('mousedown');
expect(eventClone.target).to.be(target);
expect(eventClone.clientX).to.be(1);
expect(eventClone.clientY).to.be(2);
// properties are copied from `browserEvent`
expect(eventClone.screenX).to.be(0);
expect(eventClone.screenY).to.be(0);
// properties are copied from the defaults
expect(eventClone.pointerId).to.be(0);
expect(eventClone.pressure).to.be(0);
});
});
describe('#makeEvent', function() {
it('makes a new pointer event', function() {
var event = {
type: 'mousedown',
target: target,
clientX: 1,
clientY: 2
};
var browserEvent = new goog.events.BrowserEvent(event);
var eventClone = handler.cloneEvent(browserEvent, event);
var pointerEvent = handler.makeEvent('pointerdown',
eventClone, browserEvent);
expect(pointerEvent.type).to.be('pointerdown');
expect(pointerEvent.clientX).to.be(1);
expect(pointerEvent.clientY).to.be(2);
expect(pointerEvent.screenX).to.be(0);
expect(pointerEvent.screenY).to.be(0);
expect(pointerEvent.pointerId).to.be(0);
expect(pointerEvent.pressure).to.be(0);
expect(pointerEvent.preventDefault).to.be.ok();
expect(pointerEvent).to.be.a(ol.pointer.PointerEvent);
});
});
});
goog.require('goog.dom');
goog.require('goog.dom.TagName');
goog.require('goog.events');
goog.require('goog.events.BrowserEvent');
goog.require('ol.BrowserFeature');
goog.require('ol.pointer.MouseSource');
goog.require('ol.pointer.PointerEvent');
goog.require('ol.pointer.PointerEventHandler');

View File

@@ -0,0 +1,140 @@
goog.provide('ol.test.pointer.TouchSource');
goog.require('goog.object');
describe('ol.pointer.TouchSource', function() {
var handler;
var target;
var eventSpy;
beforeEach(function() {
target = goog.dom.createElement(goog.dom.TagName.DIV);
// make sure that a mouse and touch event source is used
ol.BrowserFeature.HAS_POINTER = false;
ol.BrowserFeature.HAS_MSPOINTER = false;
ol.BrowserFeature.HAS_TOUCH = true;
handler = new ol.pointer.PointerEventHandler(target);
eventSpy = sinon.spy();
});
afterEach(function() {
handler.dispose();
});
describe('pointer event creation', function() {
it('generates pointer events for each touch contact', function() {
goog.events.listen(handler, 'pointerdown', eventSpy);
simulateTouchEvent('touchstart', [
{identifier: 3, clientX: 10, clientY: 11},
{identifier: 4, clientX: 30, clientY: 45}
]);
expect(eventSpy.calledTwice).to.be.ok();
// pointer event for the first touch contact
var pointerEvent1 = eventSpy.firstCall.args[0];
expect(pointerEvent1.pointerId).to.be(5);
expect(pointerEvent1.pointerType).to.be('touch');
expect(pointerEvent1.clientX).to.be(10);
expect(pointerEvent1.clientY).to.be(11);
// pointer event for the second touch contact
var pointerEvent2 = eventSpy.secondCall.args[0];
expect(pointerEvent2.pointerId).to.be(6);
expect(pointerEvent2.pointerType).to.be('touch');
expect(pointerEvent2.clientX).to.be(30);
expect(pointerEvent2.clientY).to.be(45);
expect(goog.object.getCount(handler.pointerMap)).to.be(2);
});
it('creates the right pointer events', function() {
goog.events.listen(handler, 'pointerdown', eventSpy);
// first touch
simulateTouchEvent('touchstart', [
{identifier: 3, clientX: 10, clientY: 11}
]);
expect(eventSpy.calledOnce).to.be.ok();
expect(goog.object.getCount(handler.pointerMap)).to.be(1);
// second touch (first touch still down)
simulateTouchEvent('touchstart', [
{identifier: 4, clientX: 30, clientY: 45}
], [{identifier: 3}, {identifier: 4}]
);
expect(eventSpy.calledTwice).to.be.ok();
expect(goog.object.getCount(handler.pointerMap)).to.be(2);
// first touch moves
var moveEventSpy = sinon.spy();
goog.events.listen(handler, 'pointermove', moveEventSpy);
simulateTouchEvent('touchmove', [
{identifier: 3, clientX: 15, clientY: 16}
], [{identifier: 3}, {identifier: 4}]
);
expect(moveEventSpy.calledOnce).to.be.ok();
// and then both touches go up
var upEventSpy = sinon.spy();
goog.events.listen(handler, 'pointerup', upEventSpy);
simulateTouchEvent('touchend', [
{identifier: 3, clientX: 15, clientY: 16},
{identifier: 4, clientX: 30, clientY: 45}
], [{identifier: 3}, {identifier: 4}]
);
expect(upEventSpy.calledTwice).to.be.ok();
expect(goog.object.getCount(handler.pointerMap)).to.be(0);
});
it('handles flawed touches', function() {
goog.events.listen(handler, 'pointerdown', eventSpy);
// first touch
simulateTouchEvent('touchstart', [
{identifier: 3, clientX: 10, clientY: 11}
]);
expect(eventSpy.calledOnce).to.be.ok();
expect(goog.object.getCount(handler.pointerMap)).to.be(1);
// second touch, but the first touch has disappeared
var cancelEventSpy = sinon.spy();
goog.events.listen(handler, 'pointercancel', cancelEventSpy);
simulateTouchEvent('touchstart', [
{identifier: 4, clientX: 30, clientY: 45}
], [{identifier: 4}]
);
expect(eventSpy.calledTwice).to.be.ok();
// the first (broken) touch is canceled
expect(cancelEventSpy.calledOnce).to.be.ok();
expect(goog.object.getCount(handler.pointerMap)).to.be(1);
});
});
function simulateTouchEvent(type, changedTouches, touches) {
var touches = goog.isDef(touches) ?
touches : changedTouches;
var event = new goog.events.BrowserEvent({
type: type,
touches: touches,
changedTouches: changedTouches
});
goog.events.fireListeners(target, type, false, event);
}
});
goog.require('goog.dom');
goog.require('goog.dom.TagName');
goog.require('goog.events');
goog.require('goog.events.BrowserEvent');
goog.require('ol.BrowserFeature');
goog.require('ol.pointer.PointerEvent');
goog.require('ol.pointer.PointerEventHandler');
goog.require('ol.pointer.TouchSource');