Finally fixing event sequences.

This commit is contained in:
ahocevar
2012-06-21 17:26:23 +02:00
parent 0a1f8cddd8
commit 81a55eb320
7 changed files with 195 additions and 120 deletions

View File

@@ -2,9 +2,8 @@ goog.provide("ol");
goog.require('ol.bounds'); goog.require('ol.bounds');
goog.require('ol.control.Control'); goog.require('ol.control.Control');
goog.require('ol.control.Navigation'); goog.require('ol.control.Navigation');
goog.require('ol.event'); goog.require('ol.event.Drag');
goog.require('ol.event.Events'); goog.require('ol.event.Events');
goog.require('ol.event.Sequence');
goog.require("ol.map"); goog.require("ol.map");
goog.require("ol.loc"); goog.require("ol.loc");
goog.require("ol.feature"); goog.require("ol.feature");

View File

@@ -78,7 +78,9 @@ ol.Map = function() {
* @private * @private
* @type {ol.event.Events} * @type {ol.event.Events}
*/ */
this.events_ = new ol.event.Events(this); this.events_ = new ol.event.Events(
this, undefined, false, ['Drag']
);
/** /**
* @private * @private

View File

@@ -20,18 +20,6 @@ ol.control.Navigation = function(opt_autoActivate) {
this.autoActivate_ = this.autoActivate_ =
goog.isDef(opt_autoActivate) ? opt_autoActivate : true; goog.isDef(opt_autoActivate) ? opt_autoActivate : true;
/**
* type {number}
* @private
*/
this.oldX = undefined;
/**
* type {number}
* @private
*/
this.oldY = undefined;
}; };
goog.inherits(ol.control.Navigation, ol.control.Control); goog.inherits(ol.control.Navigation, ol.control.Control);
@@ -54,10 +42,8 @@ ol.control.Navigation.prototype.deactivate = function() {
}; };
/** /**
* @param {Event} evt * @param {ol.event.DragEvent} evt
*/ */
ol.control.Navigation.prototype.moveMap = function(evt) { ol.control.Navigation.prototype.moveMap = function(evt) {
this.getMap().moveByPx(evt.clientX - oldX, evt.clientY - oldY); this.getMap().moveByPx(evt.dx, evt.dy);
oldX = evt.clientX;
oldY = evt.clientY;
}; };

View File

