├── .jshintrc ├── README.md ├── index.html ├── js ├── LayerList.js └── LayerList │ ├── css │ └── LayerList.css │ ├── nls │ └── LayerList.js │ └── templates │ └── LayerList.html ├── license.txt └── map.html /.jshintrc: -------------------------------------------------------------------------------- 1 | { 2 | "undef": true, 3 | "browser":true, 4 | "devel":true, 5 | "globals": { "require": false, "define":false } 6 | } -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # LayerList Widget 2 | 3 | ## Features 4 | The LayerList widget provides list of layers that allows the toggling of layer visibility. The style can be completely changed and skinned to match your own map design. 5 | 6 | ### Purpose 7 | - This widget is meant to be simple. It's general purpose is for the end user to toggle layer visibility. 8 | - A legend isn't included along side each layer. Use the legend widget for displaying information about the layers. 9 | - There are options to specify a node for a button and/or custom content. These nodes will allow you to do your app's configuration of the layer or put custom content, like a transperency slider, underneath the layer's title. 10 | 11 | ### Known Issues 12 | - Toggling of Mapservice, KML and WMS sublayers outside of the widget is not supported. 13 | - Out of scale range for sublayers not supported. 14 | 15 | [View it live](http://esri.github.io/arcgis-dijit-layer-list/) 16 | 17 | ## Quickstart 18 | 19 | ```javascript 20 | var map = response.map; 21 | //var layers = arcgisUtils.getLayerList(response); 22 | 23 | myWidget = new LayerList({ 24 | map: map, 25 | //layers: layers 26 | }, "LayerList"); 27 | myWidget.startup(); 28 | ``` 29 | 30 | [New to Github? Get started here.](https://github.com/) 31 | 32 | 33 | ## Setup 34 | Set your dojo config to load the module. 35 | 36 | var package_path = window.location.pathname.substring(0, window.location.pathname.lastIndexOf('/')); 37 | var dojoConfig = { 38 | // The locationPath logic below may look confusing but all its doing is 39 | // enabling us to load the api from a CDN and load local modules from the correct location. 40 | packages: [{ 41 | name: "application", 42 | location: package_path + '/js' 43 | }] 44 | }; 45 | 46 | ## Require module 47 | Include the module for the LayerList. 48 | 49 | require(["application/LayerList", ... ], function(LayerList, ... ){ ... }); 50 | 51 | ## Constructor 52 | 53 | LayerList(options, srcNode); 54 | 55 | ### Options (Object) 56 | |property|required|type|value|description| 57 | |---|---|---|---|---| 58 | |theme||string|esriLayerList|CSS Class for uniquely styling the widget.| 59 | |map|x|Map|null|ArcGIS JS Map| 60 | |layers|x|Object[]|null|[Operational Layers](http://resources.arcgis.com/en/help/arcgis-web-map-json/index.html#/ArcGIS_map_service_operational_layer/02qt00000018000000/) ([Layer Example](http://resources.arcgis.com/en/help/arcgis-web-map-json/index.html#/operationalLayer/02qt00000006000000/))| 61 | |visible||Boolean|true|Show the widget| 62 | |removeUnderscores||Boolean|true|Removes underscores from the layer title| 63 | |subLayers||Boolean|true|Show sublayers in the list of layers| 64 | 65 | #### Layers Object 66 | This is what the layers array should look like. 67 | 68 | ``` javascript 69 | layers = [ 70 | { 71 | layer: LayerObject // required unless featureCollection. 72 | featureCollection: featureCollection, // required unless layerObject. if the layer is a feature collection, should match AGOL feature collection response and not have a layerObject. 73 | subLayers: true, // optional 74 | content: , // optional 75 | button: , // optional 76 | visibility: true, // optional 77 | id: "my_layer" // optional 78 | }, 79 | { 80 | ... 81 | } 82 | ]; 83 | ``` 84 | 85 | ## Properties 86 | |property|type|description| 87 | |---|---|---| 88 | |theme|string|CSS Class for uniquely styling the widget.| 89 | |map|Map|ArcGIS JS Map| 90 | |layers|Array|Array of layers| 91 | |visible|Boolean|Show the widget| 92 | |loaded|Boolean|If the widget has been loaded.| 93 | |removeUnderscores||Boolean|Removes underscores from the layer title| 94 | 95 | 96 | ## Methods 97 | ### startup 98 | startup(): Start the widget. 99 | ### destroy 100 | destroy(): Destroy the widget. 101 | ### refresh 102 | refresh(): reload all layers and properties that may have changed. 103 | 104 | ## Events 105 | ### load 106 | #### Example 107 | on(widget, 'load', function(evt){…}) 108 | 109 | ### refresh 110 | #### Example 111 | on(widget, 'refresh', function(evt){…}) 112 | 113 | ### toggle 114 | #### Example 115 | on(widget, 'toggle', function(evt){…}) 116 | #### Event Object 117 | ``` javascript 118 | { 119 | layerIndex: Integer, 120 | subLayerIndex: Integer, 121 | visible: Boolean 122 | } 123 | ``` 124 | 125 | 126 | ## Requirements 127 | 128 | * Notepad or HTML editor 129 | * A little background with Javascript 130 | * Experience with the [ArcGIS Javascript API](http://www.esri.com/) would help. 131 | 132 | ## Resources 133 | 134 | * [ArcGIS for JavaScript API Resource Center](http://help.arcgis.com/en/webapi/javascript/arcgis/index.html) 135 | * [ArcGIS Blog](http://blogs.esri.com/esri/arcgis/) 136 | * [twitter@esri](http://twitter.com/esri) 137 | 138 | ## Issues 139 | 140 | Find a bug or want to request a new feature? Please let us know by submitting an issue. 141 | 142 | ## Contributing 143 | 144 | Anyone and everyone is welcome to contribute. 145 | 146 | ## Licensing 147 | Copyright 2012 Esri 148 | 149 | Licensed under the Apache License, Version 2.0 (the "License"); 150 | you may not use this file except in compliance with the License. 151 | You may obtain a copy of the License at 152 | 153 | http://www.apache.org/licenses/LICENSE-2.0 154 | 155 | Unless required by applicable law or agreed to in writing, software 156 | distributed under the License is distributed on an "AS IS" BASIS, 157 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 158 | See the License for the specific language governing permissions and 159 | limitations under the License. 160 | 161 | A copy of the license is available in the repository's [license.txt](https://raw.github.com/Esri/arcgis-dijit-layer-list/master/license.txt) file. 162 | 163 | [](Esri Tags: ArcGIS JavaScript API Layer LayerList Table of Contents Public) 164 | [](Esri Language: JavaScript) 165 | -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | LayerList Dijit 8 | 9 | 10 | 11 | 55 | 56 | 57 | 58 |
59 |
60 |
61 |
62 |
63 |
64 |

