OpenLayers OpenLayers

Ticket #1240: history.patch

File history.patch, 27.0 kB (added by tschaub, 1 year ago)

control navigation history

  • tests/Control/test_NavigationHistory.html

    old new  
     1<html> 
     2<head> 
     3  <script src="../../lib/OpenLayers.js"></script> 
     4  <script type="text/javascript"> 
     5 
     6    function test_initialize(t) { 
     7        t.plan(4); 
     8        control = new OpenLayers.Control.NavigationHistory(); 
     9        t.ok(control instanceof OpenLayers.Control.NavigationHistory, 
     10             "constructor returns correct instance"); 
     11        t.eq(control.displayClass, "olControlNavigationHistory", 
     12             "displayClass is correct"); 
     13        t.ok(control.next instanceof OpenLayers.Control.Button, 
     14             "constructor creates next control"); 
     15        t.ok(control.previous instanceof OpenLayers.Control.Button, 
     16             "constructor creates previous control"); 
     17    } 
     18 
     19    function test_destroy(t) { 
     20        t.plan(2); 
     21        control = new OpenLayers.Control.NavigationHistory(); 
     22        control.next.destroy = function() { 
     23            t.ok(true, "destroy calls next.destroy"); 
     24        } 
     25        control.previous.destroy = function() { 
     26            t.ok(true, "destroy calls previous.destroy"); 
     27        } 
     28        control.destroy(); 
     29    } 
     30     
     31    function test_previous(t) { 
     32        var numStates = 10; 
     33         
     34        t.plan( 
     35            numStates * 3 // for lon, lat, zoom 
     36            + 3 // for confirming that previous with empty stack works 
     37        );        
     38 
     39        var history = new Array(numStates); 
     40        for(var i=0; i<numStates; ++i) { 
     41            history[i] = { 
     42                center: new OpenLayers.LonLat( 
     43                    (i * 360 / numStates) - 180, (i * 180 / numStates) - 90 
     44                ), 
     45                zoom: i 
     46            }; 
     47        } 
     48         
     49        var map = new OpenLayers.Map("map"); 
     50        var layer = new OpenLayers.Layer( 
     51            "test", {isBaseLayer: true} 
     52        ); 
     53        map.addLayer(layer); 
     54        var control = new OpenLayers.Control.NavigationHistory(); 
     55        map.addControl(control); 
     56         
     57        // set previous states 
     58        for(i=0; i<numStates; ++i) { 
     59            map.setCenter(history[i].center, history[i].zoom); 
     60        } 
     61        // test previous states 
     62        for(i=numStates-1; i>=0; --i) { 
     63            t.eq(map.getCenter().lon, history[i].center.lon, "(step " + i + ") lon correct"); 
     64            t.eq(map.getCenter().lat, history[i].center.lat, "(step " + i + ") lat correct"); 
     65            t.eq(map.getZoom(), history[i].zoom, "(step " + i + ") zoom correct"); 
     66            control.previous.trigger(); 
     67        } 
     68        // test previous with empty stack 
     69        t.eq(map.getCenter().lon, history[0].center.lon, "(step 0 again) lon correct"); 
     70        t.eq(map.getCenter().lat, history[0].center.lat, "(step 0 again) lat correct"); 
     71        t.eq(map.getZoom(), history[0].zoom, "(step 0 again) zoom correct"); 
     72    } 
     73 
     74    function test_next(t) { 
     75        var numStates = 10; 
     76         
     77        t.plan( 
     78            numStates * 3 // for lon, lat, zoom 
     79            + 3 // for confirming that next with empty stack works 
     80        ); 
     81 
     82        var history = new Array(numStates); 
     83        for(var i=0; i<numStates; ++i) { 
     84            history[i] = { 
     85                center: new OpenLayers.LonLat( 
     86                    (i * 360 / numStates) - 180, (i * 180 / numStates) - 90 
     87                ), 
     88                zoom: i 
     89            }; 
     90        } 
     91         
     92        var map = new OpenLayers.Map("map"); 
     93        var layer = new OpenLayers.Layer( 
     94            "test", {isBaseLayer: true} 
     95        ); 
     96        map.addLayer(layer); 
     97        var control = new OpenLayers.Control.NavigationHistory(); 
     98        map.addControl(control); 
     99         
     100        // set previous states 
     101        for(i=0; i<numStates; ++i) { 
     102            map.setCenter(history[i].center, history[i].zoom); 
     103        } 
     104        // set next states 
     105        for(i=numStates-1; i>=0; --i) { 
     106            control.previous.trigger(); 
     107        } 
     108        // test next states 
     109        for(i=0; i<numStates; ++i) { 
     110            t.eq(map.getCenter().lon, history[i].center.lon, "(step " + i + ") lon correct"); 
     111            t.eq(map.getCenter().lat, history[i].center.lat, "(step " + i + ") lat correct"); 
     112            t.eq(map.getZoom(), history[i].zoom, "(step " + i + ") zoom correct"); 
     113            control.next.trigger(); 
     114        } 
     115        // test next with empty stack 
     116        t.eq(map.getCenter().lon, history[numStates-1].center.lon, "(step " + (numStates-1) + " again) lon correct"); 
     117        t.eq(map.getCenter().lat, history[numStates-1].center.lat, "(step " + (numStates-1) + " again) lat correct"); 
     118        t.eq(map.getZoom(), history[numStates-1].zoom, "(step " + (numStates-1) + " again) zoom correct"); 
     119    } 
     120     
     121    function test_limit(t) { 
     122        var numStates = 10; 
     123        var limit = 3; 
     124         
     125        t.plan( 
     126            numStates * 6 // for previous & next lon, lat, zoom 
     127        ); 
     128 
     129        var history = new Array(numStates); 
     130        for(var i=0; i<numStates; ++i) { 
     131            history[i] = { 
     132                center: new OpenLayers.LonLat( 
     133                    (i * 360 / numStates) - 180, (i * 180 / numStates) - 90 
     134                ), 
     135                zoom: i 
     136            }; 
     137        } 
     138         
     139        var map = new OpenLayers.Map("map"); 
     140        var layer = new OpenLayers.Layer( 
     141            "test", {isBaseLayer: true} 
     142        ); 
     143        map.addLayer(layer); 
     144        var control = new OpenLayers.Control.NavigationHistory({limit: limit}); 
     145        map.addControl(control); 
     146         
     147        // set previous states 
     148        for(i=0; i<numStates; ++i) { 
     149            map.setCenter(history[i].center, history[i].zoom); 
     150        } 
     151        // test previous states (only up to limit should work) 
     152        var state; 
     153        for(i=numStates-1; i>=0; --i) { 
     154            state = Math.max(i, numStates - limit - 1); 
     155            t.eq(map.getCenter().lon, history[state].center.lon, "(previous step " + i + ") lon correct: state " + state); 
     156            t.eq(map.getCenter().lat, history[state].center.lat, "(previous step " + i + ") lat correct: state " + state); 
     157            t.eq(map.getZoom(), history[state].zoom, "(previous step " + i + ") zoom correct: state " + state); 
     158            control.previous.trigger(); 
     159        } 
     160        // test next states 
     161        for(i=0; i<numStates; ++i) { 
     162            state = Math.min(numStates - 1, numStates - limit - 1 + i); 
     163            t.eq(map.getCenter().lon, history[state].center.lon, "(next step " + i + ") lon correct: state " + state); 
     164            t.eq(map.getCenter().lat, history[state].center.lat, "(next step " + i + ") lat correct: state " + state); 
     165            t.eq(map.getZoom(), history[state].zoom, "(next step " + i + ") zoom correct: state " + state); 
     166            control.next.trigger(); 
     167        } 
     168         
     169    } 
     170 
     171  </script> 
     172</head> 
     173<body> 
     174    <div id="map" style="width: 100px; height: 100px;"/> 
     175</body> 
     176</html> 
  • tests/list-tests.html

    old new  
    8383    <li>Control/test_LayerSwitcher.html</li> 
    8484    <li>Control/test_ModifyFeature.html</li> 
    8585    <li>Control/test_MousePosition.html</li> 
     86    <li>Control/test_NavigationHistory.html</li> 
    8687    <li>Control/test_MouseToolbar.html</li> 
    8788    <li>Control/test_Navigation.html</li> 
    8889    <li>Control/test_NavToolbar.html</li> 
  • theme/default/style.css

    old new  
    110110  position: relative; 
    111111} 
    112112 
     113.olControlNavigationHistoryPreviousItemActive {  
     114   background-image: url("img/view_previous_on.png"); 
     115   background-repeat: no-repeat; 
     116   width:  24px; 
     117   height: 24px; 
     118} 
     119.olControlNavigationHistoryPreviousItemInactive {  
     120   background-image: url("img/view_previous_off.png"); 
     121   background-repeat: no-repeat; 
     122   width:  24px; 
     123   height: 24px; 
     124} 
     125.olControlNavigationHistoryNextItemActive {  
     126   background-image: url("img/view_next_on.png"); 
     127   background-repeat: no-repeat; 
     128   width:  24px; 
     129   height: 24px; 
     130} 
     131.olControlNavigationHistoryNextItemInactive {  
     132   background-image: url("img/view_next_off.png"); 
     133   background-repeat: no-repeat; 
     134   width:  24px; 
     135   height: 24px; 
     136} 
     137 
    113138.olControlNavToolbar .olControlNavigationItemActive {  
    114139  background-image: url("img/panning-hand-on.png"); 
    115140  background-repeat: no-repeat; 
  • lib/OpenLayers/Control/NavigationHistory.js

    old new  
     1/* Copyright (c) 2006-2008 MetaCarta, Inc., published under the Clear BSD 
     2 * license.  See http://svn.openlayers.org/trunk/openlayers/license.txt for the 
     3 * full text of the license. */ 
     4 
     5/** 
     6 * @requires OpenLayers/Control.js 
     7 */ 
     8 
     9/** 
     10 * Class: OpenLayers.Control.NavigationHistory 
     11 * A navigation history control.  This is a meta-control, that creates two 
     12 *     dependent controls: <previous> and <next>.  Call the trigger method 
     13 *     on the <previous> and <next> controls to restore previous and next 
     14 *     history states.  The previous and next controls will become active 
     15 *     when there are available states to restore and will become deactive 
     16 *     when there are no states to restore. 
     17 * 
     18 * Inherits from: 
     19 *  - <OpenLayers.Control.Control> 
     20 */ 
     21OpenLayers.Control.NavigationHistory = OpenLayers.Class(OpenLayers.Control, { 
     22 
     23    /** 
     24     * Property: type 
     25     * {String} Note that this control is not intended to be added directly 
     26     *     to a control panel.  Instead, add the sub-controls previous and 
     27     *     next.  These sub-controls are button type controls that activate 
     28     *     and deactivate themselves.  If this parent control is added to 
     29     *     a panel, it will act as a toggle. 
     30     */ 
     31    type: OpenLayers.Control.TYPE_TOGGLE, 
     32 
     33    /** 
     34     * APIProperty: previous 
     35     * {OpenLayers.Control} A button type control whose trigger method restores 
     36     *     the previous state managed by this control. 
     37     */ 
     38    previous: null, 
     39     
     40    /** 
     41     * APIProperty: previousOptions 
     42     * {Object} Set this property on the options argument of the constructor 
     43     *     to set optional properties on the <previous> control. 
     44     */ 
     45    previousOptions: null, 
     46     
     47    /** 
     48     * APIProperty: next 
     49     * {OpenLayers.Control} A button type control whose trigger method restores 
     50     *     the next state managed by this control. 
     51     */ 
     52    next: null, 
     53 
     54    /** 
     55     * APIProperty: nextOptions 
     56     * {Object} Set this property on the options argument of the constructor 
     57     *     to set optional properties on the <next> control. 
     58     */ 
     59    nextOptions: null, 
     60 
     61    /** 
     62     * APIProperty: limit 
     63     * {Integer} Optional limit on the number of history items to retain.  If 
     64     *     null, there is no limit.  Default is 50. 
     65     */ 
     66    limit: 50, 
     67 
     68    /** 
     69     * Property: activateOnDraw 
     70     * {Boolean} Activate the control when it is first added to the map. 
     71     *     Default is true. 
     72     */ 
     73    activateOnDraw: true, 
     74 
     75    /** 
     76     * Property: clearOnDeactivate 
     77     * {Boolean} Clear the history when the control is deactivated.  Default 
     78     *     is false. 
     79     */ 
     80    clearOnDeactivate: false, 
     81 
     82    /** 
     83     * Property: events 
     84     * {<OpenLayers.Events>} An events object that will be used for registering 
     85     *     listeners.  Defaults to the map events for this control. 
     86     */ 
     87    events: null, 
     88 
     89    /** 
     90     * Property: registry 
     91     * {Object} An object with keys corresponding to event types.  Values 
     92     *     are functions that return an object representing the current state. 
     93     */ 
     94    registry: null, 
     95 
     96    /** 
     97     * Property: nextStack 
     98     * {Array} Array of items in the history. 
     99     */ 
     100    nextStack: null, 
     101 
     102    /** 
     103     * Property: previousStack 
     104     * {Array} List of items in the history.  First item represents the current 
     105     *     state. 
     106     */ 
     107    previousStack: null, 
     108     
     109    /** 
     110     * Property: listeners 
     111     * {Object} An object containing properties corresponding to event types. 
     112     *     This object is used to configure the control and is modified on 
     113     *     construction. 
     114     */ 
     115    listeners: null, 
     116     
     117    /** 
     118     * Property: restoring 
     119     * {Boolean} Currently restoring a history state.  This is set to true 
     120     *     before callilng restore and set to false after restore returns. 
     121     */ 
     122    restoring: false, 
     123 
     124    /** 
     125     * Constructor: OpenLayers.Control.NavigationHistory  
     126     *  
     127     * Parameters: 
     128     * options - {Object} An optional object whose properties will be used 
     129     *     to extend the control. 
     130     */ 
     131    initialize: function(options) { 
     132        OpenLayers.Control.prototype.initialize.apply(this, [options]); 
     133         
     134        this.registry = OpenLayers.Util.extend({ 
     135            "moveend": function() { 
     136                return { 
     137                    center: this.map.getCenter(), 
     138                    resolution: this.map.getResolution()                 
     139                }; 
     140            } 
     141        }, this.registry); 
     142         
     143        this.clear(); 
     144 
     145        var previousOptions = { 
     146            trigger: OpenLayers.Function.bind(this.previousTrigger, this), 
     147            displayClass: this.displayClass + "Previous", 
     148            onActivate: function() {}, 
     149            onDeactivate: function() {} 
     150        }; 
     151        if(options) { 
     152            OpenLayers.Util.extend(previousOptions, options.previousOptions); 
     153        } 
     154        this.previous = new OpenLayers.Control.Button(previousOptions); 
     155         
     156        var nextOptions = { 
     157            trigger: OpenLayers.Function.bind(this.nextTrigger, this), 
     158            displayClass: this.displayClass + "Next", 
     159            onActivate: function() {}, 
     160            onDeactivate: function() {} 
     161        }; 
     162        if(options) { 
     163            OpenLayers.Util.extend(nextOptions, options.nextOptions); 
     164        } 
     165        this.next = new OpenLayers.Control.Button(nextOptions); 
     166 
     167    }, 
     168     
     169    /** 
     170     * Method: onPreviousChange 
     171     * Called when the previous history stack changes. 
     172     * 
     173     * Parameters: 
     174     * state - {Object} An object representing the state to be restored 
     175     *     if previous is triggered again or null if no previous states remain. 
     176     * length - {Integer} The number of remaining previous states that can 
     177     *     be restored. 
     178     */ 
     179    onPreviousChange: function(state, length) { 
     180        if(state && !this.previous.active) { 
     181            this.previous.activate(); 
     182            this.previous.onActivate(); 
     183        } else if(!state && this.previous.active) { 
     184            this.previous.deactivate(); 
     185            this.previous.onDeactivate(); 
     186        } 
     187    }, 
     188     
     189    /** 
     190     * Method: onNextChange 
     191     * Called when the next history stack changes. 
     192     * 
     193     * Parameters: 
     194     * state - {Object} An object representing the state to be restored 
     195     *     if next is triggered again or null if no next states remain. 
     196     * length - {Integer} The number of remaining next states that can 
     197     *     be restored. 
     198     */ 
     199    onNextChange: function(state, length) { 
     200        if(state && !this.next.active) { 
     201            this.next.activate(); 
     202            this.next.onActivate(); 
     203        } else if(!state && this.next.active) { 
     204            this.next.deactivate(); 
     205            this.next.onDeactivate(); 
     206        } 
     207    }, 
     208     
     209    /** 
     210     * APIMethod: destroy 
     211     * Destroy the control. 
     212     */ 
     213    destroy: function() { 
     214        OpenLayers.Control.prototype.destroy.apply(this); 
     215        this.previous.destroy(); 
     216        this.next.destroy(); 
     217        this.deactivate(); 
     218        for(var prop in this) { 
     219            this[prop] = null; 
     220        } 
     221    }, 
     222     
     223    /**  
     224     * Method: setMap 
     225     * Set the map property for the control and <previous> and <next> child 
     226     *     controls. 
     227     * 
     228     * Parameters: 
     229     * map - {<OpenLayers.Map>}  
     230     */ 
     231    setMap: function(map) { 
     232        this.map = map; 
     233        this.next.setMap(map); 
     234        this.previous.setMap(map); 
     235    }, 
     236 
     237    /** 
     238     * Method: draw 
     239     * Called when the control is added to the map. 
     240     */ 
     241    draw: function() { 
     242        OpenLayers.Control.prototype.draw.apply(this, arguments); 
     243        this.next.draw(); 
     244        this.previous.draw(); 
     245        if(this.activateOnDraw) { 
     246            this.activate(); 
     247        } 
     248    }, 
     249     
     250    /** 
     251     * Method: previousTrigger 
     252     * Restore the previous state.  If no items are in the previous history 
     253     *     stack, this has no effect. 
     254     * 
     255     * Returns: 
     256     * {Object} Item representing state that was restored.  Undefined if no 
     257     *     items are in the previous history stack. 
     258     */ 
     259    previousTrigger: function() { 
     260        var current = this.previousStack.shift(); 
     261        var state = this.previousStack.shift(); 
     262        if(state != undefined) { 
     263            this.nextStack.unshift(current); 
     264            this.previousStack.unshift(state); 
     265            this.restoring = true; 
     266            this.restore(state); 
     267            this.restoring = false; 
     268            this.onNextChange(this.nextStack[0], this.nextStack.length); 
     269            this.onPreviousChange( 
     270                this.previousStack[1], this.previousStack.length - 1 
     271            ); 
     272        } else { 
     273            this.previousStack.unshift(current); 
     274        } 
     275        return state; 
     276    }, 
     277     
     278    /** 
     279     * APIMethod: nextTrigger 
     280     * Restore the next state.  If no items are in the next history 
     281     *     stack, this has no effect.  The next history stack is populated 
     282     *     as states are restored from the previous history stack. 
     283     * 
     284     * Returns: 
     285     * {Object} Item representing state that was restored.  Undefined if no 
     286     *     items are in the next history stack. 
     287     */ 
     288    nextTrigger: function() { 
     289        var state = this.nextStack.shift(); 
     290        if(state != undefined) { 
     291            this.previousStack.unshift(state); 
     292            this.restoring = true; 
     293            this.restore(state); 
     294            this.restoring = false; 
     295            this.onNextChange(this.nextStack[0], this.nextStack.length); 
     296            this.onPreviousChange( 
     297                this.previousStack[1], this.previousStack.length - 1 
     298            ); 
     299        } 
     300        return state; 
     301    }, 
     302     
     303    /** 
     304     * APIMethod: clear 
     305     * Clear history. 
     306     */ 
     307    clear: function() { 
     308        this.previousStack = []; 
     309        this.nextStack = []; 
     310    }, 
     311 
     312    /** 
     313     * Method: restore 
     314     * Update the state with the given object. 
     315     * 
     316     * Parameters: 
     317     * state - {Object} An object representing the state to restore. 
     318     */ 
     319    restore: function(state) { 
     320        var zoom = this.map.getZoomForResolution(state.resolution); 
     321        this.map.setCenter(state.center, zoom); 
     322    }, 
     323     
     324    /** 
     325     * Method: setListeners 
     326     * Sets functions to be registered in the listeners object. 
     327     */ 
     328    setListeners: function() { 
     329        this.listeners = {}; 
     330        for(var type in this.registry) { 
     331            this.listeners[type] = OpenLayers.Function.bind(function() { 
     332                if(!this.restoring) { 
     333                    var state = this.registry[type].apply(this, arguments); 
     334                    this.previousStack.unshift(state); 
     335                    if(this.previousStack.length > 1) { 
     336                        this.onPreviousChange( 
     337                            this.previousStack[1], this.previousStack.length - 1 
     338                        ); 
     339                    } 
     340                    if(this.previousStack.length > (this.limit + 1)) { 
     341                        this.previousStack.pop(); 
     342                    } 
     343                    if(this.nextStack.length > 0) { 
     344                        this.nextStack = []; 
     345                        this.onNextChange(null, 0); 
     346                    } 
     347                } 
     348                return true; 
     349            }, this); 
     350        } 
     351    }, 
     352 
     353    /** 
     354     * APIMethod: activate 
     355     * Activate the control.  This registers any listeners. 
     356     * 
     357     * Returns: 
     358     * {Boolean} Control successfully activated. 
     359     */ 
     360    activate: function() { 
     361        var activated = false; 
     362        if(this.map) { 
     363            if(OpenLayers.Control.prototype.activate.apply(this)) { 
     364                if(this.listeners == null) { 
     365                    this.setListeners(); 
     366                } 
     367                if(!this.events) { 
     368                    this.events = this.map.events; 
     369                } 
     370                for(var type in this.listeners) { 
     371                    this.events.register(type, this, this.listeners[type]); 
     372                } 
     373                activated = true; 
     374                if(this.previousStack.length == 0) { 
     375                    this.initStack(); 
     376                } 
     377            } 
     378        } 
     379        return activated; 
     380    }, 
     381     
     382    /** 
     383     * Method: initStack 
     384     * Called after the control is activated if the previous history stack is 
     385     *     empty. 
     386     */ 
     387    initStack: function() { 
     388        if(this.map.getCenter()) { 
     389            this.listeners.moveend(); 
     390        } 
     391    }, 
     392     
     393    /** 
     394     * APIMethod: deactivate 
     395     * Deactivate the control.  This unregisters any listeners. 
     396     * 
     397     * Returns: 
     398     * {Boolean} Control successfully deactivated. 
     399     */ 
     400    deactivate: function() { 
     401        var deactivated = false; 
     402        if(this.map) { 
     403            if(OpenLayers.Control.prototype.deactivate.apply(this)) { 
     404                for(var type in this.listeners) { 
     405                    this.events.unregister( 
     406                        type, this, this.listeners[type] 
     407                    ); 
     408                } 
     409                if(this.clearOnDeactivate) { 
     410                    this.clear(); 
     411                } 
     412                deactivated = true; 
     413            } 
     414        } 
     415        return deactivated; 
     416    }, 
     417     
     418    CLASS_NAME: "OpenLayers.Control.NavigationHistory" 
     419}); 
     420 
  • lib/OpenLayers.js

    old new  
    155155            "OpenLayers/Control/ModifyFeature.js", 
    156156            "OpenLayers/Control/Panel.js", 
    157157            "OpenLayers/Control/SelectFeature.js", 
     158            "OpenLayers/Control/NavigationHistory.js", 
    158159            "OpenLayers/Geometry.js", 
    159160            "OpenLayers/Geometry/Rectangle.js", 
    160161            "OpenLayers/Geometry/Collection.js", 
  • examples/navigation-history.html

    old new  
     1<html xmlns="http://www.w3.org/1999/xhtml"> 
     2    <head> 
     3        <title>OpenLayers Navigation History Example</title> 
     4        <style type="text/css"> 
     5            #map { 
     6                width: 512px; 
     7                height: 256px; 
     8                border: 1px solid gray; 
     9            } 
     10            #panel { 
     11                right: 0px; 
     12                height: 30px;  
     13                width: 200px; 
     14            } 
     15            #panel div {  
     16                float: left; 
     17                margin: 5px; 
     18            } 
     19                 
     20        </style> 
     21        <script src="../lib/OpenLayers.js"></script> 
     22        <script type="text/javascript"> 
     23            var map, nav, panel; 
     24 
     25            // preload images if you care 
     26            var preload = [ 
     27                "../theme/default/img/view_previous_on.png", 
     28                "../theme/default/img/view_next_on.png" 
     29            ]; 
     30            var img = new Array(preload.length); 
     31            for(var i=0; i<preload.length; ++i) { 
     32                img[i] = new Image(); 
     33                img[i].src = preload[i]; 
     34            } 
     35             
     36            function init(){ 
     37                map = new OpenLayers.Map('map'); 
     38                 
     39                // set any application specific behavior here 
     40                // this will become unnecessary when controls have better event handling 
     41                var previousOptions = { 
     42                    onActivate: function() {panel.redraw();}, 
     43                    onDeactivate: function() {panel.redraw();} 
     44                }; 
     45                var nextOptions = { 
     46                    onActivate: function() {panel.redraw();}, 
     47                    onDeactivate: function() {panel.redraw();} 
     48                }; 
     49                var options = { 
     50                    previousOptions: previousOptions, 
     51                    nextOptions: nextOptions 
     52                }; 
     53                nav = new OpenLayers.Control.NavigationHistory(options); 
     54                map.addControl(nav); 
     55 
     56                panel = new OpenLayers.Control.Panel( 
     57                    {div: document.getElementById("panel")} 
     58                ); 
     59                panel.addControls([nav.next, nav.previous]); 
     60                map.addControl(panel); 
     61                 
     62                var layer = new OpenLayers.Layer.WMS( 
     63                    "OpenLayers WMS", 
     64                    "http://labs.metacarta.com/wms/vmap0", 
     65                    {layers: 'basic'} 
     66                ); 
     67                map.addLayer(layer); 
     68                map.zoomToMaxExtent(); 
     69 
     70 
     71            } 
     72        </script> 
     73    </head> 
     74    <body onload="init()"> 
     75        <h1 id="title">Map Navigation History Example</h1> 
     76 
     77        <div id="tags"> 
     78        </div> 
     79 
     80        <p id="shortdesc"> 
     81            A control for zooming to previous and next map extents. 
     82        </p> 
     83 
     84        <div id="map"></div> 
     85        Map navigation history controls<div id="panel"><div> 
     86        <div id="docs"></div> 
     87    </body> 
     88</html>