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


/*
 * @class TileLayer
 * @inherits GridLayer
 * @aka L.TileLayer
 * Used to load and display tile layers on the map. Note that most tile servers require attribution, which you can set under `Layer`. Extends `GridLayer`.
 *
 * @example
 *
 * ```js
 * L.tileLayer('https://tile.openstreetmap.org/{z}/{x}/{y}.png?{foo}', {foo: 'bar', attribution: '&copy; <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors'}).addTo(map);
 * ```
 *
 * @section URL template

		unloadInvisibleTiles: L.Browser.mobile,
		updateWhenIdle: L.Browser.mobile,
		reuseTiles: false
	},

	initialize: function (url, options, urlParams) {
 *
 * `{s}` means one of the available subdomains (used sequentially to help with browser parallel requests per domain limitation; subdomain values are specified in options; `a`, `b` or `c` by default, can be omitted), `{z}` — zoom level, `{x}` and `{y}` — tile coordinates. `{r}` can be used to add "&commat;2x" to the URL to load retina tiles.
 *
 * You can use custom keys in the template, which will be [evaluated](#util-template) from TileLayer options, like this:
 *
 * ```
 * L.tileLayer('https://{s}.somedomain.com/{foo}/{z}/{x}/{y}.png', {foo: 'bar'});
 * ```
 */


