LayerSwitcher.js 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521
  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. * @requires OpenLayers/Control.js
  7. * @requires OpenLayers/Lang.js
  8. * @requires OpenLayers/Util.js
  9. * @requires OpenLayers/Events/buttonclick.js
  10. */
  11. /**
  12. * Class: OpenLayers.Control.LayerSwitcher
  13. * The LayerSwitcher control displays a table of contents for the map. This
  14. * allows the user interface to switch between BaseLasyers and to show or hide
  15. * Overlays. By default the switcher is shown minimized on the right edge of
  16. * the map, the user may expand it by clicking on the handle.
  17. *
  18. * To create the LayerSwitcher outside of the map, pass the Id of a html div
  19. * as the first argument to the constructor.
  20. *
  21. * Inherits from:
  22. * - <OpenLayers.Control>
  23. */
  24. OpenLayers.Control.LayerSwitcher = OpenLayers.Class(OpenLayers.Control, {
  25. /**
  26. * Property: layerStates
  27. * {Array(Object)} Basically a copy of the "state" of the map's layers
  28. * the last time the control was drawn. We have this in order to avoid
  29. * unnecessarily redrawing the control.
  30. */
  31. layerStates: null,
  32. // DOM Elements
  33. /**
  34. * Property: layersDiv
  35. * {DOMElement}
  36. */
  37. layersDiv: null,
  38. /**
  39. * Property: baseLayersDiv
  40. * {DOMElement}
  41. */
  42. baseLayersDiv: null,
  43. /**
  44. * Property: baseLayers
  45. * {Array(Object)}
  46. */
  47. baseLayers: null,
  48. /**
  49. * Property: dataLbl
  50. * {DOMElement}
  51. */
  52. dataLbl: null,
  53. /**
  54. * Property: dataLayersDiv
  55. * {DOMElement}
  56. */
  57. dataLayersDiv: null,
  58. /**
  59. * Property: dataLayers
  60. * {Array(Object)}
  61. */
  62. dataLayers: null,
  63. /**
  64. * Property: minimizeDiv
  65. * {DOMElement}
  66. */
  67. minimizeDiv: null,
  68. /**
  69. * Property: maximizeDiv
  70. * {DOMElement}
  71. */
  72. maximizeDiv: null,
  73. /**
  74. * APIProperty: ascending
  75. * {Boolean}
  76. */
  77. ascending: true,
  78. /**
  79. * Constructor: OpenLayers.Control.LayerSwitcher
  80. *
  81. * Parameters:
  82. * options - {Object}
  83. */
  84. initialize: function(options) {
  85. OpenLayers.Control.prototype.initialize.apply(this, arguments);
  86. this.layerStates = [];
  87. },
  88. /**
  89. * APIMethod: destroy
  90. */
  91. destroy: function() {
  92. //clear out layers info and unregister their events
  93. this.clearLayersArray("base");
  94. this.clearLayersArray("data");
  95. this.map.events.un({
  96. buttonclick: this.onButtonClick,
  97. addlayer: this.redraw,
  98. changelayer: this.redraw,
  99. removelayer: this.redraw,
  100. changebaselayer: this.redraw,
  101. scope: this
  102. });
  103. this.events.unregister("buttonclick", this, this.onButtonClick);
  104. OpenLayers.Control.prototype.destroy.apply(this, arguments);
  105. },
  106. /**
  107. * Method: setMap
  108. *
  109. * Properties:
  110. * map - {<OpenLayers.Map>}
  111. */
  112. setMap: function(map) {
  113. OpenLayers.Control.prototype.setMap.apply(this, arguments);
  114. this.map.events.on({
  115. addlayer: this.redraw,
  116. changelayer: this.redraw,
  117. removelayer: this.redraw,
  118. changebaselayer: this.redraw,
  119. scope: this
  120. });
  121. if (this.outsideViewport) {
  122. this.events.attachToElement(this.div);
  123. this.events.register("buttonclick", this, this.onButtonClick);
  124. } else {
  125. this.map.events.register("buttonclick", this, this.onButtonClick);
  126. }
  127. },
  128. /**
  129. * Method: draw
  130. *
  131. * Returns:
  132. * {DOMElement} A reference to the DIV DOMElement containing the
  133. * switcher tabs.
  134. */
  135. draw: function() {
  136. OpenLayers.Control.prototype.draw.apply(this);
  137. // create layout divs
  138. this.loadContents();
  139. // set mode to minimize
  140. if(!this.outsideViewport) {
  141. this.minimizeControl();
  142. }
  143. // populate div with current info
  144. this.redraw();
  145. return this.div;
  146. },
  147. /**
  148. * Method: onButtonClick
  149. *
  150. * Parameters:
  151. * evt - {Event}
  152. */
  153. onButtonClick: function(evt) {
  154. var button = evt.buttonElement;
  155. if (button === this.minimizeDiv) {
  156. this.minimizeControl();
  157. } else if (button === this.maximizeDiv) {
  158. this.maximizeControl();
  159. } else if (button._layerSwitcher === this.id) {
  160. if (button["for"]) {
  161. button = document.getElementById(button["for"]);
  162. }
  163. if (!button.disabled) {
  164. if (button.type == "radio") {
  165. button.checked = true;
  166. this.map.setBaseLayer(this.map.getLayer(button._layer));
  167. } else {
  168. button.checked = !button.checked;
  169. this.updateMap();
  170. }
  171. }
  172. }
  173. },
  174. /**
  175. * Method: clearLayersArray
  176. * User specifies either "base" or "data". we then clear all the
  177. * corresponding listeners, the div, and reinitialize a new array.
  178. *
  179. * Parameters:
  180. * layersType - {String}
  181. */
  182. clearLayersArray: function(layersType) {
  183. this[layersType + "LayersDiv"].innerHTML = "";
  184. this[layersType + "Layers"] = [];
  185. },
  186. /**
  187. * Method: checkRedraw
  188. * Checks if the layer state has changed since the last redraw() call.
  189. *
  190. * Returns:
  191. * {Boolean} The layer state changed since the last redraw() call.
  192. */
  193. checkRedraw: function() {
  194. if ( !this.layerStates.length ||
  195. (this.map.layers.length != this.layerStates.length) ) {
  196. return true;
  197. }
  198. for (var i = 0, len = this.layerStates.length; i < len; i++) {
  199. var layerState = this.layerStates[i];
  200. var layer = this.map.layers[i];
  201. if ( (layerState.name != layer.name) ||
  202. (layerState.inRange != layer.inRange) ||
  203. (layerState.id != layer.id) ||
  204. (layerState.visibility != layer.visibility) ) {
  205. return true;
  206. }
  207. }
  208. return false;
  209. },
  210. /**
  211. * Method: redraw
  212. * Goes through and takes the current state of the Map and rebuilds the
  213. * control to display that state. Groups base layers into a
  214. * radio-button group and lists each data layer with a checkbox.
  215. *
  216. * Returns:
  217. * {DOMElement} A reference to the DIV DOMElement containing the control
  218. */
  219. redraw: function() {
  220. //if the state hasn't changed since last redraw, no need
  221. // to do anything. Just return the existing div.
  222. if (!this.checkRedraw()) {
  223. return this.div;
  224. }
  225. //clear out previous layers
  226. this.clearLayersArray("base");
  227. this.clearLayersArray("data");
  228. var containsOverlays = false;
  229. var containsBaseLayers = false;
  230. // Save state -- for checking layer if the map state changed.
  231. // We save this before redrawing, because in the process of redrawing
  232. // we will trigger more visibility changes, and we want to not redraw
  233. // and enter an infinite loop.
  234. var len = this.map.layers.length;
  235. this.layerStates = new Array(len);
  236. for (var i=0; i <len; i++) {
  237. var layer = this.map.layers[i];
  238. this.layerStates[i] = {
  239. 'name': layer.name,
  240. 'visibility': layer.visibility,
  241. 'inRange': layer.inRange,
  242. 'id': layer.id
  243. };
  244. }
  245. var layers = this.map.layers.slice();
  246. if (!this.ascending) { layers.reverse(); }
  247. for(var i=0, len=layers.length; i<len; i++) {
  248. var layer = layers[i];
  249. var baseLayer = layer.isBaseLayer;
  250. if (layer.displayInLayerSwitcher) {
  251. if (baseLayer) {
  252. containsBaseLayers = true;
  253. } else {
  254. containsOverlays = true;
  255. }
  256. // only check a baselayer if it is *the* baselayer, check data
  257. // layers if they are visible
  258. var checked = (baseLayer) ? (layer == this.map.baseLayer)
  259. : layer.getVisibility();
  260. // create input element
  261. var inputElem = document.createElement("input"),
  262. // The input shall have an id attribute so we can use
  263. // labels to interact with them.
  264. inputId = OpenLayers.Util.createUniqueID(
  265. this.id + "_input_"
  266. );
  267. inputElem.id = inputId;
  268. inputElem.name = (baseLayer) ? this.id + "_baseLayers" : layer.name;
  269. inputElem.type = (baseLayer) ? "radio" : "checkbox";
  270. inputElem.value = layer.name;
  271. inputElem.checked = checked;
  272. inputElem.defaultChecked = checked;
  273. inputElem.className = "olButton";
  274. inputElem._layer = layer.id;
  275. inputElem._layerSwitcher = this.id;
  276. if (!baseLayer && !layer.inRange) {
  277. inputElem.disabled = true;
  278. }
  279. // create span
  280. var labelSpan = document.createElement("label");
  281. // this isn't the DOM attribute 'for', but an arbitrary name we
  282. // use to find the appropriate input element in <onButtonClick>
  283. labelSpan["for"] = inputElem.id;
  284. OpenLayers.Element.addClass(labelSpan, "labelSpan olButton");
  285. labelSpan._layer = layer.id;
  286. labelSpan._layerSwitcher = this.id;
  287. if (!baseLayer && !layer.inRange) {
  288. labelSpan.style.color = "gray";
  289. }
  290. labelSpan.innerHTML = layer.name;
  291. labelSpan.style.verticalAlign = (baseLayer) ? "bottom"
  292. : "baseline";
  293. // create line break
  294. var br = document.createElement("br");
  295. var groupArray = (baseLayer) ? this.baseLayers
  296. : this.dataLayers;
  297. groupArray.push({
  298. 'layer': layer,
  299. 'inputElem': inputElem,
  300. 'labelSpan': labelSpan
  301. });
  302. var groupDiv = (baseLayer) ? this.baseLayersDiv
  303. : this.dataLayersDiv;
  304. groupDiv.appendChild(inputElem);
  305. groupDiv.appendChild(labelSpan);
  306. groupDiv.appendChild(br);
  307. }
  308. }
  309. // if no overlays, dont display the overlay label
  310. this.dataLbl.style.display = (containsOverlays) ? "" : "none";
  311. // if no baselayers, dont display the baselayer label
  312. this.baseLbl.style.display = (containsBaseLayers) ? "" : "none";
  313. return this.div;
  314. },
  315. /**
  316. * Method: updateMap
  317. * Cycles through the loaded data and base layer input arrays and makes
  318. * the necessary calls to the Map object such that that the map's
  319. * visual state corresponds to what the user has selected in
  320. * the control.
  321. */
  322. updateMap: function() {
  323. // set the newly selected base layer
  324. for(var i=0, len=this.baseLayers.length; i<len; i++) {
  325. var layerEntry = this.baseLayers[i];
  326. if (layerEntry.inputElem.checked) {
  327. this.map.setBaseLayer(layerEntry.layer, false);
  328. }
  329. }
  330. // set the correct visibilities for the overlays
  331. for(var i=0, len=this.dataLayers.length; i<len; i++) {
  332. var layerEntry = this.dataLayers[i];
  333. layerEntry.layer.setVisibility(layerEntry.inputElem.checked);
  334. }
  335. },
  336. /**
  337. * Method: maximizeControl
  338. * Set up the labels and divs for the control
  339. *
  340. * Parameters:
  341. * e - {Event}
  342. */
  343. maximizeControl: function(e) {
  344. // set the div's width and height to empty values, so
  345. // the div dimensions can be controlled by CSS
  346. this.div.style.width = "";
  347. this.div.style.height = "";
  348. this.showControls(false);
  349. if (e != null) {
  350. OpenLayers.Event.stop(e);
  351. }
  352. },
  353. /**
  354. * Method: minimizeControl
  355. * Hide all the contents of the control, shrink the size,
  356. * add the maximize icon
  357. *
  358. * Parameters:
  359. * e - {Event}
  360. */
  361. minimizeControl: function(e) {
  362. // to minimize the control we set its div's width
  363. // and height to 0px, we cannot just set "display"
  364. // to "none" because it would hide the maximize
  365. // div
  366. this.div.style.width = "0px";
  367. this.div.style.height = "0px";
  368. this.showControls(true);
  369. if (e != null) {
  370. OpenLayers.Event.stop(e);
  371. }
  372. },
  373. /**
  374. * Method: showControls
  375. * Hide/Show all LayerSwitcher controls depending on whether we are
  376. * minimized or not
  377. *
  378. * Parameters:
  379. * minimize - {Boolean}
  380. */
  381. showControls: function(minimize) {
  382. this.maximizeDiv.style.display = minimize ? "" : "none";
  383. this.minimizeDiv.style.display = minimize ? "none" : "";
  384. this.layersDiv.style.display = minimize ? "none" : "";
  385. },
  386. /**
  387. * Method: loadContents
  388. * Set up the labels and divs for the control
  389. */
  390. loadContents: function() {
  391. // layers list div
  392. this.layersDiv = document.createElement("div");
  393. this.layersDiv.id = this.id + "_layersDiv";
  394. OpenLayers.Element.addClass(this.layersDiv, "layersDiv");
  395. this.baseLbl = document.createElement("div");
  396. this.baseLbl.innerHTML = OpenLayers.i18n("Base Layer");
  397. OpenLayers.Element.addClass(this.baseLbl, "baseLbl");
  398. this.baseLayersDiv = document.createElement("div");
  399. OpenLayers.Element.addClass(this.baseLayersDiv, "baseLayersDiv");
  400. this.dataLbl = document.createElement("div");
  401. this.dataLbl.innerHTML = OpenLayers.i18n("Overlays");
  402. OpenLayers.Element.addClass(this.dataLbl, "dataLbl");
  403. this.dataLayersDiv = document.createElement("div");
  404. OpenLayers.Element.addClass(this.dataLayersDiv, "dataLayersDiv");
  405. if (this.ascending) {
  406. this.layersDiv.appendChild(this.baseLbl);
  407. this.layersDiv.appendChild(this.baseLayersDiv);
  408. this.layersDiv.appendChild(this.dataLbl);
  409. this.layersDiv.appendChild(this.dataLayersDiv);
  410. } else {
  411. this.layersDiv.appendChild(this.dataLbl);
  412. this.layersDiv.appendChild(this.dataLayersDiv);
  413. this.layersDiv.appendChild(this.baseLbl);
  414. this.layersDiv.appendChild(this.baseLayersDiv);
  415. }
  416. this.div.appendChild(this.layersDiv);
  417. // maximize button div
  418. var img = OpenLayers.Util.getImageLocation('layer-switcher-maximize.png');
  419. this.maximizeDiv = OpenLayers.Util.createAlphaImageDiv(
  420. "OpenLayers_Control_MaximizeDiv",
  421. null,
  422. null,
  423. img,
  424. "absolute");
  425. OpenLayers.Element.addClass(this.maximizeDiv, "maximizeDiv olButton");
  426. this.maximizeDiv.style.display = "none";
  427. this.div.appendChild(this.maximizeDiv);
  428. // minimize button div
  429. var img = OpenLayers.Util.getImageLocation('layer-switcher-minimize.png');
  430. this.minimizeDiv = OpenLayers.Util.createAlphaImageDiv(
  431. "OpenLayers_Control_MinimizeDiv",
  432. null,
  433. null,
  434. img,
  435. "absolute");
  436. OpenLayers.Element.addClass(this.minimizeDiv, "minimizeDiv olButton");
  437. this.minimizeDiv.style.display = "none";
  438. this.div.appendChild(this.minimizeDiv);
  439. },
  440. CLASS_NAME: "OpenLayers.Control.LayerSwitcher"
  441. });