1/* global L */ 2 3// A layer control which provides for layer groupings. 4// Author: Ishmael Smyrnow 5L.Control.GroupedLayers = L.Control.extend({ 6 7 options: { 8 collapsed: true, 9 position: 'topright', 10 autoZIndex: true, 11 exclusiveGroups: [], 12 groupCheckboxes: false 13 }, 14 15 initialize: function (baseLayers, groupedOverlays, options) { 16 var i, j; 17 L.Util.setOptions(this, options); 18 19 this._layers = []; 20 this._lastZIndex = 0; 21 this._handlingClick = false; 22 this._groupList = []; 23 this._domGroups = []; 24 25 for (i in baseLayers) { 26 this._addLayer(baseLayers[i], i); 27 } 28 29 for (i in groupedOverlays) { 30 for (j in groupedOverlays[i]) { 31 this._addLayer(groupedOverlays[i][j], j, i, true); 32 } 33 } 34 }, 35 36 onAdd: function (map) { 37 this._initLayout(); 38 this._update(); 39 40 map 41 .on('layeradd', this._onLayerChange, this) 42 .on('layerremove', this._onLayerChange, this); 43 44 return this._container; 45 }, 46 47 onRemove: function (map) { 48 map 49 .off('layeradd', this._onLayerChange, this) 50 .off('layerremove', this._onLayerChange, this); 51 }, 52 53 addBaseLayer: function (layer, name) { 54 this._addLayer(layer, name); 55 this._update(); 56 return this; 57 }, 58 59 addOverlay: function (layer, name, group) { 60 this._addLayer(layer, name, group, true); 61 this._update(); 62 return this; 63 }, 64 65 removeLayer: function (layer) { 66 var id = L.Util.stamp(layer); 67 var _layer = this._getLayer(id); 68 if (_layer) { 69 delete this._layers[this._layers.indexOf(_layer)]; 70 } 71 this._update(); 72 return this; 73 }, 74 75 _getLayer: function (id) { 76 for (var i = 0; i < this._layers.length; i++) { 77 if (this._layers[i] && L.stamp(this._layers[i].layer) === id) { 78 return this._layers[i]; 79 } 80 } 81 }, 82 83 _initLayout: function () { 84 var className = 'leaflet-control-layers', 85 container = this._container = L.DomUtil.create('div', className); 86 87 // Makes this work on IE10 Touch devices by stopping it from firing a mouseout event when the touch is released 88 container.setAttribute('aria-haspopup', true); 89 90 if (L.Browser.touch) { 91 L.DomEvent.on(container, 'click', L.DomEvent.stopPropagation); 92 } else { 93 L.DomEvent.disableClickPropagation(container); 94 L.DomEvent.on(container, 'wheel', L.DomEvent.stopPropagation); 95 } 96 97 var form = this._form = L.DomUtil.create('form', className + '-list'); 98 99 if (this.options.collapsed) { 100 if (!L.Browser.android) { 101 L.DomEvent 102 .on(container, 'mouseover', this._expand, this) 103 .on(container, 'mouseout', this._collapse, this); 104 } 105 var link = this._layersLink = L.DomUtil.create('a', className + '-toggle', container); 106 link.href = '#'; 107 link.title = 'Layers'; 108 109 if (L.Browser.touch) { 110 L.DomEvent 111 .on(link, 'click', L.DomEvent.stop) 112 .on(link, 'click', this._expand, this); 113 } else { 114 L.DomEvent.on(link, 'focus', this._expand, this); 115 } 116 117 this._map.on('click', this._collapse, this); 118 // TODO keyboard accessibility 119 } else { 120 this._expand(); 121 } 122 123 this._baseLayersList = L.DomUtil.create('div', className + '-base', form); 124 this._separator = L.DomUtil.create('div', className + '-separator', form); 125 this._overlaysList = L.DomUtil.create('div', className + '-overlays', form); 126 127 container.appendChild(form); 128 }, 129 130 _addLayer: function (layer, name, group, overlay) { 131 var id = L.Util.stamp(layer); 132 133 var _layer = { 134 layer: layer, 135 name: name, 136 overlay: overlay 137 }; 138 this._layers.push(_layer); 139 140 group = group || ''; 141 var groupId = this._indexOf(this._groupList, group); 142 143 if (groupId === -1) { 144 groupId = this._groupList.push(group) - 1; 145 } 146 147 var exclusive = (this._indexOf(this.options.exclusiveGroups, group) !== -1); 148 149 _layer.group = { 150 name: group, 151 id: groupId, 152 exclusive: exclusive 153 }; 154 155 if (this.options.autoZIndex && layer.setZIndex) { 156 this._lastZIndex++; 157 layer.setZIndex(this._lastZIndex); 158 } 159 }, 160 161 _update: function () { 162 if (!this._container) { 163 return; 164 } 165 166 this._baseLayersList.innerHTML = ''; 167 this._overlaysList.innerHTML = ''; 168 this._domGroups.length = 0; 169 170 var baseLayersPresent = false, 171 overlaysPresent = false, 172 i, obj; 173 174 for (var i = 0; i < this._layers.length; i++) { 175 obj = this._layers[i]; 176 this._addItem(obj); 177 overlaysPresent = overlaysPresent || obj.overlay; 178 baseLayersPresent = baseLayersPresent || !obj.overlay; 179 } 180 181 this._separator.style.display = overlaysPresent && baseLayersPresent ? '' : 'none'; 182 }, 183 184 _onLayerChange: function (e) { 185 var obj = this._getLayer(L.Util.stamp(e.layer)), 186 type; 187 188 if (!obj) { 189 return; 190 } 191 192 if (!this._handlingClick) { 193 this._update(); 194 } 195 196 if (obj.overlay) { 197 type = e.type === 'layeradd' ? 'overlayadd' : 'overlayremove'; 198 } else { 199 type = e.type === 'layeradd' ? 'baselayerchange' : null; 200 } 201 202 if (type) { 203 this._map.fire(type, obj); 204 } 205 }, 206 207 // IE7 bugs out if you create a radio dynamically, so you have to do it this hacky way (see http://bit.ly/PqYLBe) 208 _createRadioElement: function (name, checked) { 209 var radioHtml = '<input type="radio" class="leaflet-control-layers-selector" name="' + name + '"'; 210 if (checked) { 211 radioHtml += ' checked="checked"'; 212 } 213 radioHtml += '/>'; 214 215 var radioFragment = document.createElement('div'); 216 radioFragment.innerHTML = radioHtml; 217 218 return radioFragment.firstChild; 219 }, 220 221 _addItem: function (obj) { 222 var label = document.createElement('label'), 223 input, 224 checked = this._map.hasLayer(obj.layer), 225 container, 226 groupRadioName; 227 228 if (obj.overlay) { 229 if (obj.group.exclusive) { 230 groupRadioName = 'leaflet-exclusive-group-layer-' + obj.group.id; 231 input = this._createRadioElement(groupRadioName, checked); 232 } else { 233 input = document.createElement('input'); 234 input.type = 'checkbox'; 235 input.className = 'leaflet-control-layers-selector'; 236 input.defaultChecked = checked; 237 } 238 } else { 239 input = this._createRadioElement('leaflet-base-layers', checked); 240 } 241 242 input.layerId = L.Util.stamp(obj.layer); 243 input.groupID = obj.group.id; 244 L.DomEvent.on(input, 'click', this._onInputClick, this); 245 246 var name = document.createElement('span'); 247 name.innerHTML = ' ' + obj.name; 248 249 label.appendChild(input); 250 label.appendChild(name); 251 252 if (obj.overlay) { 253 container = this._overlaysList; 254 255 var groupContainer = this._domGroups[obj.group.id]; 256 257 // Create the group container if it doesn't exist 258 if (!groupContainer) { 259 groupContainer = document.createElement('div'); 260 groupContainer.className = 'leaflet-control-layers-group'; 261 groupContainer.id = 'leaflet-control-layers-group-' + obj.group.id; 262 263 var groupLabel = document.createElement('label'); 264 groupLabel.className = 'leaflet-control-layers-group-label'; 265 266 if (obj.group.name !== '' && !obj.group.exclusive) { 267 // ------ add a group checkbox with an _onInputClickGroup function 268 if (this.options.groupCheckboxes) { 269 var groupInput = document.createElement('input'); 270 groupInput.type = 'checkbox'; 271 groupInput.className = 'leaflet-control-layers-group-selector'; 272 groupInput.groupID = obj.group.id; 273 groupInput.legend = this; 274 L.DomEvent.on(groupInput, 'click', this._onGroupInputClick, groupInput); 275 groupLabel.appendChild(groupInput); 276 } 277 } 278 279 var groupName = document.createElement('span'); 280 groupName.className = 'leaflet-control-layers-group-name'; 281 groupName.innerHTML = obj.group.name; 282 groupLabel.appendChild(groupName); 283 284 groupContainer.appendChild(groupLabel); 285 container.appendChild(groupContainer); 286 287 this._domGroups[obj.group.id] = groupContainer; 288 } 289 290 container = groupContainer; 291 } else { 292 container = this._baseLayersList; 293 } 294 295 container.appendChild(label); 296 297 return label; 298 }, 299 300 _onGroupInputClick: function () { 301 var i, input, obj; 302 303 var this_legend = this.legend; 304 this_legend._handlingClick = true; 305 306 var inputs = this_legend._form.getElementsByTagName('input');
307 var inputsLen = inputs.length; 308 309 for (i = 0; i < inputsLen; i++) { 310 input = inputs[i]; 311 if (input.groupID === this.groupID && input.className === 'leaflet-control-layers-selector') { 312 input.checked = this.checked; 313 obj = this_legend._getLayer(input.layerId); 314 if (input.checked && !this_legend._map.hasLayer(obj.layer)) { 315 this_legend._map.addLayer(obj.layer); 316 } else if (!input.checked && this_legend._map.hasLayer(obj.layer)) { 317 this_legend._map.removeLayer(obj.layer); 318 } 319 } 320 } 321 322 this_legend._handlingClick = false; 323 }, 324 325 _onInputClick: function () { 326 var i, input, obj, 327 inputs = this._form.getElementsByTagName('input'), 328 inputsLen = inputs.length; 329 330 this._handlingClick = true; 331 332 for (i = 0; i < inputsLen; i++) { 333 input = inputs[i]; 334 if (input.className === 'leaflet-control-layers-selector') { 335 obj = this._getLayer(input.layerId); 336 337 if (input.checked && !this._map.hasLayer(obj.layer)) { 338 this._map.addLayer(obj.layer); 339 } else if (!input.checked && this._map.hasLayer(obj.layer)) { 340 this._map.removeLayer(obj.layer); 341 } 342 } 343 } 344 345 this._handlingClick = false; 346 }, 347 348 _expand: function () { 349 L.DomUtil.addClass(this._container, 'leaflet-control-layers-expanded'); 350 // permits to have a scrollbar if overlays heighter than the map. 351 var acceptableHeight = this._map._size.y - (this._container.offsetTop * 4); 352 if (acceptableHeight < this._form.clientHeight) { 353 L.DomUtil.addClass(this._form, 'leaflet-control-layers-scrollbar'); 354 this._form.style.height = acceptableHeight + 'px'; 355 } 356 }, 357 358 _collapse: function () { 359 this._container.className = this._container.className.replace(' leaflet-control-layers-expanded', ''); 360 }, 361 362 _indexOf: function (arr, obj) { 363 for (var i = 0, j = arr.length; i < j; i++) { 364 if (arr[i] === obj) { 365 return i; 366 } 367 } 368 return -1; 369 } 370}); 371 372L.control.groupedLayers = function (baseLayers, groupedOverlays, options) { 373 return new L.Control.GroupedLayers(baseLayers, groupedOverlays, options); 374};
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.