diff --git a/examples/highlight-feature.html b/examples/highlight-feature.html
new file mode 100644
index 0000000000..cf7869c77f
--- /dev/null
+++ b/examples/highlight-feature.html
@@ -0,0 +1,80 @@
+
+
+ SelectFeature Control for Select and Highlight
+
+
+
+
+
+
+
+
+ OpenLayers Select and Highlight Feature Example
+
+ Select features on click, highlight features on hover.
+
+
+ Select features by clicking on them. Just highlight features by hovering over
+ them.
+
+
diff --git a/lib/OpenLayers/Control/SelectFeature.js b/lib/OpenLayers/Control/SelectFeature.js
index 67232a0342..774a2afac4 100644
--- a/lib/OpenLayers/Control/SelectFeature.js
+++ b/lib/OpenLayers/Control/SelectFeature.js
@@ -19,6 +19,16 @@
* -
*/
OpenLayers.Control.SelectFeature = OpenLayers.Class(OpenLayers.Control, {
+
+ /**
+ * Constant: EVENT_TYPES
+ *
+ * Supported event types:
+ * - *beforefeaturehighlighted* Triggered before a feature is highlighted
+ * - *featurehighlighted* Triggered when a feature is highlighted
+ * - *featureunhighlighted* Triggered when a feature is unhighlighted
+ */
+ EVENT_TYPES: ["beforefeaturehighlighted", "featurehighlighted", "featureunhighlighted"],
/**
* Property: multipleKey
@@ -60,6 +70,14 @@ OpenLayers.Control.SelectFeature = OpenLayers.Class(OpenLayers.Control, {
* ignores clicks and only listens to mouse moves.
*/
hover: false,
+
+ /**
+ * APIProperty: highlightOnly
+ * {Boolean} If true do not actually select features (i.e. place them in the
+ * layer's selected features array), just highlight them. This property has
+ * no effect if hover is false. Defaults to false.
+ */
+ highlightOnly: false,
/**
* APIProperty: box
@@ -150,6 +168,11 @@ OpenLayers.Control.SelectFeature = OpenLayers.Class(OpenLayers.Control, {
* options - {Object}
*/
initialize: function(layers, options) {
+ // concatenate events specific to this control with those from the base
+ this.EVENT_TYPES =
+ OpenLayers.Control.SelectFeature.prototype.EVENT_TYPES.concat(
+ OpenLayers.Control.prototype.EVENT_TYPES
+ );
OpenLayers.Control.prototype.initialize.apply(this, [options]);
if(!(layers instanceof Array)) {
layers = [layers];
@@ -331,9 +354,14 @@ OpenLayers.Control.SelectFeature = OpenLayers.Class(OpenLayers.Control, {
* feature - {}
*/
overFeature: function(feature) {
- if(this.hover &&
- (OpenLayers.Util.indexOf(feature.layer.selectedFeatures, feature) == -1)) {
- this.select(feature);
+ var layer = feature.layer;
+ if(this.hover) {
+ if(this.highlightOnly) {
+ this.highlight(feature);
+ } else if(OpenLayers.Util.indexOf(
+ layer.selectedFeatures, feature) == -1) {
+ this.select(feature);
+ }
}
},
@@ -347,9 +375,68 @@ OpenLayers.Control.SelectFeature = OpenLayers.Class(OpenLayers.Control, {
*/
outFeature: function(feature) {
if(this.hover) {
- this.unselect(feature);
+ if(this.highlightOnly) {
+ // we do nothing if we're not the last highlighter of the
+ // feature
+ if(feature._lastHighlighter == this.id) {
+ // if another select control had highlighted the feature before
+ // we did it ourself then we use that control to highlight the
+ // feature as it was before we highlighted it, else we just
+ // unhighlight it
+ if(feature._prevHighlighter &&
+ feature._prevHighlighter != this.id) {
+ delete feature._lastHighlighter;
+ var control = this.map.getControl(
+ feature._prevHighlighter);
+ if(control) {
+ control.highlight(feature);
+ }
+ } else {
+ this.unhighlight(feature);
+ }
+ }
+ } else {
+ this.unselect(feature);
+ }
}
},
+
+ /**
+ * Method: highlight
+ * Redraw feature with the select style.
+ *
+ * Parameters:
+ * feature - {}
+ */
+ highlight: function(feature) {
+ var layer = feature.layer;
+ var cont = this.events.triggerEvent("beforefeaturehighlighted", {
+ feature : feature
+ });
+ if(cont !== false) {
+ feature._prevHighlighter = feature._lastHighlighter;
+ feature._lastHighlighter = this.id;
+ var style = this.selectStyle || this.renderIntent;
+ layer.drawFeature(feature, style);
+ this.events.triggerEvent("featurehighlighted", {feature : feature});
+ }
+ },
+
+ /**
+ * Method: unhighlight
+ * Redraw feature with the "default" style
+ *
+ * Parameters:
+ * feature - {}
+ */
+ unhighlight: function(feature) {
+ var layer = feature.layer;
+ feature._lastHighlighter = feature._prevHighlighter;
+ delete feature._prevHighlighter;
+ layer.drawFeature(feature, feature.style || feature.layer.style ||
+ "default");
+ this.events.triggerEvent("featureunhighlighted", {feature : feature});
+ },
/**
* Method: select
@@ -369,10 +456,7 @@ OpenLayers.Control.SelectFeature = OpenLayers.Class(OpenLayers.Control, {
if(cont !== false) {
layer.selectedFeatures.push(feature);
this.layerData = {};
-
- var selectStyle = this.selectStyle || this.renderIntent;
-
- layer.drawFeature(feature, selectStyle);
+ this.highlight(feature);
layer.events.triggerEvent("featureselected", {feature: feature});
this.onSelect.call(this.scope, feature);
}
@@ -390,7 +474,7 @@ OpenLayers.Control.SelectFeature = OpenLayers.Class(OpenLayers.Control, {
unselect: function(feature) {
var layer = feature.layer;
// Store feature style for restoration later
- layer.drawFeature(feature, "default");
+ this.unhighlight(feature);
OpenLayers.Util.removeItem(layer.selectedFeatures, feature);
layer.events.triggerEvent("featureunselected", {feature: feature});
this.onUnselect.call(this.scope, feature);
diff --git a/tests/Control/SelectFeature.html b/tests/Control/SelectFeature.html
index e6adc0ed2c..53b2ce04a3 100644
--- a/tests/Control/SelectFeature.html
+++ b/tests/Control/SelectFeature.html
@@ -206,6 +206,167 @@
control.deactivate();
}
+ function test_highlighyOnly(t) {
+ t.plan(23);
+
+ /*
+ * setup
+ */
+
+ var map, layer, ctrl1, ctrl2, _feature, feature, evt, _style;
+
+ map = new OpenLayers.Map("map");
+ layer = new OpenLayers.Layer.Vector("name", {isBaseLayer: true});
+ map.addLayer(layer);
+
+ ctrl1 = new OpenLayers.Control.SelectFeature(layer, {
+ highlightOnly: false,
+ hover: false
+ });
+ map.addControl(ctrl1);
+
+ ctrl2 = new OpenLayers.Control.SelectFeature(layer, {
+ highlightOnly: true,
+ hover: true
+ });
+ map.addControl(ctrl2);
+
+ ctrl2.activate();
+ ctrl1.activate();
+
+ feature = new OpenLayers.Feature.Vector();
+ feature.layer = layer;
+
+ // override the layer's getFeatureFromEvent func so that it always
+ // returns the feature referenced to by _feature
+ layer.getFeatureFromEvent = function(evt) { return _feature; };
+
+ evt = {xy: new OpenLayers.Pixel(Math.random(), Math.random())};
+
+ map.zoomToMaxExtent();
+
+ /*
+ * tests
+ */
+
+ // with renderIntent
+
+ ctrl1.renderIntent = "select";
+ ctrl2.renderIntent = "temporary";
+
+ // mouse over feature, feature is drawn with "temporary"
+ _feature = feature;
+ evt.type = "mousemove";
+ map.events.triggerEvent("mousemove", evt);
+ t.eq(feature.renderIntent, "temporary",
+ "feature drawn with expected render intent after \"mouseover\"");
+ t.eq(feature._lastHighlighter, ctrl2.id,
+ "feature._lastHighlighter properly set after \"mouseover\"");
+ t.eq(feature._prevHighlighter, undefined,
+ "feature._prevHighlighter properly set after \"mouseover\"");
+
+ // click in feature, feature is drawn with "select"
+ _feature = feature;
+ evt.type = "click";
+ map.events.triggerEvent("click", evt);
+ t.eq(feature.renderIntent, "select",
+ "feature drawn with expected render intent after \"clickin\"");
+ t.eq(feature._lastHighlighter, ctrl1.id,
+ "feature._lastHighlighter properly set after \"clickin\"");
+ t.eq(feature._prevHighlighter, ctrl2.id,
+ "feature._prevHighlighter properly set after \"clickin\"");
+
+ // mouse out of feature, feature is still drawn with "select"
+ _feature = null;
+ evt.type = "mousemove";
+ map.events.triggerEvent("mousemove", evt);
+ t.eq(feature.renderIntent, "select",
+ "feature drawn with expected render intent after \"mouseout\"");
+ t.eq(feature._lastHighlighter, ctrl1.id,
+ "feature._lastHighlighter properly set after \"nouseout\"");
+ t.ok(feature._prevHighlighter, ctrl2.id,
+ "feature._prevHighlighter properly set after \"mouseout\"");
+
+ // mouse over feature again, feature is drawn with "temporary"
+ _feature = feature;
+ evt.type = "mousemove";
+ map.events.triggerEvent("mousemove", evt);
+ t.eq(feature.renderIntent, "temporary",
+ "feature drawn with expected render intent after \"mouseover\"");
+ t.eq(feature._lastHighlighter, ctrl2.id,
+ "feature._lastHighlighter properly set after \"mouseover\"");
+ t.eq(feature._prevHighlighter, ctrl1.id,
+ "feature._prevHighlighter properly set after \"mouseover\"");
+
+ // mouve out of feature again, feature is still drawn with "select"
+ _feature = null;
+ evt.type = "mousemove";
+ map.events.triggerEvent("mousemove", evt);
+ t.eq(feature.renderIntent, "select",
+ "feature drawn with expected render intent after \"mouseout\"");
+ t.eq(feature._lastHighlighter, ctrl1.id,
+ "feature._lastHighlighter properly set after \"mouseout\"");
+ t.eq(feature._prevHighlighter, undefined,
+ "feature._prevHighlighter properly set after \"mouseout\"");
+
+ // click out of feature, feature is drawn with "default"
+ _feature = null;
+ evt.type = "click";
+ map.events.triggerEvent("click", evt);
+ t.eq(feature.renderIntent, "default",
+ "feature drawn with expected render intent after \"clickout\"");
+ t.eq(feature._lastHighlighter, undefined,
+ "feature._lastHighlighter properly set after \"clickout\"");
+ t.eq(feature._prevHighlighter, undefined,
+ "feature._prevHighlighter properly set after \"clickout\"");
+
+ // with selectStyle
+
+ ctrl1.selectStyle = OpenLayers.Feature.Vector.style["select"];
+ ctrl2.selectStyle = OpenLayers.Feature.Vector.style["temporary"];
+
+ layer.renderer.drawFeature = function(f, s) {
+ var style = OpenLayers.Feature.Vector.style[_style];
+ t.eq(s, style, "renderer drawFeature called with expected style obj (" + _style + ")");
+ };
+
+ // mouse over feature, feature is drawn with "temporary"
+ _feature = feature;
+ _style = "temporary";
+ evt.type = "mousemove";
+ map.events.triggerEvent("mousemove", evt);
+
+ // click in feature, feature is drawn with "select"
+ _feature = feature;
+ _style = "select";
+ evt.type = "click";
+ map.events.triggerEvent("click", evt);
+
+ // mouse out of feature, feature is still drawn with "select" and
+ // the renderer drawFeature method should not be called
+ _feature = null;
+ evt.type = "mousemove";
+ map.events.triggerEvent("mousemove", evt);
+
+ // mouse over feature again, feature is drawn with "temporary"
+ _feature = feature;
+ _style = "temporary";
+ evt.type = "mousemove";
+ map.events.triggerEvent("mousemove", evt);
+
+ // mouve out of feature again, feature is still drawn with "select"
+ _feature = null;
+ _style = "select";
+ evt.type = "mousemove";
+ map.events.triggerEvent("mousemove", evt);
+
+ // click out of feature, feature is drawn with "default"
+ _feature = null;
+ _style = "default";
+ evt.type = "click";
+ map.events.triggerEvent("click", evt);
+ }
+