<NME> Control.Layers.js
<BEF> 
import {Control} from './Control';
import * as Util from '../core/Util';
import * as DomEvent from '../dom/DomEvent';
import * as DomUtil from '../dom/DomUtil';

/*
 * @class Control.Layers
 * @aka L.Control.Layers
 * @inherits Control
 *
 * The layers control gives users the ability to switch between different base layers and switch overlays on/off (check out the [detailed example](https://leafletjs.com/examples/layers-control/)). Extends `Control`.
 *
 * @example
 *
 * ```js
 * var baseLayers = {
 * 	"Mapbox": mapbox,
 * 	"OpenStreetMap": osm
 * };
 *
 * var overlays = {
 * 	"Marker": marker,
 * 	"Roads": roadsLayer
 * };
 *
 * L.control.layers(baseLayers, overlays).addTo(map);
 * ```
 *
 * The `baseLayers` and `overlays` parameters are object literals with layer names as keys and `Layer` objects as values:
 *
 * ```js
 * {
 *     "<someName1>": layer1,
 *     "<someName2>": layer2
 * }
 * ```
 *
 * The layer names can contain HTML, which allows you to add additional styling to the items:
 *
 * ```js
 * {"<img src='my-layer-icon' /> <span class='my-layer-item'>My Layer</span>": myLayer}
 * ```
 */

