|
/* Copyright (c) 2006-2010 by OpenLayers Contributors (see authors.txt for |
|
* full list of contributors). Published under the Clear BSD license. |
|
* See http://svn.openlayers.org/trunk/openlayers/license.txt for the |
|
* full text of the license. */ |
|
|
|
/** |
|
* @requires OpenLayers/Control.js |
|
*/ |
|
|
|
/** |
|
* Class: OpenLayers.Control.LayerSwitcher |
|
* The LayerSwitcher control displays a table of contents for the map. This |
|
* allows the user interface to switch between BaseLasyers and to show or hide |
|
* Overlays. By default the switcher is shown minimized on the right edge of |
|
* the map, the user may expand it by clicking on the handle. |
|
* |
|
* To create the LayerSwitcher outside of the map, pass the Id of a html div |
|
* as the first argument to the constructor. |
|
* |
|
* Inherits from: |
|
* - <OpenLayers.Control> |
|
*/ |
|
OpenLayers.Control.LayerSwitcher = |
|
OpenLayers.Class(OpenLayers.Control, { |
|
|
|
/** |
|
* APIProperty: roundedCorner |
|
* {Boolean} If true the Rico library is used for rounding the corners |
|
* of the layer switcher div, defaults to true. |
|
*/ |
|
roundedCorner: true, |
|
|
|
/** |
|
* APIProperty: roundedCornerColor |
|
* {String} The color of the rounded corners, only applies if roundedCorner |
|
* is true, defaults to "darkblue". |
|
*/ |
|
roundedCornerColor: "darkblue", |
|
|
|
/** |
|
* Property: layerStates |
|
* {Array(Object)} Basically a copy of the "state" of the map's layers |
|
* the last time the control was drawn. We have this in order to avoid |
|
* unnecessarily redrawing the control. |
|
*/ |
|
layerStates: null, |
|
|
|
|
|
// DOM Elements |
|
|
|
/** |
|
* Property: layersDiv |
|
* {DOMElement} |
|
*/ |
|
layersDiv: null, |
|
|
|
/** |
|
* Property: baseLayersDiv |
|
* {DOMElement} |
|
*/ |
|
baseLayersDiv: null, |
|
|
|
/** |
|
* Property: baseLayers |
|
* {Array(<OpenLayers.Layer>)} |
|
*/ |
|
baseLayers: null, |
|
|
|
|
|
/** |
|
* Property: dataLbl |
|
* {DOMElement} |
|
*/ |
|
dataLbl: null, |
|
|
|
/** |
|
* Property: dataLayersDiv |
|
* {DOMElement} |
|
*/ |
|
dataLayersDiv: null, |
|
|
|
/** |
|
* Property: dataLayers |
|
* {Array(<OpenLayers.Layer>)} |
|
*/ |
|
dataLayers: null, |
|
|
|
|
|
/** |
|
* Property: minimizeDiv |
|
* {DOMElement} |
|
*/ |
|
minimizeDiv: null, |
|
|
|
/** |
|
* Property: maximizeDiv |
|
* {DOMElement} |
|
*/ |
|
maximizeDiv: null, |
|
|
|
/** |
|
* APIProperty: ascending |
|
* {Boolean} |
|
*/ |
|
ascending: true, |
|
|
|
/** |
|
* Constructor: OpenLayers.Control.LayerSwitcher |
|
* |
|
* Parameters: |
|
* options - {Object} |
|
*/ |
|
initialize: function(options) { |
|
OpenLayers.Control.prototype.initialize.apply(this, arguments); |
|
this.layerStates = []; |
|
}, |
|
|
|
/** |
|
* APIMethod: destroy |
|
*/ |
|
destroy: function() { |
|
|
|
OpenLayers.Event.stopObservingElement(this.div); |
|
|
|
OpenLayers.Event.stopObservingElement(this.minimizeDiv); |
|
OpenLayers.Event.stopObservingElement(this.maximizeDiv); |
|
|
|
//clear out layers info and unregister their events |
|
this.clearLayersArray("base"); |
|
this.clearLayersArray("data"); |
|
|
|
this.map.events.un({ |
|
"addlayer": this.redraw, |
|
"changelayer": this.redraw, |
|
"removelayer": this.redraw, |
|
"changebaselayer": this.redraw, |
|
scope: this |
|
}); |
|
|
|
OpenLayers.Control.prototype.destroy.apply(this, arguments); |
|
}, |
|
|
|
/** |
|
* Method: setMap |
|
* |
|
* Properties: |
|
* map - {<OpenLayers.Map>} |
|
*/ |
|
setMap: function(map) { |
|
OpenLayers.Control.prototype.setMap.apply(this, arguments); |
|
|
|
this.map.events.on({ |
|
"addlayer": this.redraw, |
|
"changelayer": this.redraw, |
|
"removelayer": this.redraw, |
|
"changebaselayer": this.redraw, |
|
scope: this |
|
}); |
|
}, |
|
|
|
/** |
|
* Method: draw |
|
* |
|
* Returns: |
|
* {DOMElement} A reference to the DIV DOMElement containing the |
|
* switcher tabs. |
|
*/ |
|
draw: function() { |
|
OpenLayers.Control.prototype.draw.apply(this); |
|
|
|
// create layout divs |
|
this.loadContents(); |
|
|
|
// set mode to minimize |
|
if(!this.outsideViewport) { |
|
this.minimizeControl(); |
|
} |
|
|
|
// populate div with current info |
|
this.redraw(); |
|
|
|
return this.div; |
|
}, |
|
|
|
/** |
|
* Method: clearLayersArray |
|
* User specifies either "base" or "data". we then clear all the |
|
* corresponding listeners, the div, and reinitialize a new array. |
|
* |
|
* Parameters: |
|
* layersType - {String} |
|
*/ |
|
clearLayersArray: function(layersType) { |
|
var layers = this[layersType + "Layers"]; |
|
if (layers) { |
|
for(var i=0, len=layers.length; i<len ; i++) { |
|
var layer = layers[i]; |
|
OpenLayers.Event.stopObservingElement(layer.inputElem); |
|
OpenLayers.Event.stopObservingElement(layer.labelSpan); |
|
} |
|
} |
|
this[layersType + "LayersDiv"].innerHTML = ""; |
|
this[layersType + "Layers"] = []; |
|
}, |
|
|
|
|
|
/** |
|
* Method: checkRedraw |
|
* Checks if the layer state has changed since the last redraw() call. |
|
* |
|
* Returns: |
|
* {Boolean} The layer state changed since the last redraw() call. |
|
*/ |
|
checkRedraw: function() { |
|
var redraw = false; |
|
if ( !this.layerStates.length || |
|
(this.map.layers.length != this.layerStates.length) ) { |
|
redraw = true; |
|
} else { |
|
for (var i=0, len=this.layerStates.length; i<len; i++) { |
|
var layerState = this.layerStates[i]; |
|
var layer = this.map.layers[i]; |
|
if ( (layerState.name != layer.name) || |
|
(layerState.inRange != layer.inRange) || |
|
(layerState.id != layer.id) || |
|
(layerState.visibility != layer.visibility) ) { |
|
redraw = true; |
|
break; |
|
} |
|
} |
|
} |
|
return redraw; |
|
}, |
|
|
|
/** |
|
* Method: redraw |
|
* Goes through and takes the current state of the Map and rebuilds the |
|
* control to display that state. Groups base layers into a |
|
* radio-button group and lists each data layer with a checkbox. |
|
* |
|
* Returns: |
|
* {DOMElement} A reference to the DIV DOMElement containing the control |
|
*/ |
|
redraw: function() { |
|
//if the state hasn't changed since last redraw, no need |
|
// to do anything. Just return the existing div. |
|
if (!this.checkRedraw()) { |
|
return this.div; |
|
} |
|
|
|
//clear out previous layers |
|
this.clearLayersArray("base"); |
|
this.clearLayersArray("data"); |
|
|
|
var containsOverlays = false; |
|
var containsBaseLayers = false; |
|
|
|
// Save state -- for checking layer if the map state changed. |
|
// We save this before redrawing, because in the process of redrawing |
|
// we will trigger more visibility changes, and we want to not redraw |
|
// and enter an infinite loop. |
|
var len = this.map.layers.length; |
|
this.layerStates = new Array(len); |
|
for (var i=0; i <len; i++) { |
|
var layer = this.map.layers[i]; |
|
this.layerStates[i] = { |
|
'name': layer.name, |
|
'visibility': layer.visibility, |
|
'inRange': layer.inRange, |
|
'id': layer.id |
|
}; |
|
} |
|
|
|
var layers = this.map.layers.slice(); |
|
if (!this.ascending) { layers.reverse(); } |
|
for(var i=0, len=layers.length; i<len; i++) { |
|
var layer = layers[i]; |
|
var baseLayer = layer.isBaseLayer; |
|
|
|
if (layer.displayInLayerSwitcher) { |
|
|
|
if (baseLayer) { |
|
containsBaseLayers = true; |
|
} else { |
|
containsOverlays = true; |
|
} |
|
|
|
// only check a baselayer if it is *the* baselayer, check data |
|
// layers if they are visible |
|
var checked = (baseLayer) ? (layer == this.map.baseLayer) |
|
: layer.getVisibility(); |
|
|
|
// create input element |
|
var inputElem = document.createElement("input"); |
|
inputElem.id = this.id + "_input_" + layer.name; |
|
inputElem.name = (baseLayer) ? this.id + "_baseLayers" : layer.name; |
|
inputElem.type = (baseLayer) ? "radio" : "checkbox"; |
|
inputElem.value = layer.name; |
|
inputElem.checked = checked; |
|
inputElem.defaultChecked = checked; |
|
|
|
if (!baseLayer && !layer.inRange) { |
|
inputElem.disabled = true; |
|
} |
|
var context = { |
|
'inputElem': inputElem, |
|
'layer': layer, |
|
'layerSwitcher': this |
|
}; |
|
OpenLayers.Event.observe(inputElem, "mouseup", |
|
OpenLayers.Function.bindAsEventListener(this.onInputClick, |
|
context) |
|
); |
|
|
|
// create span |
|
var labelSpan = document.createElement("span"); |
|
OpenLayers.Element.addClass(labelSpan, "labelSpan") |
|
if (!baseLayer && !layer.inRange) { |
|
labelSpan.style.color = "gray"; |
|
} |
|
labelSpan.innerHTML = layer.name; |
|
labelSpan.style.verticalAlign = (baseLayer) ? "bottom" |
|
: "baseline"; |
|
OpenLayers.Event.observe(labelSpan, "click", |
|
OpenLayers.Function.bindAsEventListener(this.onInputClick, |
|
context) |
|
); |
|
// create line break |
|
var br = document.createElement("br"); |
|
|
|
|
|
var groupArray = (baseLayer) ? this.baseLayers |
|
: this.dataLayers; |
|
groupArray.push({ |
|
'layer': layer, |
|
'inputElem': inputElem, |
|
'labelSpan': labelSpan |
|
}); |
|
|
|
|
|
var groupDiv = (baseLayer) ? this.baseLayersDiv |
|
: this.dataLayersDiv; |
|
groupDiv.appendChild(inputElem); |
|
groupDiv.appendChild(labelSpan); |
|
groupDiv.appendChild(br); |
|
} |
|
} |
|
|
|
// if no overlays, dont display the overlay label |
|
this.dataLbl.style.display = (containsOverlays) ? "" : "none"; |
|
|
|
// if no baselayers, dont display the baselayer label |
|
this.baseLbl.style.display = (containsBaseLayers) ? "" : "none"; |
|
|
|
return this.div; |
|
}, |
|
|
|
/** |
|
* Method: |
|
* A label has been clicked, check or uncheck its corresponding input |
|
* |
|
* Parameters: |
|
* e - {Event} |
|
* |
|
* Context: |
|
* - {DOMElement} inputElem |
|
* - {<OpenLayers.Control.LayerSwitcher>} layerSwitcher |
|
* - {<OpenLayers.Layer>} layer |
|
*/ |
|
|
|
onInputClick: function(e) { |
|
|
|
if (!this.inputElem.disabled) { |
|
if (this.inputElem.type == "radio") { |
|
this.inputElem.checked = true; |
|
this.layer.map.setBaseLayer(this.layer); |
|
} else { |
|
this.inputElem.checked = !this.inputElem.checked; |
|