Project

General

Profile

Download (13.2 KB) Statistics
| Branch: | Tag: | Revision:
1
/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
2
 * full list of contributors). Published under the 2-clause BSD license.
3
 * See license.txt in the OpenLayers distribution or repository for the
4
 * full text of the license. */
5

    
6
/**
7
 * @requires OpenLayers/Control.js
8
 * @requires OpenLayers/Control/Button.js
9
 */
10

    
11
/**
12
 * Class: OpenLayers.Control.NavigationHistory
13
 * A navigation history control.  This is a meta-control, that creates two
14
 *     dependent controls: <previous> and <next>.  Call the trigger method
15
 *     on the <previous> and <next> controls to restore previous and next
16
 *     history states.  The previous and next controls will become active
17
 *     when there are available states to restore and will become deactive
18
 *     when there are no states to restore.
19
 *
20
 * Inherits from:
21
 *  - <OpenLayers.Control>
22
 */
23
OpenLayers.Control.NavigationHistory = OpenLayers.Class(OpenLayers.Control, {
24

    
25
    /**
26
     * Property: type
27
     * {String} Note that this control is not intended to be added directly
28
     *     to a control panel.  Instead, add the sub-controls previous and
29
     *     next.  These sub-controls are button type controls that activate
30
     *     and deactivate themselves.  If this parent control is added to
31
     *     a panel, it will act as a toggle.
32
     */
33
    type: OpenLayers.Control.TYPE_TOGGLE,
34

    
35
    /**
36
     * APIProperty: previous
37
     * {<OpenLayers.Control>} A button type control whose trigger method restores
38
     *     the previous state managed by this control.
39
     */
40
    previous: null,
41
    
42
    /**
43
     * APIProperty: previousOptions
44
     * {Object} Set this property on the options argument of the constructor
45
     *     to set optional properties on the <previous> control.
46
     */
47
    previousOptions: null,
48
    
49
    /**
50
     * APIProperty: next
51
     * {<OpenLayers.Control>} A button type control whose trigger method restores
52
     *     the next state managed by this control.
53
     */
54
    next: null,
55

    
56
    /**
57
     * APIProperty: nextOptions
58
     * {Object} Set this property on the options argument of the constructor
59
     *     to set optional properties on the <next> control.
60
     */
61
    nextOptions: null,
62

    
63
    /**
64
     * APIProperty: limit
65
     * {Integer} Optional limit on the number of history items to retain.  If
66
     *     null, there is no limit.  Default is 50.
67
     */
68
    limit: 50,
69

    
70
    /**
71
     * APIProperty: autoActivate
72
     * {Boolean} Activate the control when it is added to a map.  Default is
73
     *     true.
74
     */
75
    autoActivate: true,
76

    
77
    /**
78
     * Property: clearOnDeactivate
79
     * {Boolean} Clear the history when the control is deactivated.  Default
80
     *     is false.
81
     */
82
    clearOnDeactivate: false,
83

    
84
    /**
85
     * Property: registry
86
     * {Object} An object with keys corresponding to event types.  Values
87
     *     are functions that return an object representing the current state.
88
     */
89
    registry: null,
90

    
91
    /**
92
     * Property: nextStack
93
     * {Array} Array of items in the history.
94
     */
95
    nextStack: null,
96

    
97
    /**
98
     * Property: previousStack
99
     * {Array} List of items in the history.  First item represents the current
100
     *     state.
101
     */
102
    previousStack: null,
103
    
104
    /**
105
     * Property: listeners
106
     * {Object} An object containing properties corresponding to event types.
107
     *     This object is used to configure the control and is modified on
108
     *     construction.
109
     */
110
    listeners: null,
111
    
112
    /**
113
     * Property: restoring
114
     * {Boolean} Currently restoring a history state.  This is set to true
115
     *     before calling restore and set to false after restore returns.
116
     */
117
    restoring: false,
118
    
119
    /**
120
     * Constructor: OpenLayers.Control.NavigationHistory 
121
     * 
122
     * Parameters:
123
     * options - {Object} An optional object whose properties will be used
124
     *     to extend the control.
125
     */
126
    initialize: function(options) {
127
        OpenLayers.Control.prototype.initialize.apply(this, [options]);
128
        
129
        this.registry = OpenLayers.Util.extend({
130
            "moveend": this.getState
131
        }, this.registry);
132
        
133
        var previousOptions = {
134
            trigger: OpenLayers.Function.bind(this.previousTrigger, this),
135
            displayClass: this.displayClass + " " + this.displayClass + "Previous"
136
        };
137
        OpenLayers.Util.extend(previousOptions, this.previousOptions);
138
        this.previous = new OpenLayers.Control.Button(previousOptions);
139
        
140
        var nextOptions = {
141
            trigger: OpenLayers.Function.bind(this.nextTrigger, this),
142
            displayClass: this.displayClass + " " + this.displayClass + "Next"
143
        };
144
        OpenLayers.Util.extend(nextOptions, this.nextOptions);
145
        this.next = new OpenLayers.Control.Button(nextOptions);
146

    
147
        this.clear();
148
    },
149
    
150
    /**
151
     * Method: onPreviousChange
152
     * Called when the previous history stack changes.
153
     *
154
     * Parameters:
155
     * state - {Object} An object representing the state to be restored
156
     *     if previous is triggered again or null if no previous states remain.
157
     * length - {Integer} The number of remaining previous states that can
158
     *     be restored.
159
     */
160
    onPreviousChange: function(state, length) {
161
        if(state && !this.previous.active) {
162
            this.previous.activate();
163
        } else if(!state && this.previous.active) {
164
            this.previous.deactivate();
165
        }
166
    },
167
    
168
    /**
169
     * Method: onNextChange
170
     * Called when the next history stack changes.
171
     *
172
     * Parameters:
173
     * state - {Object} An object representing the state to be restored
174
     *     if next is triggered again or null if no next states remain.
175
     * length - {Integer} The number of remaining next states that can
176
     *     be restored.
177
     */
178
    onNextChange: function(state, length) {
179
        if(state && !this.next.active) {
180
            this.next.activate();
181
        } else if(!state && this.next.active) {
182
            this.next.deactivate();
183
        }
184
    },
185
    
186
    /**
187
     * APIMethod: destroy
188
     * Destroy the control.
189
     */
190
    destroy: function() {
191
        OpenLayers.Control.prototype.destroy.apply(this);
192
        this.previous.destroy();
193
        this.next.destroy();
194
        this.deactivate();
195
        for(var prop in this) {
196
            this[prop] = null;
197
        }
198
    },
199
    
200
    /** 
201
     * Method: setMap
202
     * Set the map property for the control and <previous> and <next> child
203
     *     controls.
204
     *
205
     * Parameters:
206
     * map - {<OpenLayers.Map>} 
207
     */
208
    setMap: function(map) {
209
        this.map = map;
210
        this.next.setMap(map);
211
        this.previous.setMap(map);
212
    },
213

    
214
    /**
215
     * Method: draw
216
     * Called when the control is added to the map.
217
     */
218
    draw: function() {
219
        OpenLayers.Control.prototype.draw.apply(this, arguments);
220
        this.next.draw();
221
        this.previous.draw();
222
    },
223
    
224
    /**
225
     * Method: previousTrigger
226
     * Restore the previous state.  If no items are in the previous history
227
     *     stack, this has no effect.
228
     *
229
     * Returns:
230
     * {Object} Item representing state that was restored.  Undefined if no
231
     *     items are in the previous history stack.
232
     */
233
    previousTrigger: function() {
234
        var current = this.previousStack.shift();
235
        var state = this.previousStack.shift();
236
        if(state != undefined) {
237
            this.nextStack.unshift(current);
238
            this.previousStack.unshift(state);
239
            this.restoring = true;
240
            this.restore(state);
241
            this.restoring = false;
242
            this.onNextChange(this.nextStack[0], this.nextStack.length);
243
            this.onPreviousChange(
244
                this.previousStack[1], this.previousStack.length - 1
245
            );
246
        } else {
247
            this.previousStack.unshift(current);
248
        }
249
        return state;
250
    },
251
    
252
    /**
253
     * APIMethod: nextTrigger
254
     * Restore the next state.  If no items are in the next history
255
     *     stack, this has no effect.  The next history stack is populated
256
     *     as states are restored from the previous history stack.
257
     *
258
     * Returns:
259
     * {Object} Item representing state that was restored.  Undefined if no
260
     *     items are in the next history stack.
261
     */
262
    nextTrigger: function() {
263
        var state = this.nextStack.shift();
264
        if(state != undefined) {
265
            this.previousStack.unshift(state);
266
            this.restoring = true;
267
            this.restore(state);
268
            this.restoring = false;
269
            this.onNextChange(this.nextStack[0], this.nextStack.length);
270
            this.onPreviousChange(
271
                this.previousStack[1], this.previousStack.length - 1
272
            );
273
        }
274
        return state;
275
    },
276
    
277
    /**
278
     * APIMethod: clear
279
     * Clear history.
280
     */
281
    clear: function() {
282
        this.previousStack = [];
283
        this.previous.deactivate();
284
        this.nextStack = [];
285
        this.next.deactivate();
286
    },
287

    
288
    /**
289
     * Method: getState
290
     * Get the current state and return it.
291
     *
292
     * Returns:
293
     * {Object} An object representing the current state.
294
     */
295
    getState: function() {
296
        return {
297
            center: this.map.getCenter(),
298
            resolution: this.map.getResolution(),
299
            projection: this.map.getProjectionObject(),
300
            units: this.map.getProjectionObject().getUnits() || 
301
                this.map.units || this.map.baseLayer.units
302
        };
303
    },
304

    
305
    /**
306
     * Method: restore
307
     * Update the state with the given object.
308
     *
309
     * Parameters:
310
     * state - {Object} An object representing the state to restore.
311
     */
312
    restore: function(state) {
313
        var center, zoom;
314
        if (this.map.getProjectionObject() == state.projection) { 
315
            zoom = this.map.getZoomForResolution(state.resolution);
316
            center = state.center;
317
        } else {
318
            center = state.center.clone();
319
            center.transform(state.projection, this.map.getProjectionObject());
320
            var sourceUnits = state.units;
321
            var targetUnits = this.map.getProjectionObject().getUnits() || 
322
                this.map.units || this.map.baseLayer.units;
323
            var resolutionFactor = sourceUnits && targetUnits ? 
324
                OpenLayers.INCHES_PER_UNIT[sourceUnits] / OpenLayers.INCHES_PER_UNIT[targetUnits] : 1;
325
            zoom = this.map.getZoomForResolution(resolutionFactor*state.resolution); 
326
        }
327
        this.map.setCenter(center, zoom);
328
    },
329
    
330
    /**
331
     * Method: setListeners
332
     * Sets functions to be registered in the listeners object.
333
     */
334
    setListeners: function() {
335
        this.listeners = {};
336
        for(var type in this.registry) {
337
            this.listeners[type] = OpenLayers.Function.bind(function() {
338
                if(!this.restoring) {
339
                    var state = this.registry[type].apply(this, arguments);
340
                    this.previousStack.unshift(state);
341
                    if(this.previousStack.length > 1) {
342
                        this.onPreviousChange(
343
                            this.previousStack[1], this.previousStack.length - 1
344
                        );
345
                    }
346
                    if(this.previousStack.length > (this.limit + 1)) {
347
                        this.previousStack.pop();
348
                    }
349
                    if(this.nextStack.length > 0) {
350
                        this.nextStack = [];
351
                        this.onNextChange(null, 0);
352
                    }
353
                }
354
                return true;
355
            }, this);
356
        }
357
    },
358

    
359
    /**
360
     * APIMethod: activate
361
     * Activate the control.  This registers any listeners.
362
     *
363
     * Returns:
364
     * {Boolean} Control successfully activated.
365
     */
366
    activate: function() {
367
        var activated = false;
368
        if(this.map) {
369
            if(OpenLayers.Control.prototype.activate.apply(this)) {
370
                if(this.listeners == null) {
371
                    this.setListeners();
372
                }
373
                for(var type in this.listeners) {
374
                    this.map.events.register(type, this, this.listeners[type]);
375
                }
376
                activated = true;
377
                if(this.previousStack.length == 0) {
378
                    this.initStack();
379
                }
380
            }
381
        }
382
        return activated;
383
    },
384
    
385
    /**
386
     * Method: initStack
387
     * Called after the control is activated if the previous history stack is
388
     *     empty.
389
     */
390
    initStack: function() {
391
        if(this.map.getCenter()) {
392
            this.listeners.moveend();
393
        }
394
    },
395
    
396
    /**
397
     * APIMethod: deactivate
398
     * Deactivate the control.  This unregisters any listeners.
399
     *
400
     * Returns:
401
     * {Boolean} Control successfully deactivated.
402
     */
403
    deactivate: function() {
404
        var deactivated = false;
405
        if(this.map) {
406
            if(OpenLayers.Control.prototype.deactivate.apply(this)) {
407
                for(var type in this.listeners) {
408
                    this.map.events.unregister(
409
                        type, this, this.listeners[type]
410
                    );
411
                }
412
                if(this.clearOnDeactivate) {
413
                    this.clear();
414
                }
415
                deactivated = true;
416
            }
417
        }
418
        return deactivated;
419
    },
420
    
421
    CLASS_NAME: "OpenLayers.Control.NavigationHistory"
422
});
423

    
(20-20/45)