@@ -1,57 +1,119 @@
goog.provide('ol.event.Drag'); goog.provide('ol.event.Drag');
goog.provide('ol.event.DragEvent');
goog.require('ol.event.Sequence'); goog.require('ol.event.ISequence');
goog.require('goog.functions');
goog.require('goog.fx.Dragger'); goog.require('goog.fx.Dragger');
goog.require('goog.fx.DragEvent');
goog.require('goog.fx.Dragger.EventType'); goog.require('goog.fx.Dragger.EventType');
goog.require('goog.functions');
/** /**
* @constructor * @constructor
* @extends {ol.event.Sequence} * @param {Element} target The element that will be dragged.
* @extends {goog.fx.Dragger}
* @implements {ol.event.ISequence}
* @export * @export
*/ */
ol.event.Drag = function() { ol.event.Drag = function(target) {
goog.base(this, target);
goog.base(this); /**
* @private
* @type {number} clientX of the previous event
*/
this.previousX_ = 0;
/**
* @private
* @type {number} clientY of the previous event
*/
this.previousY_ = 0;
};
goog.inherits(ol.event.Drag, goog.fx.Dragger);
/**
* @param {string|goog.fx.DragEvent} e
* @return {boolean} If anyone called preventDefault on the event object (or
* if any of the handlers returns false this will also return false.
*/
ol.event.Drag.prototype.dispatchEvent = function(e) {
if (e instanceof goog.fx.DragEvent) {
if (e.type === goog.fx.Dragger.EventType.START) {
e.type = ol.event.Drag.EventType.DRAGSTART;
} else if (e.type === goog.fx.Dragger.EventType.END) {
e.type = ol.event.Drag.EventType.DRAGEND;
}
}
return goog.base(this, 'dispatchEvent', e);
};
/** @inheritDoc */ /** @inheritDoc */
this.eventType_ = { ol.event.Drag.prototype.startDrag = function(e) {
this.previousX_ = e.clientX;
this.previousY_ = e.clientY;
e.type = ol.event.Drag.EventType.DRAGSTART;
goog.base(this, 'startDrag', e);
};
/** @inheritDoc */
ol.event.Drag.prototype.doDrag = function(e, x, y, dragFromScroll) {
e.dx = e.clientX - this.previousX_;
e.dy = e.clientX - this.previousY_;
goog.base(this, 'doDrag', e, x, y, dragFromScroll);
};
/** @override */
ol.event.Drag.prototype.defaultAction = function(x, y) {};
/** @inheritDoc */
ol.event.Drag.prototype.getEventTypes = function() {
return ol.event.Drag.EventType;
};
/** @inheritDoc */
ol.event.Drag.prototype.destroy = ol.event.Drag.prototype.dispose;
/**
* Object representing a drag event
*
* @param {string} type Event type.
* @param {goog.fx.Dragger} dragobj Drag object initiating event.
* @param {number} clientX X-coordinate relative to the viewport.
* @param {number} clientY Y-coordinate relative to the viewport.
* @param {goog.events.BrowserEvent} browserEvent Object representing the
* browser event that caused this drag event.
* @constructor
* @extends {goog.fx.DragEvent}
*/
ol.event.DragEvent = function(type, dragobj, clientX, clientY, browserEvent) {
goog.base(this, type, dragobj, clientX, clientY, browserEvent);
/**
* The move delta in X direction since the previous drag event
*
* @type {number}
*/
this.dx = 0;
/**
* The move delta in Y direction since the previous drag event
*
* @type {number}
*/
this.dy = 0;
};
goog.inherits(ol.event.DragEvent, goog.fx.DragEvent);
/**
* @type {Object.<string, string>}
*/
ol.event.Drag.EventType = {
DRAGSTART: 'dragstart', DRAGSTART: 'dragstart',
DRAG: 'drag', DRAG: goog.fx.Dragger.EventType.DRAG,
DRAGEND: 'dragend' DRAGEND: 'dragend'
}; };
var providedEvents = this.getProvidedEvents(),
oldX, oldY;
providedEvents[this.eventType_.DRAGSTART] = {};
providedEvents[this.eventType_.DRAGSTART]
[goog.fx.Dragger.EventType.START] = function(evt) {
oldX = evt.screenX;
oldY = evt.screenY;
};
providedEvents[this.eventType_.DRAG] = {};
providedEvents[this.eventType_.DRAG]
[goog.fx.Dragger.EventType.DRAG] = function(evt) {
evt.dx = evt.screenX - oldX;
evt.dy = evt.screenY - oldY;
oldX = evt.screenX;
oldY = evt.screenY;
};
providedEvents[this.eventType_.DRAGEND] = {};
providedEvents[this.eventType_.DRAGEND]
[goog.fx.Dragger.EventType.END] = true;
};
goog.inherits(ol.event.Drag, ol.event.Sequence);
/** @inheritDoc */
ol.event.Drag.prototype.setElement = function(element) {
goog.base(this, 'setElement', element);
if (goog.isDefAndNotNull(element)) {
this.dragger_ = new goog.fx.Dragger(element);
} else if (this.dragger_) {
this.dragger_.dispose();
}
};

View File