export const Layers = Control.extend({
	// @section
	// @aka Control.Layers options
	options: {
		// @option collapsed: Boolean = true
		// If `true`, the control will be collapsed into an icon and expanded on mouse hover, touch, or keyboard activation.
		collapsed: true,
		position: 'topright',

		// @option autoZIndex: Boolean = true
		// If `true`, the control will assign zIndexes in increasing order to all of its layers so that the order is preserved when switching them on/off.
		autoZIndex: true,

		// @option hideSingleBase: Boolean = false
		// If `true`, the base layers in the control will be hidden when there is only one.
		hideSingleBase: false,

		// @option sortLayers: Boolean = false
		// Whether to sort the layers. When `false`, layers will keep the order
		// in which they were added to the control.
		sortLayers: false,

		// @option sortFunction: Function = *
		// A [compare function](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Array/sort)
			link.href = '#';
			link.title = 'Layers';

			L.DomEvent.addListener(link, L.Browser.touch ? 'click' : 'focus', this._expand, this);

			this._map.on('movestart', this._collapse, this);
			// TODO keyboard accessibility
		this._handlingClick = false;

		for (const i in baseLayers) {
			this._addLayer(baseLayers[i], i);
		}

		for (const i in overlays) {
			this._addLayer(overlays[i], i, true);
		}
	},

	onAdd(map) {
		this._initLayout();
		this._update();

		this._map = map;
		map.on('zoomend', this._checkDisabledLayers, this);

		for (let i = 0; i < this._layers.length; i++) {
			this._layers[i].layer.on('add remove', this._onLayerChange, this);
		}

		return this._container;
	},

	addTo(map) {
		Control.prototype.addTo.call(this, map);
		// Trigger expand after Layers Control has been inserted into DOM so that is now has an actual height.
		return this._expandIfNotCollapsed();
	},

	onRemove() {
		this._map.off('zoomend', this._checkDisabledLayers, this);

		for (let i = 0; i < this._layers.length; i++) {
			this._layers[i].layer.off('add remove', this._onLayerChange, this);
		}
	},

	// @method addBaseLayer(layer: Layer, name: String): this
	// Adds a base layer (radio button entry) with the given name to the control.
	addBaseLayer(layer, name) {
		this._addLayer(layer, name);
		return (this._map) ? this._update() : this;
	},

	// @method addOverlay(layer: Layer, name: String): this
	// Adds an overlay (checkbox entry) with the given name to the control.
	addOverlay(layer, name) {
		this._addLayer(layer, name, true);
		return (this._map) ? this._update() : this;
	},

	// @method removeLayer(layer: Layer): this
	// Remove the given layer from the control.
	removeLayer(layer) {
		layer.off('add remove', this._onLayerChange, this);

		const obj = this._getLayer(Util.stamp(layer));
		if (obj) {
			this._layers.splice(this._layers.indexOf(obj), 1);
		}
		return (this._map) ? this._update() : this;
	},

	// @method expand(): this
	// Expand the control container if collapsed.
	expand() {
		DomUtil.addClass(this._container, 'leaflet-control-layers-expanded');
		this._section.style.height = null;
		const acceptableHeight = this._map.getSize().y - (this._container.offsetTop + 50);
		if (acceptableHeight < this._section.clientHeight) {
			DomUtil.addClass(this._section, 'leaflet-control-layers-scrollbar');
			this._section.style.height = `${acceptableHeight}px`;
		} else {
			DomUtil.removeClass(this._section, 'leaflet-control-layers-scrollbar');
		}
		this._checkDisabledLayers();
		return this;
	},

	// @method collapse(): this
	// Collapse the control container if expanded.
	collapse() {
		DomUtil.removeClass(this._container, 'leaflet-control-layers-expanded');
		return this;
	},

	_initLayout() {
		const className = 'leaflet-control-layers',
		    container = this._container = DomUtil.create('div', className),
		    collapsed = this.options.collapsed;

		// makes this work on IE touch devices by stopping it from firing a mouseout event when the touch is released
		container.setAttribute('aria-haspopup', true);

		DomEvent.disableClickPropagation(container);
		DomEvent.disableScrollPropagation(container);

		const section = this._section = DomUtil.create('fieldset', `${className}-list`);

		if (collapsed) {
			this._map.on('click', this.collapse, this);

			DomEvent.on(container, {
				mouseenter: this._expandSafely,
				mouseleave: this.collapse
			}, this);
		}

		const link = this._layersLink = DomUtil.create('a', `${className}-toggle`, container);
		link.href = '#';
		link.title = 'Layers';
		link.setAttribute('role', 'button');

		DomEvent.on(link, {
			keydown(e) {
				if (e.keyCode === 13) {
					this._expandSafely();
				}
			},
			// Certain screen readers intercept the key event and instead send a click event
			click(e) {
				DomEvent.preventDefault(e);
				this._expandSafely();
			}
		}, this);

		if (!collapsed) {
			this.expand();
		}

		this._baseLayersList = DomUtil.create('div', `${className}-base`, section);
		this._separator = DomUtil.create('div', `${className}-separator`, section);
		this._overlaysList = DomUtil.create('div', `${className}-overlays`, section);

		container.appendChild(section);
	},

	_getLayer(id) {
		for (let i = 0; i < this._layers.length; i++) {

			if (this._layers[i] && Util.stamp(this._layers[i].layer) === id) {
				return this._layers[i];
			}
		}
	},

	_addLayer(layer, name, overlay) {
		if (this._map) {
			layer.on('add remove', this._onLayerChange, this);
		}

		this._layers.push({
			layer,
			name,
			overlay
		});

		if (this.options.sortLayers) {
			this._layers.sort(((a, b) => this.options.sortFunction(a.layer, b.layer, a.name, b.name)));
		}

		if (this.options.autoZIndex && layer.setZIndex) {
			this._lastZIndex++;
			layer.setZIndex(this._lastZIndex);
		}

		this._expandIfNotCollapsed();
	},

	_update() {
		if (!this._container) { return this; }

		DomUtil.empty(this._baseLayersList);
		DomUtil.empty(this._overlaysList);

		this._layerControlInputs = [];
		let baseLayersPresent, overlaysPresent, i, obj, baseLayersCount = 0;

		for (i = 0; i < this._layers.length; i++) {
			obj = this._layers[i];
			this._addItem(obj);
			overlaysPresent = overlaysPresent || obj.overlay;
			baseLayersPresent = baseLayersPresent || !obj.overlay;
			baseLayersCount += !obj.overlay ? 1 : 0;
		}

		// Hide base layers section if there's only one layer.
		if (this.options.hideSingleBase) {
			baseLayersPresent = baseLayersPresent && baseLayersCount > 1;
			this._baseLayersList.style.display = baseLayersPresent ? '' : 'none';
		}

		this._separator.style.display = overlaysPresent && baseLayersPresent ? '' : 'none';

		return this;
	},

	_onLayerChange(e) {
		if (!this._handlingClick) {
			this._update();
		}

		const obj = this._getLayer(Util.stamp(e.target));

		// @namespace Map
		// @section Layer events
		// @event baselayerchange: LayersControlEvent
		// Fired when the base layer is changed through the [layers control](#control-layers).
		// @event overlayadd: LayersControlEvent
		// Fired when an overlay is selected through the [layers control](#control-layers).
		// @event overlayremove: LayersControlEvent
		// Fired when an overlay is deselected through the [layers control](#control-layers).
		// @namespace Control.Layers
		const type = obj.overlay ?
			(e.type === 'add' ? 'overlayadd' : 'overlayremove') :
			(e.type === 'add' ? 'baselayerchange' : null);

		if (type) {
			this._map.fire(type, obj);
		}
	},

	// IE7 bugs out if you create a radio dynamically, so you have to do it this hacky way (see https://stackoverflow.com/a/119079)
	_createRadioElement(name, checked) {

		const radioHtml = `<input type="radio" class="leaflet-control-layers-selector" name="${name}"${checked ? ' checked="checked"' : ''}/>`;

		const radioFragment = document.createElement('div');
		radioFragment.innerHTML = radioHtml;

		return radioFragment.firstChild;
	},

	_addItem(obj) {
		const label = document.createElement('label'),
		      checked = this._map.hasLayer(obj.layer);
		let input;

		if (obj.overlay) {
			input = document.createElement('input');
			input.type = 'checkbox';
			input.className = 'leaflet-control-layers-selector';
			input.defaultChecked = checked;
		} else {
			input = this._createRadioElement(`leaflet-base-layers_${Util.stamp(this)}`, checked);
		}

		this._layerControlInputs.push(input);
		input.layerId = Util.stamp(obj.layer);

		DomEvent.on(input, 'click', this._onInputClick, this);

		const name = document.createElement('span');
		name.innerHTML = ` ${obj.name}`;

		// Helps from preventing layer control flicker when checkboxes are disabled
		// https://github.com/Leaflet/Leaflet/issues/2771
		const holder = document.createElement('span');

		label.appendChild(holder);
		holder.appendChild(input);
		holder.appendChild(name);

		const container = obj.overlay ? this._overlaysList : this._baseLayersList;
		container.appendChild(label);

		this._checkDisabledLayers();
		return label;
	},

	_onInputClick() {
		const inputs = this._layerControlInputs,
		      addedLayers = [],
		      removedLayers = [];
		let input, layer;

		this._handlingClick = true;

		for (let i = inputs.length - 1; i >= 0; i--) {
			input = inputs[i];
			layer = this._getLayer(input.layerId).layer;

			if (input.checked) {
				addedLayers.push(layer);
			} else if (!input.checked) {
				removedLayers.push(layer);
			}
		}

		// Bugfix issue 2318: Should remove all old layers before readding new ones
		for (let i = 0; i < removedLayers.length; i++) {
			if (this._map.hasLayer(removedLayers[i])) {
				this._map.removeLayer(removedLayers[i]);
			}
		}
		for (let i = 0; i < addedLayers.length; i++) {
			if (!this._map.hasLayer(addedLayers[i])) {
				this._map.addLayer(addedLayers[i]);
			}
		}

		this._handlingClick = false;

		this._refocusOnMap();
	},

	_checkDisabledLayers() {
		const inputs = this._layerControlInputs,
		      zoom = this._map.getZoom();
		let input, layer;

		for (let i = inputs.length - 1; i >= 0; i--) {
			input = inputs[i];
			layer = this._getLayer(input.layerId).layer;
			input.disabled = (layer.options.minZoom !== undefined && zoom < layer.options.minZoom) ||
			                 (layer.options.maxZoom !== undefined && zoom > layer.options.maxZoom);

		}
	},

	_expandIfNotCollapsed() {
		if (this._map && !this.options.collapsed) {
			this.expand();
		}
		return this;
	},

	_expandSafely() {
		const section = this._section;
		DomEvent.on(section, 'click', DomEvent.preventDefault);
		this.expand();
		setTimeout(() => {
			DomEvent.off(section, 'click', DomEvent.preventDefault);
		});
	}

});


