diff options
Diffstat (limited to 'misc/openlayers/lib/OpenLayers/Control/LayerSwitcher.js')
-rw-r--r-- | misc/openlayers/lib/OpenLayers/Control/LayerSwitcher.js | 521 |
1 files changed, 521 insertions, 0 deletions
diff --git a/misc/openlayers/lib/OpenLayers/Control/LayerSwitcher.js b/misc/openlayers/lib/OpenLayers/Control/LayerSwitcher.js new file mode 100644 index 0000000..668f5c3 --- /dev/null +++ b/misc/openlayers/lib/OpenLayers/Control/LayerSwitcher.js @@ -0,0 +1,521 @@ +/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for + * full list of contributors). Published under the 2-clause BSD license. + * See license.txt in the OpenLayers distribution or repository for the + * full text of the license. */ + +/** + * @requires OpenLayers/Control.js + * @requires OpenLayers/Lang.js + * @requires OpenLayers/Util.js + * @requires OpenLayers/Events/buttonclick.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, { + + /** + * 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(Object)} + */ + baseLayers: null, + + + /** + * Property: dataLbl + * {DOMElement} + */ + dataLbl: null, + + /** + * Property: dataLayersDiv + * {DOMElement} + */ + dataLayersDiv: null, + + /** + * Property: dataLayers + * {Array(Object)} + */ + 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() { + + //clear out layers info and unregister their events + this.clearLayersArray("base"); + this.clearLayersArray("data"); + + this.map.events.un({ + buttonclick: this.onButtonClick, + addlayer: this.redraw, + changelayer: this.redraw, + removelayer: this.redraw, + changebaselayer: this.redraw, + scope: this + }); + this.events.unregister("buttonclick", this, this.onButtonClick); + + 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 + }); + if (this.outsideViewport) { + this.events.attachToElement(this.div); + this.events.register("buttonclick", this, this.onButtonClick); + } else { + this.map.events.register("buttonclick", this, this.onButtonClick); + } + }, + + /** + * 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: onButtonClick + * + * Parameters: + * evt - {Event} + */ + onButtonClick: function(evt) { + var button = evt.buttonElement; + if (button === this.minimizeDiv) { + this.minimizeControl(); + } else if (button === this.maximizeDiv) { + this.maximizeControl(); + } else if (button._layerSwitcher === this.id) { + if (button["for"]) { + button = document.getElementById(button["for"]); + } + if (!button.disabled) { + if (button.type == "radio") { + button.checked = true; + this.map.setBaseLayer(this.map.getLayer(button._layer)); + } else { + button.checked = !button.checked; + this.updateMap(); + } + } + } + }, + + /** + * 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) { + 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() { + if ( !this.layerStates.length || + (this.map.layers.length != this.layerStates.length) ) { + return true; + } + + 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) ) { + return true; + } + } + + return false; + }, + + /** + * 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"), + // The input shall have an id attribute so we can use + // labels to interact with them. + inputId = OpenLayers.Util.createUniqueID( + this.id + "_input_" + ); + + inputElem.id = inputId; + inputElem.name = (baseLayer) ? this.id + "_baseLayers" : layer.name; + inputElem.type = (baseLayer) ? "radio" : "checkbox"; + inputElem.value = layer.name; + inputElem.checked = checked; + inputElem.defaultChecked = checked; + inputElem.className = "olButton"; + inputElem._layer = layer.id; + inputElem._layerSwitcher = this.id; + + if (!baseLayer && !layer.inRange) { + inputElem.disabled = true; + } + + // create span + var labelSpan = document.createElement("label"); + // this isn't the DOM attribute 'for', but an arbitrary name we + // use to find the appropriate input element in <onButtonClick> + labelSpan["for"] = inputElem.id; + OpenLayers.Element.addClass(labelSpan, "labelSpan olButton"); + labelSpan._layer = layer.id; + labelSpan._layerSwitcher = this.id; + if (!baseLayer && !layer.inRange) { + labelSpan.style.color = "gray"; + } + labelSpan.innerHTML = layer.name; + labelSpan.style.verticalAlign = (baseLayer) ? "bottom" + : "baseline"; + // 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: updateMap + * Cycles through the loaded data and base layer input arrays and makes + * the necessary calls to the Map object such that that the map's + * visual state corresponds to what the user has selected in + * the control. + */ + updateMap: function() { + + // set the newly selected base layer + for(var i=0, len=this.baseLayers.length; i<len; i++) { + var layerEntry = this.baseLayers[i]; + if (layerEntry.inputElem.checked) { + this.map.setBaseLayer(layerEntry.layer, false); + } + } + + // set the correct visibilities for the overlays + for(var i=0, len=this.dataLayers.length; i<len; i++) { + var layerEntry = this.dataLayers[i]; + layerEntry.layer.setVisibility(layerEntry.inputElem.checked); + } + + }, + + /** + * Method: maximizeControl + * Set up the labels and divs for the control + * + * Parameters: + * e - {Event} + */ + maximizeControl: function(e) { + + // set the div's width and height to empty values, so + // the div dimensions can be controlled by CSS + this.div.style.width = ""; + this.div.style.height = ""; + + this.showControls(false); + + if (e != null) { + OpenLayers.Event.stop(e); + } + }, + + /** + * Method: minimizeControl + * Hide all the contents of the control, shrink the size, + * add the maximize icon + * + * Parameters: + * e - {Event} + */ + minimizeControl: function(e) { + + // to minimize the control we set its div's width + // and height to 0px, we cannot just set "display" + // to "none" because it would hide the maximize + // div + this.div.style.width = "0px"; + this.div.style.height = "0px"; + + this.showControls(true); + + if (e != null) { + OpenLayers.Event.stop(e); + } + }, + + /** + * Method: showControls + * Hide/Show all LayerSwitcher controls depending on whether we are + * minimized or not + * + * Parameters: + * minimize - {Boolean} + */ + showControls: function(minimize) { + + this.maximizeDiv.style.display = minimize ? "" : "none"; + this.minimizeDiv.style.display = minimize ? "none" : ""; + + this.layersDiv.style.display = minimize ? "none" : ""; + }, + + /** + * Method: loadContents + * Set up the labels and divs for the control + */ + loadContents: function() { + + // layers list div + this.layersDiv = document.createElement("div"); + this.layersDiv.id = this.id + "_layersDiv"; + OpenLayers.Element.addClass(this.layersDiv, "layersDiv"); + + this.baseLbl = document.createElement("div"); + this.baseLbl.innerHTML = OpenLayers.i18n("Base Layer"); + OpenLayers.Element.addClass(this.baseLbl, "baseLbl"); + + this.baseLayersDiv = document.createElement("div"); + OpenLayers.Element.addClass(this.baseLayersDiv, "baseLayersDiv"); + + this.dataLbl = document.createElement("div"); + this.dataLbl.innerHTML = OpenLayers.i18n("Overlays"); + OpenLayers.Element.addClass(this.dataLbl, "dataLbl"); + + this.dataLayersDiv = document.createElement("div"); + OpenLayers.Element.addClass(this.dataLayersDiv, "dataLayersDiv"); + + if (this.ascending) { + this.layersDiv.appendChild(this.baseLbl); + this.layersDiv.appendChild(this.baseLayersDiv); + this.layersDiv.appendChild(this.dataLbl); + this.layersDiv.appendChild(this.dataLayersDiv); + } else { + this.layersDiv.appendChild(this.dataLbl); + this.layersDiv.appendChild(this.dataLayersDiv); + this.layersDiv.appendChild(this.baseLbl); + this.layersDiv.appendChild(this.baseLayersDiv); + } + + this.div.appendChild(this.layersDiv); + + // maximize button div + var img = OpenLayers.Util.getImageLocation('layer-switcher-maximize.png'); + this.maximizeDiv = OpenLayers.Util.createAlphaImageDiv( + "OpenLayers_Control_MaximizeDiv", + null, + null, + img, + "absolute"); + OpenLayers.Element.addClass(this.maximizeDiv, "maximizeDiv olButton"); + this.maximizeDiv.style.display = "none"; + + this.div.appendChild(this.maximizeDiv); + + // minimize button div + var img = OpenLayers.Util.getImageLocation('layer-switcher-minimize.png'); + this.minimizeDiv = OpenLayers.Util.createAlphaImageDiv( + "OpenLayers_Control_MinimizeDiv", + null, + null, + img, + "absolute"); + OpenLayers.Element.addClass(this.minimizeDiv, "minimizeDiv olButton"); + this.minimizeDiv.style.display = "none"; + + this.div.appendChild(this.minimizeDiv); + }, + + CLASS_NAME: "OpenLayers.Control.LayerSwitcher" +}); |