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