/* 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.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 - {} */ 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 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