OpenLayers OpenLayers

Changeset 5506

Show
Ignore:
Timestamp:
12/19/07 06:09:44 (1 year ago)
Author:
elemoine
Message:

Enables unselecting selected features by clicking outside any feature. This is the default mode of the modify feature control. If you want to use the old mode where a click on a selected feature unselects that feature, set the toggle option to true. If you really want the old mode and not have selected features be unselected when clicking outside any feature, set the clickout option to false. The patch also adds the properties toggleKey and multipleKey to the modify feature control. With these one can temporarily enable the toggle and multiple mode, respectively. See the select-feature.html example to see all this in action. Thanks to pvalsecc and tschaub for the great colloration on this. (closes #1137)

Files:

Legend:

Unmodified
Added
Removed
Modified
Copied
Moved
  • trunk/openlayers/examples/select-feature.html

    r5362 r5506  
    1414    <script src="../lib/OpenLayers.js"></script> 
    1515    <script type="text/javascript"> 
    16         var map, drawControls, select
     16        var map, drawControls
    1717        OpenLayers.Feature.Vector.style['default']['strokeWidth'] = '2'; 
    1818        function init(){ 
     
    3939                    vectors, OpenLayers.Handler.Polygon 
    4040                ), 
    41                 select: new OpenLayers.Control.SelectFeature(vectors), 
    42                 hover: new OpenLayers.Control.SelectFeature( 
    43                     vectors, {hover: true} 
     41                select: new OpenLayers.Control.SelectFeature( 
     42                    vectors, 
     43                    { 
     44                        clickout: false, toggle: false, 
     45                        multiple: false, hover: false, 
     46                        toggleKey: "ctrlKey", // ctrl key removes from selection 
     47                        multipleKey: "shiftKey" // shift key adds to selection 
     48                    } 
    4449                ) 
    4550            }; 
     
    6065                    control.deactivate(); 
    6166                } 
     67            } 
     68        } 
     69 
     70        function update() { 
     71            var clickout = document.getElementById("clickout").checked; 
     72            drawControls.select.clickout = clickout; 
     73            var hover = document.getElementById("hover").checked; 
     74            drawControls.select.hover = hover; 
     75            if(hover && drawControls.select.active) { 
     76                // turn on/off to clear feature property of handler 
     77                drawControls.select.deactivate(); 
     78                drawControls.select.activate(); 
    6279            } 
    6380        } 
     
    95112            <input type="radio" name="type" value="select" id="selectToggle" 
    96113                   onclick="toggleControl(this);" /> 
    97             <label for="selectToggle">select feature on click</label> 
     114            <label for="selectToggle">select feature</label> 
     115            <ul> 
     116                <li> 
     117                    <input id="clickout" type="checkbox" 
     118                           name="clickout" onchange="update()" /> 
     119                    <label for="clickout">click out to unselect features</label> 
     120                </li> 
     121                <li> 
     122                    <input id="hover" type="checkbox" 
     123                           name="hover" onchange="update()" /> 
     124                    <label for="hover">hover to select features</label> 
     125                </li> 
     126            </ul> 
    98127        </li> 
    99         <li> 
    100             <input type="radio" name="type" value="hover" id="hoverToggle" 
    101                    onclick="toggleControl(this);" /> 
    102             <label for="hoverToggle">select feature on hover</label> 
    103         </li> 
    104     </ul>         
     128    </ul> 
     129    <p>Use the shift key to select multiple features.  Use the ctrl key to 
     130    toggle selection on features one at a time.  Note: the "clickout" option has no 
     131    effect when "hover" is selected.</p> 
    105132  </body> 
    106133</html> 
  • trunk/openlayers/lib/OpenLayers/Control/ModifyFeature.js

    r5470 r5506  
    3030 
    3131    /** 
     32     * APIProperty: clickout 
     33     * {Boolean} Unselect features when clicking outside any feature. 
     34     *     Default is true. 
     35     */ 
     36    clickout: true, 
     37 
     38    /** 
     39     * APIProperty: toggle 
     40     * {Boolean} Unselect a selected feature on click. 
     41     *      Default is true. 
     42     */ 
     43    toggle: true, 
     44 
     45    /** 
    3246     * Property: layer 
    3347     * {<OpenLayers.Layer.Vector>} 
     
    166180        var selectOptions = { 
    167181            geometryTypes: this.geometryTypes, 
     182            clickout: this.clickout, 
     183            toggle: this.toggle, 
    168184            onSelect: function(feature) { 
    169185                control.selectFeature.apply(control, [feature]); 
  • trunk/openlayers/lib/OpenLayers/Control/SelectFeature.js

    r5157 r5506  
    1717     
    1818    /** 
     19     * Property: multipleKey 
     20     * {String} An event modifier ('altKey' or 'shiftKey') that temporarily sets 
     21     *     the <multiple> property to true.  Default is null. 
     22     */ 
     23    multipleKey: null, 
     24     
     25    /** 
     26     * Property: toggleKey 
     27     * {String} An event modifier ('altKey' or 'shiftKey') that temporarily sets 
     28     *     the <toggle> property to true.  Default is null. 
     29     */ 
     30    toggleKey: null, 
     31     
     32    /** 
    1933     * APIProperty: multiple 
    20      * {Boolean} Allow selection of multiple geometries 
     34     * {Boolean} Allow selection of multiple geometries.  Default is false. 
    2135     */ 
    2236    multiple: false,  
     37 
     38    /** 
     39     * APIProperty: clickout 
     40     * {Boolean} Unselect features when clicking outside any feature. 
     41     *     Default is true. 
     42     */ 
     43    clickout: true, 
     44 
     45    /** 
     46     * APIProperty: toggle 
     47     * {Boolean} Unselect a selected feature on click.  Default is false.  Only 
     48     *     has meaning if hover is false. 
     49     */ 
     50    toggle: false, 
    2351 
    2452    /** 
     
    86114        this.callbacks = OpenLayers.Util.extend({ 
    87115                                                  click: this.clickFeature, 
     116                                                  clickout: this.clickoutFeature, 
    88117                                                  over: this.overFeature, 
    89118                                                  out: this.outFeature 
    90119                                                }, this.callbacks); 
    91         var handlerOptions = {geometryTypes: this.geometryTypes}; 
     120        var handlerOptions = { geometryTypes: this.geometryTypes}; 
    92121        this.handler = new OpenLayers.Handler.Feature(this, layer, 
    93122                                                      this.callbacks, 
     
    96125 
    97126    /** 
     127     * Method: unselectAll 
     128     * Unselect all selected features. 
     129     */ 
     130    unselectAll: function() { 
     131        // we'll want an option to supress notification here 
     132        while (this.layer.selectedFeatures.length > 0) { 
     133            this.unselect(this.layer.selectedFeatures[0]); 
     134        } 
     135    }, 
     136 
     137    /** 
    98138     * Method: clickFeature 
    99      * Called when the feature handler detects a click on a feature 
     139     * Called on click in a feature 
     140     * Only responds if this.hover is false. 
    100141     * 
    101142     * Parameters: 
     
    106147            return; 
    107148        } 
    108         if (this.multiple) { 
    109             if(OpenLayers.Util.indexOf(this.layer.selectedFeatures, feature) > -1) { 
     149        var selected = (OpenLayers.Util.indexOf(this.layer.selectedFeatures, 
     150                                                feature) > -1); 
     151        if(!this.multiple && !this.handler.evt[this.multipleKey]) { 
     152            // perhaps an "except" argument 
     153            this.unselectAll(); 
     154        } 
     155        if(selected) { 
     156            if(this.toggle || this.handler.evt[this.toggleKey]) { 
     157                // notify here 
    110158                this.unselect(feature); 
    111159            } else { 
     160                // don't notify here - could be removed if unselectAll is modified 
    112161                this.select(feature); 
    113162            } 
    114163        } else { 
    115             if(OpenLayers.Util.indexOf(this.layer.selectedFeatures, feature) > -1) { 
    116                 this.unselect(feature); 
    117             } else { 
    118                 if (this.layer.selectedFeatures) { 
    119                     for (var i = 0; i < this.layer.selectedFeatures.length; i++) { 
    120                         this.unselect(this.layer.selectedFeatures[i]); 
    121                     } 
    122                 } 
    123                 this.select(feature); 
    124             } 
     164            this.select(feature); 
     165        } 
     166    }, 
     167 
     168    /** 
     169     * Method: clickoutFeature 
     170     * Called on click outside a previously clicked (selected) feature. 
     171     * Only responds if this.hover is false. 
     172     * 
     173     * Parameters: 
     174     * feature - {<OpenLayers.Vector.Feature>}  
     175     */ 
     176    clickoutFeature: function(feature) { 
     177        if(!this.hover && this.clickout) { 
     178            this.unselectAll(); 
    125179        } 
    126180    }, 
     
    128182    /** 
    129183     * Method: overFeature 
    130      * Called when the feature handler detects a mouse-over on a feature. 
     184     * Called on over a feature. 
    131185     * Only responds if this.hover is true. 
    132186     * 
     
    135189     */ 
    136190    overFeature: function(feature) { 
    137         if(!this.hover) { 
    138             return; 
    139         } 
    140         if(!(OpenLayers.Util.indexOf(this.layer.selectedFeatures, feature) > -1)) { 
     191        if(this.hover && 
     192           (OpenLayers.Util.indexOf(this.layer.selectedFeatures, feature) == -1)) { 
    141193            this.select(feature); 
    142194        } 
     
    145197    /** 
    146198     * Method: outFeature 
    147      * Called when the feature handler detects a mouse-out on a feature. 
     199     * Called on out of a selected feature. 
    148200     * Only responds if this.hover is true. 
    149201     * 
     
    152204     */ 
    153205    outFeature: function(feature) { 
    154         if(!this.hover) { 
    155             return; 
    156         } 
    157         this.unselect(feature); 
     206        if(this.hover) { 
     207            this.unselect(feature); 
     208        } 
    158209    }, 
    159210     
  • trunk/openlayers/lib/OpenLayers/Handler.js

    r4985 r5506  
    198198    */ 
    199199    callback: function (name, args) { 
    200         if (this.callbacks[name]) { 
     200        if (name && this.callbacks[name]) { 
    201201            this.callbacks[name].apply(this.control, args); 
    202202        } 
  • trunk/openlayers/lib/OpenLayers/Handler/Feature.js

    r4985 r5506  
    88 *  
    99 * Class: OpenLayers.Handler.Feature  
    10  * Handler to respond to mouse events related to a drawn feature. 
    11  * Callbacks will be called for over, move, out, up, down, and click 
    12  * (corresponding to the equivalent mouse events). 
     10 * Handler to respond to mouse events related to a drawn feature.  Callbacks 
     11 *     with the following keys will be notified of the following events 
     12 *     associated with features: click, clickout, over, out, and dblclick. 
     13 * 
     14 * This handler stops event propagation for mousedown and mouseup if those 
     15 *     browser events target features that can be selected. 
    1316 */ 
    1417OpenLayers.Handler.Feature = OpenLayers.Class(OpenLayers.Handler, { 
    1518 
    1619    /** 
     20     * Property: EVENTMAP 
     21     * {Object} A object mapping the browser events to objects with callback 
     22     *     keys for in and out. 
     23     */ 
     24    EVENTMAP: { 
     25        'click': {'in': 'click', 'out': 'clickout'}, 
     26        'mousemove': {'in': 'over', 'out': 'out'}, 
     27        'dblclick': {'in': 'dblclick', 'out': null}, 
     28        'mousedown': {'in': null, 'out': null}, 
     29        'mouseup': {'in': null, 'out': null} 
     30    }, 
     31 
     32    /** 
     33     * Property: feature 
     34     * {<OpenLayers.Feature.Vector>} The last feature that was handled. 
     35     */ 
     36    feature: null, 
     37 
     38    /** 
     39     * Property: down 
     40     * {<OpenLayers.Pixel>} The location of the last mousedown. 
     41     */ 
     42    down: null, 
     43 
     44    /** 
     45     * Property: up 
     46     * {<OpenLayers.Pixel>} The location of the last mouseup. 
     47     */ 
     48    up: null, 
     49     
     50    /** 
     51     * Property: clickoutTolerance 
     52     * {Number} The number of pixels the mouse can move during a click that 
     53     *     still constitutes a click out.  When dragging the map, clicks should 
     54     *     not trigger the clickout property unless this tolerance is reached. 
     55     *     Default is 4. 
     56     */ 
     57    clickoutTolerance: 4, 
     58 
     59    /** 
     60     * Property: geometryTypes 
    1761     * To restrict dragging to a limited set of geometry types, send a list 
    1862     * of strings corresponding to the geometry class names. 
     
    2771     */ 
    2872    layerIndex: null, 
    29      
    30     /** 
    31      * Property: feature 
    32      * {<OpenLayers.Feature.Vector>} 
    33      */ 
    34     feature: null, 
    3573     
    3674    /** 
     
    5088    }, 
    5189 
     90 
     91    /** 
     92     * Method: mousedown 
     93     * Handle mouse down.  Stop propagation if a feature is targeted by this 
     94     *     event (stops map dragging during feature selection). 
     95     *  
     96     * Parameters: 
     97     * evt - {Event}  
     98     */ 
     99    mousedown: function(evt) { 
     100        this.down = evt.xy; 
     101        return !this.handle(evt); 
     102    }, 
     103     
     104    /** 
     105     * Method: mouseup 
     106     * Handle mouse up.  Stop propagation if a feature is targeted by this 
     107     *     event. 
     108     *  
     109     * Parameters: 
     110     * evt - {Event}  
     111     */ 
     112    mouseup: function(evt) { 
     113        this.up = evt.xy; 
     114        return !this.handle(evt); 
     115    }, 
     116 
    52117    /** 
    53118     * Method: click 
    54      * Handle click.  Call the "click" callback if down on a feature. 
    55      *  
    56      * Parameters: 
    57      * evt - {Event}  
     119     * Handle click.  Call the "click" callback if click on a feature, 
     120     *     or the "clickout" callback if click outside any feature. 
     121     *  
     122     * Parameters: 
     123     * evt - {Event}  
     124     * 
     125     * Returns: 
     126     * {Boolean} 
    58127     */ 
    59128    click: function(evt) { 
    60         var selected = this.select('click', evt); 
    61         return !selected;  // stop event propagation if selected 
    62     }, 
    63  
    64     /** 
    65      * Method: mousedown 
    66      * Handle mouse down.  Call the "down" callback if down on a feature. 
    67      *  
    68      * Parameters: 
    69      * evt - {Event}  
    70      */ 
    71     mousedown: function(evt) { 
    72         var selected = this.select('down', evt); 
    73         return !selected;  // stop event propagation if selected 
    74     }, 
    75      
     129        return !this.handle(evt); 
     130    }, 
     131         
    76132    /** 
    77133     * Method: mousemove 
    78      * Handle mouse moves.  Call the "move" callback if moving over a feature. 
    79      * Call the "over" callback if moving over a feature for the first time. 
    80      * Call the "out" callback if moving off of a feature. 
    81      *  
    82      * Parameters: 
    83      * evt - {Event}  
     134     * Handle mouse moves.  Call the "over" callback if move over a feature, 
     135     *     or the "out" callback if move outside any feature. 
     136     *  
     137     * Parameters: 
     138     * evt - {Event}  
     139     * 
     140     * Returns: 
     141     * {Boolean} 
    84142     */ 
    85143    mousemove: function(evt) { 
    86         this.select('move', evt); 
     144        this.handle(evt); 
    87145        return true; 
    88146    }, 
    89  
    90     /** 
    91      * Method: mouseup 
    92      * Handle mouse up.  Call the "up" callback if up on a feature. 
    93      *  
    94      * Parameters: 
    95      * evt - {Event}  
    96      */ 
    97     mouseup: function(evt) { 
    98         var selected = this.select('up', evt); 
    99         return !selected;  // stop event propagation if selected         
    100     }, 
    101147     
    102148    /** 
    103149     * Method: dblclick 
    104      * Capture double-clicks.  Let the event continue propagating if the  
    105      *     double-click doesn't hit a feature.  Otherwise call the dblclick 
    106      *     callback. 
    107      * 
    108      * Parameters: 
    109      * evt - {Event}  
     150     * Handle dblclick.  Call the "dblclick" callback if dblclick on a feature. 
     151     * 
     152     * Parameters: 
     153     * evt - {Event}  
     154     * 
     155     * Returns: 
     156     * {Boolean} 
    110157     */ 
    111158    dblclick: function(evt) { 
    112         var selected = this.select('dblclick', evt); 
    113         return !selected;  // stop event propagation if selected         
    114     }, 
    115  
    116     /** 
    117      * Method: select 
    118      * Trigger the appropriate callback if a feature is under the mouse. 
    119      * 
    120      * Parameters: 
    121      * type - {String} Callback key 
    122      * 
    123      * Returns: 
    124      * {Boolean} A feature was selected 
    125      */ 
    126     select: function(type, evt) { 
     159        return !this.handle(evt); 
     160    }, 
     161 
     162    /** 
     163     * Method: geometryTypeMatches 
     164     * Return true if the geometry type of the passed feature matches 
     165     *     one of the geometry types in the geometryTypes array. 
     166     * 
     167     * Parameters: 
     168     * feature - {<OpenLayers.Vector.Feature>} 
     169     * 
     170     * Returns: 
     171     * {Boolean} 
     172     */ 
     173    geometryTypeMatches: function(feature) { 
     174        return this.geometryTypes == null || 
     175            OpenLayers.Util.indexOf(this.geometryTypes, 
     176                                    feature.geometry.CLASS_NAME) > -1; 
     177    }, 
     178 
     179    /** 
     180     * Method: handle 
     181     * 
     182     * Parameters: 
     183     * evt - {Event} 
     184     * 
     185     * Returns: 
     186     * {Boolean} Stop event propagation. 
     187     */ 
     188    handle: function(evt) { 
     189        var type = evt.type; 
     190        var stopEvtPropag = false; 
     191        var lastFeature = this.feature; 
    127192        var feature = this.layer.getFeatureFromEvent(evt); 
    128         var selected = false; 
    129193        if(feature) { 
    130             if(this.geometryTypes == null || 
    131                (OpenLayers.Util.indexOf(this.geometryTypes, 
    132                                         feature.geometry.CLASS_NAME) > -1)) { 
    133                 // three cases: 
    134                 // over a new, out of the last and over a new, or still on the last 
    135                 if(!this.feature) { 
    136                     // over a new feature 
    137                     this.callback('over', [feature]); 
    138                 } else if(this.feature != feature) { 
    139                     // out of the last and over a new 
    140                     this.callback('out', [this.feature]); 
    141                     this.callback('over', [feature]); 
     194            if(this.geometryTypeMatches(feature)) { 
     195                if(lastFeature && (lastFeature != feature)) { 
     196                    // out of last feature 
     197                    this.triggerCallback(type, 'out', [lastFeature]); 
    142198                } 
    143                 this.feature = feature
    144                 this.callback(type, [feature])
    145                 selected = true; 
     199                this.triggerCallback(type, 'in', [feature])
     200                lastFeature = feature
     201                stopEvtPropag = true; 
    146202            } else { 
    147                 if(this.feature && (this.feature != feature)) { 
    148                     // out of the last and over a new 
    149                     this.callback('out', [this.feature]); 
    150                     this.feature = null
     203                if(lastFeature && (lastFeature != feature)) { 
     204                    // out of last feature 
     205                    this.triggerCallback(type, 'out', [lastFeature]); 
     206                    lastFeature = feature
    151207                } 
    152                 selected = false; 
    153208            } 
    154209        } else { 
    155             if(this.feature) { 
    156                 // out of the last 
    157                 this.callback('out', [this.feature]); 
    158                 this.feature = null; 
    159             } 
    160             selected = false; 
    161         } 
    162         return selected; 
     210            if(lastFeature) { 
     211                this.triggerCallback(type, 'out', [lastFeature]); 
     212                lastFeature = null; 
     213            } 
     214        } 
     215        if(lastFeature) { 
     216            this.feature = lastFeature; 
     217        } 
     218        return stopEvtPropag; 
     219    }, 
     220     
     221    /** 
     222     * Method: triggerCallback 
     223     * Call the callback keyed in the event map with the supplied arguments. 
     224     *     For click out, the <clickoutTolerance> is checked first. 
     225     * 
     226     * Parameters: 
     227     * type - {String} 
     228     */ 
     229    triggerCallback: function(type, mode, args) { 
     230        var key = this.EVENTMAP[type][mode]; 
     231        if(key) { 
     232            if(type == 'click' && mode == 'out' && this.up && this.down) { 
     233                // for clickout, only trigger callback if tolerance is met 
     234                var dpx = Math.sqrt( 
     235                    Math.pow(this.up.x - this.down.x, 2) + 
     236                    Math.pow(this.up.y - this.down.y, 2) 
     237                ); 
     238                if(dpx <= this.clickoutTolerance) { 
     239                    this.callback(key, args); 
     240                } 
     241            } else { 
     242                this.callback(key, args); 
     243            } 
     244        } 
    163245    }, 
    164246 
     
    171253     */ 
    172254    activate: function() { 
     255        var activated = false; 
    173256        if(OpenLayers.Handler.prototype.activate.apply(this, arguments)) { 
    174257            this.layerIndex = this.layer.div.style.zIndex; 
    175258            this.layer.div.style.zIndex = this.map.Z_INDEX_BASE['Popup'] - 1; 
    176             return true; 
    177         } else { 
    178             return false; 
    179         } 
     259            activated = true; 
     260        } 
     261        return activated; 
    180262    }, 
    181263     
     
    188270     */ 
    189271    deactivate: function() { 
     272        var deactivated = false; 
    190273        if(OpenLayers.Handler.prototype.deactivate.apply(this, arguments)) { 
    191274            if (this.layer && this.layer.div) { 
    192275                this.layer.div.style.zIndex = this.layerIndex; 
    193             }     
    194             return true; 
    195         } else { 
    196             return false; 
    197         } 
     276            } 
     277            this.feature = null; 
     278            this.down = null; 
     279            this.up = null; 
     280            deactivated = true; 
     281        } 
     282        return deactivated; 
    198283    }, 
    199284 
  • trunk/openlayers/tests/Control/test_DragFeature.html

    r4341 r5506  
    8686            return "foo"; 
    8787        } 
    88         map.events.triggerEvent("mousemove"); 
     88        map.events.triggerEvent("mousemove", {type: "mousemove"}); 
    8989         
    9090        t.eq(control.feature, "foo", 
     
    108108            return "foo"; 
    109109        } 
    110         map.events.triggerEvent("mousemove"); 
     110        map.events.triggerEvent("mousemove", {type: "mousemove"}); 
    111111 
    112112        // simulate a mousedown on a feature 
     
    115115            t.eq(pixel, "bar", "onStart called with the correct pixel"); 
    116116        } 
    117         map.events.triggerEvent("mousedown", {xy: "bar", which: 1}); 
     117        map.events.triggerEvent("mousedown", {xy: "bar", which: 1, type: "mousemove"}); 
    118118 
    119119        t.eq(control.lastPixel, "bar", 
     
    153153 
    154154        // simulate a mouseover on a feature 
    155         map.events.triggerEvent("mousemove"); 
     155        map.events.triggerEvent("mousemove", {type: "mousemove"}); 
    156156 
    157157        // simulate a mousedown on a feature 
    158158        var down = new OpenLayers.Pixel(0, 0); 
    159         map.events.triggerEvent("mousedown", {xy: down, which: 1}); 
     159        map.events.triggerEvent("mousedown", {xy: down, which: 1, type: "mousemove"}); 
    160160 
    161161        // simulate a mousemove on a feature 
    162162        var move = new OpenLayers.Pixel(1, 2); 
    163         map.events.triggerEvent("mousemove", {xy: move, which: 1}); 
     163        map.events.triggerEvent("mousemove", {xy: move, which: 1, type: "mousemove"}); 
    164164         
    165165    } 
     
    180180            return "foo"; 
    181181        }; 
    182         map.events.triggerEvent("mousemove"); 
     182        map.events.triggerEvent("mousemove", {type: "mousemove"}); 
    183183        t.eq(control.feature, "foo", 
    184184             "feature is set on mouse over"); 
     
    204204            return "foo"; 
    205205        }; 
    206         map.events.triggerEvent("mousemove"); 
     206        map.events.triggerEvent("mousemove", {type: "mousemove"}); 
    207207        t.eq(control.feature, "foo", 
    208208             "feature is set on mouse over"); 
     
    212212            return null; 
    213213        }; 
    214         map.events.triggerEvent("mousemove"); 
     214        map.events.triggerEvent("mousemove", {type: "mousemove"}); 
    215215        t.ok(control.feature == null, 
    216216             "feature is set to null on mouse out"); 
  • trunk/openlayers/tests/Handler/test_Feature.html

    r4243 r5506  
    100100  
    101101    function test_Handler_feature_geometrytype_limit(t) { 
    102         t.plan(2); 
     102        t.plan(1); 
    103103        var feature = new OpenLayers.Feature.Vector(new OpenLayers.Geometry.Point(0,0)); 
    104104        var map = new OpenLayers.Map('map'); 
     
    113113            t.eq(featurelist[0].id, feature.id, "Correct feature called back on"); 
    114114        } 
    115         handler.select("foo", {});  
    116         handler.feature = null; 
     115        handler.handle({type: "click"}); 
     116        handler.feature = null; 
     117        handler.lastFeature = null; 
    117118        handler.callback = function(type,featurelist) { 
    118119            t.fail("Shouldn't have called back on " + featurelist[0].geometry); 
    119120        }     
    120121        feature = new OpenLayers.Feature.Vector(new OpenLayers.Geometry.LineString(0,0)); 
    121         handler.select("foo", {});  
     122        handler.handle("click", {});  
    122123    } 
    123124    function test_Handler_Feature_callbacks(t) { 
    124         t.plan(75); 
     125        t.plan(9); 
    125126         
    126127        var map = new OpenLayers.Map('map', {controls: []}); 
     
    129130        var layer = new OpenLayers.Layer(); 
    130131        map.addLayer(layer); 
    131          
    132         var evtsToTest = [ 
    133             { 
    134                 shortName: "down", 
    135                 longName: "mousedown" 
    136             }, 
    137             { 
    138                 shortName: "move", 
    139                 longName: "mousemove" 
    140             }, 
    141             { 
    142                 shortName: "up", 
    143                 longName: "mouseup" 
    144             }, 
    145             { 
    146                 shortName: "click", 
    147                 longName: "click" 
    148             }, 
    149             { 
    150                 shortName: "dblclick", 
    151                 longName: "dblclick" 
    152             } 
    153        ]; 
    154   
    155         var numEvents = {}; 
     132  
    156133        var callbacks = {}; 
    157   
    158         var newFeature
    159         var oldFeature; 
    160   
     134        var newFeature, lastFeature; 
     135        var evtPx = {xy: new OpenLayers.Pixel(Math.random(), Math.random())}
     136  
     137        // define a callback factory function 
    161138        function getCallback(evt, feature) { 
    162139            return function(f) { 
    163                 t.ok(f == feature, "callback called with proper feature"); 
    164                 numEvents[evt]++; 
     140                t.ok(f == feature, evt + " callback called with proper feature"); 
    165141            }; 
    166142        } 
    167143  
     144        // override the layer's getFeatureFromEvent func so that it always 
     145        // returns newFeature 
    168146        layer.getFeatureFromEvent = function(evt) { return newFeature; }; 
    169147  
    170148        var handler = new OpenLayers.Handler.Feature(control, layer, callbacks); 
    171149        handler.activate(); 
    172   
    173         for (var i = 0; i < evtsToTest.length; i++) { 
    174             evt = evtsToTest[i]; 
    175   
    176             var evtShortName = evt.shortName; 
    177             var evtLongName = evt.longName; 
    178   
    179             var evtPx = {xy: new OpenLayers.Pixel(Math.random(), Math.random())}; 
    180   
    181             handler.feature = null; 
    182   
    183             numEvents[evtShortName] = 0; 
    184             numEvents["over"] = 0; 
    185             numEvents["out"] = 0; 
    186             oldFeature = null; 
    187             newFeature = new OpenLayers.Feature.Vector(); 
    188             callbacks[evtShortName] = getCallback(evtShortName, newFeature); 
    189             callbacks["over"] = getCallback("over", newFeature); 
    190             callbacks["out"] = getCallback("out", oldFeature); 
    191             map.events.triggerEvent(evtLongName, evtPx); 
    192             t.ok(numEvents[evtShortName] == 1, evtShortName + " triggered click callback"); 
    193             t.ok(numEvents["over"] == 1, evtShortName + " triggered over callbacks"); 
    194             t.ok(numEvents["out"] == 0, evtShortName + " did not trigger out callback"); 
    195   
    196             numEvents[evtShortName] = 0; 
    197             numEvents["over"] = 0; 
    198             numEvents["out"] = 0; 
    199             oldFeature = newFeature; 
    200             newFeature = new OpenLayers.Feature.Vector(); 
    201             callbacks[evtShortName] = getCallback(evtShortName, newFeature); 
    202             callbacks["over"] = getCallback("over", newFeature); 
    203             callbacks["out"] = getCallback("out", oldFeature); 
    204             map.events.triggerEvent(evtLongName, evtPx); 
    205             t.ok(numEvents[evtShortName] == 1, evtShortName + " triggered click callback"); 
    206             t.ok(numEvents["over"] == 1, evtShortName + " triggered over callbacks"); 
    207             t.ok(numEvents["out"] == 1, evtShortName + " triggered out callback"); 
    208   
    209             numEvents[evtShortName] = 0; 
    210             numEvents["over"] = 0; 
    211             numEvents["out"] = 0; 
    212             oldFeature =  newFeature; 
    213             callbacks[evtShortName] = getCallback(evtShortName, newFeature); 
    214             callbacks["over"] = getCallback("over", newFeature); 
    215             callbacks["out"] = getCallback("out", oldFeature); 
    216             map.events.triggerEvent(evtLongName, evtPx); 
    217             t.ok(numEvents[evtShortName] == 1, evtShortName + " triggered click callback"); 
    218             t.ok(numEvents["over"] == 0, evtShortName + " did not trigger over callbacks"); 
    219             t.ok(numEvents["out"] == 0, evtShortName + " did not trigger out callback"); 
    220         } 
     150 
     151        // test click in new feature 
     152        // only 'click' callback should be called 
     153        handler.feature = null; 
     154        lastFeature = null; 
     155        newFeature = new OpenLayers.Feature.Vector(); 
     156        callbacks['click'] = getCallback('click', newFeature); 
     157        callbacks['clickout'] = getCallback('clickout', lastFeature); 
     158        evtPx.type = "click"; 
     159        map.events.triggerEvent('click', evtPx); 
     160 
     161        // test click in new feature and out of last feature 
     162        // both 'click' and 'clickout' callbacks should be called 
     163        lastFeature = newFeature; 
     164        newFeature = new OpenLayers.Feature.Vector(); 
     165        callbacks['click'] = getCallback('click', newFeature); 
     166        callbacks['clickout'] = getCallback('clickout', lastFeature); 
     167        evtPx.type = "click"; 
     168        map.events.triggerEvent('click', evtPx); 
     169 
     170        // test click out of last feature 
     171        // only 'clickout' callback should be called 
     172        lastFeature = newFeature; 
     173        newFeature = null; 
     174        callbacks['click'] = getCallback('click', newFeature); 
     175        callbacks['clickout'] = getCallback('clickout', lastFeature); 
     176        evtPx.type = "click"; 
     177        map.events.triggerEvent('click', evtPx); 
     178 
     179        // test over a new feature 
     180        // only 'over' callback should be called 
     181        handler.feature = null; 
     182        lastFeature = null; 
     183        newFeature = new OpenLayers.Feature.Vector(); 
     184        callbacks['over'] = getCallback('over', newFeature); 
     185        callbacks['out'] = getCallback('out', lastFeature); 
     186        evtPx.type = "mousemove"; 
     187        map.events.triggerEvent('mousemove', evtPx); 
     188 
     189        // test over a new feature and out of last feature 
     190        // both 'over' and 'out' callbacks should be called 
     191        lastFeature = newFeature; 
     192        newFeature = new OpenLayers.Feature.Vector(); 
     193        callbacks['over'] = getCallback('over', newFeature); 
     194        callbacks['out'] = getCallback('out', lastFeature); 
     195        evtPx.type = "mousemove"; 
     196        map.events.triggerEvent('mousemove', evtPx); 
     197 
     198        // test out of last feature 
     199        // only 'out' callback should be called 
     200        lastFeature = newFeature; 
     201        newFeature = null; 
     202        callbacks['over'] = getCallback('over', newFeature); 
     203        callbacks['out'] = getCallback('out', lastFeature); 
     204        evtPx.type = "mousemove"; 
     205        map.events.triggerEvent('mousemove', evtPx); 
     206 
     207        // test dblclick on a feature 
     208        // 'dblclick' callback should be called 
     209        handler.feature = null; 
     210        lastFeature = null; 
     211        newFeature = new OpenLayers.Feature.Vector(); 
     212        callbacks['dblclick'] = getCallback('dblclick', newFeature); 
     213        evtPx.type = "dblclick"; 
     214        map.events.triggerEvent('dblclick', evtPx); 
    221215    } 
    222216