export const TileLayer = GridLayer.extend({

	// @section
	// @aka TileLayer options
	options: {
		// @option minZoom: Number = 0
		// The minimum zoom level down to which this layer will be displayed (inclusive).
		minZoom: 0,

		// @option maxZoom: Number = 18
		// The maximum zoom level up to which this layer will be displayed (inclusive).
		maxZoom: 18,

		// @option subdomains: String|String[] = 'abc'
		// Subdomains of the tile service. Can be passed in the form of one string (where each letter is a subdomain name) or an array of strings.
		subdomains: 'abc',

		// @option errorTileUrl: String = ''
		// URL to the tile image to show in place of the tile that failed to load.
		errorTileUrl: '',

		// @option zoomOffset: Number = 0
		// The zoom number used in tile URLs will be offset with this value.
		zoomOffset: 0,

		// @option tms: Boolean = false
		// If `true`, inverses Y axis numbering for tiles (turn this on for [TMS](https://en.wikipedia.org/wiki/Tile_Map_Service) services).
		tms: false,

		// @option zoomReverse: Boolean = false
		// If set to true, the zoom number used in tile URLs will be reversed (`maxZoom - zoom` instead of `zoom`)
		zoomReverse: false,

		// @option detectRetina: Boolean = false
		// If `true` and user is on a retina display, it will request four tiles of half the specified size and a bigger zoom level in place of one to utilize the high resolution.
		detectRetina: false,

		// @option crossOrigin: Boolean|String = false
		// Whether the crossOrigin attribute will be added to the tiles.
		// If a String is provided, all tiles will have their crossOrigin attribute set to the String provided. This is needed if you want to access tile pixel data.
		// Refer to [CORS Settings](https://developer.mozilla.org/en-US/docs/Web/HTML/CORS_settings_attributes) for valid String values.
		crossOrigin: false,

		// @option referrerPolicy: Boolean|String = false
		// Whether the referrerPolicy attribute will be added to the tiles.
		// If a String is provided, all tiles will have their referrerPolicy attribute set to the String provided.
		// This may be needed if your map's rendering context has a strict default but your tile provider expects a valid referrer
		// (e.g. to validate an API token).
		// Refer to [HTMLImageElement.referrerPolicy](https://developer.mozilla.org/en-US/docs/Web/API/HTMLImageElement/referrerPolicy) for valid String values.
		referrerPolicy: false
	},

	initialize(url, options) {

		this._url = url;

		options = Util.setOptions(this, options);

		// detecting retina displays, adjusting tileSize and zoom levels
		if (options.detectRetina && Browser.retina && options.maxZoom > 0) {

			options.tileSize = Math.floor(options.tileSize / 2);

			if (!options.zoomReverse) {
				options.zoomOffset++;
				options.maxZoom = Math.max(options.minZoom, options.maxZoom - 1);
			} else {
				options.zoomOffset--;
				options.minZoom = Math.min(options.maxZoom, options.minZoom + 1);
			}

			options.minZoom = Math.max(0, options.minZoom);
		} else if (!options.zoomReverse) {
			// make sure maxZoom is gte minZoom
			options.maxZoom = Math.max(options.minZoom, options.maxZoom);
		} else {
			// make sure minZoom is lte maxZoom
			options.minZoom = Math.min(options.maxZoom, options.minZoom);
		}

		if (typeof options.subdomains === 'string') {
			options.subdomains = options.subdomains.split('');
		}

		this.on('tileunload', this._onTileRemove);
	},

	// @method setUrl(url: String, noRedraw?: Boolean): this
	// Updates the layer's URL template and redraws it (unless `noRedraw` is set to `true`).
	// If the URL does not change, the layer will not be redrawn unless
	// the noRedraw parameter is set to false.
	setUrl(url, noRedraw) {
		if (this._url === url && noRedraw === undefined) {
			noRedraw = true;
		}

		this._url = url;

		if (!noRedraw) {
			this.redraw();
		}
		return this;
		var bounds = this._map.getPixelBounds(),
			zoom = this._map.getZoom(),
			tileSize = this.options.tileSize;
			
		if (zoom > this.options.maxZoom || zoom < this.options.minZoom) {
			return;
		}
		const tile = document.createElement('img');

		DomEvent.on(tile, 'load', this._tileOnLoad.bind(this, done, tile));
		DomEvent.on(tile, 'error', this._tileOnError.bind(this, done, tile));

		if (this.options.crossOrigin || this.options.crossOrigin === '') {
			tile.crossOrigin = this.options.crossOrigin === true ? '' : this.options.crossOrigin;
		}

		// for this new option we follow the documented behavior
		// more closely by only setting the property when string
		if (typeof this.options.referrerPolicy === 'string') {
			tile.referrerPolicy = this.options.referrerPolicy;
		}

		// The alt attribute is set to the empty string,
		// allowing screen readers to ignore the decorative image tiles.
		// https://www.w3.org/WAI/tutorials/images/decorative/
		// https://www.w3.org/TR/html-aria/#el-img-empty-alt
		tile.alt = '';

		tile.src = this.getTileUrl(coords);

		return tile;
	},

	// @section Extension methods
	// @uninheritable
	// Layers extending `TileLayer` might reimplement the following method.
	// @method getTileUrl(coords: Object): String
	// Called only internally, returns the URL for a tile given its coordinates.
	// Classes extending `TileLayer` can override this function to provide custom tile URL naming schemes.
	getTileUrl(coords) {
		const data = {
			r: Browser.retina ? '@2x' : '',
			s: this._getSubdomain(coords),
			x: coords.x,
			y: coords.y,
			z: this._getZoomForUrl()
		};
		if (this._map && !this._map.options.crs.infinite) {
			const invertedY = this._globalTileRange.max.y - coords.y;
			if (this.options.tms) {
				data['y'] = invertedY;
			}
			data['-y'] = invertedY;
		}

		return Util.template(this._url, Util.extend(data, this.options));
	},

	_tileOnLoad(done, tile) {
		done(null, tile);
	},

	_tileOnError(done, tile, e) {
					tile = this._tiles[key];
					this.fire("tileunload", {tile: tile, url: tile.src});

					// evil, don't do this! crashes Android 3, produces load errors, doesn't solve memory leaks
					// this._tiles[key].src = '';

					//tile.src = 'data:image/gif;base64,R0lGODlhAQABAAD/ACwAAAAAAQABAAACADs=';

					if (tile.parentNode === this._container) {
						this._container.removeChild(tile);
					}
					if (this.options.reuseTiles) {
						this._unusedTiles.push(this._tiles[key]);
					}
					delete this._tiles[key];
				}
			}
		      zoomReverse = this.options.zoomReverse,
		      zoomOffset = this.options.zoomOffset;

		if (zoomReverse) {
			zoom = maxZoom - zoom;
		}

		return zoom + zoomOffset;
	},

	_getSubdomain(tilePoint) {
		const index = Math.abs(tilePoint.x + tilePoint.y) % this.options.subdomains.length;
		return this.options.subdomains[index];
	},

	// stops loading all tiles in the background layer
	_abortLoading() {
		let i, tile;
		for (i in this._tiles) {
			if (this._tiles[i].coords.z !== this._tileZoom) {
				tile = this._tiles[i].el;

				tile.onload = Util.falseFn;
				tile.onerror = Util.falseFn;

				if (!tile.complete) {
					tile.src = Util.emptyImageUrl;
					const coords = this._tiles[i].coords;
					DomUtil.remove(tile);
					delete this._tiles[i];
					// @event tileabort: TileEvent
					// Fired when a tile was loading but is now not wanted.
					this.fire('tileabort', {
						tile,
						coords
					});
				}
			}
		}
	},

	_removeTile(key) {
		const tile = this._tiles[key];
		if (!tile) { return; }

		// Cancels any pending http requests associated with the tile
		tile.el.setAttribute('src', Util.emptyImageUrl);

		return GridLayer.prototype._removeTile.call(this, key);
	},

	_tileReady(coords, err, tile) {
		if (!this._map || (tile && tile.getAttribute('src') === Util.emptyImageUrl)) {
			return;
		}

		return GridLayer.prototype._tileReady.call(this, coords, err, tile);
	}
});


