text: import {GridLayer} from './GridLayer';
  import * as 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. Extends `GridLayer`.
   *
   * @example
   *
   * ```js
   * L.tileLayer('http://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png?{foo}', {foo: 'bar'}).addTo(map);
   * ```
   *
   * @section URL template
   * @example
   *
   * A string of the following form:
   *
   * ```
   * 'http://{s}.somedomain.com/blabla/{z}/{x}/{y}{r}.png'
   * ```
   *
   * `{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('http://{s}.somedomain.com/{foo}/{z}/{x}/{y}.png', {foo: 'bar'});
   * ```
   */
  
  
  export var 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 = false
  		// If true, all tiles will have their crossOrigin attribute set to ''. This is needed if you want to access tile pixel data.
  		crossOrigin: false
  	},
  
  	initialize: function (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--;
  			} else {
  				options.zoomOffset--;
  				options.minZoom++;
  			}
  
  			options.minZoom = Math.max(0, options.minZoom);
  		}
  
  		if (typeof options.subdomains === 'string') {
  			options.subdomains = options.subdomains.split('');
  		}
  
  		// for https://github.com/Leaflet/Leaflet/issues/137
  		if (!Browser.android) {
  			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`).
  	setUrl: function (url, noRedraw) {
  		this._url = url;
  
  		if (!noRedraw) {
  			this.redraw();
  		}
  		return this;
  	},
  
  	// @method createTile(coords: Object, done?: Function): HTMLElement
  	// Called only internally, overrides GridLayer's [`createTile()`](#gridlayer-createtile)
  	// to return an `<img>` HTML element with the appropriate image URL given `coords`. The `done`
  	// callback is called when the tile has been loaded.
  	createTile: function (coords, done) {
  		var tile = document.createElement('img');
  
  		DomEvent.on(tile, 'load', Util.bind(this._tileOnLoad, this, done, tile));
  		DomEvent.on(tile, 'error', Util.bind(this._tileOnError, this, done, tile));
  
  		if (this.options.crossOrigin) {
  			tile.crossOrigin = '';
  		}
  
  		/*
  		 Alt tag is set to empty string to keep screen readers from reading URL and for compliance reasons
  		 http://www.w3.org/TR/WCAG20-TECHS/H67
  		*/
  		tile.alt = '';
  
  		/*
  		 Set role="presentation" to force screen readers to ignore this
  		 https://www.w3.org/TR/wai-aria/roles#textalternativecomputation
  		*/
  		tile.setAttribute('role', 'presentation');
  
  		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: function (coords) {
  		var data = {
  			r: Browser.retina ? '@2x' : '',
  			s: this._getSubdomain(coords),
  			x: coords.x,
  			y: coords.y,
  			z: coords.z ? coords.z : this._getZoomForUrl()
  		};
  		if (this._map && !this._map.options.crs.infinite) {
  			var 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: function (done, tile) {
  		// For https://github.com/Leaflet/Leaflet/issues/3332
  		if (Browser.ielt9) {
  			setTimeout(Util.bind(done, this, null, tile), 0);
  		} else {
  			done(null, tile);
  		}
  	},
  
  	_tileOnError: function (done, tile, e) {
  		var errorUrl = this.options.errorTileUrl;
  		if (errorUrl && tile.getAttribute('src') !== errorUrl) {
  			tile.src = errorUrl;
  		}
  		done(e, tile);
  	},
  
  	_onTileRemove: function (e) {
  		e.tile.onload = null;
  	},
  
  	_getZoomForUrl: function () {
  		var zoom = this._tileZoom,
  		maxZoom = this.options.maxZoom,
  		zoomReverse = this.options.zoomReverse,
  		zoomOffset = this.options.zoomOffset;
  
  		if (zoomReverse) {
  			zoom = maxZoom - zoom;
  		}
  
  		return zoom + zoomOffset;
  	},
  
  	_getSubdomain: function (tilePoint) {
  		var 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: function () {
  		var 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;
  					DomUtil.remove(tile);
  					delete this._tiles[i];
  				}
  			}
  		}
  	}
  });
  
  
  // @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);
  }
  