More trip planner testing with colors
[busui.git] / labs / openlayers / lib / OpenLayers / Control / LayerSwitcher.js
blob:a/labs/openlayers/lib/OpenLayers/Control/LayerSwitcher.js -> blob:b/labs/openlayers/lib/OpenLayers/Control/LayerSwitcher.js
  /* 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;