Change Options

65 |
    66 |
  • Theme: 67 | 68 |
  • 69 |
  • Visible: 70 | 71 |
  • 72 |
  • Show Legend: 73 | 74 |
  • 75 |
  • Show Opacity Slider: 76 | 77 |
  • 78 |
79 |

Documentation & Download 80 |

81 |
82 | 94 | 95 | 223 | 224 | 225 | -------------------------------------------------------------------------------- /js/LayerList.js: -------------------------------------------------------------------------------- 1 | define([ 2 | "dojo/_base/array", 3 | "dojo/_base/declare", 4 | "dojo/_base/lang", 5 | 6 | "esri/kernel", 7 | "dojo/uacss", 8 | 9 | "dojo/Deferred", 10 | "dojo/on", 11 | 12 | "dojo/dom-class", 13 | "dojo/dom-style", 14 | "dojo/dom-construct", 15 | "dojo/dom-attr", 16 | 17 | "dojo/i18n!./LayerList/nls/LayerList", 18 | 19 | "dijit/_WidgetBase", 20 | "dijit/_TemplatedMixin", 21 | 22 | "esri/promiseList", 23 | 24 | "esri/layerUtils", 25 | 26 | "dojo/text!./LayerList/templates/LayerList.html" 27 | ], 28 | function ( 29 | array, declare, lang, 30 | esriNS, has, 31 | Deferred, on, 32 | domClass, domStyle, domConstruct, domAttr, 33 | i18n, 34 | _WidgetBase, _TemplatedMixin, 35 | promiseList, 36 | layerUtils, 37 | dijitTemplate 38 | ) { 39 | 40 | var Widget = declare([_WidgetBase, _TemplatedMixin], { 41 | 42 | templateString: dijitTemplate, 43 | 44 | defaults: { 45 | theme: "esriLayerList", 46 | map: null, 47 | layers: null, 48 | subLayers: true, 49 | showOpacitySlider: false, 50 | showLegend: false, 51 | removeUnderscores: true, 52 | visible: true 53 | }, 54 | 55 | // lifecycle: 1 56 | constructor: function (options) { 57 | // mix properties 58 | var properties = lang.mixin({}, this.defaults, options); 59 | this.set(properties); 60 | // classes 61 | this.css = { 62 | container: "esriContainer", 63 | noLayers: "esriNoLayers", 64 | noLayersText: "esriNoLayersText", 65 | slider: "esriSlider", 66 | legend: "esriLegend", 67 | list: "esriList", 68 | listExpand: "esriListExpand", 69 | listVisible: "esriListVisible", 70 | subList: "esriSubList", 71 | hasSubList: "esriHasSubList", 72 | subListLayer: "esriSubListLayer", 73 | layer: "esriLayer", 74 | layerScaleInvisible: "esriScaleInvisible", 75 | title: "esriTitle", 76 | titleContainer: "esriTitleContainer", 77 | checkbox: "esriCheckbox", 78 | label: "esriLabel", 79 | button: "esriButton", 80 | content: "esriContent", 81 | clear: "esriClear" 82 | }; 83 | }, 84 | 85 | postCreate: function () { 86 | this.inherited(arguments); 87 | var _self = this; 88 | // when checkbox is clicked 89 | this.own(on(this._layersNode, "." + this.css.checkbox + ":click", function () { 90 | var data, subData; 91 | // layer index 92 | data = domAttr.get(this, "data-layer-index"); 93 | // subLayer index 94 | subData = domAttr.get(this, "data-sublayer-index"); 95 | // expand/collapse if necessary 96 | _self._toggleState(data, subData); 97 | // toggle layer visibility 98 | _self._toggleLayer(data, subData); 99 | })); 100 | }, 101 | 102 | // start widget. called by user 103 | startup: function () { 104 | this.inherited(arguments); 105 | this._mapLoaded(this.map).then(lang.hitch(this, this._init)); 106 | }, 107 | 108 | // connections/subscriptions will be cleaned up during the destroy() lifecycle phase 109 | destroy: function () { 110 | this._removeEvents(); 111 | this.inherited(arguments); 112 | }, 113 | 114 | /* ---------------- */ 115 | /* Public Functions */ 116 | /* ---------------- */ 117 | 118 | refresh: function () { 119 | // all layer info 120 | var layers = this.layers; 121 | // store nodes here 122 | this._nodes = []; 123 | var promises = []; 124 | // if we got layers 125 | if (layers && layers.length) { 126 | for (var i = 0; i < layers.length; i++) { 127 | promises.push(this._layerLoaded(i)); 128 | } 129 | } 130 | // wait for layers to load or fail 131 | return promiseList(promises).always(lang.hitch(this, function (response) { 132 | this._loadedLayers = response; 133 | this._removeEvents(); 134 | this._createLayerNodes(); 135 | this._setLayerEvents(); 136 | this.emit("refresh"); 137 | })); 138 | }, 139 | 140 | /* ---------------- */ 141 | /* Private Functions */ 142 | /* ---------------- */ 143 | 144 | _mapLoaded: function (map) { 145 | var def = new Deferred(); 146 | if (map) { 147 | // when map is loaded 148 | if (map.loaded) { 149 | def.resolve(); 150 | } else { 151 | on.once(map, "load", lang.hitch(this, function () { 152 | def.resolve(); 153 | })); 154 | } 155 | } else { 156 | def.resolve(); 157 | } 158 | return def.promise; 159 | }, 160 | 161 | _layerLoaded: function (layerIndex) { 162 | var layers = this.layers; 163 | var layerInfo = layers[layerIndex]; 164 | var layer = layerInfo.layer; 165 | // returned event 166 | var evt = { 167 | layer: layer, 168 | layerInfo: layerInfo, 169 | layerIndex: layerIndex 170 | }; 171 | var def = new Deferred(); 172 | if (layer) { 173 | if (layer.loaded) { 174 | // nothing to do 175 | def.resolve(evt); 176 | } else if (layer.loadError) { 177 | def.reject(layer.loadError); 178 | } else { 179 | var loadedEvent, errorEvent; 180 | // once layer is loaded 181 | loadedEvent = on.once(layer, "load", lang.hitch(this, function () { 182 | errorEvent.remove(); 183 | def.resolve(evt); 184 | })); 185 | // error occurred loading layer 186 | errorEvent = on.once(layer, "error", lang.hitch(this, function (error) { 187 | loadedEvent.remove(); 188 | def.reject(error); 189 | })); 190 | } 191 | } else { 192 | def.resolve(evt); 193 | } 194 | return def.promise; 195 | }, 196 | 197 | _checkboxStatus: function (layerInfo) { 198 | return !!layerInfo.visibility; 199 | }, 200 | 201 | _WMSVisible: function (layerInfo, subLayerInfo) { 202 | var visibleLayers = []; 203 | if (layerInfo && layerInfo.layer) { 204 | visibleLayers = layerInfo.layer.visibleLayers; 205 | } 206 | return array.indexOf(visibleLayers, subLayerInfo.name) > -1; 207 | }, 208 | 209 | _subCheckboxStatus: function (layerInfo, subLayerInfo) { 210 | var checked; 211 | var layerType = layerInfo.layer.declaredClass; 212 | switch (layerType) { 213 | case "esri.layers.KMLLayer": 214 | checked = subLayerInfo.visible; 215 | break; 216 | case "esri.layers.WMSLayer": 217 | checked = this._WMSVisible(layerInfo, subLayerInfo); 218 | break; 219 | default: 220 | checked = subLayerInfo.defaultVisibility; 221 | } 222 | return checked; 223 | }, 224 | 225 | _getLayerTitle: function (e) { 226 | var title = "", 227 | layer = e.layer, 228 | layerInfo = e.layerInfo; 229 | // get best title 230 | if (layerInfo && layerInfo.title) { 231 | title = layerInfo.title; 232 | } else if (layer && layer.arcgisProps && layer.arcgisProps.title) { 233 | title = layer.arcgisProps.title; 234 | } else if (layer && layer.name) { 235 | title = layer.name; 236 | } else if (layerInfo && layerInfo.id) { 237 | title = layerInfo.id; 238 | } else if (layer && layer.id) { 239 | title = layer.id; 240 | } 241 | // optionally remove underscores 242 | return this.removeUnderscores ? title.replace(/_/g, " ") : title; 243 | }, 244 | 245 | _showSublayers: function (layerInfo) { 246 | return layerInfo.hasOwnProperty("subLayers") ? layerInfo.subLayers : this.subLayers; 247 | }, 248 | 249 | _opacityChange: function (value) { 250 | if (this.layer) { 251 | this.layer.setOpacity(value); 252 | } else if (this.layers) { 253 | for (var i = 0; i < this.layers.length; i++) { 254 | if (this.layers[i].layerObject) { 255 | this.layers[i].layerObject.setOpacity(value); 256 | } 257 | } 258 | } 259 | }, 260 | 261 | _legend: function (titleNode, layerInfo) { 262 | require(["esri/dijit/Legend"], lang.hitch(this, function (Legend) { 263 | var legendInfo = [layerInfo]; 264 | if (layerInfo && layerInfo.featureCollection && layerInfo.featureCollection.layers) { 265 | legendInfo = layerInfo.featureCollection.layers; 266 | for (var i = 0; i < legendInfo.length; i++) { 267 | legendInfo[i].layer = legendInfo[i].layerObject; 268 | } 269 | } 270 | var legendDiv = domConstruct.create("div", { 271 | className: this.css.legend 272 | }, titleNode); 273 | var legend = new Legend({ 274 | map: this.map, 275 | layerInfos: legendInfo 276 | }, domConstruct.create("div")); 277 | domConstruct.place(legend.domNode, legendDiv); 278 | legend.startup(); 279 | })); 280 | }, 281 | 282 | _slider: function (titleNode, layer, layers, opacity) { 283 | require(["dijit/form/HorizontalSlider"], lang.hitch(this, function (HorizontalSlider) { 284 | var sliderDiv = domConstruct.create("div", { 285 | className: this.css.slider 286 | }, titleNode); 287 | var slider = new HorizontalSlider({ 288 | showButtons: false, 289 | minimum: 0.1, 290 | maximum: 1, 291 | layer: layer, 292 | layers: layers, 293 | discreteValues: 0.1, 294 | value: opacity, 295 | onChange: this._opacityChange 296 | }, domConstruct.create("div")); 297 | domConstruct.place(slider.domNode, sliderDiv); 298 | slider.startup(); 299 | })); 300 | }, 301 | 302 | _createLayerNodes: function () { 303 | // clear node 304 | this._layersNode.innerHTML = ""; 305 | this._noLayersNode.innerHTML = ""; 306 | domClass.remove(this._container, this.css.noLayers); 307 | var loadedLayers = this._loadedLayers; 308 | if (loadedLayers && loadedLayers.length) { 309 | // create nodes for each layer 310 | for (var i = 0; i < loadedLayers.length; i++) { 311 | var response = loadedLayers[i]; 312 | if (response) { 313 | var layer = response.layer; 314 | var layerIndex = response.layerIndex; 315 | var layerInfo = response.layerInfo; 316 | if (layerInfo) { 317 | if (layerInfo.featureCollection && !layerInfo.hasOwnProperty("visibility")) { 318 | var firstLayer = layerInfo.featureCollection.layers[0]; 319 | if (firstLayer && firstLayer.layerObject) { 320 | layerInfo.visibility = firstLayer.layerObject.visible; 321 | } 322 | } 323 | // set visibility on layer info if not set 324 | if (layer && !layerInfo.hasOwnProperty("visibility")) { 325 | layerInfo.visibility = layerInfo.layer.visible; 326 | } 327 | // set layer info id 328 | if (layer && !layerInfo.hasOwnProperty("id")) { 329 | layerInfo.id = layerInfo.layer.id; 330 | } 331 | var subLayers; 332 | // layer node 333 | var layerNode = domConstruct.create("li", { 334 | className: this.css.layer 335 | }); 336 | // currently visible layer 337 | if (layer && !layer.visibleAtMapScale) { 338 | domClass.add(layerNode, this.css.layerScaleInvisible); 339 | } 340 | domConstruct.place(layerNode, this._layersNode, "first"); 341 | // title of layer 342 | var titleNode = domConstruct.create("div", { 343 | className: this.css.title 344 | }, layerNode); 345 | // nodes for subLayers 346 | var subNodes = []; 347 | var layerType; 348 | if (layer) { 349 | layerType = layer.declaredClass; 350 | } 351 | // get parent layer checkbox status 352 | var status = this._checkboxStatus(layerInfo); 353 | // title container 354 | var titleContainerNode = domConstruct.create("div", { 355 | className: this.css.titleContainer 356 | }, titleNode); 357 | var id = this.id + "_checkbox_" + layerIndex; 358 | // Title checkbox 359 | var checkboxNode = domConstruct.create("input", { 360 | type: "checkbox", 361 | id: id, 362 | "data-layer-index": layerIndex, 363 | className: this.css.checkbox 364 | }, titleContainerNode); 365 | domAttr.set(checkboxNode, "checked", status); 366 | // optional button icon 367 | var buttonNode; 368 | if (layerInfo.button) { 369 | buttonNode = domConstruct.create("div", { 370 | className: this.css.button 371 | }, titleContainerNode); 372 | domConstruct.place(layerInfo.button, buttonNode); 373 | } 374 | // Title text 375 | var title = this._getLayerTitle(response); 376 | var labelNode = domConstruct.create("label", { 377 | className: this.css.label, 378 | textContent: title 379 | }, titleContainerNode); 380 | domAttr.set(labelNode, "for", id); 381 | // clear css 382 | var clearNode = domConstruct.create("div", { 383 | className: this.css.clear 384 | }, titleContainerNode); 385 | // opacity slider 386 | if (layerInfo.hasOwnProperty("showOpacitySlider") ? layerInfo.showOpacitySlider : this.showOpacitySlider) { 387 | var layers, opacity; 388 | if (!layer && layerInfo.featureCollection) { 389 | layers = layerInfo.featureCollection.layers; 390 | opacity = layerInfo.featureCollection.layers[0].opacity; 391 | } else { 392 | opacity = layer.opacity; 393 | } 394 | this._slider(titleNode, layer, layers, opacity); 395 | } 396 | // legend 397 | if (layerInfo.hasOwnProperty("showLegend") ? layerInfo.showLegend : this.showLegend) { 398 | this._legend(titleNode, layerInfo); 399 | } 400 | // optional custom content 401 | var contentNode; 402 | if (layerInfo.content) { 403 | contentNode = domConstruct.create("div", { 404 | className: this.css.content 405 | }, titleNode); 406 | domConstruct.place(layerInfo.content, contentNode); 407 | } 408 | // lets save all the nodes for events 409 | var nodesObj = { 410 | checkbox: checkboxNode, 411 | title: titleNode, 412 | titleContainer: titleContainerNode, 413 | label: labelNode, 414 | layer: layerNode, 415 | clear: clearNode, 416 | button: buttonNode, 417 | content: contentNode, 418 | subNodes: subNodes 419 | }; 420 | this._nodes[layerIndex] = nodesObj; 421 | domClass.toggle(layerNode, this.css.listVisible, status); 422 | if (layer) { 423 | // subLayers from thier info. Also WMS layers 424 | subLayers = layer.layerInfos; 425 | // KML subLayers 426 | if (layerType === "esri.layers.KMLLayer") { 427 | subLayers = layer.folders; 428 | } 429 | // if we have more than one subLayer and layer is of valid type for subLayers 430 | if (this._showSublayers(layerInfo) && layerType !== "esri.layers.ArcGISTiledMapServiceLayer" && subLayers && subLayers.length) { 431 | domClass.add(layerNode, this.css.hasSubList); 432 | domClass.toggle(layerNode, this.css.listExpand, status); 433 | // create subLayer list 434 | var subListNode = domConstruct.create("ul", { 435 | className: this.css.subList 436 | }, layerNode); 437 | var oneSubLayerOn; 438 | var subSubLists = []; 439 | // create each subLayer item 440 | for (var j = 0; j < subLayers.length; j++) { 441 | // subLayer info 442 | var subLayer = subLayers[j]; 443 | var subLayerIndex; 444 | var parentId = -1; 445 | var subSubListNode = null; 446 | // Dynamic Map Service 447 | if (layerType === "esri.layers.ArcGISDynamicMapServiceLayer") { 448 | subLayerIndex = subLayer.id; 449 | parentId = subLayer.parentLayerId; 450 | } 451 | // KML 452 | else if (layerType === "esri.layers.KMLLayer") { 453 | subLayerIndex = subLayer.id; 454 | parentId = subLayer.parentFolderId; 455 | } 456 | // WMS 457 | else if (layerType === "esri.layers.WMSLayer") { 458 | subLayerIndex = subLayer.name; 459 | parentId = -1; 460 | } 461 | // place subLayers not in the root 462 | if (parentId !== -1) { 463 | var parent = this._nodes[layerIndex].subNodes[parentId]; 464 | if (subSubLists[parentId]) { 465 | subSubListNode = subSubLists[parentId]; 466 | } else { 467 | var parentLayer = parent.subLayer; 468 | subSubListNode = domConstruct.create("ul", { 469 | className: this.css.subList 470 | }, parentLayer); 471 | domClass.add(parentLayer, this.css.hasSubList); 472 | domClass.toggle(parentLayer, [this.css.listVisible, this.css.listExpand], subChecked); 473 | subSubLists[parentId] = subSubListNode; 474 | } 475 | } 476 | // default checked state 477 | var subChecked = this._subCheckboxStatus(layerInfo, subLayer); 478 | if (subChecked && !oneSubLayerOn) { 479 | oneSubLayerOn = true; 480 | } 481 | var subId = this.id + "_checkbox_sub_" + layerIndex + "_" + subLayerIndex; 482 | // list item node 483 | var subLayerNode = domConstruct.create("li", { 484 | className: this.css.subListLayer 485 | }, subSubListNode || subListNode); 486 | // title of subLayer layer 487 | var subTitleNode = domConstruct.create("div", { 488 | className: this.css.title 489 | }, subLayerNode); 490 | // subLayer title container 491 | var subTitleContainerNode = domConstruct.create("div", { 492 | className: this.css.titleContainer 493 | }, subTitleNode); 494 | // subLayer checkbox 495 | var subCheckboxNode = domConstruct.create("input", { 496 | type: "checkbox", 497 | id: subId, 498 | "data-layer-index": layerIndex, 499 | "data-sublayer-index": subLayerIndex, 500 | className: this.css.checkbox 501 | }, subTitleContainerNode); 502 | domAttr.set(subCheckboxNode, "checked", subChecked); 503 | // subLayer Title text 504 | var subTitle = subLayer.title || subLayer.name || ""; 505 | var subLabelNode = domConstruct.create("label", { 506 | className: this.css.label, 507 | textContent: subTitle 508 | }, subTitleContainerNode); 509 | domAttr.set(subLabelNode, "for", subId); 510 | // subLayer clear css 511 | var subClearNode = domConstruct.create("div", { 512 | className: this.css.clear 513 | }, subTitleContainerNode); 514 | // object of subLayer nodes 515 | var subNode = { 516 | subList: subListNode, 517 | subSubList: subSubListNode, 518 | subLayer: subLayerNode, 519 | subTitle: subTitleNode, 520 | subTitleContainer: subTitleContainerNode, 521 | subCheckbox: subCheckboxNode, 522 | subLabel: subLabelNode, 523 | subClear: subClearNode 524 | }; 525 | // add node to array 526 | subNodes[subLayerIndex] = subNode; 527 | } 528 | } 529 | } 530 | } 531 | } 532 | } 533 | } else { 534 | domClass.add(this._container, this.css.noLayers); 535 | domAttr.set(this._noLayersNode, "textContent", i18n.widgets.layerList.noLayers); 536 | } 537 | }, 538 | 539 | _removeEvents: function () { 540 | // layer visibility events 541 | if (this._layerEvents && this._layerEvents.length) { 542 | for (var i = 0; i < this._layerEvents.length; i++) { 543 | this._layerEvents[i].remove(); 544 | } 545 | } 546 | this._layerEvents = []; 547 | }, 548 | 549 | _emitToggle: function (layerIndex, subLayerIndex, visible) { 550 | // emit event 551 | this.emit("toggle", { 552 | layerIndex: layerIndex, 553 | subLayerIndex: subLayerIndex, 554 | visible: visible 555 | }); 556 | }, 557 | 558 | _toggleVisible: function (index, visible) { 559 | var node = this._nodes[index].checkbox; 560 | domClass.toggle(this._nodes[index].layer, this.css.listVisible, visible); 561 | var checked = domAttr.get(node, "checked"); 562 | if (domClass.contains(this._nodes[index].layer, this.css.hasSubList)) { 563 | domClass.toggle(this._nodes[index].layer, this.css.listExpand, checked); 564 | } 565 | if (checked !== visible) { 566 | // update checkbox and layer visibility classes 567 | domAttr.set(node, "checked", visible); 568 | this._emitToggle(index, null, visible); 569 | } 570 | }, 571 | 572 | _layerVisChangeEvent: function (response, featureCollection, subLayerIndex) { 573 | var layer; 574 | // layer is a feature collection 575 | if (featureCollection) { 576 | // all subLayers 577 | var fcLayers = response.layerInfo.featureCollection.layers; 578 | // current layer object to setup event for 579 | layer = fcLayers[subLayerIndex].layer; 580 | } else { 581 | // layer object for event 582 | layer = response.layer; 583 | } 584 | // layer visibility changes 585 | var visChange = on(layer, "visibility-change", lang.hitch(this, function (evt) { 586 | if (featureCollection) { 587 | this._featureCollectionVisible(response.layerIndex, evt.visible); 588 | } else { 589 | // update checkbox and layer visibility classes 590 | this._toggleVisible(response.layerIndex, evt.visible); 591 | } 592 | })); 593 | this._layerEvents.push(visChange); 594 | if (!featureCollection) { 595 | // scale visibility changes 596 | var scaleVisChange = on(layer, "scale-visibility-change", lang.hitch(this, function (evt) { 597 | var visible = evt.target.visibleAtMapScale; 598 | domClass.toggle(this._nodes[response.layerIndex].layer, this.css.layerScaleInvisible, !visible); 599 | })); 600 | this._layerEvents.push(scaleVisChange); 601 | // show out of scale range for sublayers if its a map service 602 | if (layer.declaredClass === "esri.layers.ArcGISDynamicMapServiceLayer") { 603 | // on map LOD change 604 | var zoomChange = on(this.map, "zoom-end", lang.hitch(this, function () { 605 | // gray out invisible sublayers 606 | this._subLayerScale(response); 607 | })); 608 | this._layerEvents.push(zoomChange); 609 | // gray out invisible sublayers 610 | this._subLayerScale(response); 611 | } 612 | } 613 | }, 614 | 615 | // gray out invisible sublayers 616 | _subLayerScale: function (response) { 617 | var layer = response.layer; 618 | var dynLayerInfos = layer.createDynamicLayerInfosFromLayerInfos(); 619 | var layersInScale = layerUtils._getLayersForScale(this.map.getScale(), dynLayerInfos); 620 | array.forEach(dynLayerInfos, lang.hitch(this, function (layerInfo) { 621 | if (!layerInfo.subLayerIds) { // skip group layers 622 | var subLayerId = layerInfo.id; 623 | var subLayerNode = this._nodes[response.layerIndex].subNodes[subLayerId].subLayer; 624 | var scaleInvisible = false; 625 | // if visible and in scale 626 | if (array.indexOf(layersInScale, subLayerId) === -1) { 627 | scaleInvisible = true; 628 | } 629 | domClass.toggle(subLayerNode, this.css.layerScaleInvisible, scaleInvisible); 630 | } 631 | })); 632 | }, 633 | 634 | _layerEvent: function (response) { 635 | var layerInfo = response.layerInfo; 636 | // feature collection layer 637 | if (layerInfo.featureCollection && layerInfo.featureCollection.layers && layerInfo.featureCollection.layers.length) { 638 | // feature collection layers 639 | var fsLayers = layerInfo.featureCollection.layers; 640 | if (fsLayers && fsLayers.length) { 641 | // make event for each layer 642 | for (var i = 0; i < fsLayers.length; i++) { 643 | // layer visibility changes 644 | this._layerVisChangeEvent(response, true, i); 645 | } 646 | } 647 | } else { 648 | // layer visibility changes 649 | this._layerVisChangeEvent(response); 650 | // todo: need to figure out way to support visibility change of map service, WMS, & KML sublayers outside of widget 651 | } 652 | }, 653 | 654 | _getVisibleLayers: function (layer, subLayerIndex) { 655 | var layerInfos = layer.layerInfos; 656 | var i; 657 | // array for setting visible layers 658 | var visibleLayers = [-1]; 659 | 660 | if (typeof subLayerIndex !== "undefined") { 661 | var newVis = !layerInfos[subLayerIndex].defaultVisibility; 662 | // reverse current visibility of sublayer 663 | layerInfos[subLayerIndex].defaultVisibility = newVis; 664 | } 665 | 666 | // for each sublayer 667 | for (i = 0; i < layerInfos.length; i++) { 668 | var info = layerInfos[i]; 669 | // push to visible layers if it's visible 670 | if (info.defaultVisibility) { 671 | visibleLayers.push(info.id); 672 | var negative = array.lastIndexOf(visibleLayers, -1); 673 | if (negative !== -1) { 674 | visibleLayers.splice(negative, 1); 675 | } 676 | } 677 | } 678 | //Now that the array of visibleLayer Ids is assembled, 679 | //strip off Ids of invisible child layers, and 680 | //Ids of group layers (group layer Ids should not be submitted 681 | //in .setVisible() or loss of toggle control madness ensues. 682 | //Remove layers whos parents are not visible: 683 | var noInvisibleParents = []; 684 | for (i = 0; i < visibleLayers.length; i++) { 685 | var id = visibleLayers[i]; 686 | var hasParentsInVisibleArray = this._allIdsPresent(layer, id, visibleLayers); 687 | if (hasParentsInVisibleArray) { 688 | noInvisibleParents.push(id); 689 | } 690 | } 691 | var noGroups = []; 692 | for (var j = 0; j < noInvisibleParents.length; j++) { 693 | var lyrInfo = this._getLayerInfo(layer, noInvisibleParents[j]); 694 | if (lyrInfo && lyrInfo.subLayerIds === null) { 695 | noGroups.push(noInvisibleParents[j]); 696 | } 697 | } 698 | // note: set -1 if array is empty. 699 | if (!noGroups.length) { 700 | noGroups = [-1]; 701 | } 702 | return noGroups; 703 | }, 704 | 705 | _toggleState: function (layerIndex, subLayerIndex) { 706 | var layerNode, checkboxNode; 707 | layerIndex = parseInt(layerIndex, 10); 708 | var layerNodes = this._nodes[layerIndex]; 709 | if (subLayerIndex !== null) { 710 | subLayerIndex = parseInt(subLayerIndex, 10); 711 | layerNode = layerNodes.subNodes[subLayerIndex].subLayer; 712 | checkboxNode = layerNodes.subNodes[subLayerIndex].subCheckbox; 713 | } else { 714 | layerNode = layerNodes.layer; 715 | checkboxNode = layerNodes.checkbox; 716 | } 717 | var status = domAttr.get(checkboxNode, "checked"); 718 | if (domClass.contains(layerNode, this.css.hasSubList)) { 719 | domClass.toggle(layerNode, this.css.listExpand, status); 720 | } 721 | domClass.toggle(layerNode, this.css.listVisible, status); 722 | }, 723 | 724 | _toggleLayer: function (layerIndex, subLayerIndex) { 725 | // all layers 726 | if (this.layers && this.layers.length) { 727 | var newVis; 728 | layerIndex = parseInt(layerIndex, 10); 729 | var layerInfo = this.layers[layerIndex]; 730 | var layer = layerInfo.layer; 731 | var layerType; 732 | if (layer) { 733 | layerType = layer.declaredClass; 734 | } 735 | var featureCollection = layerInfo.featureCollection; 736 | var visibleLayers; 737 | var i; 738 | // feature collection layer 739 | if (featureCollection) { 740 | // new visibility 741 | newVis = !layerInfo.visibility; 742 | // set visibility for layer reference 743 | layerInfo.visibility = newVis; 744 | // toggle all sub layers 745 | for (i = 0; i < featureCollection.layers.length; i++) { 746 | var fcLayer = featureCollection.layers[i].layerObject; 747 | // toggle to new visibility 748 | fcLayer.setVisibility(newVis); 749 | } 750 | } 751 | // layer 752 | else if (layer) { 753 | // we're toggling a sublayer 754 | if (subLayerIndex !== null) { 755 | // Map Service Layer 756 | if (layerType === "esri.layers.ArcGISDynamicMapServiceLayer") { 757 | subLayerIndex = parseInt(subLayerIndex, 10); 758 | visibleLayers = this._getVisibleLayers(layer, subLayerIndex); 759 | // set visible sublayers which are not grouped 760 | layer.setVisibleLayers(visibleLayers); 761 | } 762 | // KML Layer 763 | else if (layerType === "esri.layers.KMLLayer") { 764 | subLayerIndex = parseInt(subLayerIndex, 10); 765 | var folders = layer.folders; 766 | // for each sublayer 767 | for (i = 0; i < folders.length; i++) { 768 | var folder = folders[i]; 769 | if (folder.id === subLayerIndex) { 770 | layer.setFolderVisibility(folder, !folder.visible); 771 | break; 772 | } 773 | } 774 | } else if (layerType === "esri.layers.WMSLayer") { 775 | visibleLayers = layer.visibleLayers; 776 | var found = array.indexOf(visibleLayers, subLayerIndex); 777 | if (found === -1) { 778 | visibleLayers.push(subLayerIndex); 779 | } else { 780 | visibleLayers.splice(found, 1); 781 | } 782 | layer.setVisibleLayers(visibleLayers); 783 | } 784 | } 785 | // parent map layer 786 | else { 787 | if (layerType === "esri.layers.ArcGISDynamicMapServiceLayer") { 788 | visibleLayers = this._getVisibleLayers(layer); 789 | layer.setVisibleLayers(visibleLayers); 790 | } 791 | // reverse current visibility of parent layer 792 | newVis = !layer.visible; 793 | // new visibility of parent layer 794 | layerInfo.visibility = newVis; 795 | layer.setVisibility(newVis); 796 | } 797 | } 798 | // Just layer object 799 | else { 800 | newVis = !layerInfo.visible; 801 | layerInfo.setVisibility(newVis); 802 | } 803 | // emit event 804 | this._emitToggle(layerIndex, subLayerIndex, newVis); 805 | } 806 | }, 807 | 808 | _featureCollectionVisible: function (index, visible) { 809 | var layer = this.layers[index]; 810 | // all layers either visible or not 811 | var equal; 812 | // feature collection layers turned on by default 813 | var visibleLayers = layer.visibleLayers; 814 | // feature collection layers 815 | var layers = layer.featureCollection.layers; 816 | // if we have layers set 817 | if (visibleLayers && visibleLayers.length) { 818 | // check if all layers have same visibility 819 | equal = array.every(visibleLayers, function (item) { 820 | // check if current layer has same as first layer 821 | return layers[item].layer.visible === visible; 822 | }); 823 | } else { 824 | // check if all layers have same visibility 825 | equal = array.every(layers, function (item) { 826 | // check if current layer has same as first layer 827 | return item.layer.visible === visible; 828 | }); 829 | } 830 | // all are the same 831 | if (equal) { 832 | this._toggleVisible(index, visible); 833 | } 834 | }, 835 | 836 | _setLayerEvents: function () { 837 | // this function sets up all the events for layers 838 | var layers = this._loadedLayers; 839 | if (layers && layers.length) { 840 | // get all layers 841 | for (var i = 0; i < layers.length; i++) { 842 | var response = layers[i]; 843 | // if we have a layer 844 | if (response.layer) { 845 | // create necessary events 846 | this._layerEvent(response); 847 | } 848 | } 849 | } 850 | }, 851 | 852 | _allIdsPresent: function (layer, layerId, arrayOfIds) { 853 | //Returns false if any Ids are not present in the supplied array of Ids. 854 | var parentIds = this._walkUpLayerIds(layer, layerId); 855 | //If any of the parentIds are NOT in the arrayOfIds return false: 856 | return array.every(parentIds, function (id) { 857 | return array.indexOf(arrayOfIds, id) > -1; 858 | }); 859 | }, 860 | 861 | _walkUpLayerIds: function (layer, layerId) { 862 | //returns array of layerIds of all parents of layerId 863 | var layerInfo = this._getLayerInfo(layer, layerId); 864 | var parentLayerInfo; 865 | var parentLayerIds = []; 866 | if (layerInfo) { 867 | //If the current layerInfo layerInfo doesn't have a parent, 868 | //then we're at the top of the hierarchy and should return the result. 869 | while (layerInfo.parentLayerId !== -1) { 870 | //A parent exists, save the info and add to the array: 871 | parentLayerInfo = this._getLayerInfo(layer, layerInfo.parentLayerId); 872 | if (parentLayerInfo) { 873 | parentLayerIds.push(parentLayerInfo.id); 874 | } 875 | //Move up hierarchy: reassign the layerInfo to the parent. Loop. 876 | layerInfo = parentLayerInfo; 877 | } 878 | } 879 | return parentLayerIds; 880 | }, 881 | 882 | _getLayerInfo: function (layer, layerId) { 883 | //Get the layerInfo for layerId from the layer: 884 | var info; 885 | for (var i = 0; i < layer.layerInfos.length; i++) { 886 | var layerInfo = layer.layerInfos[i]; 887 | if (layerInfo.id === layerId) { 888 | //we have our desired layerInfo. 889 | info = layerInfo; 890 | break; 891 | } 892 | } 893 | return info; 894 | }, 895 | 896 | _isSupportedLayerType: function (layer) { 897 | return layer && !layer._basemapGalleryLayerType || layer && layer._basemapGalleryLayerType !== "basemap"; 898 | }, 899 | 900 | _createLayerInfo: function (layer) { 901 | return { 902 | layer: layer 903 | }; 904 | }, 905 | 906 | _updateAllMapLayers: function () { 907 | if (this.map && (!this.layers || !this.layers.length)) { 908 | var layers = []; 909 | // get all non graphic layers 910 | array.forEach(this.map.layerIds, function (layerId) { 911 | var layer = this.map.getLayer(layerId); 912 | if (this._isSupportedLayerType(layer)) { 913 | layers.push(this._createLayerInfo(layer)); 914 | } 915 | }, this); 916 | // get all graphic layers 917 | array.forEach(this.map.graphicsLayerIds, function (layerId) { 918 | var layer = this.map.getLayer(layerId); 919 | // check drawMode so we don't include layers created for pop-ups 920 | if (this._isSupportedLayerType(layer) && layer._params && layer._params.drawMode) { 921 | layers.push(this._createLayerInfo(layer)); 922 | } 923 | }, this); 924 | this._set("layers", layers); 925 | } 926 | }, 927 | 928 | _init: function () { 929 | this._visible(); 930 | this._updateAllMapLayers(); 931 | this.refresh().always(lang.hitch(this, function () { 932 | this.set("loaded", true); 933 | this.emit("load"); 934 | })); 935 | }, 936 | 937 | _visible: function () { 938 | if (this.visible) { 939 | domStyle.set(this.domNode, "display", "block"); 940 | } else { 941 | domStyle.set(this.domNode, "display", "none"); 942 | } 943 | }, 944 | 945 | /* stateful properties */ 946 | 947 | _setThemeAttr: function (newVal) { 948 | if (this.domNode) { 949 | domClass.remove(this.domNode, this.theme); 950 | domClass.add(this.domNode, newVal); 951 | } 952 | this._set("theme", newVal); 953 | }, 954 | 955 | _setMapAttr: function (newVal) { 956 | this._set("map", newVal); 957 | if (this._created) { 958 | this._mapLoaded(this.map).then(lang.hitch(this, function () { 959 | this._updateAllMapLayers(); 960 | this.refresh(); 961 | })); 962 | } 963 | }, 964 | 965 | _setLayersAttr: function (newVal) { 966 | this._set("layers", newVal); 967 | if (this._created) { 968 | this.refresh(); 969 | } 970 | }, 971 | 972 | _setRemoveUnderscoresAttr: function (newVal) { 973 | this._set("removeUnderscores", newVal); 974 | if (this._created) { 975 | this.refresh(); 976 | } 977 | }, 978 | 979 | _setSubLayersAttr: function (newVal) { 980 | this._set("subLayers", newVal); 981 | if (this._created) { 982 | this.refresh(); 983 | } 984 | }, 985 | 986 | _setShowOpacitySliderAttr: function (newVal) { 987 | this._set("showOpacitySlider", newVal); 988 | if (this._created) { 989 | this.refresh(); 990 | } 991 | }, 992 | 993 | _setShowLegendAttr: function (newVal) { 994 | this._set("showLegend", newVal); 995 | if (this._created) { 996 | this.refresh(); 997 | } 998 | }, 999 | 1000 | _setVisibleAttr: function (newVal) { 1001 | this._set("visible", newVal); 1002 | if (this._created) { 1003 | this._visible(); 1004 | } 1005 | } 1006 | 1007 | }); 1008 | if (has("extend-esri")) { 1009 | lang.setObject("dijit.LayerList", Widget, esriNS); 1010 | } 1011 | return Widget; 1012 | }); -------------------------------------------------------------------------------- /js/LayerList/css/LayerList.css: -------------------------------------------------------------------------------- 1 | .esriLayerList .esriList { 2 | background-color: #ededed; 3 | border-top: 1px solid #e0e0e0; 4 | } 5 | 6 | .esriLayerList .esriNoLayers .esriList { 7 | display: none; 8 | } 9 | 10 | .esriLayerList .esriNoLayersText { 11 | display: none; 12 | } 13 | 14 | .esriLayerList .esriNoLayers .esriNoLayersText { 15 | display: block; 16 | padding: 10px; 17 | } 18 | 19 | .esriLayerList .esriList, 20 | .esriLayerList .esriLayer, 21 | .esriLayerList .esriSubList, 22 | .esriLayerList .esriSubListLayer { 23 | list-style: none; 24 | margin: 0; 25 | padding: 0; 26 | } 27 | 28 | .esriLayerList .esriSubList { 29 | margin: 0 0 0 12px; 30 | display: none; 31 | } 32 | 33 | .dj_rtl .esriLayerList .esriSubList { 34 | margin: 0 20px 0 0; 35 | } 36 | 37 | .esriLayerList .esriListExpand > .esriSubList { 38 | display: block; 39 | } 40 | 41 | .esriLayerList .esriLegend { 42 | padding: 0 20px; 43 | display: none; 44 | } 45 | 46 | .esriLayerList .esriLegend .esriLegendService > table, 47 | .esriLayerList .esriLegend .esriLegendMsg { 48 | display: none; 49 | } 50 | 51 | .esriLayerList .esriListVisible .esriLegend { 52 | display: block; 53 | } 54 | 55 | .esriLayerList .esriSlider { 56 | padding: 5px 20px 15px 20px; 57 | display: none; 58 | } 59 | 60 | .esriLayerList .esriListVisible .esriSlider { 61 | display: block; 62 | } 63 | 64 | .esriLayerList .esriTitle { 65 | border-bottom: 1px solid #e0e0e0; 66 | font-size: 16px; 67 | line-height: 20px; 68 | background-color: #f8f8f8; 69 | color: #555; 70 | } 71 | 72 | .esriLayerList .esriSubList .esriTitle { 73 | border-left: 1px solid #e0e0e0; 74 | } 75 | 76 | .dj_rtl .esriLayerList .esriSubList .esriTitle { 77 | border-left: 0; 78 | border-right: 1px solid #e0e0e0; 79 | } 80 | 81 | .esriLayerList .esriLabel { 82 | display: block; 83 | padding: 10px 20px 10px 32px; 84 | margin: 0; 85 | word-wrap: break-word; 86 | } 87 | 88 | .dj_rtl .esriLayerList .esriLabel { 89 | padding: 10px 32px 10px 20px; 90 | } 91 | 92 | .esriLayerList .esriScaleInvisible .esriLabel { 93 | color: #999; 94 | } 95 | 96 | .esriLayerList .esriCheckbox { 97 | float: left; 98 | height: 16px; 99 | width: 16px; 100 | padding: 0; 101 | margin: 12px 5px 12px 10px; 102 | } 103 | 104 | .dj_rtl .esriLayerList .esriCheckbox { 105 | float: right; 106 | margin: 12px 10px 12px 5px; 107 | } 108 | 109 | .esriLayerList .esriButton { 110 | float: right; 111 | font-size: 16px; 112 | line-height: 20px; 113 | cursor: pointer; 114 | color: #999; 115 | } 116 | 117 | .esriLayerList .esriButton:hover, 118 | .esriLayerList .esriButton:active { 119 | color: #333; 120 | } 121 | 122 | .esriLayerList .esriContent { 123 | margin: 0 20px 5px 32px; 124 | } 125 | 126 | .dj_rtl .esriLayerList .esriContent { 127 | margin: 0 32px 5px 20px; 128 | } 129 | 130 | .dj_rtl .esriLayerList .esriButton { 131 | float: left; 132 | } 133 | 134 | .esriLayerList .esriClear { 135 | clear: both; 136 | } -------------------------------------------------------------------------------- /js/LayerList/nls/LayerList.js: -------------------------------------------------------------------------------- 1 | define({ 2 | root: ({ 3 | widgets: { 4 | layerList: { 5 | noLayers: "No layers to display." 6 | } 7 | } 8 | }) 9 | }); -------------------------------------------------------------------------------- /js/LayerList/templates/LayerList.html: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /license.txt: -------------------------------------------------------------------------------- 1 | Apache License – 2.0 2 | 3 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 4 | 5 | 1. Definitions. 6 | 7 | "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. 8 | 9 | "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. 10 | 11 | "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. 12 | 13 | "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. 14 | 15 | "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. 16 | 17 | "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. 18 | 19 | "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). 20 | 21 | "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. 22 | 23 | "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." 24 | 25 | "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 26 | 27 | 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 28 | 29 | 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 30 | 31 | 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: 32 | 33 | 1. You must give any other recipients of the Work or Derivative Works a copy of this License; and 34 | 2. You must cause any modified files to carry prominent notices stating that You changed the files; and 35 | 3. You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and 36 | 4. If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 37 | 38 | 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 39 | 40 | 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 41 | 42 | 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 43 | 44 | 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 45 | 46 | 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. 47 | 48 | END OF TERMS AND CONDITIONS 49 | -------------------------------------------------------------------------------- /map.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | LayerList Dijit 8 | 9 | 10 | 42 | 43 | 44 | 45 |
46 |
47 |
48 |
49 |
50 |
51 |

Documentation & Download 52 |

53 |
54 | 55 |
56 | 57 |
58 | 70 | 71 | 211 | 212 | 213 | 214 | --------------------------------------------------------------------------------