// @factory L.tilelayer(urlTemplate: String, options?: TileLayer options)
// Instantiates a tile layer object given a `URL template` and optionally an options object.

export function tileLayer(url, options) {
	return new TileLayer(url, options);
}

<MSG> Fixed Android 3 stability issues

<DFF> @@ -21,7 +21,7 @@ L.TileLayer = L.Class.extend({
 
 		unloadInvisibleTiles: L.Browser.mobile,
 		updateWhenIdle: L.Browser.mobile,
-		reuseTiles: false
+		reuseTiles: L.Browser.mobile
 	},
 
 	initialize: function (url, options, urlParams) {
@@ -141,7 +141,7 @@ L.TileLayer = L.Class.extend({
 		var bounds = this._map.getPixelBounds(),
 			zoom = this._map.getZoom(),
 			tileSize = this.options.tileSize;
-			
+
 		if (zoom > this.options.maxZoom || zoom < this.options.minZoom) {
 			return;
 		}
@@ -204,17 +204,14 @@ L.TileLayer = L.Class.extend({
 					tile = this._tiles[key];
 					this.fire("tileunload", {tile: tile, url: tile.src});
 
-					// evil, don't do this! crashes Android 3, produces load errors, doesn't solve memory leaks
-					// this._tiles[key].src = '';
-
-					//tile.src = 'data:image/gif;base64,R0lGODlhAQABAAD/ACwAAAAAAQABAAACADs=';
-
 					if (tile.parentNode === this._container) {
 						this._container.removeChild(tile);
 					}
 					if (this.options.reuseTiles) {
 						this._unusedTiles.push(this._tiles[key]);
 					}
+					//tile.src = 'data:image/gif;base64,R0lGODlhAQABAAD/ACwAAAAAAQABAAACADs=';
+
 					delete this._tiles[key];
 				}
 			}

{'commit': 'diff --git a/dist/leaflet.css b/dist/leaflet.css\nindex 196826e9..119ddb2b 100644\n--- a/dist/leaflet.css\n+++ b/dist/leaflet.css\n@@ -17,7 +17,7 @@\n .leaflet-container {\r\n \toverflow: hidden;\r\n \t}\r\n-.leaflet-tile-pane {\r\n+.leaflet-tile-pane, .leaflet-container {\r\n \t-webkit-transform: translate3d(0,0,0);\r\n \t}\r\n .leaflet-tile,\r\n@@ -320,4 +320,4 @@ a.leaflet-active {\n \t}\r\n .leaflet-popup-close-button {\r\n \tbackground: white url(images/popup-close.png);\r\n-\t}\n\\ No newline at end of file\n+\t}\r\ndiff --git a/src/dom/DomUtil.js b/src/dom/DomUtil.js\nindex a7f0e0e4..db102016 100644\n--- a/src/dom/DomUtil.js\n+++ b/src/dom/DomUtil.js\n@@ -133,8 +133,13 @@ L.DomUtil = {\n \r\n \tsetPosition: function (el, point) {\r\n \t\tel._leaflet_pos = point;\r\n-\t\tif (L.Browser.webkit && !L.Browser.android) {\r\n+\t\tif (L.Browser.webkit3d) {\r\n \t\t\tel.style[L.DomUtil.TRANSFORM] =  L.DomUtil.getTranslateString(point);\r\n+\r\n+\t\t\tif (L.Browser.android) {\r\n+\t\t\t\tel.style[\'-webkit-perspective\'] = \'1000\';\r\n+\t\t\t\tel.style[\'-webkit-backface-visibility\'] = \'hidden\';\r\n+\t\t\t}\r\n \t\t} else {\r\n \t\t\tel.style.left = point.x + \'px\';\r\n \t\t\tel.style.top = point.y + \'px\';\r\ndiff --git a/src/layer/tile/TileLayer.js b/src/layer/tile/TileLayer.js\nindex a982f7fa..d3f17487 100644\n--- a/src/layer/tile/TileLayer.js\n+++ b/src/layer/tile/TileLayer.js\n@@ -21,7 +21,7 @@ L.TileLayer = L.Class.extend({\n \r\n \t\tunloadInvisibleTiles: L.Browser.mobile,\r\n \t\tupdateWhenIdle: L.Browser.mobile,\r\n-\t\treuseTiles: false\r\n+\t\treuseTiles: L.Browser.mobile\r\n \t},\r\n \r\n \tinitialize: function (url, options, urlParams) {\r\n@@ -141,7 +141,7 @@ L.TileLayer = L.Class.extend({\n \t\tvar bounds = this._map.getPixelBounds(),\r\n \t\t\tzoom = this._map.getZoom(),\r\n \t\t\ttileSize = this.options.tileSize;\r\n-\t\t\t\r\n+\r\n \t\tif (zoom > this.options.maxZoom || zoom < this.options.minZoom) {\r\n \t\t\treturn;\r\n \t\t}\r\n@@ -204,17 +204,14 @@ L.TileLayer = L.Class.extend({\n \t\t\t\t\ttile = this._tiles[key];\r\n \t\t\t\t\tthis.fire("tileunload", {tile: tile, url: tile.src});\r\n \r\n-\t\t\t\t\t// evil, don\'t do this! crashes Android 3, produces load errors, doesn\'t solve memory leaks\r\n-\t\t\t\t\t// this._tiles[key].src = \'\';\r\n-\r\n-\t\t\t\t\t//tile.src = \'data:image/gif;base64,R0lGODlhAQABAAD/ACwAAAAAAQABAAACADs=\';\r\n-\r\n \t\t\t\t\tif (tile.parentNode === this._container) {\r\n \t\t\t\t\t\tthis._container.removeChild(tile);\r\n \t\t\t\t\t}\r\n \t\t\t\t\tif (this.options.reuseTiles) {\r\n \t\t\t\t\t\tthis._unusedTiles.push(this._tiles[key]);\r\n \t\t\t\t\t}\r\n+\t\t\t\t\t//tile.src = \'data:image/gif;base64,R0lGODlhAQABAAD/ACwAAAAAAQABAAACADs=\';\r\n+\r\n \t\t\t\t\tdelete this._tiles[key];\r\n \t\t\t\t}\r\n \t\t\t}\r\n', 'commit_subject': 'Fixed Android 3 stability issues', 'repo_name': 'Leaflet/Leaflet', 'committer': {'name': 'mourner', 'email': 'af9b9b551d320013945f6a952e71e091983bc228@gmail.com', 'time_sec': '1329141660', 'tz_offset': '120', 'date': {'seconds': '1329141660'}}, 'commit_tree': 'af4e29efb713b659b4b18672c3f6de7a73394b8b', 'commit_message': 'Fixed Android 3 stability issues\n', 'language_name': 'Shell', 'license': 'bsd-2-clause', 'hunks': '@@ -21,7 +21,7 @@ L.TileLayer = L.Class.extend({\n \r\n \t\tunloadInvisibleTiles: L.Browser.mobile,\r\n \t\tupdateWhenIdle: L.Browser.mobile,\r\n-\t\treuseTiles: false\r\n+\t\treuseTiles: L.Browser.mobile\r\n \t},\r\n \r\n \tinitialize: function (url, options, urlParams) {\r\n@@ -141,7 +141,7 @@ L.TileLayer = L.Class.extend({\n \t\tvar bounds = this._map.getPixelBounds(),\r\n \t\t\tzoom = this._map.getZoom(),\r\n \t\t\ttileSize = this.options.tileSize;\r\n-\t\t\t\r\n+\r\n \t\tif (zoom > this.options.maxZoom || zoom < this.options.minZoom) {\r\n \t\t\treturn;\r\n \t\t}\r\n@@ -204,17 +204,14 @@ L.TileLayer = L.Class.extend({\n \t\t\t\t\ttile = this._tiles[key];\r\n \t\t\t\t\tthis.fire("tileunload", {tile: tile, url: tile.src});\r\n \r\n-\t\t\t\t\t// evil, don\'t do this! crashes Android 3, produces load errors, doesn\'t solve memory leaks\r\n-\t\t\t\t\t// this._tiles[key].src = \'\';\r\n-\r\n-\t\t\t\t\t//tile.src = \'data:image/gif;base64,R0lGODlhAQABAAD/ACwAAAAAAQABAAACADs=\';\r\n-\r\n \t\t\t\t\tif (tile.parentNode === this._container) {\r\n \t\t\t\t\t\tthis._container.removeChild(tile);\r\n \t\t\t\t\t}\r\n \t\t\t\t\tif (this.options.reuseTiles) {\r\n \t\t\t\t\t\tthis._unusedTiles.push(this._tiles[key]);\r\n \t\t\t\t\t}\r\n+\t\t\t\t\t//tile.src = \'data:image/gif;base64,R0lGODlhAQABAAD/ACwAAAAAAQABAAACADs=\';\r\n+\r\n \t\t\t\t\tdelete this._tiles[key];\r\n \t\t\t\t}\r\n \t\t\t}\r\n', 'addition_count': 4, 'deletion_count': 7, 'src_file': 'a/src/layer/tile/TileLayer.js', 'tgt_file': 'b/src/layer/tile/TileLayer.js', 'file_extension': '.js', 'before_file': 'import {GridLayer} from \'./GridLayer\';\nimport Browser from \'../../core/Browser\';\nimport * as Util from \'../../core/Util\';\nimport * as DomEvent from \'../../dom/DomEvent\';\nimport * as DomUtil from \'../../dom/DomUtil\';\n\n\n/*\n * @class TileLayer\n * @inherits GridLayer\n * @aka L.TileLayer\n * Used to load and display tile layers on the map. Note that most tile servers require attribution, which you can set under `Layer`. Extends `GridLayer`.\n *\n * @example\n *\n * ```js\n * L.tileLayer(\'https://tile.openstreetmap.org/{z}/{x}/{y}.png?{foo}\', {foo: \'bar\', attribution: \'&copy; <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors\'}).addTo(map);\n * ```\n *\n * @section URL template\n\r\n\t\tunloadInvisibleTiles: L.Browser.mobile,\r\n\t\tupdateWhenIdle: L.Browser.mobile,\r\n\t\treuseTiles: false\r\n\t},\r\n\r\n\tinitialize: function (url, options, urlParams) {\r\n *\n * `{s}` means one of the available subdomains (used sequentially to help with browser parallel requests per domain limitation; subdomain values are specified in options; `a`, `b` or `c` by default, can be omitted), `{z}` — zoom level, `{x}` and `{y}` — tile coordinates. `{r}` can be used to add "&commat;2x" to the URL to load retina tiles.\n *\n * You can use custom keys in the template, which will be [evaluated](#util-template) from TileLayer options, like this:\n *\n * ```\n * L.tileLayer(\'https://{s}.somedomain.com/{foo}/{z}/{x}/{y}.png\', {foo: \'bar\'});\n * ```\n */\n\n\nexport const TileLayer = GridLayer.extend({\n\n\t// @section\n\t// @aka TileLayer options\n\toptions: {\n\t\t// @option minZoom: Number = 0\n\t\t// The minimum zoom level down to which this layer will be displayed (inclusive).\n\t\tminZoom: 0,\n\n\t\t// @option maxZoom: Number = 18\n\t\t// The maximum zoom level up to which this layer will be displayed (inclusive).\n\t\tmaxZoom: 18,\n\n\t\t// @option subdomains: String|String[] = \'abc\'\n\t\t// Subdomains of the tile service. Can be passed in the form of one string (where each letter is a subdomain name) or an array of strings.\n\t\tsubdomains: \'abc\',\n\n\t\t// @option errorTileUrl: String = \'\'\n\t\t// URL to the tile image to show in place of the tile that failed to load.\n\t\terrorTileUrl: \'\',\n\n\t\t// @option zoomOffset: Number = 0\n\t\t// The zoom number used in tile URLs will be offset with this value.\n\t\tzoomOffset: 0,\n\n\t\t// @option tms: Boolean = false\n\t\t// If `true`, inverses Y axis numbering for tiles (turn this on for [TMS](https://en.wikipedia.org/wiki/Tile_Map_Service) services).\n\t\ttms: false,\n\n\t\t// @option zoomReverse: Boolean = false\n\t\t// If set to true, the zoom number used in tile URLs will be reversed (`maxZoom - zoom` instead of `zoom`)\n\t\tzoomReverse: false,\n\n\t\t// @option detectRetina: Boolean = false\n\t\t// If `true` and user is on a retina display, it will request four tiles of half the specified size and a bigger zoom level in place of one to utilize the high resolution.\n\t\tdetectRetina: false,\n\n\t\t// @option crossOrigin: Boolean|String = false\n\t\t// Whether the crossOrigin attribute will be added to the tiles.\n\t\t// If a String is provided, all tiles will have their crossOrigin attribute set to the String provided. This is needed if you want to access tile pixel data.\n\t\t// Refer to [CORS Settings](https://developer.mozilla.org/en-US/docs/Web/HTML/CORS_settings_attributes) for valid String values.\n\t\tcrossOrigin: false,\n\n\t\t// @option referrerPolicy: Boolean|String = false\n\t\t// Whether the referrerPolicy attribute will be added to the tiles.\n\t\t// If a String is provided, all tiles will have their referrerPolicy attribute set to the String provided.\n\t\t// This may be needed if your map\'s rendering context has a strict default but your tile provider expects a valid referrer\n\t\t// (e.g. to validate an API token).\n\t\t// Refer to [HTMLImageElement.referrerPolicy](https://developer.mozilla.org/en-US/docs/Web/API/HTMLImageElement/referrerPolicy) for valid String values.\n\t\treferrerPolicy: false\n\t},\n\n\tinitialize(url, options) {\n\n\t\tthis._url = url;\n\n\t\toptions = Util.setOptions(this, options);\n\n\t\t// detecting retina displays, adjusting tileSize and zoom levels\n\t\tif (options.detectRetina && Browser.retina && options.maxZoom > 0) {\n\n\t\t\toptions.tileSize = Math.floor(options.tileSize / 2);\n\n\t\t\tif (!options.zoomReverse) {\n\t\t\t\toptions.zoomOffset++;\n\t\t\t\toptions.maxZoom = Math.max(options.minZoom, options.maxZoom - 1);\n\t\t\t} else {\n\t\t\t\toptions.zoomOffset--;\n\t\t\t\toptions.minZoom = Math.min(options.maxZoom, options.minZoom + 1);\n\t\t\t}\n\n\t\t\toptions.minZoom = Math.max(0, options.minZoom);\n\t\t} else if (!options.zoomReverse) {\n\t\t\t// make sure maxZoom is gte minZoom\n\t\t\toptions.maxZoom = Math.max(options.minZoom, options.maxZoom);\n\t\t} else {\n\t\t\t// make sure minZoom is lte maxZoom\n\t\t\toptions.minZoom = Math.min(options.maxZoom, options.minZoom);\n\t\t}\n\n\t\tif (typeof options.subdomains === \'string\') {\n\t\t\toptions.subdomains = options.subdomains.split(\'\');\n\t\t}\n\n\t\tthis.on(\'tileunload\', this._onTileRemove);\n\t},\n\n\t// @method setUrl(url: String, noRedraw?: Boolean): this\n\t// Updates the layer\'s URL template and redraws it (unless `noRedraw` is set to `true`).\n\t// If the URL does not change, the layer will not be redrawn unless\n\t// the noRedraw parameter is set to false.\n\tsetUrl(url, noRedraw) {\n\t\tif (this._url === url && noRedraw === undefined) {\n\t\t\tnoRedraw = true;\n\t\t}\n\n\t\tthis._url = url;\n\n\t\tif (!noRedraw) {\n\t\t\tthis.redraw();\n\t\t}\n\t\treturn this;\n\t\tvar bounds = this._map.getPixelBounds(),\r\n\t\t\tzoom = this._map.getZoom(),\r\n\t\t\ttileSize = this.options.tileSize;\r\n\t\t\t\r\n\t\tif (zoom > this.options.maxZoom || zoom < this.options.minZoom) {\r\n\t\t\treturn;\r\n\t\t}\r\n\t\tconst tile = document.createElement(\'img\');\n\n\t\tDomEvent.on(tile, \'load\', this._tileOnLoad.bind(this, done, tile));\n\t\tDomEvent.on(tile, \'error\', this._tileOnError.bind(this, done, tile));\n\n\t\tif (this.options.crossOrigin || this.options.crossOrigin === \'\') {\n\t\t\ttile.crossOrigin = this.options.crossOrigin === true ? \'\' : this.options.crossOrigin;\n\t\t}\n\n\t\t// for this new option we follow the documented behavior\n\t\t// more closely by only setting the property when string\n\t\tif (typeof this.options.referrerPolicy === \'string\') {\n\t\t\ttile.referrerPolicy = this.options.referrerPolicy;\n\t\t}\n\n\t\t// The alt attribute is set to the empty string,\n\t\t// allowing screen readers to ignore the decorative image tiles.\n\t\t// https://www.w3.org/WAI/tutorials/images/decorative/\n\t\t// https://www.w3.org/TR/html-aria/#el-img-empty-alt\n\t\ttile.alt = \'\';\n\n\t\ttile.src = this.getTileUrl(coords);\n\n\t\treturn tile;\n\t},\n\n\t// @section Extension methods\n\t// @uninheritable\n\t// Layers extending `TileLayer` might reimplement the following method.\n\t// @method getTileUrl(coords: Object): String\n\t// Called only internally, returns the URL for a tile given its coordinates.\n\t// Classes extending `TileLayer` can override this function to provide custom tile URL naming schemes.\n\tgetTileUrl(coords) {\n\t\tconst data = {\n\t\t\tr: Browser.retina ? \'@2x\' : \'\',\n\t\t\ts: this._getSubdomain(coords),\n\t\t\tx: coords.x,\n\t\t\ty: coords.y,\n\t\t\tz: this._getZoomForUrl()\n\t\t};\n\t\tif (this._map && !this._map.options.crs.infinite) {\n\t\t\tconst invertedY = this._globalTileRange.max.y - coords.y;\n\t\t\tif (this.options.tms) {\n\t\t\t\tdata[\'y\'] = invertedY;\n\t\t\t}\n\t\t\tdata[\'-y\'] = invertedY;\n\t\t}\n\n\t\treturn Util.template(this._url, Util.extend(data, this.options));\n\t},\n\n\t_tileOnLoad(done, tile) {\n\t\tdone(null, tile);\n\t},\n\n\t_tileOnError(done, tile, e) {\n\t\t\t\t\ttile = this._tiles[key];\r\n\t\t\t\t\tthis.fire("tileunload", {tile: tile, url: tile.src});\r\n\r\n\t\t\t\t\t// evil, don\'t do this! crashes Android 3, produces load errors, doesn\'t solve memory leaks\r\n\t\t\t\t\t// this._tiles[key].src = \'\';\r\n\r\n\t\t\t\t\t//tile.src = \'data:image/gif;base64,R0lGODlhAQABAAD/ACwAAAAAAQABAAACADs=\';\r\n\r\n\t\t\t\t\tif (tile.parentNode === this._container) {\r\n\t\t\t\t\t\tthis._container.removeChild(tile);\r\n\t\t\t\t\t}\r\n\t\t\t\t\tif (this.options.reuseTiles) {\r\n\t\t\t\t\t\tthis._unusedTiles.push(this._tiles[key]);\r\n\t\t\t\t\t}\r\n\t\t\t\t\tdelete this._tiles[key];\r\n\t\t\t\t}\r\n\t\t\t}\r\n\t\t      zoomReverse = this.options.zoomReverse,\n\t\t      zoomOffset = this.options.zoomOffset;\n\n\t\tif (zoomReverse) {\n\t\t\tzoom = maxZoom - zoom;\n\t\t}\n\n\t\treturn zoom + zoomOffset;\n\t},\n\n\t_getSubdomain(tilePoint) {\n\t\tconst index = Math.abs(tilePoint.x + tilePoint.y) % this.options.subdomains.length;\n\t\treturn this.options.subdomains[index];\n\t},\n\n\t// stops loading all tiles in the background layer\n\t_abortLoading() {\n\t\tlet i, tile;\n\t\tfor (i in this._tiles) {\n\t\t\tif (this._tiles[i].coords.z !== this._tileZoom) {\n\t\t\t\ttile = this._tiles[i].el;\n\n\t\t\t\ttile.onload = Util.falseFn;\n\t\t\t\ttile.onerror = Util.falseFn;\n\n\t\t\t\tif (!tile.complete) {\n\t\t\t\t\ttile.src = Util.emptyImageUrl;\n\t\t\t\t\tconst coords = this._tiles[i].coords;\n\t\t\t\t\tDomUtil.remove(tile);\n\t\t\t\t\tdelete this._tiles[i];\n\t\t\t\t\t// @event tileabort: TileEvent\n\t\t\t\t\t// Fired when a tile was loading but is now not wanted.\n\t\t\t\t\tthis.fire(\'tileabort\', {\n\t\t\t\t\t\ttile,\n\t\t\t\t\t\tcoords\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t},\n\n\t_removeTile(key) {\n\t\tconst tile = this._tiles[key];\n\t\tif (!tile) { return; }\n\n\t\t// Cancels any pending http requests associated with the tile\n\t\ttile.el.setAttribute(\'src\', Util.emptyImageUrl);\n\n\t\treturn GridLayer.prototype._removeTile.call(this, key);\n\t},\n\n\t_tileReady(coords, err, tile) {\n\t\tif (!this._map || (tile && tile.getAttribute(\'src\') === Util.emptyImageUrl)) {\n\t\t\treturn;\n\t\t}\n\n\t\treturn GridLayer.prototype._tileReady.call(this, coords, err, tile);\n\t}\n});\n\n\n// @factory L.tilelayer(urlTemplate: String, options?: TileLayer options)\n// Instantiates a tile layer object given a `URL template` and optionally an options object.\n\nexport function tileLayer(url, options) {\n\treturn new TileLayer(url, options);\n}\n', 'message': 'Fixed Android 3 stability issues\n', 'author_name': 'mourner', 'lang': 'js'}