Skip to content

Commit

Permalink
Merge pull request #921 from OpenGeoscience/auto-selection-api
Browse files Browse the repository at this point in the history
Add a selectionAPI='auto' mode for features.
  • Loading branch information
manthey authored Oct 1, 2018
2 parents 98d48bc + 89b6d62 commit 32cdc11
Show file tree
Hide file tree
Showing 3 changed files with 86 additions and 9 deletions.
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@

## Unreleased

### Features
- Feature selection API is now enabled automatically if any event handlers are bounds to the feature (#921)

### Changes
- Removed the dependency on the vgl module for the `object` and `timestamp` classes (#918)

Expand Down
70 changes: 61 additions & 9 deletions src/feature.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,11 @@ var geo_event = require('./event');
*
* @typedef {object} geo.feature.spec
* @property {geo.layer} [layer] the parent layer associated with the feature.
* @property {boolean} [selectionAPI=false] If truthy, enable selection events
* on the feature. Selection events are those in {@link geo.event.feature}.
* They can be bound via a call like
* @property {boolean|'auto'} [selectionAPI='auto'] If `'auto'`, enable
* selection events if any {@link geo.event.feature} events are bound to the
* feature. Otherwise, if truthy, enable selection events on the feature.
* Selection events are those in {@link geo.event.feature}. They can be
* bound via a call like
* ```
* feature.geoOn(geo.event.feature.mousemove, function (evt) {
* // do something with the feature
Expand Down Expand Up @@ -111,8 +113,10 @@ var feature = function (arg) {

var m_this = this,
s_exit = this._exit,
s_geoOn = this.geoOn,
s_geoOff = this.geoOff,
m_ready,
m_selectionAPI = arg.selectionAPI === undefined ? false : !!arg.selectionAPI,
m_selectionAPI = arg.selectionAPI === undefined ? 'auto' : arg.selectionAPI,
m_style = {},
m_layer = arg.layer === undefined ? null : arg.layer,
m_gcs = arg.gcs,
Expand Down Expand Up @@ -772,22 +776,29 @@ var feature = function (arg) {
/**
* Get/Set if the selection API is enabled for this feature.
*
* @param {boolean} [arg] `undefined` to return the selectionAPI state, or a
* boolean to change the state.
* @param {boolean|string} [arg] `undefined` to return the selectionAPI
* state, a boolean to change the state, or `'auto'` to set the state
* based on the existence of event handlers. When getting the state, if
* `direct` is not specified, `'auto'` is never returned.
* @param {boolean} [direct] If `true`, when getting the selectionAPI state,
* disregard the state of the parent layer, and when setting, refresh the
* state regardless of whether it has changed or not.
* @returns {boolean|this} Either the selectionAPI state (if getting) or the
* feature (if setting).
* @returns {boolean|string|this} Either the selectionAPI state or the
* feature instance.
*/
this.selectionAPI = function (arg, direct) {
if (arg === undefined) {
if (!direct && m_layer && m_layer.selectionAPI && !m_layer.selectionAPI()) {
return false;
}
if (!direct && m_selectionAPI === 'auto') {
return !!m_this.geoIsOn(Object.values(geo_event.feature));
}
return m_selectionAPI;
}
arg = !!arg;
if (arg !== 'auto') {
arg = !!arg;
}
if (arg !== m_selectionAPI || direct) {
m_selectionAPI = arg;
m_this._unbindMouseHandlers();
Expand Down Expand Up @@ -848,6 +859,47 @@ var feature = function (arg) {
this._update = function () {
};

/**
* Bind an event handler to this object.
*
* @param {string} event An event from {@link geo.event} or a user-defined
* value.
* @param {function} handler A function that is called when `event` is
* triggered. The function is passed a {@link geo.event} object.
* @returns {this}
*/
this.geoOn = function (event, handler) {
var isAuto = m_this.selectionAPI(undefined, true) === 'auto',
selection = isAuto && m_this.selectionAPI();
var result = s_geoOn.apply(m_this, arguments);
if (isAuto && !selection && m_this.selectionAPI()) {
m_this._bindMouseHandlers();
}
return result;
};

/**
* Remove handlers from one event or an array of events. If no event is
* provided all handlers will be removed.
*
* @param {string|string[]} [event] An event or a list of events from
* {@link geo.event} or defined by the user, or `undefined` to remove all
* events (in which case `arg` is ignored).
* @param {(function|function[])?} [arg] A function or array of functions to
* remove from the events or a falsy value to remove all handlers from the
* events.
* @returns {this}
*/
this.geoOff = function (event, arg) {
var isAuto = m_this.selectionAPI(undefined, true) === 'auto',
selection = isAuto && m_this.selectionAPI();
var result = s_geoOff.apply(m_this, arguments);
if (isAuto && selection && !m_this.selectionAPI()) {
m_this._unbindMouseHandlers();
}
return result;
};

/**
* Destroy. Unbind mouse handlers, clear internal variables, and call the
* parent destroy method.
Expand Down
22 changes: 22 additions & 0 deletions tests/cases/feature.js
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ describe('geo.feature', function () {
feat = geo.feature({layer: layer});
expect(feat.style('opacity')).toBe(1);
expect(feat.selectionAPI()).toBe(false);
expect(feat.selectionAPI(undefined, true)).toBe('auto');
});
it('_exit', function () {
feat = geo.feature({layer: layer});
Expand Down Expand Up @@ -338,6 +339,8 @@ describe('geo.feature', function () {
expect(feat.style('data')).toEqual([1]);
});
it('selectionAPI', function () {
var bindSpy, unbindSpy;

expect(feat.selectionAPI()).toBe(false);
feat = geo.feature({layer: layer, renderer: layer.renderer(), selectionAPI: true});
expect(feat.selectionAPI()).toBe(true);
Expand All @@ -362,6 +365,25 @@ describe('geo.feature', function () {
expect(feat.selectionAPI()).toBe(false);
expect(feat.selectionAPI(true, true)).toBe(feat);
expect(feat.selectionAPI()).toBe(true);

// auto will change the selection mode depending on bound events
bindSpy = sinon.spy(feat, '_bindMouseHandlers');
unbindSpy = sinon.spy(feat, '_unbindMouseHandlers');
expect(feat.selectionAPI('auto')).toBe(feat);
expect(feat.selectionAPI()).toBe(false);
expect(feat.selectionAPI(undefined, true)).toBe('auto');
expect(bindSpy.callCount).toBe(1);
expect(unbindSpy.callCount).toBe(1);
feat.geoOn(geo.event.feature.mouseover, function () {});
expect(feat.selectionAPI()).toBe(true);
expect(feat.selectionAPI(undefined, true)).toBe('auto');
expect(bindSpy.callCount).toBe(2);
expect(unbindSpy.callCount).toBe(2);
feat.geoOff(geo.event.feature.mouseover);
expect(feat.selectionAPI()).toBe(false);
expect(feat.selectionAPI(undefined, true)).toBe('auto');
expect(bindSpy.callCount).toBe(2);
expect(unbindSpy.callCount).toBe(3);
});
it('ready', function () {
feat = geo.feature({layer: layer});
Expand Down

0 comments on commit 32cdc11

Please sign in to comment.