@@ -6,7 +6,6 @@ goog.require('goog.events.EventType');
goog.require('goog.events.EventTarget'); goog.require('goog.events.EventTarget');
goog.require('goog.events.Listener'); goog.require('goog.events.Listener');
goog.require('goog.style'); goog.require('goog.style');
goog.require('goog.fx.Dragger');
/** /**
* Determine whether event was caused by a single touch * Determine whether event was caused by a single touch
@@ -45,8 +44,8 @@ ol.event.isMultiTouch = function(evt) {
* property on the event object that is passed, which represents the * property on the event object that is passed, which represents the
* relative position of the pointer to the {@code element}. Default is * relative position of the pointer to the {@code element}. Default is
* false. * false.
* @param {Array.<Object>=} opt_sequences Event sequences to register with this * @param {Array.<String>=} opt_sequences Event sequences to register with
* events instance. * this Events instance.
* @export * @export
*/ */
ol.event.Events = function(object, opt_element, opt_includeXY, opt_sequences) { ol.event.Events = function(object, opt_element, opt_includeXY, opt_sequences) {
@@ -75,24 +74,21 @@ ol.event.Events = function(object, opt_element, opt_includeXY, opt_sequences) {
/** /**
* @private * @private
* @type {!Array.<Object>} * @type {Array.<String>}
*/
this.sequenceProviders_ = goog.isDef(opt_sequences) ? opt_sequences : [];
/**
* @private
* @type {Array.<ol.event.ISequence>}
*/ */
this.sequences_ = []; this.sequences_ = [];
if (goog.isDef(opt_element)) {
this.setElement(opt_element); this.setElement(opt_element);
this.setSequences(opt_sequences);
};
goog.inherits(ol.event.Events, goog.events.EventTarget);
/**
* @param {Array.<ol.event.Sequence>} sequences
*/
ol.event.Events.prototype.setSequences = function(sequences) {
this.sequences_ = sequences || [];
for (var i=0, ii=this.sequences_.length; i<ii; ++i) {
this.sequences_[i].setElement(this.element_);
} }
}; };
goog.inherits(ol.event.Events, goog.events.EventTarget);
/** /**
* @return {Object} The object that this instance is bound to. * @return {Object} The object that this instance is bound to.
@@ -129,20 +125,22 @@ ol.event.Events.prototype.getElement = function() {
* @export * @export
*/ */
ol.event.Events.prototype.setElement = function(element) { ol.event.Events.prototype.setElement = function(element) {
var t, types = {}; var types, t;
goog.object.extend(types, goog.events.EventType);
goog.object.extend(types, goog.fx.Dragger.EventType);
if (this.element_) { if (this.element_) {
types = this.getBrowserEventTypes();
for (t in types) { for (t in types) {
// register the event cross-browser // register the event cross-browser
goog.events.unlisten( goog.events.unlisten(
this.element_, types[t], this.handleBrowserEvent, false, this this.element_, types[t], this.handleBrowserEvent, false, this
); );
} }
this.destroySequences();
delete this.element_; delete this.element_;
} }
this.element_ = element || null; this.element_ = element || null;
if (goog.isDefAndNotNull(element)) { if (goog.isDefAndNotNull(element)) {
this.createSequences(element);
types = this.getBrowserEventTypes();
for (t in types) { for (t in types) {
// register the event cross-browser // register the event cross-browser
goog.events.listen( goog.events.listen(
@@ -150,7 +148,34 @@ ol.event.Events.prototype.setElement = function(element) {
); );
} }
} }
this.setSequences(this.sequences_); };
/**
* @param {EventTarget} target
*/
ol.event.Events.prototype.createSequences = function(target) {
for (var i=0, ii=this.sequenceProviders_.length; i<ii; ++i) {
this.sequences_.push(new ol.event[this.sequenceProviders_[i]](target));
}
};
ol.event.Events.prototype.destroySequences = function() {
for (var i=this.sequences_.length-1; i>=0; --i) {
this.sequences_[i].destroy();
}
this.sequences_ = [];
};
/**
* @return {Object.<string, string>}
*/
ol.event.Events.prototype.getBrowserEventTypes = function() {
var types = {};
goog.object.extend(types, goog.events.EventType);
for (var i=this.sequences_.length-1; i>=0; --i) {
goog.object.extend(types, this.sequences_[i].getEventTypes());
}
return types;
}; };
/** /**
@@ -202,21 +227,21 @@ ol.event.Events.prototype.unregister = function(type, listener, opt_scope) {
* Trigger a specified registered event. * Trigger a specified registered event.
* *
* @param {string} type The type of the event to trigger. * @param {string} type The type of the event to trigger.
* @param {Object} evt The event object that will be passed to listeners. * @param {Object=} opt_evt The event object that will be passed to listeners.
* *
* @return {boolean} The last listener return. If a listener returns false, * @return {boolean} The last listener return. If a listener returns false,
* the chain of listeners will stop getting called. * the chain of listeners will stop getting called.
* @export * @export
*/ */
ol.event.Events.prototype.triggerEvent = function(type, evt) { ol.event.Events.prototype.triggerEvent = function(type, opt_evt) {
var returnValue, var returnValue,
listeners = goog.events.getListeners(this, type, true) listeners = goog.events.getListeners(this, type, true)
.concat(goog.events.getListeners(this, type, false)); .concat(goog.events.getListeners(this, type, false));
if (arguments.length === 1) { if (arguments.length === 1) {
evt = {type: type}; opt_evt = {type: type};
} }
for (var i=0, ii=listeners.length; i<ii; ++i) { for (var i=0, ii=listeners.length; i<ii; ++i) {
returnValue = listeners[i].handleEvent(evt); returnValue = listeners[i].handleEvent(opt_evt);
if (returnValue === false) { if (returnValue === false) {
break; break;
} }
@@ -224,31 +249,6 @@ ol.event.Events.prototype.triggerEvent = function(type, evt) {
return returnValue; return returnValue;
}; };
/**
* @param {Event} evt
*/
ol.event.Events.prototype.handleSequences = function(evt) {
var sequences = this.sequences_,
type = evt.type,
sequenceEvt, browserEvent, handled, providedEvents, providedEventType;
for (var i=0, ii=sequences.length; i<ii; ++i) {
providedEvents = sequences[i].getProvidedEvents();
for (providedEventType in providedEvents) {
// clone the original event
sequenceEvt = {}; goog.object.extend(sequenceEvt, evt);
browserEvent = providedEvents[providedEventType];
handled = browserEvent[type];
if (goog.typeOf(handled) === "function") {
handled = handled(evt);
}
if (handled) {
sequenceEvt.type = providedEventType;
this.dispatchEvent(sequenceEvt);
}
}
}
};
/** /**
* Basically just a wrapper to the triggerEvent() function, but takes * Basically just a wrapper to the triggerEvent() function, but takes
* care to set a property 'xy' on the event with the current mouse position. * care to set a property 'xy' on the event with the current mouse position.
@@ -285,7 +285,6 @@ ol.event.Events.prototype.handleBrowserEvent = function(evt) {
var element = /** @type {!Element} */ this.element_; var element = /** @type {!Element} */ this.element_;
evt.xy = goog.style.getRelativePosition(evt, element); evt.xy = goog.style.getRelativePosition(evt, element);
} }
this.handleSequences(evt);
this.dispatchEvent(evt); this.dispatchEvent(evt);
}; };
@@ -294,7 +293,7 @@ ol.event.Events.prototype.handleBrowserEvent = function(evt) {
* @export * @export
*/ */
ol.event.Events.prototype.destroy = function() { ol.event.Events.prototype.destroy = function() {
this.setElement(); this.setElement(null);
for (var p in this) { for (var p in this) {
delete this[p]; delete this[p];
} }

20
src/ol/event/ISequence.js Normal file
View File

@@ -0,0 +1,20 @@
goog.provide('ol.event.ISequence');
/**
* Interface for event sequences
*
* @interface
* @param {Element} target The element that will be listened to for browser
* events.
*/
ol.event.ISequence = function(target) {};
/**
* @return {Object.<string, string>} element
*/
ol.event.ISequence.prototype.getEventTypes = function() {};
/**
* Destroys the sequence
*/
ol.event.ISequence.prototype.destroy = function() {};

View File

@@ -123,29 +123,36 @@ describe("ol.event.Events", function() {
events.destroy(); events.destroy();
}); });
it("can map browser events to sequences", function() { it("can be extended with event sequences", function() {
var element = document.createElement("div"), var element = document.createElement("div"),
events = new ol.event.Events("foo", element), events = new ol.event.Events("foo", element, false, ["Drag"]),
mockEvt; mockEvt;
// mock dom object // mock dom object
goog.object.extend(element, new goog.events.EventTarget()); goog.object.extend(element, new goog.events.EventTarget());
log = []; log = [];
events.register('start', logFn); events.register('dragstart', logFn);
events.register('drag', logFn); events.register('drag', logFn);
events.register('end', logFn); events.register('dragend', logFn);
mockEvt = new goog.events.BrowserEvent({type: "start", button: null}); mockEvt = new goog.events.BrowserEvent({
type: "dragstart", button: null
});
element.dispatchEvent(mockEvt); element.dispatchEvent(mockEvt);
mockEvt = new goog.events.BrowserEvent({type: "drag", button: null}); mockEvt = new goog.events.BrowserEvent({
type: "drag", button: null
});
element.dispatchEvent(mockEvt); element.dispatchEvent(mockEvt);
mockEvt = new goog.events.BrowserEvent({type: "end", button: null}); mockEvt = new goog.events.BrowserEvent({
type: "dragend", button: null
});
element.dispatchEvent(mockEvt); element.dispatchEvent(mockEvt);
expect(log[0].evt.type).toBe("start"); expect(log.length).toBe(3);
expect(log[0].evt.type).toBe("dragstart");
expect(log[1].evt.type).toBe("drag"); expect(log[1].evt.type).toBe("drag");
expect(log[2].evt.type).toBe("end"); expect(log[2].evt.type).toBe("dragend");
events.destroy(); events.destroy();
}); });