// @factory L.control.layers(baselayers?: Object, overlays?: Object, options?: Control.Layers options)
// Creates a layers control with the given layers. Base layers will be switched with radio buttons, while overlays will be switched with checkboxes. Note that all base layers should be passed in the base layers object, but only one should be added to the map during map instantiation.
export const layers = function (baseLayers, overlays, options) {
	return new Layers(baseLayers, overlays, options);
};

<MSG> Merge pull request #638 from jacobtoye/PreventDefault

Prevent default action when opening layers control
<DFF> @@ -70,7 +70,15 @@ L.Control.Layers = L.Control.extend({
 			link.href = '#';
 			link.title = 'Layers';
 
-			L.DomEvent.addListener(link, L.Browser.touch ? 'click' : 'focus', this._expand, this);
+			if (L.Browser.touch) {
+				L.DomEvent
+					.addListener(link, 'click', L.DomEvent.stopPropagation)
+					.addListener(link, 'click', L.DomEvent.preventDefault)
+					.addListener(link, 'click', this._expand, this);
+			}
+			else {
+				L.DomEvent.addListener(link, 'focus', this._expand, this);
+			}
 
 			this._map.on('movestart', this._collapse, this);
 			// TODO keyboard accessibility

{'commit': "diff --git a/src/control/Control.Layers.js b/src/control/Control.Layers.js\nindex 0c943bba..61c7564e 100644\n--- a/src/control/Control.Layers.js\n+++ b/src/control/Control.Layers.js\n@@ -70,7 +70,15 @@ L.Control.Layers = L.Control.extend({\n \t\t\tlink.href = '#';\r\n \t\t\tlink.title = 'Layers';\r\n \r\n-\t\t\tL.DomEvent.addListener(link, L.Browser.touch ? 'click' : 'focus', this._expand, this);\r\n+\t\t\tif (L.Browser.touch) {\r\n+\t\t\t\tL.DomEvent\r\n+\t\t\t\t\t.addListener(link, 'click', L.DomEvent.stopPropagation)\r\n+\t\t\t\t\t.addListener(link, 'click', L.DomEvent.preventDefault)\r\n+\t\t\t\t\t.addListener(link, 'click', this._expand, this);\r\n+\t\t\t}\r\n+\t\t\telse {\r\n+\t\t\t\tL.DomEvent.addListener(link, 'focus', this._expand, this);\r\n+\t\t\t}\r\n \r\n \t\t\tthis._map.on('movestart', this._collapse, this);\r\n \t\t\t// TODO keyboard accessibility\r\n", 'commit_subject': 'Merge pull request #638 from jacobtoye/PreventDefault', 'repo_name': 'Leaflet/Leaflet', 'committer': {'name': 'Vladimir Agafonkin', 'email': 'af9b9b551d320013945f6a952e71e091983bc228@gmail.com', 'time_sec': '1340010066', 'tz_offset': '-420', 'date': {'seconds': '1340010066'}}, 'commit_tree': '18373ad9d717d9fe7a0685999c41c1ebabbdc39d', 'commit_message': 'Merge pull request #638 from jacobtoye/PreventDefault\n\nPrevent default action when opening layers control', 'language_name': 'JavaScript', 'license': 'bsd-2-clause', 'hunks': "@@ -70,7 +70,15 @@ L.Control.Layers = L.Control.extend({\n \t\t\tlink.href = '#';\r\n \t\t\tlink.title = 'Layers';\r\n \r\n-\t\t\tL.DomEvent.addListener(link, L.Browser.touch ? 'click' : 'focus', this._expand, this);\r\n+\t\t\tif (L.Browser.touch) {\r\n+\t\t\t\tL.DomEvent\r\n+\t\t\t\t\t.addListener(link, 'click', L.DomEvent.stopPropagation)\r\n+\t\t\t\t\t.addListener(link, 'click', L.DomEvent.preventDefault)\r\n+\t\t\t\t\t.addListener(link, 'click', this._expand, this);\r\n+\t\t\t}\r\n+\t\t\telse {\r\n+\t\t\t\tL.DomEvent.addListener(link, 'focus', this._expand, this);\r\n+\t\t\t}\r\n \r\n \t\t\tthis._map.on('movestart', this._collapse, this);\r\n \t\t\t// TODO keyboard accessibility\r\n", 'addition_count': 9, 'deletion_count': 1, 'src_file': 'a/src/control/Control.Layers.js', 'tgt_file': 'b/src/control/Control.Layers.js', 'file_extension': '.js', 'before_file': '\nimport {Control} from \'./Control\';\nimport * as Util from \'../core/Util\';\nimport * as DomEvent from \'../dom/DomEvent\';\nimport * as DomUtil from \'../dom/DomUtil\';\n\n/*\n * @class Control.Layers\n * @aka L.Control.Layers\n * @inherits Control\n *\n * The layers control gives users the ability to switch between different base layers and switch overlays on/off (check out the [detailed example](https://leafletjs.com/examples/layers-control/)). Extends `Control`.\n *\n * @example\n *\n * ```js\n * var baseLayers = {\n * \t"Mapbox": mapbox,\n * \t"OpenStreetMap": osm\n * };\n *\n * var overlays = {\n * \t"Marker": marker,\n * \t"Roads": roadsLayer\n * };\n *\n * L.control.layers(baseLayers, overlays).addTo(map);\n * ```\n *\n * The `baseLayers` and `overlays` parameters are object literals with layer names as keys and `Layer` objects as values:\n *\n * ```js\n * {\n *     "<someName1>": layer1,\n *     "<someName2>": layer2\n * }\n * ```\n *\n * The layer names can contain HTML, which allows you to add additional styling to the items:\n *\n * ```js\n * {"<img src=\'my-layer-icon\' /> <span class=\'my-layer-item\'>My Layer</span>": myLayer}\n * ```\n */\n\nexport const Layers = Control.extend({\n\t// @section\n\t// @aka Control.Layers options\n\toptions: {\n\t\t// @option collapsed: Boolean = true\n\t\t// If `true`, the control will be collapsed into an icon and expanded on mouse hover, touch, or keyboard activation.\n\t\tcollapsed: true,\n\t\tposition: \'topright\',\n\n\t\t// @option autoZIndex: Boolean = true\n\t\t// If `true`, the control will assign zIndexes in increasing order to all of its layers so that the order is preserved when switching them on/off.\n\t\tautoZIndex: true,\n\n\t\t// @option hideSingleBase: Boolean = false\n\t\t// If `true`, the base layers in the control will be hidden when there is only one.\n\t\thideSingleBase: false,\n\n\t\t// @option sortLayers: Boolean = false\n\t\t// Whether to sort the layers. When `false`, layers will keep the order\n\t\t// in which they were added to the control.\n\t\tsortLayers: false,\n\n\t\t// @option sortFunction: Function = *\n\t\t// A [compare function](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Array/sort)\n\t\t\tlink.href = \'#\';\r\n\t\t\tlink.title = \'Layers\';\r\n\r\n\t\t\tL.DomEvent.addListener(link, L.Browser.touch ? \'click\' : \'focus\', this._expand, this);\r\n\r\n\t\t\tthis._map.on(\'movestart\', this._collapse, this);\r\n\t\t\t// TODO keyboard accessibility\r\n\t\tthis._handlingClick = false;\n\n\t\tfor (const i in baseLayers) {\n\t\t\tthis._addLayer(baseLayers[i], i);\n\t\t}\n\n\t\tfor (const i in overlays) {\n\t\t\tthis._addLayer(overlays[i], i, true);\n\t\t}\n\t},\n\n\tonAdd(map) {\n\t\tthis._initLayout();\n\t\tthis._update();\n\n\t\tthis._map = map;\n\t\tmap.on(\'zoomend\', this._checkDisabledLayers, this);\n\n\t\tfor (let i = 0; i < this._layers.length; i++) {\n\t\t\tthis._layers[i].layer.on(\'add remove\', this._onLayerChange, this);\n\t\t}\n\n\t\treturn this._container;\n\t},\n\n\taddTo(map) {\n\t\tControl.prototype.addTo.call(this, map);\n\t\t// Trigger expand after Layers Control has been inserted into DOM so that is now has an actual height.\n\t\treturn this._expandIfNotCollapsed();\n\t},\n\n\tonRemove() {\n\t\tthis._map.off(\'zoomend\', this._checkDisabledLayers, this);\n\n\t\tfor (let i = 0; i < this._layers.length; i++) {\n\t\t\tthis._layers[i].layer.off(\'add remove\', this._onLayerChange, this);\n\t\t}\n\t},\n\n\t// @method addBaseLayer(layer: Layer, name: String): this\n\t// Adds a base layer (radio button entry) with the given name to the control.\n\taddBaseLayer(layer, name) {\n\t\tthis._addLayer(layer, name);\n\t\treturn (this._map) ? this._update() : this;\n\t},\n\n\t// @method addOverlay(layer: Layer, name: String): this\n\t// Adds an overlay (checkbox entry) with the given name to the control.\n\taddOverlay(layer, name) {\n\t\tthis._addLayer(layer, name, true);\n\t\treturn (this._map) ? this._update() : this;\n\t},\n\n\t// @method removeLayer(layer: Layer): this\n\t// Remove the given layer from the control.\n\tremoveLayer(layer) {\n\t\tlayer.off(\'add remove\', this._onLayerChange, this);\n\n\t\tconst obj = this._getLayer(Util.stamp(layer));\n\t\tif (obj) {\n\t\t\tthis._layers.splice(this._layers.indexOf(obj), 1);\n\t\t}\n\t\treturn (this._map) ? this._update() : this;\n\t},\n\n\t// @method expand(): this\n\t// Expand the control container if collapsed.\n\texpand() {\n\t\tDomUtil.addClass(this._container, \'leaflet-control-layers-expanded\');\n\t\tthis._section.style.height = null;\n\t\tconst acceptableHeight = this._map.getSize().y - (this._container.offsetTop + 50);\n\t\tif (acceptableHeight < this._section.clientHeight) {\n\t\t\tDomUtil.addClass(this._section, \'leaflet-control-layers-scrollbar\');\n\t\t\tthis._section.style.height = `${acceptableHeight}px`;\n\t\t} else {\n\t\t\tDomUtil.removeClass(this._section, \'leaflet-control-layers-scrollbar\');\n\t\t}\n\t\tthis._checkDisabledLayers();\n\t\treturn this;\n\t},\n\n\t// @method collapse(): this\n\t// Collapse the control container if expanded.\n\tcollapse() {\n\t\tDomUtil.removeClass(this._container, \'leaflet-control-layers-expanded\');\n\t\treturn this;\n\t},\n\n\t_initLayout() {\n\t\tconst className = \'leaflet-control-layers\',\n\t\t    container = this._container = DomUtil.create(\'div\', className),\n\t\t    collapsed = this.options.collapsed;\n\n\t\t// makes this work on IE touch devices by stopping it from firing a mouseout event when the touch is released\n\t\tcontainer.setAttribute(\'aria-haspopup\', true);\n\n\t\tDomEvent.disableClickPropagation(container);\n\t\tDomEvent.disableScrollPropagation(container);\n\n\t\tconst section = this._section = DomUtil.create(\'fieldset\', `${className}-list`);\n\n\t\tif (collapsed) {\n\t\t\tthis._map.on(\'click\', this.collapse, this);\n\n\t\t\tDomEvent.on(container, {\n\t\t\t\tmouseenter: this._expandSafely,\n\t\t\t\tmouseleave: this.collapse\n\t\t\t}, this);\n\t\t}\n\n\t\tconst link = this._layersLink = DomUtil.create(\'a\', `${className}-toggle`, container);\n\t\tlink.href = \'#\';\n\t\tlink.title = \'Layers\';\n\t\tlink.setAttribute(\'role\', \'button\');\n\n\t\tDomEvent.on(link, {\n\t\t\tkeydown(e) {\n\t\t\t\tif (e.keyCode === 13) {\n\t\t\t\t\tthis._expandSafely();\n\t\t\t\t}\n\t\t\t},\n\t\t\t// Certain screen readers intercept the key event and instead send a click event\n\t\t\tclick(e) {\n\t\t\t\tDomEvent.preventDefault(e);\n\t\t\t\tthis._expandSafely();\n\t\t\t}\n\t\t}, this);\n\n\t\tif (!collapsed) {\n\t\t\tthis.expand();\n\t\t}\n\n\t\tthis._baseLayersList = DomUtil.create(\'div\', `${className}-base`, section);\n\t\tthis._separator = DomUtil.create(\'div\', `${className}-separator`, section);\n\t\tthis._overlaysList = DomUtil.create(\'div\', `${className}-overlays`, section);\n\n\t\tcontainer.appendChild(section);\n\t},\n\n\t_getLayer(id) {\n\t\tfor (let i = 0; i < this._layers.length; i++) {\n\n\t\t\tif (this._layers[i] && Util.stamp(this._layers[i].layer) === id) {\n\t\t\t\treturn this._layers[i];\n\t\t\t}\n\t\t}\n\t},\n\n\t_addLayer(layer, name, overlay) {\n\t\tif (this._map) {\n\t\t\tlayer.on(\'add remove\', this._onLayerChange, this);\n\t\t}\n\n\t\tthis._layers.push({\n\t\t\tlayer,\n\t\t\tname,\n\t\t\toverlay\n\t\t});\n\n\t\tif (this.options.sortLayers) {\n\t\t\tthis._layers.sort(((a, b) => this.options.sortFunction(a.layer, b.layer, a.name, b.name)));\n\t\t}\n\n\t\tif (this.options.autoZIndex && layer.setZIndex) {\n\t\t\tthis._lastZIndex++;\n\t\t\tlayer.setZIndex(this._lastZIndex);\n\t\t}\n\n\t\tthis._expandIfNotCollapsed();\n\t},\n\n\t_update() {\n\t\tif (!this._container) { return this; }\n\n\t\tDomUtil.empty(this._baseLayersList);\n\t\tDomUtil.empty(this._overlaysList);\n\n\t\tthis._layerControlInputs = [];\n\t\tlet baseLayersPresent, overlaysPresent, i, obj, baseLayersCount = 0;\n\n\t\tfor (i = 0; i < this._layers.length; i++) {\n\t\t\tobj = this._layers[i];\n\t\t\tthis._addItem(obj);\n\t\t\toverlaysPresent = overlaysPresent || obj.overlay;\n\t\t\tbaseLayersPresent = baseLayersPresent || !obj.overlay;\n\t\t\tbaseLayersCount += !obj.overlay ? 1 : 0;\n\t\t}\n\n\t\t// Hide base layers section if there\'s only one layer.\n\t\tif (this.options.hideSingleBase) {\n\t\t\tbaseLayersPresent = baseLayersPresent && baseLayersCount > 1;\n\t\t\tthis._baseLayersList.style.display = baseLayersPresent ? \'\' : \'none\';\n\t\t}\n\n\t\tthis._separator.style.display = overlaysPresent && baseLayersPresent ? \'\' : \'none\';\n\n\t\treturn this;\n\t},\n\n\t_onLayerChange(e) {\n\t\tif (!this._handlingClick) {\n\t\t\tthis._update();\n\t\t}\n\n\t\tconst obj = this._getLayer(Util.stamp(e.target));\n\n\t\t// @namespace Map\n\t\t// @section Layer events\n\t\t// @event baselayerchange: LayersControlEvent\n\t\t// Fired when the base layer is changed through the [layers control](#control-layers).\n\t\t// @event overlayadd: LayersControlEvent\n\t\t// Fired when an overlay is selected through the [layers control](#control-layers).\n\t\t// @event overlayremove: LayersControlEvent\n\t\t// Fired when an overlay is deselected through the [layers control](#control-layers).\n\t\t// @namespace Control.Layers\n\t\tconst type = obj.overlay ?\n\t\t\t(e.type === \'add\' ? \'overlayadd\' : \'overlayremove\') :\n\t\t\t(e.type === \'add\' ? \'baselayerchange\' : null);\n\n\t\tif (type) {\n\t\t\tthis._map.fire(type, obj);\n\t\t}\n\t},\n\n\t// IE7 bugs out if you create a radio dynamically, so you have to do it this hacky way (see https://stackoverflow.com/a/119079)\n\t_createRadioElement(name, checked) {\n\n\t\tconst radioHtml = `<input type="radio" class="leaflet-control-layers-selector" name="${name}"${checked ? \' checked="checked"\' : \'\'}/>`;\n\n\t\tconst radioFragment = document.createElement(\'div\');\n\t\tradioFragment.innerHTML = radioHtml;\n\n\t\treturn radioFragment.firstChild;\n\t},\n\n\t_addItem(obj) {\n\t\tconst label = document.createElement(\'label\'),\n\t\t      checked = this._map.hasLayer(obj.layer);\n\t\tlet input;\n\n\t\tif (obj.overlay) {\n\t\t\tinput = document.createElement(\'input\');\n\t\t\tinput.type = \'checkbox\';\n\t\t\tinput.className = \'leaflet-control-layers-selector\';\n\t\t\tinput.defaultChecked = checked;\n\t\t} else {\n\t\t\tinput = this._createRadioElement(`leaflet-base-layers_${Util.stamp(this)}`, checked);\n\t\t}\n\n\t\tthis._layerControlInputs.push(input);\n\t\tinput.layerId = Util.stamp(obj.layer);\n\n\t\tDomEvent.on(input, \'click\', this._onInputClick, this);\n\n\t\tconst name = document.createElement(\'span\');\n\t\tname.innerHTML = ` ${obj.name}`;\n\n\t\t// Helps from preventing layer control flicker when checkboxes are disabled\n\t\t// https://github.com/Leaflet/Leaflet/issues/2771\n\t\tconst holder = document.createElement(\'span\');\n\n\t\tlabel.appendChild(holder);\n\t\tholder.appendChild(input);\n\t\tholder.appendChild(name);\n\n\t\tconst container = obj.overlay ? this._overlaysList : this._baseLayersList;\n\t\tcontainer.appendChild(label);\n\n\t\tthis._checkDisabledLayers();\n\t\treturn label;\n\t},\n\n\t_onInputClick() {\n\t\tconst inputs = this._layerControlInputs,\n\t\t      addedLayers = [],\n\t\t      removedLayers = [];\n\t\tlet input, layer;\n\n\t\tthis._handlingClick = true;\n\n\t\tfor (let i = inputs.length - 1; i >= 0; i--) {\n\t\t\tinput = inputs[i];\n\t\t\tlayer = this._getLayer(input.layerId).layer;\n\n\t\t\tif (input.checked) {\n\t\t\t\taddedLayers.push(layer);\n\t\t\t} else if (!input.checked) {\n\t\t\t\tremovedLayers.push(layer);\n\t\t\t}\n\t\t}\n\n\t\t// Bugfix issue 2318: Should remove all old layers before readding new ones\n\t\tfor (let i = 0; i < removedLayers.length; i++) {\n\t\t\tif (this._map.hasLayer(removedLayers[i])) {\n\t\t\t\tthis._map.removeLayer(removedLayers[i]);\n\t\t\t}\n\t\t}\n\t\tfor (let i = 0; i < addedLayers.length; i++) {\n\t\t\tif (!this._map.hasLayer(addedLayers[i])) {\n\t\t\t\tthis._map.addLayer(addedLayers[i]);\n\t\t\t}\n\t\t}\n\n\t\tthis._handlingClick = false;\n\n\t\tthis._refocusOnMap();\n\t},\n\n\t_checkDisabledLayers() {\n\t\tconst inputs = this._layerControlInputs,\n\t\t      zoom = this._map.getZoom();\n\t\tlet input, layer;\n\n\t\tfor (let i = inputs.length - 1; i >= 0; i--) {\n\t\t\tinput = inputs[i];\n\t\t\tlayer = this._getLayer(input.layerId).layer;\n\t\t\tinput.disabled = (layer.options.minZoom !== undefined && zoom < layer.options.minZoom) ||\n\t\t\t                 (layer.options.maxZoom !== undefined && zoom > layer.options.maxZoom);\n\n\t\t}\n\t},\n\n\t_expandIfNotCollapsed() {\n\t\tif (this._map && !this.options.collapsed) {\n\t\t\tthis.expand();\n\t\t}\n\t\treturn this;\n\t},\n\n\t_expandSafely() {\n\t\tconst section = this._section;\n\t\tDomEvent.on(section, \'click\', DomEvent.preventDefault);\n\t\tthis.expand();\n\t\tsetTimeout(() => {\n\t\t\tDomEvent.off(section, \'click\', DomEvent.preventDefault);\n\t\t});\n\t}\n\n});\n\n\n// @factory L.control.layers(baselayers?: Object, overlays?: Object, options?: Control.Layers options)\n// Creates a layers control with the given layers. Base layers will be switched with radio buttons, while overlays will be switched with checkboxes. Note that all base layers should be passed in the base layers object, but only one should be added to the map during map instantiation.\nexport const layers = function (baseLayers, overlays, options) {\n\treturn new Layers(baseLayers, overlays, options);\n};\n', 'message': 'Merge pull request #638 from jacobtoye/PreventDefault\n\nPrevent default action when opening layers control', 'author_name': 'Vladimir Agafonkin', 'lang': 'Layers'}