src/pyams_gis/resources/js/leaflet-1.0.3.js
changeset 0 c73bb834ccbe
equal deleted inserted replaced
-1:000000000000 0:c73bb834ccbe
       
     1 /*
       
     2  Leaflet 1.0.3+ed36a04, a JS library for interactive maps. http://leafletjs.com
       
     3  (c) 2010-2016 Vladimir Agafonkin, (c) 2010-2011 CloudMade
       
     4 */
       
     5 (function (window, document, undefined) {
       
     6 var L = {
       
     7 	version: "1.0.3+ed36a04"
       
     8 };
       
     9 
       
    10 function expose() {
       
    11 	var oldL = window.L;
       
    12 
       
    13 	L.noConflict = function () {
       
    14 		window.L = oldL;
       
    15 		return this;
       
    16 	};
       
    17 
       
    18 	window.L = L;
       
    19 }
       
    20 
       
    21 // define Leaflet for Node module pattern loaders, including Browserify
       
    22 if (typeof module === 'object' && typeof module.exports === 'object') {
       
    23 	module.exports = L;
       
    24 
       
    25 // define Leaflet as an AMD module
       
    26 } else if (typeof define === 'function' && define.amd) {
       
    27 	define(L);
       
    28 }
       
    29 
       
    30 // define Leaflet as a global L variable, saving the original L to restore later if needed
       
    31 if (typeof window !== 'undefined') {
       
    32 	expose();
       
    33 }
       
    34 
       
    35 
       
    36 
       
    37 /*
       
    38  * @namespace Util
       
    39  *
       
    40  * Various utility functions, used by Leaflet internally.
       
    41  */
       
    42 
       
    43 L.Util = {
       
    44 
       
    45 	// @function extend(dest: Object, src?: Object): Object
       
    46 	// Merges the properties of the `src` object (or multiple objects) into `dest` object and returns the latter. Has an `L.extend` shortcut.
       
    47 	extend: function (dest) {
       
    48 		var i, j, len, src;
       
    49 
       
    50 		for (j = 1, len = arguments.length; j < len; j++) {
       
    51 			src = arguments[j];
       
    52 			for (i in src) {
       
    53 				dest[i] = src[i];
       
    54 			}
       
    55 		}
       
    56 		return dest;
       
    57 	},
       
    58 
       
    59 	// @function create(proto: Object, properties?: Object): Object
       
    60 	// Compatibility polyfill for [Object.create](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Object/create)
       
    61 	create: Object.create || (function () {
       
    62 		function F() {}
       
    63 		return function (proto) {
       
    64 			F.prototype = proto;
       
    65 			return new F();
       
    66 		};
       
    67 	})(),
       
    68 
       
    69 	// @function bind(fn: Function, …): Function
       
    70 	// Returns a new function bound to the arguments passed, like [Function.prototype.bind](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Function/bind).
       
    71 	// Has a `L.bind()` shortcut.
       
    72 	bind: function (fn, obj) {
       
    73 		var slice = Array.prototype.slice;
       
    74 
       
    75 		if (fn.bind) {
       
    76 			return fn.bind.apply(fn, slice.call(arguments, 1));
       
    77 		}
       
    78 
       
    79 		var args = slice.call(arguments, 2);
       
    80 
       
    81 		return function () {
       
    82 			return fn.apply(obj, args.length ? args.concat(slice.call(arguments)) : arguments);
       
    83 		};
       
    84 	},
       
    85 
       
    86 	// @function stamp(obj: Object): Number
       
    87 	// Returns the unique ID of an object, assiging it one if it doesn't have it.
       
    88 	stamp: function (obj) {
       
    89 		/*eslint-disable */
       
    90 		obj._leaflet_id = obj._leaflet_id || ++L.Util.lastId;
       
    91 		return obj._leaflet_id;
       
    92 		/*eslint-enable */
       
    93 	},
       
    94 
       
    95 	// @property lastId: Number
       
    96 	// Last unique ID used by [`stamp()`](#util-stamp)
       
    97 	lastId: 0,
       
    98 
       
    99 	// @function throttle(fn: Function, time: Number, context: Object): Function
       
   100 	// Returns a function which executes function `fn` with the given scope `context`
       
   101 	// (so that the `this` keyword refers to `context` inside `fn`'s code). The function
       
   102 	// `fn` will be called no more than one time per given amount of `time`. The arguments
       
   103 	// received by the bound function will be any arguments passed when binding the
       
   104 	// function, followed by any arguments passed when invoking the bound function.
       
   105 	// Has an `L.bind` shortcut.
       
   106 	throttle: function (fn, time, context) {
       
   107 		var lock, args, wrapperFn, later;
       
   108 
       
   109 		later = function () {
       
   110 			// reset lock and call if queued
       
   111 			lock = false;
       
   112 			if (args) {
       
   113 				wrapperFn.apply(context, args);
       
   114 				args = false;
       
   115 			}
       
   116 		};
       
   117 
       
   118 		wrapperFn = function () {
       
   119 			if (lock) {
       
   120 				// called too soon, queue to call later
       
   121 				args = arguments;
       
   122 
       
   123 			} else {
       
   124 				// call and lock until later
       
   125 				fn.apply(context, arguments);
       
   126 				setTimeout(later, time);
       
   127 				lock = true;
       
   128 			}
       
   129 		};
       
   130 
       
   131 		return wrapperFn;
       
   132 	},
       
   133 
       
   134 	// @function wrapNum(num: Number, range: Number[], includeMax?: Boolean): Number
       
   135 	// Returns the number `num` modulo `range` in such a way so it lies within
       
   136 	// `range[0]` and `range[1]`. The returned value will be always smaller than
       
   137 	// `range[1]` unless `includeMax` is set to `true`.
       
   138 	wrapNum: function (x, range, includeMax) {
       
   139 		var max = range[1],
       
   140 		    min = range[0],
       
   141 		    d = max - min;
       
   142 		return x === max && includeMax ? x : ((x - min) % d + d) % d + min;
       
   143 	},
       
   144 
       
   145 	// @function falseFn(): Function
       
   146 	// Returns a function which always returns `false`.
       
   147 	falseFn: function () { return false; },
       
   148 
       
   149 	// @function formatNum(num: Number, digits?: Number): Number
       
   150 	// Returns the number `num` rounded to `digits` decimals, or to 5 decimals by default.
       
   151 	formatNum: function (num, digits) {
       
   152 		var pow = Math.pow(10, digits || 5);
       
   153 		return Math.round(num * pow) / pow;
       
   154 	},
       
   155 
       
   156 	// @function trim(str: String): String
       
   157 	// Compatibility polyfill for [String.prototype.trim](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String/Trim)
       
   158 	trim: function (str) {
       
   159 		return str.trim ? str.trim() : str.replace(/^\s+|\s+$/g, '');
       
   160 	},
       
   161 
       
   162 	// @function splitWords(str: String): String[]
       
   163 	// Trims and splits the string on whitespace and returns the array of parts.
       
   164 	splitWords: function (str) {
       
   165 		return L.Util.trim(str).split(/\s+/);
       
   166 	},
       
   167 
       
   168 	// @function setOptions(obj: Object, options: Object): Object
       
   169 	// Merges the given properties to the `options` of the `obj` object, returning the resulting options. See `Class options`. Has an `L.setOptions` shortcut.
       
   170 	setOptions: function (obj, options) {
       
   171 		if (!obj.hasOwnProperty('options')) {
       
   172 			obj.options = obj.options ? L.Util.create(obj.options) : {};
       
   173 		}
       
   174 		for (var i in options) {
       
   175 			obj.options[i] = options[i];
       
   176 		}
       
   177 		return obj.options;
       
   178 	},
       
   179 
       
   180 	// @function getParamString(obj: Object, existingUrl?: String, uppercase?: Boolean): String
       
   181 	// Converts an object into a parameter URL string, e.g. `{a: "foo", b: "bar"}`
       
   182 	// translates to `'?a=foo&b=bar'`. If `existingUrl` is set, the parameters will
       
   183 	// be appended at the end. If `uppercase` is `true`, the parameter names will
       
   184 	// be uppercased (e.g. `'?A=foo&B=bar'`)
       
   185 	getParamString: function (obj, existingUrl, uppercase) {
       
   186 		var params = [];
       
   187 		for (var i in obj) {
       
   188 			params.push(encodeURIComponent(uppercase ? i.toUpperCase() : i) + '=' + encodeURIComponent(obj[i]));
       
   189 		}
       
   190 		return ((!existingUrl || existingUrl.indexOf('?') === -1) ? '?' : '&') + params.join('&');
       
   191 	},
       
   192 
       
   193 	// @function template(str: String, data: Object): String
       
   194 	// Simple templating facility, accepts a template string of the form `'Hello {a}, {b}'`
       
   195 	// and a data object like `{a: 'foo', b: 'bar'}`, returns evaluated string
       
   196 	// `('Hello foo, bar')`. You can also specify functions instead of strings for
       
   197 	// data values — they will be evaluated passing `data` as an argument.
       
   198 	template: function (str, data) {
       
   199 		return str.replace(L.Util.templateRe, function (str, key) {
       
   200 			var value = data[key];
       
   201 
       
   202 			if (value === undefined) {
       
   203 				throw new Error('No value provided for variable ' + str);
       
   204 
       
   205 			} else if (typeof value === 'function') {
       
   206 				value = value(data);
       
   207 			}
       
   208 			return value;
       
   209 		});
       
   210 	},
       
   211 
       
   212 	templateRe: /\{ *([\w_\-]+) *\}/g,
       
   213 
       
   214 	// @function isArray(obj): Boolean
       
   215 	// Compatibility polyfill for [Array.isArray](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Array/isArray)
       
   216 	isArray: Array.isArray || function (obj) {
       
   217 		return (Object.prototype.toString.call(obj) === '[object Array]');
       
   218 	},
       
   219 
       
   220 	// @function indexOf(array: Array, el: Object): Number
       
   221 	// Compatibility polyfill for [Array.prototype.indexOf](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Array/indexOf)
       
   222 	indexOf: function (array, el) {
       
   223 		for (var i = 0; i < array.length; i++) {
       
   224 			if (array[i] === el) { return i; }
       
   225 		}
       
   226 		return -1;
       
   227 	},
       
   228 
       
   229 	// @property emptyImageUrl: String
       
   230 	// Data URI string containing a base64-encoded empty GIF image.
       
   231 	// Used as a hack to free memory from unused images on WebKit-powered
       
   232 	// mobile devices (by setting image `src` to this string).
       
   233 	emptyImageUrl: 'data:image/gif;base64,R0lGODlhAQABAAD/ACwAAAAAAQABAAACADs='
       
   234 };
       
   235 
       
   236 (function () {
       
   237 	// inspired by http://paulirish.com/2011/requestanimationframe-for-smart-animating/
       
   238 
       
   239 	function getPrefixed(name) {
       
   240 		return window['webkit' + name] || window['moz' + name] || window['ms' + name];
       
   241 	}
       
   242 
       
   243 	var lastTime = 0;
       
   244 
       
   245 	// fallback for IE 7-8
       
   246 	function timeoutDefer(fn) {
       
   247 		var time = +new Date(),
       
   248 		    timeToCall = Math.max(0, 16 - (time - lastTime));
       
   249 
       
   250 		lastTime = time + timeToCall;
       
   251 		return window.setTimeout(fn, timeToCall);
       
   252 	}
       
   253 
       
   254 	var requestFn = window.requestAnimationFrame || getPrefixed('RequestAnimationFrame') || timeoutDefer,
       
   255 	    cancelFn = window.cancelAnimationFrame || getPrefixed('CancelAnimationFrame') ||
       
   256 	               getPrefixed('CancelRequestAnimationFrame') || function (id) { window.clearTimeout(id); };
       
   257 
       
   258 
       
   259 	// @function requestAnimFrame(fn: Function, context?: Object, immediate?: Boolean): Number
       
   260 	// Schedules `fn` to be executed when the browser repaints. `fn` is bound to
       
   261 	// `context` if given. When `immediate` is set, `fn` is called immediately if
       
   262 	// the browser doesn't have native support for
       
   263 	// [`window.requestAnimationFrame`](https://developer.mozilla.org/docs/Web/API/window/requestAnimationFrame),
       
   264 	// otherwise it's delayed. Returns a request ID that can be used to cancel the request.
       
   265 	L.Util.requestAnimFrame = function (fn, context, immediate) {
       
   266 		if (immediate && requestFn === timeoutDefer) {
       
   267 			fn.call(context);
       
   268 		} else {
       
   269 			return requestFn.call(window, L.bind(fn, context));
       
   270 		}
       
   271 	};
       
   272 
       
   273 	// @function cancelAnimFrame(id: Number): undefined
       
   274 	// Cancels a previous `requestAnimFrame`. See also [window.cancelAnimationFrame](https://developer.mozilla.org/docs/Web/API/window/cancelAnimationFrame).
       
   275 	L.Util.cancelAnimFrame = function (id) {
       
   276 		if (id) {
       
   277 			cancelFn.call(window, id);
       
   278 		}
       
   279 	};
       
   280 })();
       
   281 
       
   282 // shortcuts for most used utility functions
       
   283 L.extend = L.Util.extend;
       
   284 L.bind = L.Util.bind;
       
   285 L.stamp = L.Util.stamp;
       
   286 L.setOptions = L.Util.setOptions;
       
   287 
       
   288 
       
   289 
       
   290 
       
   291 // @class Class
       
   292 // @aka L.Class
       
   293 
       
   294 // @section
       
   295 // @uninheritable
       
   296 
       
   297 // Thanks to John Resig and Dean Edwards for inspiration!
       
   298 
       
   299 L.Class = function () {};
       
   300 
       
   301 L.Class.extend = function (props) {
       
   302 
       
   303 	// @function extend(props: Object): Function
       
   304 	// [Extends the current class](#class-inheritance) given the properties to be included.
       
   305 	// Returns a Javascript function that is a class constructor (to be called with `new`).
       
   306 	var NewClass = function () {
       
   307 
       
   308 		// call the constructor
       
   309 		if (this.initialize) {
       
   310 			this.initialize.apply(this, arguments);
       
   311 		}
       
   312 
       
   313 		// call all constructor hooks
       
   314 		this.callInitHooks();
       
   315 	};
       
   316 
       
   317 	var parentProto = NewClass.__super__ = this.prototype;
       
   318 
       
   319 	var proto = L.Util.create(parentProto);
       
   320 	proto.constructor = NewClass;
       
   321 
       
   322 	NewClass.prototype = proto;
       
   323 
       
   324 	// inherit parent's statics
       
   325 	for (var i in this) {
       
   326 		if (this.hasOwnProperty(i) && i !== 'prototype') {
       
   327 			NewClass[i] = this[i];
       
   328 		}
       
   329 	}
       
   330 
       
   331 	// mix static properties into the class
       
   332 	if (props.statics) {
       
   333 		L.extend(NewClass, props.statics);
       
   334 		delete props.statics;
       
   335 	}
       
   336 
       
   337 	// mix includes into the prototype
       
   338 	if (props.includes) {
       
   339 		L.Util.extend.apply(null, [proto].concat(props.includes));
       
   340 		delete props.includes;
       
   341 	}
       
   342 
       
   343 	// merge options
       
   344 	if (proto.options) {
       
   345 		props.options = L.Util.extend(L.Util.create(proto.options), props.options);
       
   346 	}
       
   347 
       
   348 	// mix given properties into the prototype
       
   349 	L.extend(proto, props);
       
   350 
       
   351 	proto._initHooks = [];
       
   352 
       
   353 	// add method for calling all hooks
       
   354 	proto.callInitHooks = function () {
       
   355 
       
   356 		if (this._initHooksCalled) { return; }
       
   357 
       
   358 		if (parentProto.callInitHooks) {
       
   359 			parentProto.callInitHooks.call(this);
       
   360 		}
       
   361 
       
   362 		this._initHooksCalled = true;
       
   363 
       
   364 		for (var i = 0, len = proto._initHooks.length; i < len; i++) {
       
   365 			proto._initHooks[i].call(this);
       
   366 		}
       
   367 	};
       
   368 
       
   369 	return NewClass;
       
   370 };
       
   371 
       
   372 
       
   373 // @function include(properties: Object): this
       
   374 // [Includes a mixin](#class-includes) into the current class.
       
   375 L.Class.include = function (props) {
       
   376 	L.extend(this.prototype, props);
       
   377 	return this;
       
   378 };
       
   379 
       
   380 // @function mergeOptions(options: Object): this
       
   381 // [Merges `options`](#class-options) into the defaults of the class.
       
   382 L.Class.mergeOptions = function (options) {
       
   383 	L.extend(this.prototype.options, options);
       
   384 	return this;
       
   385 };
       
   386 
       
   387 // @function addInitHook(fn: Function): this
       
   388 // Adds a [constructor hook](#class-constructor-hooks) to the class.
       
   389 L.Class.addInitHook = function (fn) { // (Function) || (String, args...)
       
   390 	var args = Array.prototype.slice.call(arguments, 1);
       
   391 
       
   392 	var init = typeof fn === 'function' ? fn : function () {
       
   393 		this[fn].apply(this, args);
       
   394 	};
       
   395 
       
   396 	this.prototype._initHooks = this.prototype._initHooks || [];
       
   397 	this.prototype._initHooks.push(init);
       
   398 	return this;
       
   399 };
       
   400 
       
   401 
       
   402 
       
   403 /*
       
   404  * @class Evented
       
   405  * @aka L.Evented
       
   406  * @inherits Class
       
   407  *
       
   408  * A set of methods shared between event-powered classes (like `Map` and `Marker`). Generally, events allow you to execute some function when something happens with an object (e.g. the user clicks on the map, causing the map to fire `'click'` event).
       
   409  *
       
   410  * @example
       
   411  *
       
   412  * ```js
       
   413  * map.on('click', function(e) {
       
   414  * 	alert(e.latlng);
       
   415  * } );
       
   416  * ```
       
   417  *
       
   418  * Leaflet deals with event listeners by reference, so if you want to add a listener and then remove it, define it as a function:
       
   419  *
       
   420  * ```js
       
   421  * function onClick(e) { ... }
       
   422  *
       
   423  * map.on('click', onClick);
       
   424  * map.off('click', onClick);
       
   425  * ```
       
   426  */
       
   427 
       
   428 
       
   429 L.Evented = L.Class.extend({
       
   430 
       
   431 	/* @method on(type: String, fn: Function, context?: Object): this
       
   432 	 * Adds a listener function (`fn`) to a particular event type of the object. You can optionally specify the context of the listener (object the this keyword will point to). You can also pass several space-separated types (e.g. `'click dblclick'`).
       
   433 	 *
       
   434 	 * @alternative
       
   435 	 * @method on(eventMap: Object): this
       
   436 	 * Adds a set of type/listener pairs, e.g. `{click: onClick, mousemove: onMouseMove}`
       
   437 	 */
       
   438 	on: function (types, fn, context) {
       
   439 
       
   440 		// types can be a map of types/handlers
       
   441 		if (typeof types === 'object') {
       
   442 			for (var type in types) {
       
   443 				// we don't process space-separated events here for performance;
       
   444 				// it's a hot path since Layer uses the on(obj) syntax
       
   445 				this._on(type, types[type], fn);
       
   446 			}
       
   447 
       
   448 		} else {
       
   449 			// types can be a string of space-separated words
       
   450 			types = L.Util.splitWords(types);
       
   451 
       
   452 			for (var i = 0, len = types.length; i < len; i++) {
       
   453 				this._on(types[i], fn, context);
       
   454 			}
       
   455 		}
       
   456 
       
   457 		return this;
       
   458 	},
       
   459 
       
   460 	/* @method off(type: String, fn?: Function, context?: Object): this
       
   461 	 * Removes a previously added listener function. If no function is specified, it will remove all the listeners of that particular event from the object. Note that if you passed a custom context to `on`, you must pass the same context to `off` in order to remove the listener.
       
   462 	 *
       
   463 	 * @alternative
       
   464 	 * @method off(eventMap: Object): this
       
   465 	 * Removes a set of type/listener pairs.
       
   466 	 *
       
   467 	 * @alternative
       
   468 	 * @method off: this
       
   469 	 * Removes all listeners to all events on the object.
       
   470 	 */
       
   471 	off: function (types, fn, context) {
       
   472 
       
   473 		if (!types) {
       
   474 			// clear all listeners if called without arguments
       
   475 			delete this._events;
       
   476 
       
   477 		} else if (typeof types === 'object') {
       
   478 			for (var type in types) {
       
   479 				this._off(type, types[type], fn);
       
   480 			}
       
   481 
       
   482 		} else {
       
   483 			types = L.Util.splitWords(types);
       
   484 
       
   485 			for (var i = 0, len = types.length; i < len; i++) {
       
   486 				this._off(types[i], fn, context);
       
   487 			}
       
   488 		}
       
   489 
       
   490 		return this;
       
   491 	},
       
   492 
       
   493 	// attach listener (without syntactic sugar now)
       
   494 	_on: function (type, fn, context) {
       
   495 		this._events = this._events || {};
       
   496 
       
   497 		/* get/init listeners for type */
       
   498 		var typeListeners = this._events[type];
       
   499 		if (!typeListeners) {
       
   500 			typeListeners = [];
       
   501 			this._events[type] = typeListeners;
       
   502 		}
       
   503 
       
   504 		if (context === this) {
       
   505 			// Less memory footprint.
       
   506 			context = undefined;
       
   507 		}
       
   508 		var newListener = {fn: fn, ctx: context},
       
   509 		    listeners = typeListeners;
       
   510 
       
   511 		// check if fn already there
       
   512 		for (var i = 0, len = listeners.length; i < len; i++) {
       
   513 			if (listeners[i].fn === fn && listeners[i].ctx === context) {
       
   514 				return;
       
   515 			}
       
   516 		}
       
   517 
       
   518 		listeners.push(newListener);
       
   519 	},
       
   520 
       
   521 	_off: function (type, fn, context) {
       
   522 		var listeners,
       
   523 		    i,
       
   524 		    len;
       
   525 
       
   526 		if (!this._events) { return; }
       
   527 
       
   528 		listeners = this._events[type];
       
   529 
       
   530 		if (!listeners) {
       
   531 			return;
       
   532 		}
       
   533 
       
   534 		if (!fn) {
       
   535 			// Set all removed listeners to noop so they are not called if remove happens in fire
       
   536 			for (i = 0, len = listeners.length; i < len; i++) {
       
   537 				listeners[i].fn = L.Util.falseFn;
       
   538 			}
       
   539 			// clear all listeners for a type if function isn't specified
       
   540 			delete this._events[type];
       
   541 			return;
       
   542 		}
       
   543 
       
   544 		if (context === this) {
       
   545 			context = undefined;
       
   546 		}
       
   547 
       
   548 		if (listeners) {
       
   549 
       
   550 			// find fn and remove it
       
   551 			for (i = 0, len = listeners.length; i < len; i++) {
       
   552 				var l = listeners[i];
       
   553 				if (l.ctx !== context) { continue; }
       
   554 				if (l.fn === fn) {
       
   555 
       
   556 					// set the removed listener to noop so that's not called if remove happens in fire
       
   557 					l.fn = L.Util.falseFn;
       
   558 
       
   559 					if (this._firingCount) {
       
   560 						/* copy array in case events are being fired */
       
   561 						this._events[type] = listeners = listeners.slice();
       
   562 					}
       
   563 					listeners.splice(i, 1);
       
   564 
       
   565 					return;
       
   566 				}
       
   567 			}
       
   568 		}
       
   569 	},
       
   570 
       
   571 	// @method fire(type: String, data?: Object, propagate?: Boolean): this
       
   572 	// Fires an event of the specified type. You can optionally provide an data
       
   573 	// object — the first argument of the listener function will contain its
       
   574 	// properties. The event can optionally be propagated to event parents.
       
   575 	fire: function (type, data, propagate) {
       
   576 		if (!this.listens(type, propagate)) { return this; }
       
   577 
       
   578 		var event = L.Util.extend({}, data, {type: type, target: this});
       
   579 
       
   580 		if (this._events) {
       
   581 			var listeners = this._events[type];
       
   582 
       
   583 			if (listeners) {
       
   584 				this._firingCount = (this._firingCount + 1) || 1;
       
   585 				for (var i = 0, len = listeners.length; i < len; i++) {
       
   586 					var l = listeners[i];
       
   587 					l.fn.call(l.ctx || this, event);
       
   588 				}
       
   589 
       
   590 				this._firingCount--;
       
   591 			}
       
   592 		}
       
   593 
       
   594 		if (propagate) {
       
   595 			// propagate the event to parents (set with addEventParent)
       
   596 			this._propagateEvent(event);
       
   597 		}
       
   598 
       
   599 		return this;
       
   600 	},
       
   601 
       
   602 	// @method listens(type: String): Boolean
       
   603 	// Returns `true` if a particular event type has any listeners attached to it.
       
   604 	listens: function (type, propagate) {
       
   605 		var listeners = this._events && this._events[type];
       
   606 		if (listeners && listeners.length) { return true; }
       
   607 
       
   608 		if (propagate) {
       
   609 			// also check parents for listeners if event propagates
       
   610 			for (var id in this._eventParents) {
       
   611 				if (this._eventParents[id].listens(type, propagate)) { return true; }
       
   612 			}
       
   613 		}
       
   614 		return false;
       
   615 	},
       
   616 
       
   617 	// @method once(…): this
       
   618 	// Behaves as [`on(…)`](#evented-on), except the listener will only get fired once and then removed.
       
   619 	once: function (types, fn, context) {
       
   620 
       
   621 		if (typeof types === 'object') {
       
   622 			for (var type in types) {
       
   623 				this.once(type, types[type], fn);
       
   624 			}
       
   625 			return this;
       
   626 		}
       
   627 
       
   628 		var handler = L.bind(function () {
       
   629 			this
       
   630 			    .off(types, fn, context)
       
   631 			    .off(types, handler, context);
       
   632 		}, this);
       
   633 
       
   634 		// add a listener that's executed once and removed after that
       
   635 		return this
       
   636 		    .on(types, fn, context)
       
   637 		    .on(types, handler, context);
       
   638 	},
       
   639 
       
   640 	// @method addEventParent(obj: Evented): this
       
   641 	// Adds an event parent - an `Evented` that will receive propagated events
       
   642 	addEventParent: function (obj) {
       
   643 		this._eventParents = this._eventParents || {};
       
   644 		this._eventParents[L.stamp(obj)] = obj;
       
   645 		return this;
       
   646 	},
       
   647 
       
   648 	// @method removeEventParent(obj: Evented): this
       
   649 	// Removes an event parent, so it will stop receiving propagated events
       
   650 	removeEventParent: function (obj) {
       
   651 		if (this._eventParents) {
       
   652 			delete this._eventParents[L.stamp(obj)];
       
   653 		}
       
   654 		return this;
       
   655 	},
       
   656 
       
   657 	_propagateEvent: function (e) {
       
   658 		for (var id in this._eventParents) {
       
   659 			this._eventParents[id].fire(e.type, L.extend({layer: e.target}, e), true);
       
   660 		}
       
   661 	}
       
   662 });
       
   663 
       
   664 var proto = L.Evented.prototype;
       
   665 
       
   666 // aliases; we should ditch those eventually
       
   667 
       
   668 // @method addEventListener(…): this
       
   669 // Alias to [`on(…)`](#evented-on)
       
   670 proto.addEventListener = proto.on;
       
   671 
       
   672 // @method removeEventListener(…): this
       
   673 // Alias to [`off(…)`](#evented-off)
       
   674 
       
   675 // @method clearAllEventListeners(…): this
       
   676 // Alias to [`off()`](#evented-off)
       
   677 proto.removeEventListener = proto.clearAllEventListeners = proto.off;
       
   678 
       
   679 // @method addOneTimeEventListener(…): this
       
   680 // Alias to [`once(…)`](#evented-once)
       
   681 proto.addOneTimeEventListener = proto.once;
       
   682 
       
   683 // @method fireEvent(…): this
       
   684 // Alias to [`fire(…)`](#evented-fire)
       
   685 proto.fireEvent = proto.fire;
       
   686 
       
   687 // @method hasEventListeners(…): Boolean
       
   688 // Alias to [`listens(…)`](#evented-listens)
       
   689 proto.hasEventListeners = proto.listens;
       
   690 
       
   691 L.Mixin = {Events: proto};
       
   692 
       
   693 
       
   694 
       
   695 /*
       
   696  * @namespace Browser
       
   697  * @aka L.Browser
       
   698  *
       
   699  * A namespace with static properties for browser/feature detection used by Leaflet internally.
       
   700  *
       
   701  * @example
       
   702  *
       
   703  * ```js
       
   704  * if (L.Browser.ielt9) {
       
   705  *   alert('Upgrade your browser, dude!');
       
   706  * }
       
   707  * ```
       
   708  */
       
   709 
       
   710 (function () {
       
   711 
       
   712 	var ua = navigator.userAgent.toLowerCase(),
       
   713 	    doc = document.documentElement,
       
   714 
       
   715 	    ie = 'ActiveXObject' in window,
       
   716 
       
   717 	    webkit    = ua.indexOf('webkit') !== -1,
       
   718 	    phantomjs = ua.indexOf('phantom') !== -1,
       
   719 	    android23 = ua.search('android [23]') !== -1,
       
   720 	    chrome    = ua.indexOf('chrome') !== -1,
       
   721 	    gecko     = ua.indexOf('gecko') !== -1  && !webkit && !window.opera && !ie,
       
   722 
       
   723 	    win = navigator.platform.indexOf('Win') === 0,
       
   724 
       
   725 	    mobile = typeof orientation !== 'undefined' || ua.indexOf('mobile') !== -1,
       
   726 	    msPointer = !window.PointerEvent && window.MSPointerEvent,
       
   727 	    pointer = window.PointerEvent || msPointer,
       
   728 
       
   729 	    ie3d = ie && ('transition' in doc.style),
       
   730 	    webkit3d = ('WebKitCSSMatrix' in window) && ('m11' in new window.WebKitCSSMatrix()) && !android23,
       
   731 	    gecko3d = 'MozPerspective' in doc.style,
       
   732 	    opera12 = 'OTransition' in doc.style;
       
   733 
       
   734 
       
   735 	var touch = !window.L_NO_TOUCH && (pointer || 'ontouchstart' in window ||
       
   736 			(window.DocumentTouch && document instanceof window.DocumentTouch));
       
   737 
       
   738 	L.Browser = {
       
   739 
       
   740 		// @property ie: Boolean
       
   741 		// `true` for all Internet Explorer versions (not Edge).
       
   742 		ie: ie,
       
   743 
       
   744 		// @property ielt9: Boolean
       
   745 		// `true` for Internet Explorer versions less than 9.
       
   746 		ielt9: ie && !document.addEventListener,
       
   747 
       
   748 		// @property edge: Boolean
       
   749 		// `true` for the Edge web browser.
       
   750 		edge: 'msLaunchUri' in navigator && !('documentMode' in document),
       
   751 
       
   752 		// @property webkit: Boolean
       
   753 		// `true` for webkit-based browsers like Chrome and Safari (including mobile versions).
       
   754 		webkit: webkit,
       
   755 
       
   756 		// @property gecko: Boolean
       
   757 		// `true` for gecko-based browsers like Firefox.
       
   758 		gecko: gecko,
       
   759 
       
   760 		// @property android: Boolean
       
   761 		// `true` for any browser running on an Android platform.
       
   762 		android: ua.indexOf('android') !== -1,
       
   763 
       
   764 		// @property android23: Boolean
       
   765 		// `true` for browsers running on Android 2 or Android 3.
       
   766 		android23: android23,
       
   767 
       
   768 		// @property chrome: Boolean
       
   769 		// `true` for the Chrome browser.
       
   770 		chrome: chrome,
       
   771 
       
   772 		// @property safari: Boolean
       
   773 		// `true` for the Safari browser.
       
   774 		safari: !chrome && ua.indexOf('safari') !== -1,
       
   775 
       
   776 
       
   777 		// @property win: Boolean
       
   778 		// `true` when the browser is running in a Windows platform
       
   779 		win: win,
       
   780 
       
   781 
       
   782 		// @property ie3d: Boolean
       
   783 		// `true` for all Internet Explorer versions supporting CSS transforms.
       
   784 		ie3d: ie3d,
       
   785 
       
   786 		// @property webkit3d: Boolean
       
   787 		// `true` for webkit-based browsers supporting CSS transforms.
       
   788 		webkit3d: webkit3d,
       
   789 
       
   790 		// @property gecko3d: Boolean
       
   791 		// `true` for gecko-based browsers supporting CSS transforms.
       
   792 		gecko3d: gecko3d,
       
   793 
       
   794 		// @property opera12: Boolean
       
   795 		// `true` for the Opera browser supporting CSS transforms (version 12 or later).
       
   796 		opera12: opera12,
       
   797 
       
   798 		// @property any3d: Boolean
       
   799 		// `true` for all browsers supporting CSS transforms.
       
   800 		any3d: !window.L_DISABLE_3D && (ie3d || webkit3d || gecko3d) && !opera12 && !phantomjs,
       
   801 
       
   802 
       
   803 		// @property mobile: Boolean
       
   804 		// `true` for all browsers running in a mobile device.
       
   805 		mobile: mobile,
       
   806 
       
   807 		// @property mobileWebkit: Boolean
       
   808 		// `true` for all webkit-based browsers in a mobile device.
       
   809 		mobileWebkit: mobile && webkit,
       
   810 
       
   811 		// @property mobileWebkit3d: Boolean
       
   812 		// `true` for all webkit-based browsers in a mobile device supporting CSS transforms.
       
   813 		mobileWebkit3d: mobile && webkit3d,
       
   814 
       
   815 		// @property mobileOpera: Boolean
       
   816 		// `true` for the Opera browser in a mobile device.
       
   817 		mobileOpera: mobile && window.opera,
       
   818 
       
   819 		// @property mobileGecko: Boolean
       
   820 		// `true` for gecko-based browsers running in a mobile device.
       
   821 		mobileGecko: mobile && gecko,
       
   822 
       
   823 
       
   824 		// @property touch: Boolean
       
   825 		// `true` for all browsers supporting [touch events](https://developer.mozilla.org/docs/Web/API/Touch_events).
       
   826 		// This does not necessarily mean that the browser is running in a computer with
       
   827 		// a touchscreen, it only means that the browser is capable of understanding
       
   828 		// touch events.
       
   829 		touch: !!touch,
       
   830 
       
   831 		// @property msPointer: Boolean
       
   832 		// `true` for browsers implementing the Microsoft touch events model (notably IE10).
       
   833 		msPointer: !!msPointer,
       
   834 
       
   835 		// @property pointer: Boolean
       
   836 		// `true` for all browsers supporting [pointer events](https://msdn.microsoft.com/en-us/library/dn433244%28v=vs.85%29.aspx).
       
   837 		pointer: !!pointer,
       
   838 
       
   839 
       
   840 		// @property retina: Boolean
       
   841 		// `true` for browsers on a high-resolution "retina" screen.
       
   842 		retina: (window.devicePixelRatio || (window.screen.deviceXDPI / window.screen.logicalXDPI)) > 1
       
   843 	};
       
   844 
       
   845 }());
       
   846 
       
   847 
       
   848 
       
   849 /*
       
   850  * @class Point
       
   851  * @aka L.Point
       
   852  *
       
   853  * Represents a point with `x` and `y` coordinates in pixels.
       
   854  *
       
   855  * @example
       
   856  *
       
   857  * ```js
       
   858  * var point = L.point(200, 300);
       
   859  * ```
       
   860  *
       
   861  * All Leaflet methods and options that accept `Point` objects also accept them in a simple Array form (unless noted otherwise), so these lines are equivalent:
       
   862  *
       
   863  * ```js
       
   864  * map.panBy([200, 300]);
       
   865  * map.panBy(L.point(200, 300));
       
   866  * ```
       
   867  */
       
   868 
       
   869 L.Point = function (x, y, round) {
       
   870 	// @property x: Number; The `x` coordinate of the point
       
   871 	this.x = (round ? Math.round(x) : x);
       
   872 	// @property y: Number; The `y` coordinate of the point
       
   873 	this.y = (round ? Math.round(y) : y);
       
   874 };
       
   875 
       
   876 L.Point.prototype = {
       
   877 
       
   878 	// @method clone(): Point
       
   879 	// Returns a copy of the current point.
       
   880 	clone: function () {
       
   881 		return new L.Point(this.x, this.y);
       
   882 	},
       
   883 
       
   884 	// @method add(otherPoint: Point): Point
       
   885 	// Returns the result of addition of the current and the given points.
       
   886 	add: function (point) {
       
   887 		// non-destructive, returns a new point
       
   888 		return this.clone()._add(L.point(point));
       
   889 	},
       
   890 
       
   891 	_add: function (point) {
       
   892 		// destructive, used directly for performance in situations where it's safe to modify existing point
       
   893 		this.x += point.x;
       
   894 		this.y += point.y;
       
   895 		return this;
       
   896 	},
       
   897 
       
   898 	// @method subtract(otherPoint: Point): Point
       
   899 	// Returns the result of subtraction of the given point from the current.
       
   900 	subtract: function (point) {
       
   901 		return this.clone()._subtract(L.point(point));
       
   902 	},
       
   903 
       
   904 	_subtract: function (point) {
       
   905 		this.x -= point.x;
       
   906 		this.y -= point.y;
       
   907 		return this;
       
   908 	},
       
   909 
       
   910 	// @method divideBy(num: Number): Point
       
   911 	// Returns the result of division of the current point by the given number.
       
   912 	divideBy: function (num) {
       
   913 		return this.clone()._divideBy(num);
       
   914 	},
       
   915 
       
   916 	_divideBy: function (num) {
       
   917 		this.x /= num;
       
   918 		this.y /= num;
       
   919 		return this;
       
   920 	},
       
   921 
       
   922 	// @method multiplyBy(num: Number): Point
       
   923 	// Returns the result of multiplication of the current point by the given number.
       
   924 	multiplyBy: function (num) {
       
   925 		return this.clone()._multiplyBy(num);
       
   926 	},
       
   927 
       
   928 	_multiplyBy: function (num) {
       
   929 		this.x *= num;
       
   930 		this.y *= num;
       
   931 		return this;
       
   932 	},
       
   933 
       
   934 	// @method scaleBy(scale: Point): Point
       
   935 	// Multiply each coordinate of the current point by each coordinate of
       
   936 	// `scale`. In linear algebra terms, multiply the point by the
       
   937 	// [scaling matrix](https://en.wikipedia.org/wiki/Scaling_%28geometry%29#Matrix_representation)
       
   938 	// defined by `scale`.
       
   939 	scaleBy: function (point) {
       
   940 		return new L.Point(this.x * point.x, this.y * point.y);
       
   941 	},
       
   942 
       
   943 	// @method unscaleBy(scale: Point): Point
       
   944 	// Inverse of `scaleBy`. Divide each coordinate of the current point by
       
   945 	// each coordinate of `scale`.
       
   946 	unscaleBy: function (point) {
       
   947 		return new L.Point(this.x / point.x, this.y / point.y);
       
   948 	},
       
   949 
       
   950 	// @method round(): Point
       
   951 	// Returns a copy of the current point with rounded coordinates.
       
   952 	round: function () {
       
   953 		return this.clone()._round();
       
   954 	},
       
   955 
       
   956 	_round: function () {
       
   957 		this.x = Math.round(this.x);
       
   958 		this.y = Math.round(this.y);
       
   959 		return this;
       
   960 	},
       
   961 
       
   962 	// @method floor(): Point
       
   963 	// Returns a copy of the current point with floored coordinates (rounded down).
       
   964 	floor: function () {
       
   965 		return this.clone()._floor();
       
   966 	},
       
   967 
       
   968 	_floor: function () {
       
   969 		this.x = Math.floor(this.x);
       
   970 		this.y = Math.floor(this.y);
       
   971 		return this;
       
   972 	},
       
   973 
       
   974 	// @method ceil(): Point
       
   975 	// Returns a copy of the current point with ceiled coordinates (rounded up).
       
   976 	ceil: function () {
       
   977 		return this.clone()._ceil();
       
   978 	},
       
   979 
       
   980 	_ceil: function () {
       
   981 		this.x = Math.ceil(this.x);
       
   982 		this.y = Math.ceil(this.y);
       
   983 		return this;
       
   984 	},
       
   985 
       
   986 	// @method distanceTo(otherPoint: Point): Number
       
   987 	// Returns the cartesian distance between the current and the given points.
       
   988 	distanceTo: function (point) {
       
   989 		point = L.point(point);
       
   990 
       
   991 		var x = point.x - this.x,
       
   992 		    y = point.y - this.y;
       
   993 
       
   994 		return Math.sqrt(x * x + y * y);
       
   995 	},
       
   996 
       
   997 	// @method equals(otherPoint: Point): Boolean
       
   998 	// Returns `true` if the given point has the same coordinates.
       
   999 	equals: function (point) {
       
  1000 		point = L.point(point);
       
  1001 
       
  1002 		return point.x === this.x &&
       
  1003 		       point.y === this.y;
       
  1004 	},
       
  1005 
       
  1006 	// @method contains(otherPoint: Point): Boolean
       
  1007 	// Returns `true` if both coordinates of the given point are less than the corresponding current point coordinates (in absolute values).
       
  1008 	contains: function (point) {
       
  1009 		point = L.point(point);
       
  1010 
       
  1011 		return Math.abs(point.x) <= Math.abs(this.x) &&
       
  1012 		       Math.abs(point.y) <= Math.abs(this.y);
       
  1013 	},
       
  1014 
       
  1015 	// @method toString(): String
       
  1016 	// Returns a string representation of the point for debugging purposes.
       
  1017 	toString: function () {
       
  1018 		return 'Point(' +
       
  1019 		        L.Util.formatNum(this.x) + ', ' +
       
  1020 		        L.Util.formatNum(this.y) + ')';
       
  1021 	}
       
  1022 };
       
  1023 
       
  1024 // @factory L.point(x: Number, y: Number, round?: Boolean)
       
  1025 // Creates a Point object with the given `x` and `y` coordinates. If optional `round` is set to true, rounds the `x` and `y` values.
       
  1026 
       
  1027 // @alternative
       
  1028 // @factory L.point(coords: Number[])
       
  1029 // Expects an array of the form `[x, y]` instead.
       
  1030 
       
  1031 // @alternative
       
  1032 // @factory L.point(coords: Object)
       
  1033 // Expects a plain object of the form `{x: Number, y: Number}` instead.
       
  1034 L.point = function (x, y, round) {
       
  1035 	if (x instanceof L.Point) {
       
  1036 		return x;
       
  1037 	}
       
  1038 	if (L.Util.isArray(x)) {
       
  1039 		return new L.Point(x[0], x[1]);
       
  1040 	}
       
  1041 	if (x === undefined || x === null) {
       
  1042 		return x;
       
  1043 	}
       
  1044 	if (typeof x === 'object' && 'x' in x && 'y' in x) {
       
  1045 		return new L.Point(x.x, x.y);
       
  1046 	}
       
  1047 	return new L.Point(x, y, round);
       
  1048 };
       
  1049 
       
  1050 
       
  1051 
       
  1052 /*
       
  1053  * @class Bounds
       
  1054  * @aka L.Bounds
       
  1055  *
       
  1056  * Represents a rectangular area in pixel coordinates.
       
  1057  *
       
  1058  * @example
       
  1059  *
       
  1060  * ```js
       
  1061  * var p1 = L.point(10, 10),
       
  1062  * p2 = L.point(40, 60),
       
  1063  * bounds = L.bounds(p1, p2);
       
  1064  * ```
       
  1065  *
       
  1066  * All Leaflet methods that accept `Bounds` objects also accept them in a simple Array form (unless noted otherwise), so the bounds example above can be passed like this:
       
  1067  *
       
  1068  * ```js
       
  1069  * otherBounds.intersects([[10, 10], [40, 60]]);
       
  1070  * ```
       
  1071  */
       
  1072 
       
  1073 L.Bounds = function (a, b) {
       
  1074 	if (!a) { return; }
       
  1075 
       
  1076 	var points = b ? [a, b] : a;
       
  1077 
       
  1078 	for (var i = 0, len = points.length; i < len; i++) {
       
  1079 		this.extend(points[i]);
       
  1080 	}
       
  1081 };
       
  1082 
       
  1083 L.Bounds.prototype = {
       
  1084 	// @method extend(point: Point): this
       
  1085 	// Extends the bounds to contain the given point.
       
  1086 	extend: function (point) { // (Point)
       
  1087 		point = L.point(point);
       
  1088 
       
  1089 		// @property min: Point
       
  1090 		// The top left corner of the rectangle.
       
  1091 		// @property max: Point
       
  1092 		// The bottom right corner of the rectangle.
       
  1093 		if (!this.min && !this.max) {
       
  1094 			this.min = point.clone();
       
  1095 			this.max = point.clone();
       
  1096 		} else {
       
  1097 			this.min.x = Math.min(point.x, this.min.x);
       
  1098 			this.max.x = Math.max(point.x, this.max.x);
       
  1099 			this.min.y = Math.min(point.y, this.min.y);
       
  1100 			this.max.y = Math.max(point.y, this.max.y);
       
  1101 		}
       
  1102 		return this;
       
  1103 	},
       
  1104 
       
  1105 	// @method getCenter(round?: Boolean): Point
       
  1106 	// Returns the center point of the bounds.
       
  1107 	getCenter: function (round) {
       
  1108 		return new L.Point(
       
  1109 		        (this.min.x + this.max.x) / 2,
       
  1110 		        (this.min.y + this.max.y) / 2, round);
       
  1111 	},
       
  1112 
       
  1113 	// @method getBottomLeft(): Point
       
  1114 	// Returns the bottom-left point of the bounds.
       
  1115 	getBottomLeft: function () {
       
  1116 		return new L.Point(this.min.x, this.max.y);
       
  1117 	},
       
  1118 
       
  1119 	// @method getTopRight(): Point
       
  1120 	// Returns the top-right point of the bounds.
       
  1121 	getTopRight: function () { // -> Point
       
  1122 		return new L.Point(this.max.x, this.min.y);
       
  1123 	},
       
  1124 
       
  1125 	// @method getSize(): Point
       
  1126 	// Returns the size of the given bounds
       
  1127 	getSize: function () {
       
  1128 		return this.max.subtract(this.min);
       
  1129 	},
       
  1130 
       
  1131 	// @method contains(otherBounds: Bounds): Boolean
       
  1132 	// Returns `true` if the rectangle contains the given one.
       
  1133 	// @alternative
       
  1134 	// @method contains(point: Point): Boolean
       
  1135 	// Returns `true` if the rectangle contains the given point.
       
  1136 	contains: function (obj) {
       
  1137 		var min, max;
       
  1138 
       
  1139 		if (typeof obj[0] === 'number' || obj instanceof L.Point) {
       
  1140 			obj = L.point(obj);
       
  1141 		} else {
       
  1142 			obj = L.bounds(obj);
       
  1143 		}
       
  1144 
       
  1145 		if (obj instanceof L.Bounds) {
       
  1146 			min = obj.min;
       
  1147 			max = obj.max;
       
  1148 		} else {
       
  1149 			min = max = obj;
       
  1150 		}
       
  1151 
       
  1152 		return (min.x >= this.min.x) &&
       
  1153 		       (max.x <= this.max.x) &&
       
  1154 		       (min.y >= this.min.y) &&
       
  1155 		       (max.y <= this.max.y);
       
  1156 	},
       
  1157 
       
  1158 	// @method intersects(otherBounds: Bounds): Boolean
       
  1159 	// Returns `true` if the rectangle intersects the given bounds. Two bounds
       
  1160 	// intersect if they have at least one point in common.
       
  1161 	intersects: function (bounds) { // (Bounds) -> Boolean
       
  1162 		bounds = L.bounds(bounds);
       
  1163 
       
  1164 		var min = this.min,
       
  1165 		    max = this.max,
       
  1166 		    min2 = bounds.min,
       
  1167 		    max2 = bounds.max,
       
  1168 		    xIntersects = (max2.x >= min.x) && (min2.x <= max.x),
       
  1169 		    yIntersects = (max2.y >= min.y) && (min2.y <= max.y);
       
  1170 
       
  1171 		return xIntersects && yIntersects;
       
  1172 	},
       
  1173 
       
  1174 	// @method overlaps(otherBounds: Bounds): Boolean
       
  1175 	// Returns `true` if the rectangle overlaps the given bounds. Two bounds
       
  1176 	// overlap if their intersection is an area.
       
  1177 	overlaps: function (bounds) { // (Bounds) -> Boolean
       
  1178 		bounds = L.bounds(bounds);
       
  1179 
       
  1180 		var min = this.min,
       
  1181 		    max = this.max,
       
  1182 		    min2 = bounds.min,
       
  1183 		    max2 = bounds.max,
       
  1184 		    xOverlaps = (max2.x > min.x) && (min2.x < max.x),
       
  1185 		    yOverlaps = (max2.y > min.y) && (min2.y < max.y);
       
  1186 
       
  1187 		return xOverlaps && yOverlaps;
       
  1188 	},
       
  1189 
       
  1190 	isValid: function () {
       
  1191 		return !!(this.min && this.max);
       
  1192 	}
       
  1193 };
       
  1194 
       
  1195 
       
  1196 // @factory L.bounds(topLeft: Point, bottomRight: Point)
       
  1197 // Creates a Bounds object from two coordinates (usually top-left and bottom-right corners).
       
  1198 // @alternative
       
  1199 // @factory L.bounds(points: Point[])
       
  1200 // Creates a Bounds object from the points it contains
       
  1201 L.bounds = function (a, b) {
       
  1202 	if (!a || a instanceof L.Bounds) {
       
  1203 		return a;
       
  1204 	}
       
  1205 	return new L.Bounds(a, b);
       
  1206 };
       
  1207 
       
  1208 
       
  1209 
       
  1210 /*
       
  1211  * @class Transformation
       
  1212  * @aka L.Transformation
       
  1213  *
       
  1214  * Represents an affine transformation: a set of coefficients `a`, `b`, `c`, `d`
       
  1215  * for transforming a point of a form `(x, y)` into `(a*x + b, c*y + d)` and doing
       
  1216  * the reverse. Used by Leaflet in its projections code.
       
  1217  *
       
  1218  * @example
       
  1219  *
       
  1220  * ```js
       
  1221  * var transformation = new L.Transformation(2, 5, -1, 10),
       
  1222  * 	p = L.point(1, 2),
       
  1223  * 	p2 = transformation.transform(p), //  L.point(7, 8)
       
  1224  * 	p3 = transformation.untransform(p2); //  L.point(1, 2)
       
  1225  * ```
       
  1226  */
       
  1227 
       
  1228 
       
  1229 // factory new L.Transformation(a: Number, b: Number, c: Number, d: Number)
       
  1230 // Creates a `Transformation` object with the given coefficients.
       
  1231 L.Transformation = function (a, b, c, d) {
       
  1232 	this._a = a;
       
  1233 	this._b = b;
       
  1234 	this._c = c;
       
  1235 	this._d = d;
       
  1236 };
       
  1237 
       
  1238 L.Transformation.prototype = {
       
  1239 	// @method transform(point: Point, scale?: Number): Point
       
  1240 	// Returns a transformed point, optionally multiplied by the given scale.
       
  1241 	// Only accepts actual `L.Point` instances, not arrays.
       
  1242 	transform: function (point, scale) { // (Point, Number) -> Point
       
  1243 		return this._transform(point.clone(), scale);
       
  1244 	},
       
  1245 
       
  1246 	// destructive transform (faster)
       
  1247 	_transform: function (point, scale) {
       
  1248 		scale = scale || 1;
       
  1249 		point.x = scale * (this._a * point.x + this._b);
       
  1250 		point.y = scale * (this._c * point.y + this._d);
       
  1251 		return point;
       
  1252 	},
       
  1253 
       
  1254 	// @method untransform(point: Point, scale?: Number): Point
       
  1255 	// Returns the reverse transformation of the given point, optionally divided
       
  1256 	// by the given scale. Only accepts actual `L.Point` instances, not arrays.
       
  1257 	untransform: function (point, scale) {
       
  1258 		scale = scale || 1;
       
  1259 		return new L.Point(
       
  1260 		        (point.x / scale - this._b) / this._a,
       
  1261 		        (point.y / scale - this._d) / this._c);
       
  1262 	}
       
  1263 };
       
  1264 
       
  1265 
       
  1266 
       
  1267 /*
       
  1268  * @namespace DomUtil
       
  1269  *
       
  1270  * Utility functions to work with the [DOM](https://developer.mozilla.org/docs/Web/API/Document_Object_Model)
       
  1271  * tree, used by Leaflet internally.
       
  1272  *
       
  1273  * Most functions expecting or returning a `HTMLElement` also work for
       
  1274  * SVG elements. The only difference is that classes refer to CSS classes
       
  1275  * in HTML and SVG classes in SVG.
       
  1276  */
       
  1277 
       
  1278 L.DomUtil = {
       
  1279 
       
  1280 	// @function get(id: String|HTMLElement): HTMLElement
       
  1281 	// Returns an element given its DOM id, or returns the element itself
       
  1282 	// if it was passed directly.
       
  1283 	get: function (id) {
       
  1284 		return typeof id === 'string' ? document.getElementById(id) : id;
       
  1285 	},
       
  1286 
       
  1287 	// @function getStyle(el: HTMLElement, styleAttrib: String): String
       
  1288 	// Returns the value for a certain style attribute on an element,
       
  1289 	// including computed values or values set through CSS.
       
  1290 	getStyle: function (el, style) {
       
  1291 
       
  1292 		var value = el.style[style] || (el.currentStyle && el.currentStyle[style]);
       
  1293 
       
  1294 		if ((!value || value === 'auto') && document.defaultView) {
       
  1295 			var css = document.defaultView.getComputedStyle(el, null);
       
  1296 			value = css ? css[style] : null;
       
  1297 		}
       
  1298 
       
  1299 		return value === 'auto' ? null : value;
       
  1300 	},
       
  1301 
       
  1302 	// @function create(tagName: String, className?: String, container?: HTMLElement): HTMLElement
       
  1303 	// Creates an HTML element with `tagName`, sets its class to `className`, and optionally appends it to `container` element.
       
  1304 	create: function (tagName, className, container) {
       
  1305 
       
  1306 		var el = document.createElement(tagName);
       
  1307 		el.className = className || '';
       
  1308 
       
  1309 		if (container) {
       
  1310 			container.appendChild(el);
       
  1311 		}
       
  1312 
       
  1313 		return el;
       
  1314 	},
       
  1315 
       
  1316 	// @function remove(el: HTMLElement)
       
  1317 	// Removes `el` from its parent element
       
  1318 	remove: function (el) {
       
  1319 		var parent = el.parentNode;
       
  1320 		if (parent) {
       
  1321 			parent.removeChild(el);
       
  1322 		}
       
  1323 	},
       
  1324 
       
  1325 	// @function empty(el: HTMLElement)
       
  1326 	// Removes all of `el`'s children elements from `el`
       
  1327 	empty: function (el) {
       
  1328 		while (el.firstChild) {
       
  1329 			el.removeChild(el.firstChild);
       
  1330 		}
       
  1331 	},
       
  1332 
       
  1333 	// @function toFront(el: HTMLElement)
       
  1334 	// Makes `el` the last children of its parent, so it renders in front of the other children.
       
  1335 	toFront: function (el) {
       
  1336 		el.parentNode.appendChild(el);
       
  1337 	},
       
  1338 
       
  1339 	// @function toBack(el: HTMLElement)
       
  1340 	// Makes `el` the first children of its parent, so it renders back from the other children.
       
  1341 	toBack: function (el) {
       
  1342 		var parent = el.parentNode;
       
  1343 		parent.insertBefore(el, parent.firstChild);
       
  1344 	},
       
  1345 
       
  1346 	// @function hasClass(el: HTMLElement, name: String): Boolean
       
  1347 	// Returns `true` if the element's class attribute contains `name`.
       
  1348 	hasClass: function (el, name) {
       
  1349 		if (el.classList !== undefined) {
       
  1350 			return el.classList.contains(name);
       
  1351 		}
       
  1352 		var className = L.DomUtil.getClass(el);
       
  1353 		return className.length > 0 && new RegExp('(^|\\s)' + name + '(\\s|$)').test(className);
       
  1354 	},
       
  1355 
       
  1356 	// @function addClass(el: HTMLElement, name: String)
       
  1357 	// Adds `name` to the element's class attribute.
       
  1358 	addClass: function (el, name) {
       
  1359 		if (el.classList !== undefined) {
       
  1360 			var classes = L.Util.splitWords(name);
       
  1361 			for (var i = 0, len = classes.length; i < len; i++) {
       
  1362 				el.classList.add(classes[i]);
       
  1363 			}
       
  1364 		} else if (!L.DomUtil.hasClass(el, name)) {
       
  1365 			var className = L.DomUtil.getClass(el);
       
  1366 			L.DomUtil.setClass(el, (className ? className + ' ' : '') + name);
       
  1367 		}
       
  1368 	},
       
  1369 
       
  1370 	// @function removeClass(el: HTMLElement, name: String)
       
  1371 	// Removes `name` from the element's class attribute.
       
  1372 	removeClass: function (el, name) {
       
  1373 		if (el.classList !== undefined) {
       
  1374 			el.classList.remove(name);
       
  1375 		} else {
       
  1376 			L.DomUtil.setClass(el, L.Util.trim((' ' + L.DomUtil.getClass(el) + ' ').replace(' ' + name + ' ', ' ')));
       
  1377 		}
       
  1378 	},
       
  1379 
       
  1380 	// @function setClass(el: HTMLElement, name: String)
       
  1381 	// Sets the element's class.
       
  1382 	setClass: function (el, name) {
       
  1383 		if (el.className.baseVal === undefined) {
       
  1384 			el.className = name;
       
  1385 		} else {
       
  1386 			// in case of SVG element
       
  1387 			el.className.baseVal = name;
       
  1388 		}
       
  1389 	},
       
  1390 
       
  1391 	// @function getClass(el: HTMLElement): String
       
  1392 	// Returns the element's class.
       
  1393 	getClass: function (el) {
       
  1394 		return el.className.baseVal === undefined ? el.className : el.className.baseVal;
       
  1395 	},
       
  1396 
       
  1397 	// @function setOpacity(el: HTMLElement, opacity: Number)
       
  1398 	// Set the opacity of an element (including old IE support).
       
  1399 	// `opacity` must be a number from `0` to `1`.
       
  1400 	setOpacity: function (el, value) {
       
  1401 
       
  1402 		if ('opacity' in el.style) {
       
  1403 			el.style.opacity = value;
       
  1404 
       
  1405 		} else if ('filter' in el.style) {
       
  1406 			L.DomUtil._setOpacityIE(el, value);
       
  1407 		}
       
  1408 	},
       
  1409 
       
  1410 	_setOpacityIE: function (el, value) {
       
  1411 		var filter = false,
       
  1412 		    filterName = 'DXImageTransform.Microsoft.Alpha';
       
  1413 
       
  1414 		// filters collection throws an error if we try to retrieve a filter that doesn't exist
       
  1415 		try {
       
  1416 			filter = el.filters.item(filterName);
       
  1417 		} catch (e) {
       
  1418 			// don't set opacity to 1 if we haven't already set an opacity,
       
  1419 			// it isn't needed and breaks transparent pngs.
       
  1420 			if (value === 1) { return; }
       
  1421 		}
       
  1422 
       
  1423 		value = Math.round(value * 100);
       
  1424 
       
  1425 		if (filter) {
       
  1426 			filter.Enabled = (value !== 100);
       
  1427 			filter.Opacity = value;
       
  1428 		} else {
       
  1429 			el.style.filter += ' progid:' + filterName + '(opacity=' + value + ')';
       
  1430 		}
       
  1431 	},
       
  1432 
       
  1433 	// @function testProp(props: String[]): String|false
       
  1434 	// Goes through the array of style names and returns the first name
       
  1435 	// that is a valid style name for an element. If no such name is found,
       
  1436 	// it returns false. Useful for vendor-prefixed styles like `transform`.
       
  1437 	testProp: function (props) {
       
  1438 
       
  1439 		var style = document.documentElement.style;
       
  1440 
       
  1441 		for (var i = 0; i < props.length; i++) {
       
  1442 			if (props[i] in style) {
       
  1443 				return props[i];
       
  1444 			}
       
  1445 		}
       
  1446 		return false;
       
  1447 	},
       
  1448 
       
  1449 	// @function setTransform(el: HTMLElement, offset: Point, scale?: Number)
       
  1450 	// Resets the 3D CSS transform of `el` so it is translated by `offset` pixels
       
  1451 	// and optionally scaled by `scale`. Does not have an effect if the
       
  1452 	// browser doesn't support 3D CSS transforms.
       
  1453 	setTransform: function (el, offset, scale) {
       
  1454 		var pos = offset || new L.Point(0, 0);
       
  1455 
       
  1456 		el.style[L.DomUtil.TRANSFORM] =
       
  1457 			(L.Browser.ie3d ?
       
  1458 				'translate(' + pos.x + 'px,' + pos.y + 'px)' :
       
  1459 				'translate3d(' + pos.x + 'px,' + pos.y + 'px,0)') +
       
  1460 			(scale ? ' scale(' + scale + ')' : '');
       
  1461 	},
       
  1462 
       
  1463 	// @function setPosition(el: HTMLElement, position: Point)
       
  1464 	// Sets the position of `el` to coordinates specified by `position`,
       
  1465 	// using CSS translate or top/left positioning depending on the browser
       
  1466 	// (used by Leaflet internally to position its layers).
       
  1467 	setPosition: function (el, point) { // (HTMLElement, Point[, Boolean])
       
  1468 
       
  1469 		/*eslint-disable */
       
  1470 		el._leaflet_pos = point;
       
  1471 		/*eslint-enable */
       
  1472 
       
  1473 		if (L.Browser.any3d) {
       
  1474 			L.DomUtil.setTransform(el, point);
       
  1475 		} else {
       
  1476 			el.style.left = point.x + 'px';
       
  1477 			el.style.top = point.y + 'px';
       
  1478 		}
       
  1479 	},
       
  1480 
       
  1481 	// @function getPosition(el: HTMLElement): Point
       
  1482 	// Returns the coordinates of an element previously positioned with setPosition.
       
  1483 	getPosition: function (el) {
       
  1484 		// this method is only used for elements previously positioned using setPosition,
       
  1485 		// so it's safe to cache the position for performance
       
  1486 
       
  1487 		return el._leaflet_pos || new L.Point(0, 0);
       
  1488 	}
       
  1489 };
       
  1490 
       
  1491 
       
  1492 (function () {
       
  1493 	// prefix style property names
       
  1494 
       
  1495 	// @property TRANSFORM: String
       
  1496 	// Vendor-prefixed fransform style name (e.g. `'webkitTransform'` for WebKit).
       
  1497 	L.DomUtil.TRANSFORM = L.DomUtil.testProp(
       
  1498 			['transform', 'WebkitTransform', 'OTransform', 'MozTransform', 'msTransform']);
       
  1499 
       
  1500 
       
  1501 	// webkitTransition comes first because some browser versions that drop vendor prefix don't do
       
  1502 	// the same for the transitionend event, in particular the Android 4.1 stock browser
       
  1503 
       
  1504 	// @property TRANSITION: String
       
  1505 	// Vendor-prefixed transform style name.
       
  1506 	var transition = L.DomUtil.TRANSITION = L.DomUtil.testProp(
       
  1507 			['webkitTransition', 'transition', 'OTransition', 'MozTransition', 'msTransition']);
       
  1508 
       
  1509 	L.DomUtil.TRANSITION_END =
       
  1510 			transition === 'webkitTransition' || transition === 'OTransition' ? transition + 'End' : 'transitionend';
       
  1511 
       
  1512 	// @function disableTextSelection()
       
  1513 	// Prevents the user from generating `selectstart` DOM events, usually generated
       
  1514 	// when the user drags the mouse through a page with text. Used internally
       
  1515 	// by Leaflet to override the behaviour of any click-and-drag interaction on
       
  1516 	// the map. Affects drag interactions on the whole document.
       
  1517 
       
  1518 	// @function enableTextSelection()
       
  1519 	// Cancels the effects of a previous [`L.DomUtil.disableTextSelection`](#domutil-disabletextselection).
       
  1520 	if ('onselectstart' in document) {
       
  1521 		L.DomUtil.disableTextSelection = function () {
       
  1522 			L.DomEvent.on(window, 'selectstart', L.DomEvent.preventDefault);
       
  1523 		};
       
  1524 		L.DomUtil.enableTextSelection = function () {
       
  1525 			L.DomEvent.off(window, 'selectstart', L.DomEvent.preventDefault);
       
  1526 		};
       
  1527 
       
  1528 	} else {
       
  1529 		var userSelectProperty = L.DomUtil.testProp(
       
  1530 			['userSelect', 'WebkitUserSelect', 'OUserSelect', 'MozUserSelect', 'msUserSelect']);
       
  1531 
       
  1532 		L.DomUtil.disableTextSelection = function () {
       
  1533 			if (userSelectProperty) {
       
  1534 				var style = document.documentElement.style;
       
  1535 				this._userSelect = style[userSelectProperty];
       
  1536 				style[userSelectProperty] = 'none';
       
  1537 			}
       
  1538 		};
       
  1539 		L.DomUtil.enableTextSelection = function () {
       
  1540 			if (userSelectProperty) {
       
  1541 				document.documentElement.style[userSelectProperty] = this._userSelect;
       
  1542 				delete this._userSelect;
       
  1543 			}
       
  1544 		};
       
  1545 	}
       
  1546 
       
  1547 	// @function disableImageDrag()
       
  1548 	// As [`L.DomUtil.disableTextSelection`](#domutil-disabletextselection), but
       
  1549 	// for `dragstart` DOM events, usually generated when the user drags an image.
       
  1550 	L.DomUtil.disableImageDrag = function () {
       
  1551 		L.DomEvent.on(window, 'dragstart', L.DomEvent.preventDefault);
       
  1552 	};
       
  1553 
       
  1554 	// @function enableImageDrag()
       
  1555 	// Cancels the effects of a previous [`L.DomUtil.disableImageDrag`](#domutil-disabletextselection).
       
  1556 	L.DomUtil.enableImageDrag = function () {
       
  1557 		L.DomEvent.off(window, 'dragstart', L.DomEvent.preventDefault);
       
  1558 	};
       
  1559 
       
  1560 	// @function preventOutline(el: HTMLElement)
       
  1561 	// Makes the [outline](https://developer.mozilla.org/docs/Web/CSS/outline)
       
  1562 	// of the element `el` invisible. Used internally by Leaflet to prevent
       
  1563 	// focusable elements from displaying an outline when the user performs a
       
  1564 	// drag interaction on them.
       
  1565 	L.DomUtil.preventOutline = function (element) {
       
  1566 		while (element.tabIndex === -1) {
       
  1567 			element = element.parentNode;
       
  1568 		}
       
  1569 		if (!element || !element.style) { return; }
       
  1570 		L.DomUtil.restoreOutline();
       
  1571 		this._outlineElement = element;
       
  1572 		this._outlineStyle = element.style.outline;
       
  1573 		element.style.outline = 'none';
       
  1574 		L.DomEvent.on(window, 'keydown', L.DomUtil.restoreOutline, this);
       
  1575 	};
       
  1576 
       
  1577 	// @function restoreOutline()
       
  1578 	// Cancels the effects of a previous [`L.DomUtil.preventOutline`]().
       
  1579 	L.DomUtil.restoreOutline = function () {
       
  1580 		if (!this._outlineElement) { return; }
       
  1581 		this._outlineElement.style.outline = this._outlineStyle;
       
  1582 		delete this._outlineElement;
       
  1583 		delete this._outlineStyle;
       
  1584 		L.DomEvent.off(window, 'keydown', L.DomUtil.restoreOutline, this);
       
  1585 	};
       
  1586 })();
       
  1587 
       
  1588 
       
  1589 
       
  1590 /* @class LatLng
       
  1591  * @aka L.LatLng
       
  1592  *
       
  1593  * Represents a geographical point with a certain latitude and longitude.
       
  1594  *
       
  1595  * @example
       
  1596  *
       
  1597  * ```
       
  1598  * var latlng = L.latLng(50.5, 30.5);
       
  1599  * ```
       
  1600  *
       
  1601  * All Leaflet methods that accept LatLng objects also accept them in a simple Array form and simple object form (unless noted otherwise), so these lines are equivalent:
       
  1602  *
       
  1603  * ```
       
  1604  * map.panTo([50, 30]);
       
  1605  * map.panTo({lon: 30, lat: 50});
       
  1606  * map.panTo({lat: 50, lng: 30});
       
  1607  * map.panTo(L.latLng(50, 30));
       
  1608  * ```
       
  1609  */
       
  1610 
       
  1611 L.LatLng = function (lat, lng, alt) {
       
  1612 	if (isNaN(lat) || isNaN(lng)) {
       
  1613 		throw new Error('Invalid LatLng object: (' + lat + ', ' + lng + ')');
       
  1614 	}
       
  1615 
       
  1616 	// @property lat: Number
       
  1617 	// Latitude in degrees
       
  1618 	this.lat = +lat;
       
  1619 
       
  1620 	// @property lng: Number
       
  1621 	// Longitude in degrees
       
  1622 	this.lng = +lng;
       
  1623 
       
  1624 	// @property alt: Number
       
  1625 	// Altitude in meters (optional)
       
  1626 	if (alt !== undefined) {
       
  1627 		this.alt = +alt;
       
  1628 	}
       
  1629 };
       
  1630 
       
  1631 L.LatLng.prototype = {
       
  1632 	// @method equals(otherLatLng: LatLng, maxMargin?: Number): Boolean
       
  1633 	// Returns `true` if the given `LatLng` point is at the same position (within a small margin of error). The margin of error can be overriden by setting `maxMargin` to a small number.
       
  1634 	equals: function (obj, maxMargin) {
       
  1635 		if (!obj) { return false; }
       
  1636 
       
  1637 		obj = L.latLng(obj);
       
  1638 
       
  1639 		var margin = Math.max(
       
  1640 		        Math.abs(this.lat - obj.lat),
       
  1641 		        Math.abs(this.lng - obj.lng));
       
  1642 
       
  1643 		return margin <= (maxMargin === undefined ? 1.0E-9 : maxMargin);
       
  1644 	},
       
  1645 
       
  1646 	// @method toString(): String
       
  1647 	// Returns a string representation of the point (for debugging purposes).
       
  1648 	toString: function (precision) {
       
  1649 		return 'LatLng(' +
       
  1650 		        L.Util.formatNum(this.lat, precision) + ', ' +
       
  1651 		        L.Util.formatNum(this.lng, precision) + ')';
       
  1652 	},
       
  1653 
       
  1654 	// @method distanceTo(otherLatLng: LatLng): Number
       
  1655 	// Returns the distance (in meters) to the given `LatLng` calculated using the [Haversine formula](http://en.wikipedia.org/wiki/Haversine_formula).
       
  1656 	distanceTo: function (other) {
       
  1657 		return L.CRS.Earth.distance(this, L.latLng(other));
       
  1658 	},
       
  1659 
       
  1660 	// @method wrap(): LatLng
       
  1661 	// Returns a new `LatLng` object with the longitude wrapped so it's always between -180 and +180 degrees.
       
  1662 	wrap: function () {
       
  1663 		return L.CRS.Earth.wrapLatLng(this);
       
  1664 	},
       
  1665 
       
  1666 	// @method toBounds(sizeInMeters: Number): LatLngBounds
       
  1667 	// Returns a new `LatLngBounds` object in which each boundary is `sizeInMeters/2` meters apart from the `LatLng`.
       
  1668 	toBounds: function (sizeInMeters) {
       
  1669 		var latAccuracy = 180 * sizeInMeters / 40075017,
       
  1670 		    lngAccuracy = latAccuracy / Math.cos((Math.PI / 180) * this.lat);
       
  1671 
       
  1672 		return L.latLngBounds(
       
  1673 		        [this.lat - latAccuracy, this.lng - lngAccuracy],
       
  1674 		        [this.lat + latAccuracy, this.lng + lngAccuracy]);
       
  1675 	},
       
  1676 
       
  1677 	clone: function () {
       
  1678 		return new L.LatLng(this.lat, this.lng, this.alt);
       
  1679 	}
       
  1680 };
       
  1681 
       
  1682 
       
  1683 
       
  1684 // @factory L.latLng(latitude: Number, longitude: Number, altitude?: Number): LatLng
       
  1685 // Creates an object representing a geographical point with the given latitude and longitude (and optionally altitude).
       
  1686 
       
  1687 // @alternative
       
  1688 // @factory L.latLng(coords: Array): LatLng
       
  1689 // Expects an array of the form `[Number, Number]` or `[Number, Number, Number]` instead.
       
  1690 
       
  1691 // @alternative
       
  1692 // @factory L.latLng(coords: Object): LatLng
       
  1693 // Expects an plain object of the form `{lat: Number, lng: Number}` or `{lat: Number, lng: Number, alt: Number}` instead.
       
  1694 
       
  1695 L.latLng = function (a, b, c) {
       
  1696 	if (a instanceof L.LatLng) {
       
  1697 		return a;
       
  1698 	}
       
  1699 	if (L.Util.isArray(a) && typeof a[0] !== 'object') {
       
  1700 		if (a.length === 3) {
       
  1701 			return new L.LatLng(a[0], a[1], a[2]);
       
  1702 		}
       
  1703 		if (a.length === 2) {
       
  1704 			return new L.LatLng(a[0], a[1]);
       
  1705 		}
       
  1706 		return null;
       
  1707 	}
       
  1708 	if (a === undefined || a === null) {
       
  1709 		return a;
       
  1710 	}
       
  1711 	if (typeof a === 'object' && 'lat' in a) {
       
  1712 		return new L.LatLng(a.lat, 'lng' in a ? a.lng : a.lon, a.alt);
       
  1713 	}
       
  1714 	if (b === undefined) {
       
  1715 		return null;
       
  1716 	}
       
  1717 	return new L.LatLng(a, b, c);
       
  1718 };
       
  1719 
       
  1720 
       
  1721 
       
  1722 /*
       
  1723  * @class LatLngBounds
       
  1724  * @aka L.LatLngBounds
       
  1725  *
       
  1726  * Represents a rectangular geographical area on a map.
       
  1727  *
       
  1728  * @example
       
  1729  *
       
  1730  * ```js
       
  1731  * var corner1 = L.latLng(40.712, -74.227),
       
  1732  * corner2 = L.latLng(40.774, -74.125),
       
  1733  * bounds = L.latLngBounds(corner1, corner2);
       
  1734  * ```
       
  1735  *
       
  1736  * All Leaflet methods that accept LatLngBounds objects also accept them in a simple Array form (unless noted otherwise), so the bounds example above can be passed like this:
       
  1737  *
       
  1738  * ```js
       
  1739  * map.fitBounds([
       
  1740  * 	[40.712, -74.227],
       
  1741  * 	[40.774, -74.125]
       
  1742  * ]);
       
  1743  * ```
       
  1744  *
       
  1745  * Caution: if the area crosses the antimeridian (often confused with the International Date Line), you must specify corners _outside_ the [-180, 180] degrees longitude range.
       
  1746  */
       
  1747 
       
  1748 L.LatLngBounds = function (corner1, corner2) { // (LatLng, LatLng) or (LatLng[])
       
  1749 	if (!corner1) { return; }
       
  1750 
       
  1751 	var latlngs = corner2 ? [corner1, corner2] : corner1;
       
  1752 
       
  1753 	for (var i = 0, len = latlngs.length; i < len; i++) {
       
  1754 		this.extend(latlngs[i]);
       
  1755 	}
       
  1756 };
       
  1757 
       
  1758 L.LatLngBounds.prototype = {
       
  1759 
       
  1760 	// @method extend(latlng: LatLng): this
       
  1761 	// Extend the bounds to contain the given point
       
  1762 
       
  1763 	// @alternative
       
  1764 	// @method extend(otherBounds: LatLngBounds): this
       
  1765 	// Extend the bounds to contain the given bounds
       
  1766 	extend: function (obj) {
       
  1767 		var sw = this._southWest,
       
  1768 		    ne = this._northEast,
       
  1769 		    sw2, ne2;
       
  1770 
       
  1771 		if (obj instanceof L.LatLng) {
       
  1772 			sw2 = obj;
       
  1773 			ne2 = obj;
       
  1774 
       
  1775 		} else if (obj instanceof L.LatLngBounds) {
       
  1776 			sw2 = obj._southWest;
       
  1777 			ne2 = obj._northEast;
       
  1778 
       
  1779 			if (!sw2 || !ne2) { return this; }
       
  1780 
       
  1781 		} else {
       
  1782 			return obj ? this.extend(L.latLng(obj) || L.latLngBounds(obj)) : this;
       
  1783 		}
       
  1784 
       
  1785 		if (!sw && !ne) {
       
  1786 			this._southWest = new L.LatLng(sw2.lat, sw2.lng);
       
  1787 			this._northEast = new L.LatLng(ne2.lat, ne2.lng);
       
  1788 		} else {
       
  1789 			sw.lat = Math.min(sw2.lat, sw.lat);
       
  1790 			sw.lng = Math.min(sw2.lng, sw.lng);
       
  1791 			ne.lat = Math.max(ne2.lat, ne.lat);
       
  1792 			ne.lng = Math.max(ne2.lng, ne.lng);
       
  1793 		}
       
  1794 
       
  1795 		return this;
       
  1796 	},
       
  1797 
       
  1798 	// @method pad(bufferRatio: Number): LatLngBounds
       
  1799 	// Returns bigger bounds created by extending the current bounds by a given percentage in each direction.
       
  1800 	pad: function (bufferRatio) {
       
  1801 		var sw = this._southWest,
       
  1802 		    ne = this._northEast,
       
  1803 		    heightBuffer = Math.abs(sw.lat - ne.lat) * bufferRatio,
       
  1804 		    widthBuffer = Math.abs(sw.lng - ne.lng) * bufferRatio;
       
  1805 
       
  1806 		return new L.LatLngBounds(
       
  1807 		        new L.LatLng(sw.lat - heightBuffer, sw.lng - widthBuffer),
       
  1808 		        new L.LatLng(ne.lat + heightBuffer, ne.lng + widthBuffer));
       
  1809 	},
       
  1810 
       
  1811 	// @method getCenter(): LatLng
       
  1812 	// Returns the center point of the bounds.
       
  1813 	getCenter: function () {
       
  1814 		return new L.LatLng(
       
  1815 		        (this._southWest.lat + this._northEast.lat) / 2,
       
  1816 		        (this._southWest.lng + this._northEast.lng) / 2);
       
  1817 	},
       
  1818 
       
  1819 	// @method getSouthWest(): LatLng
       
  1820 	// Returns the south-west point of the bounds.
       
  1821 	getSouthWest: function () {
       
  1822 		return this._southWest;
       
  1823 	},
       
  1824 
       
  1825 	// @method getNorthEast(): LatLng
       
  1826 	// Returns the north-east point of the bounds.
       
  1827 	getNorthEast: function () {
       
  1828 		return this._northEast;
       
  1829 	},
       
  1830 
       
  1831 	// @method getNorthWest(): LatLng
       
  1832 	// Returns the north-west point of the bounds.
       
  1833 	getNorthWest: function () {
       
  1834 		return new L.LatLng(this.getNorth(), this.getWest());
       
  1835 	},
       
  1836 
       
  1837 	// @method getSouthEast(): LatLng
       
  1838 	// Returns the south-east point of the bounds.
       
  1839 	getSouthEast: function () {
       
  1840 		return new L.LatLng(this.getSouth(), this.getEast());
       
  1841 	},
       
  1842 
       
  1843 	// @method getWest(): Number
       
  1844 	// Returns the west longitude of the bounds
       
  1845 	getWest: function () {
       
  1846 		return this._southWest.lng;
       
  1847 	},
       
  1848 
       
  1849 	// @method getSouth(): Number
       
  1850 	// Returns the south latitude of the bounds
       
  1851 	getSouth: function () {
       
  1852 		return this._southWest.lat;
       
  1853 	},
       
  1854 
       
  1855 	// @method getEast(): Number
       
  1856 	// Returns the east longitude of the bounds
       
  1857 	getEast: function () {
       
  1858 		return this._northEast.lng;
       
  1859 	},
       
  1860 
       
  1861 	// @method getNorth(): Number
       
  1862 	// Returns the north latitude of the bounds
       
  1863 	getNorth: function () {
       
  1864 		return this._northEast.lat;
       
  1865 	},
       
  1866 
       
  1867 	// @method contains(otherBounds: LatLngBounds): Boolean
       
  1868 	// Returns `true` if the rectangle contains the given one.
       
  1869 
       
  1870 	// @alternative
       
  1871 	// @method contains (latlng: LatLng): Boolean
       
  1872 	// Returns `true` if the rectangle contains the given point.
       
  1873 	contains: function (obj) { // (LatLngBounds) or (LatLng) -> Boolean
       
  1874 		if (typeof obj[0] === 'number' || obj instanceof L.LatLng || 'lat' in obj) {
       
  1875 			obj = L.latLng(obj);
       
  1876 		} else {
       
  1877 			obj = L.latLngBounds(obj);
       
  1878 		}
       
  1879 
       
  1880 		var sw = this._southWest,
       
  1881 		    ne = this._northEast,
       
  1882 		    sw2, ne2;
       
  1883 
       
  1884 		if (obj instanceof L.LatLngBounds) {
       
  1885 			sw2 = obj.getSouthWest();
       
  1886 			ne2 = obj.getNorthEast();
       
  1887 		} else {
       
  1888 			sw2 = ne2 = obj;
       
  1889 		}
       
  1890 
       
  1891 		return (sw2.lat >= sw.lat) && (ne2.lat <= ne.lat) &&
       
  1892 		       (sw2.lng >= sw.lng) && (ne2.lng <= ne.lng);
       
  1893 	},
       
  1894 
       
  1895 	// @method intersects(otherBounds: LatLngBounds): Boolean
       
  1896 	// Returns `true` if the rectangle intersects the given bounds. Two bounds intersect if they have at least one point in common.
       
  1897 	intersects: function (bounds) {
       
  1898 		bounds = L.latLngBounds(bounds);
       
  1899 
       
  1900 		var sw = this._southWest,
       
  1901 		    ne = this._northEast,
       
  1902 		    sw2 = bounds.getSouthWest(),
       
  1903 		    ne2 = bounds.getNorthEast(),
       
  1904 
       
  1905 		    latIntersects = (ne2.lat >= sw.lat) && (sw2.lat <= ne.lat),
       
  1906 		    lngIntersects = (ne2.lng >= sw.lng) && (sw2.lng <= ne.lng);
       
  1907 
       
  1908 		return latIntersects && lngIntersects;
       
  1909 	},
       
  1910 
       
  1911 	// @method overlaps(otherBounds: Bounds): Boolean
       
  1912 	// Returns `true` if the rectangle overlaps the given bounds. Two bounds overlap if their intersection is an area.
       
  1913 	overlaps: function (bounds) {
       
  1914 		bounds = L.latLngBounds(bounds);
       
  1915 
       
  1916 		var sw = this._southWest,
       
  1917 		    ne = this._northEast,
       
  1918 		    sw2 = bounds.getSouthWest(),
       
  1919 		    ne2 = bounds.getNorthEast(),
       
  1920 
       
  1921 		    latOverlaps = (ne2.lat > sw.lat) && (sw2.lat < ne.lat),
       
  1922 		    lngOverlaps = (ne2.lng > sw.lng) && (sw2.lng < ne.lng);
       
  1923 
       
  1924 		return latOverlaps && lngOverlaps;
       
  1925 	},
       
  1926 
       
  1927 	// @method toBBoxString(): String
       
  1928 	// Returns a string with bounding box coordinates in a 'southwest_lng,southwest_lat,northeast_lng,northeast_lat' format. Useful for sending requests to web services that return geo data.
       
  1929 	toBBoxString: function () {
       
  1930 		return [this.getWest(), this.getSouth(), this.getEast(), this.getNorth()].join(',');
       
  1931 	},
       
  1932 
       
  1933 	// @method equals(otherBounds: LatLngBounds): Boolean
       
  1934 	// Returns `true` if the rectangle is equivalent (within a small margin of error) to the given bounds.
       
  1935 	equals: function (bounds) {
       
  1936 		if (!bounds) { return false; }
       
  1937 
       
  1938 		bounds = L.latLngBounds(bounds);
       
  1939 
       
  1940 		return this._southWest.equals(bounds.getSouthWest()) &&
       
  1941 		       this._northEast.equals(bounds.getNorthEast());
       
  1942 	},
       
  1943 
       
  1944 	// @method isValid(): Boolean
       
  1945 	// Returns `true` if the bounds are properly initialized.
       
  1946 	isValid: function () {
       
  1947 		return !!(this._southWest && this._northEast);
       
  1948 	}
       
  1949 };
       
  1950 
       
  1951 // TODO International date line?
       
  1952 
       
  1953 // @factory L.latLngBounds(corner1: LatLng, corner2: LatLng)
       
  1954 // Creates a `LatLngBounds` object by defining two diagonally opposite corners of the rectangle.
       
  1955 
       
  1956 // @alternative
       
  1957 // @factory L.latLngBounds(latlngs: LatLng[])
       
  1958 // Creates a `LatLngBounds` object defined by the geographical points it contains. Very useful for zooming the map to fit a particular set of locations with [`fitBounds`](#map-fitbounds).
       
  1959 L.latLngBounds = function (a, b) {
       
  1960 	if (a instanceof L.LatLngBounds) {
       
  1961 		return a;
       
  1962 	}
       
  1963 	return new L.LatLngBounds(a, b);
       
  1964 };
       
  1965 
       
  1966 
       
  1967 
       
  1968 /*
       
  1969  * @namespace Projection
       
  1970  * @section
       
  1971  * Leaflet comes with a set of already defined Projections out of the box:
       
  1972  *
       
  1973  * @projection L.Projection.LonLat
       
  1974  *
       
  1975  * Equirectangular, or Plate Carree projection — the most simple projection,
       
  1976  * mostly used by GIS enthusiasts. Directly maps `x` as longitude, and `y` as
       
  1977  * latitude. Also suitable for flat worlds, e.g. game maps. Used by the
       
  1978  * `EPSG:3395` and `Simple` CRS.
       
  1979  */
       
  1980 
       
  1981 L.Projection = {};
       
  1982 
       
  1983 L.Projection.LonLat = {
       
  1984 	project: function (latlng) {
       
  1985 		return new L.Point(latlng.lng, latlng.lat);
       
  1986 	},
       
  1987 
       
  1988 	unproject: function (point) {
       
  1989 		return new L.LatLng(point.y, point.x);
       
  1990 	},
       
  1991 
       
  1992 	bounds: L.bounds([-180, -90], [180, 90])
       
  1993 };
       
  1994 
       
  1995 
       
  1996 
       
  1997 /*
       
  1998  * @namespace Projection
       
  1999  * @projection L.Projection.SphericalMercator
       
  2000  *
       
  2001  * Spherical Mercator projection — the most common projection for online maps,
       
  2002  * used by almost all free and commercial tile providers. Assumes that Earth is
       
  2003  * a sphere. Used by the `EPSG:3857` CRS.
       
  2004  */
       
  2005 
       
  2006 L.Projection.SphericalMercator = {
       
  2007 
       
  2008 	R: 6378137,
       
  2009 	MAX_LATITUDE: 85.0511287798,
       
  2010 
       
  2011 	project: function (latlng) {
       
  2012 		var d = Math.PI / 180,
       
  2013 		    max = this.MAX_LATITUDE,
       
  2014 		    lat = Math.max(Math.min(max, latlng.lat), -max),
       
  2015 		    sin = Math.sin(lat * d);
       
  2016 
       
  2017 		return new L.Point(
       
  2018 				this.R * latlng.lng * d,
       
  2019 				this.R * Math.log((1 + sin) / (1 - sin)) / 2);
       
  2020 	},
       
  2021 
       
  2022 	unproject: function (point) {
       
  2023 		var d = 180 / Math.PI;
       
  2024 
       
  2025 		return new L.LatLng(
       
  2026 			(2 * Math.atan(Math.exp(point.y / this.R)) - (Math.PI / 2)) * d,
       
  2027 			point.x * d / this.R);
       
  2028 	},
       
  2029 
       
  2030 	bounds: (function () {
       
  2031 		var d = 6378137 * Math.PI;
       
  2032 		return L.bounds([-d, -d], [d, d]);
       
  2033 	})()
       
  2034 };
       
  2035 
       
  2036 
       
  2037 
       
  2038 /*
       
  2039  * @class CRS
       
  2040  * @aka L.CRS
       
  2041  * Abstract class that defines coordinate reference systems for projecting
       
  2042  * geographical points into pixel (screen) coordinates and back (and to
       
  2043  * coordinates in other units for [WMS](https://en.wikipedia.org/wiki/Web_Map_Service) services). See
       
  2044  * [spatial reference system](http://en.wikipedia.org/wiki/Coordinate_reference_system).
       
  2045  *
       
  2046  * Leaflet defines the most usual CRSs by default. If you want to use a
       
  2047  * CRS not defined by default, take a look at the
       
  2048  * [Proj4Leaflet](https://github.com/kartena/Proj4Leaflet) plugin.
       
  2049  */
       
  2050 
       
  2051 L.CRS = {
       
  2052 	// @method latLngToPoint(latlng: LatLng, zoom: Number): Point
       
  2053 	// Projects geographical coordinates into pixel coordinates for a given zoom.
       
  2054 	latLngToPoint: function (latlng, zoom) {
       
  2055 		var projectedPoint = this.projection.project(latlng),
       
  2056 		    scale = this.scale(zoom);
       
  2057 
       
  2058 		return this.transformation._transform(projectedPoint, scale);
       
  2059 	},
       
  2060 
       
  2061 	// @method pointToLatLng(point: Point, zoom: Number): LatLng
       
  2062 	// The inverse of `latLngToPoint`. Projects pixel coordinates on a given
       
  2063 	// zoom into geographical coordinates.
       
  2064 	pointToLatLng: function (point, zoom) {
       
  2065 		var scale = this.scale(zoom),
       
  2066 		    untransformedPoint = this.transformation.untransform(point, scale);
       
  2067 
       
  2068 		return this.projection.unproject(untransformedPoint);
       
  2069 	},
       
  2070 
       
  2071 	// @method project(latlng: LatLng): Point
       
  2072 	// Projects geographical coordinates into coordinates in units accepted for
       
  2073 	// this CRS (e.g. meters for EPSG:3857, for passing it to WMS services).
       
  2074 	project: function (latlng) {
       
  2075 		return this.projection.project(latlng);
       
  2076 	},
       
  2077 
       
  2078 	// @method unproject(point: Point): LatLng
       
  2079 	// Given a projected coordinate returns the corresponding LatLng.
       
  2080 	// The inverse of `project`.
       
  2081 	unproject: function (point) {
       
  2082 		return this.projection.unproject(point);
       
  2083 	},
       
  2084 
       
  2085 	// @method scale(zoom: Number): Number
       
  2086 	// Returns the scale used when transforming projected coordinates into
       
  2087 	// pixel coordinates for a particular zoom. For example, it returns
       
  2088 	// `256 * 2^zoom` for Mercator-based CRS.
       
  2089 	scale: function (zoom) {
       
  2090 		return 256 * Math.pow(2, zoom);
       
  2091 	},
       
  2092 
       
  2093 	// @method zoom(scale: Number): Number
       
  2094 	// Inverse of `scale()`, returns the zoom level corresponding to a scale
       
  2095 	// factor of `scale`.
       
  2096 	zoom: function (scale) {
       
  2097 		return Math.log(scale / 256) / Math.LN2;
       
  2098 	},
       
  2099 
       
  2100 	// @method getProjectedBounds(zoom: Number): Bounds
       
  2101 	// Returns the projection's bounds scaled and transformed for the provided `zoom`.
       
  2102 	getProjectedBounds: function (zoom) {
       
  2103 		if (this.infinite) { return null; }
       
  2104 
       
  2105 		var b = this.projection.bounds,
       
  2106 		    s = this.scale(zoom),
       
  2107 		    min = this.transformation.transform(b.min, s),
       
  2108 		    max = this.transformation.transform(b.max, s);
       
  2109 
       
  2110 		return L.bounds(min, max);
       
  2111 	},
       
  2112 
       
  2113 	// @method distance(latlng1: LatLng, latlng2: LatLng): Number
       
  2114 	// Returns the distance between two geographical coordinates.
       
  2115 
       
  2116 	// @property code: String
       
  2117 	// Standard code name of the CRS passed into WMS services (e.g. `'EPSG:3857'`)
       
  2118 	//
       
  2119 	// @property wrapLng: Number[]
       
  2120 	// An array of two numbers defining whether the longitude (horizontal) coordinate
       
  2121 	// axis wraps around a given range and how. Defaults to `[-180, 180]` in most
       
  2122 	// geographical CRSs. If `undefined`, the longitude axis does not wrap around.
       
  2123 	//
       
  2124 	// @property wrapLat: Number[]
       
  2125 	// Like `wrapLng`, but for the latitude (vertical) axis.
       
  2126 
       
  2127 	// wrapLng: [min, max],
       
  2128 	// wrapLat: [min, max],
       
  2129 
       
  2130 	// @property infinite: Boolean
       
  2131 	// If true, the coordinate space will be unbounded (infinite in both axes)
       
  2132 	infinite: false,
       
  2133 
       
  2134 	// @method wrapLatLng(latlng: LatLng): LatLng
       
  2135 	// Returns a `LatLng` where lat and lng has been wrapped according to the
       
  2136 	// CRS's `wrapLat` and `wrapLng` properties, if they are outside the CRS's bounds.
       
  2137 	// Only accepts actual `L.LatLng` instances, not arrays.
       
  2138 	wrapLatLng: function (latlng) {
       
  2139 		var lng = this.wrapLng ? L.Util.wrapNum(latlng.lng, this.wrapLng, true) : latlng.lng,
       
  2140 		    lat = this.wrapLat ? L.Util.wrapNum(latlng.lat, this.wrapLat, true) : latlng.lat,
       
  2141 		    alt = latlng.alt;
       
  2142 
       
  2143 		return L.latLng(lat, lng, alt);
       
  2144 	},
       
  2145 
       
  2146 	// @method wrapLatLngBounds(bounds: LatLngBounds): LatLngBounds
       
  2147 	// Returns a `LatLngBounds` with the same size as the given one, ensuring
       
  2148 	// that its center is within the CRS's bounds.
       
  2149 	// Only accepts actual `L.LatLngBounds` instances, not arrays.
       
  2150 	wrapLatLngBounds: function (bounds) {
       
  2151 		var center = bounds.getCenter(),
       
  2152 		    newCenter = this.wrapLatLng(center),
       
  2153 		    latShift = center.lat - newCenter.lat,
       
  2154 		    lngShift = center.lng - newCenter.lng;
       
  2155 
       
  2156 		if (latShift === 0 && lngShift === 0) {
       
  2157 			return bounds;
       
  2158 		}
       
  2159 
       
  2160 		var sw = bounds.getSouthWest(),
       
  2161 		    ne = bounds.getNorthEast(),
       
  2162 		    newSw = L.latLng({lat: sw.lat - latShift, lng: sw.lng - lngShift}),
       
  2163 		    newNe = L.latLng({lat: ne.lat - latShift, lng: ne.lng - lngShift});
       
  2164 
       
  2165 		return new L.LatLngBounds(newSw, newNe);
       
  2166 	}
       
  2167 };
       
  2168 
       
  2169 
       
  2170 
       
  2171 /*
       
  2172  * @namespace CRS
       
  2173  * @crs L.CRS.Simple
       
  2174  *
       
  2175  * A simple CRS that maps longitude and latitude into `x` and `y` directly.
       
  2176  * May be used for maps of flat surfaces (e.g. game maps). Note that the `y`
       
  2177  * axis should still be inverted (going from bottom to top). `distance()` returns
       
  2178  * simple euclidean distance.
       
  2179  */
       
  2180 
       
  2181 L.CRS.Simple = L.extend({}, L.CRS, {
       
  2182 	projection: L.Projection.LonLat,
       
  2183 	transformation: new L.Transformation(1, 0, -1, 0),
       
  2184 
       
  2185 	scale: function (zoom) {
       
  2186 		return Math.pow(2, zoom);
       
  2187 	},
       
  2188 
       
  2189 	zoom: function (scale) {
       
  2190 		return Math.log(scale) / Math.LN2;
       
  2191 	},
       
  2192 
       
  2193 	distance: function (latlng1, latlng2) {
       
  2194 		var dx = latlng2.lng - latlng1.lng,
       
  2195 		    dy = latlng2.lat - latlng1.lat;
       
  2196 
       
  2197 		return Math.sqrt(dx * dx + dy * dy);
       
  2198 	},
       
  2199 
       
  2200 	infinite: true
       
  2201 });
       
  2202 
       
  2203 
       
  2204 
       
  2205 /*
       
  2206  * @namespace CRS
       
  2207  * @crs L.CRS.Earth
       
  2208  *
       
  2209  * Serves as the base for CRS that are global such that they cover the earth.
       
  2210  * Can only be used as the base for other CRS and cannot be used directly,
       
  2211  * since it does not have a `code`, `projection` or `transformation`. `distance()` returns
       
  2212  * meters.
       
  2213  */
       
  2214 
       
  2215 L.CRS.Earth = L.extend({}, L.CRS, {
       
  2216 	wrapLng: [-180, 180],
       
  2217 
       
  2218 	// Mean Earth Radius, as recommended for use by
       
  2219 	// the International Union of Geodesy and Geophysics,
       
  2220 	// see http://rosettacode.org/wiki/Haversine_formula
       
  2221 	R: 6371000,
       
  2222 
       
  2223 	// distance between two geographical points using spherical law of cosines approximation
       
  2224 	distance: function (latlng1, latlng2) {
       
  2225 		var rad = Math.PI / 180,
       
  2226 		    lat1 = latlng1.lat * rad,
       
  2227 		    lat2 = latlng2.lat * rad,
       
  2228 		    a = Math.sin(lat1) * Math.sin(lat2) +
       
  2229 		        Math.cos(lat1) * Math.cos(lat2) * Math.cos((latlng2.lng - latlng1.lng) * rad);
       
  2230 
       
  2231 		return this.R * Math.acos(Math.min(a, 1));
       
  2232 	}
       
  2233 });
       
  2234 
       
  2235 
       
  2236 
       
  2237 /*
       
  2238  * @namespace CRS
       
  2239  * @crs L.CRS.EPSG3857
       
  2240  *
       
  2241  * The most common CRS for online maps, used by almost all free and commercial
       
  2242  * tile providers. Uses Spherical Mercator projection. Set in by default in
       
  2243  * Map's `crs` option.
       
  2244  */
       
  2245 
       
  2246 L.CRS.EPSG3857 = L.extend({}, L.CRS.Earth, {
       
  2247 	code: 'EPSG:3857',
       
  2248 	projection: L.Projection.SphericalMercator,
       
  2249 
       
  2250 	transformation: (function () {
       
  2251 		var scale = 0.5 / (Math.PI * L.Projection.SphericalMercator.R);
       
  2252 		return new L.Transformation(scale, 0.5, -scale, 0.5);
       
  2253 	}())
       
  2254 });
       
  2255 
       
  2256 L.CRS.EPSG900913 = L.extend({}, L.CRS.EPSG3857, {
       
  2257 	code: 'EPSG:900913'
       
  2258 });
       
  2259 
       
  2260 
       
  2261 
       
  2262 /*
       
  2263  * @namespace CRS
       
  2264  * @crs L.CRS.EPSG4326
       
  2265  *
       
  2266  * A common CRS among GIS enthusiasts. Uses simple Equirectangular projection.
       
  2267  *
       
  2268  * Leaflet 1.0.x complies with the [TMS coordinate scheme for EPSG:4326](https://wiki.osgeo.org/wiki/Tile_Map_Service_Specification#global-geodetic),
       
  2269  * which is a breaking change from 0.7.x behaviour.  If you are using a `TileLayer`
       
  2270  * with this CRS, ensure that there are two 256x256 pixel tiles covering the
       
  2271  * whole earth at zoom level zero, and that the tile coordinate origin is (-180,+90),
       
  2272  * or (-180,-90) for `TileLayer`s with [the `tms` option](#tilelayer-tms) set.
       
  2273  */
       
  2274 
       
  2275 L.CRS.EPSG4326 = L.extend({}, L.CRS.Earth, {
       
  2276 	code: 'EPSG:4326',
       
  2277 	projection: L.Projection.LonLat,
       
  2278 	transformation: new L.Transformation(1 / 180, 1, -1 / 180, 0.5)
       
  2279 });
       
  2280 
       
  2281 
       
  2282 
       
  2283 /*
       
  2284  * @class Map
       
  2285  * @aka L.Map
       
  2286  * @inherits Evented
       
  2287  *
       
  2288  * The central class of the API — it is used to create a map on a page and manipulate it.
       
  2289  *
       
  2290  * @example
       
  2291  *
       
  2292  * ```js
       
  2293  * // initialize the map on the "map" div with a given center and zoom
       
  2294  * var map = L.map('map', {
       
  2295  * 	center: [51.505, -0.09],
       
  2296  * 	zoom: 13
       
  2297  * });
       
  2298  * ```
       
  2299  *
       
  2300  */
       
  2301 
       
  2302 L.Map = L.Evented.extend({
       
  2303 
       
  2304 	options: {
       
  2305 		// @section Map State Options
       
  2306 		// @option crs: CRS = L.CRS.EPSG3857
       
  2307 		// The [Coordinate Reference System](#crs) to use. Don't change this if you're not
       
  2308 		// sure what it means.
       
  2309 		crs: L.CRS.EPSG3857,
       
  2310 
       
  2311 		// @option center: LatLng = undefined
       
  2312 		// Initial geographic center of the map
       
  2313 		center: undefined,
       
  2314 
       
  2315 		// @option zoom: Number = undefined
       
  2316 		// Initial map zoom level
       
  2317 		zoom: undefined,
       
  2318 
       
  2319 		// @option minZoom: Number = undefined
       
  2320 		// Minimum zoom level of the map. Overrides any `minZoom` option set on map layers.
       
  2321 		minZoom: undefined,
       
  2322 
       
  2323 		// @option maxZoom: Number = undefined
       
  2324 		// Maximum zoom level of the map. Overrides any `maxZoom` option set on map layers.
       
  2325 		maxZoom: undefined,
       
  2326 
       
  2327 		// @option layers: Layer[] = []
       
  2328 		// Array of layers that will be added to the map initially
       
  2329 		layers: [],
       
  2330 
       
  2331 		// @option maxBounds: LatLngBounds = null
       
  2332 		// When this option is set, the map restricts the view to the given
       
  2333 		// geographical bounds, bouncing the user back if the user tries to pan
       
  2334 		// outside the view. To set the restriction dynamically, use
       
  2335 		// [`setMaxBounds`](#map-setmaxbounds) method.
       
  2336 		maxBounds: undefined,
       
  2337 
       
  2338 		// @option renderer: Renderer = *
       
  2339 		// The default method for drawing vector layers on the map. `L.SVG`
       
  2340 		// or `L.Canvas` by default depending on browser support.
       
  2341 		renderer: undefined,
       
  2342 
       
  2343 
       
  2344 		// @section Animation Options
       
  2345 		// @option zoomAnimation: Boolean = true
       
  2346 		// Whether the map zoom animation is enabled. By default it's enabled
       
  2347 		// in all browsers that support CSS3 Transitions except Android.
       
  2348 		zoomAnimation: true,
       
  2349 
       
  2350 		// @option zoomAnimationThreshold: Number = 4
       
  2351 		// Won't animate zoom if the zoom difference exceeds this value.
       
  2352 		zoomAnimationThreshold: 4,
       
  2353 
       
  2354 		// @option fadeAnimation: Boolean = true
       
  2355 		// Whether the tile fade animation is enabled. By default it's enabled
       
  2356 		// in all browsers that support CSS3 Transitions except Android.
       
  2357 		fadeAnimation: true,
       
  2358 
       
  2359 		// @option markerZoomAnimation: Boolean = true
       
  2360 		// Whether markers animate their zoom with the zoom animation, if disabled
       
  2361 		// they will disappear for the length of the animation. By default it's
       
  2362 		// enabled in all browsers that support CSS3 Transitions except Android.
       
  2363 		markerZoomAnimation: true,
       
  2364 
       
  2365 		// @option transform3DLimit: Number = 2^23
       
  2366 		// Defines the maximum size of a CSS translation transform. The default
       
  2367 		// value should not be changed unless a web browser positions layers in
       
  2368 		// the wrong place after doing a large `panBy`.
       
  2369 		transform3DLimit: 8388608, // Precision limit of a 32-bit float
       
  2370 
       
  2371 		// @section Interaction Options
       
  2372 		// @option zoomSnap: Number = 1
       
  2373 		// Forces the map's zoom level to always be a multiple of this, particularly
       
  2374 		// right after a [`fitBounds()`](#map-fitbounds) or a pinch-zoom.
       
  2375 		// By default, the zoom level snaps to the nearest integer; lower values
       
  2376 		// (e.g. `0.5` or `0.1`) allow for greater granularity. A value of `0`
       
  2377 		// means the zoom level will not be snapped after `fitBounds` or a pinch-zoom.
       
  2378 		zoomSnap: 1,
       
  2379 
       
  2380 		// @option zoomDelta: Number = 1
       
  2381 		// Controls how much the map's zoom level will change after a
       
  2382 		// [`zoomIn()`](#map-zoomin), [`zoomOut()`](#map-zoomout), pressing `+`
       
  2383 		// or `-` on the keyboard, or using the [zoom controls](#control-zoom).
       
  2384 		// Values smaller than `1` (e.g. `0.5`) allow for greater granularity.
       
  2385 		zoomDelta: 1,
       
  2386 
       
  2387 		// @option trackResize: Boolean = true
       
  2388 		// Whether the map automatically handles browser window resize to update itself.
       
  2389 		trackResize: true
       
  2390 	},
       
  2391 
       
  2392 	initialize: function (id, options) { // (HTMLElement or String, Object)
       
  2393 		options = L.setOptions(this, options);
       
  2394 
       
  2395 		this._initContainer(id);
       
  2396 		this._initLayout();
       
  2397 
       
  2398 		// hack for https://github.com/Leaflet/Leaflet/issues/1980
       
  2399 		this._onResize = L.bind(this._onResize, this);
       
  2400 
       
  2401 		this._initEvents();
       
  2402 
       
  2403 		if (options.maxBounds) {
       
  2404 			this.setMaxBounds(options.maxBounds);
       
  2405 		}
       
  2406 
       
  2407 		if (options.zoom !== undefined) {
       
  2408 			this._zoom = this._limitZoom(options.zoom);
       
  2409 		}
       
  2410 
       
  2411 		if (options.center && options.zoom !== undefined) {
       
  2412 			this.setView(L.latLng(options.center), options.zoom, {reset: true});
       
  2413 		}
       
  2414 
       
  2415 		this._handlers = [];
       
  2416 		this._layers = {};
       
  2417 		this._zoomBoundLayers = {};
       
  2418 		this._sizeChanged = true;
       
  2419 
       
  2420 		this.callInitHooks();
       
  2421 
       
  2422 		// don't animate on browsers without hardware-accelerated transitions or old Android/Opera
       
  2423 		this._zoomAnimated = L.DomUtil.TRANSITION && L.Browser.any3d && !L.Browser.mobileOpera &&
       
  2424 				this.options.zoomAnimation;
       
  2425 
       
  2426 		// zoom transitions run with the same duration for all layers, so if one of transitionend events
       
  2427 		// happens after starting zoom animation (propagating to the map pane), we know that it ended globally
       
  2428 		if (this._zoomAnimated) {
       
  2429 			this._createAnimProxy();
       
  2430 			L.DomEvent.on(this._proxy, L.DomUtil.TRANSITION_END, this._catchTransitionEnd, this);
       
  2431 		}
       
  2432 
       
  2433 		this._addLayers(this.options.layers);
       
  2434 	},
       
  2435 
       
  2436 
       
  2437 	// @section Methods for modifying map state
       
  2438 
       
  2439 	// @method setView(center: LatLng, zoom: Number, options?: Zoom/pan options): this
       
  2440 	// Sets the view of the map (geographical center and zoom) with the given
       
  2441 	// animation options.
       
  2442 	setView: function (center, zoom, options) {
       
  2443 
       
  2444 		zoom = zoom === undefined ? this._zoom : this._limitZoom(zoom);
       
  2445 		center = this._limitCenter(L.latLng(center), zoom, this.options.maxBounds);
       
  2446 		options = options || {};
       
  2447 
       
  2448 		this._stop();
       
  2449 
       
  2450 		if (this._loaded && !options.reset && options !== true) {
       
  2451 
       
  2452 			if (options.animate !== undefined) {
       
  2453 				options.zoom = L.extend({animate: options.animate}, options.zoom);
       
  2454 				options.pan = L.extend({animate: options.animate, duration: options.duration}, options.pan);
       
  2455 			}
       
  2456 
       
  2457 			// try animating pan or zoom
       
  2458 			var moved = (this._zoom !== zoom) ?
       
  2459 				this._tryAnimatedZoom && this._tryAnimatedZoom(center, zoom, options.zoom) :
       
  2460 				this._tryAnimatedPan(center, options.pan);
       
  2461 
       
  2462 			if (moved) {
       
  2463 				// prevent resize handler call, the view will refresh after animation anyway
       
  2464 				clearTimeout(this._sizeTimer);
       
  2465 				return this;
       
  2466 			}
       
  2467 		}
       
  2468 
       
  2469 		// animation didn't start, just reset the map view
       
  2470 		this._resetView(center, zoom);
       
  2471 
       
  2472 		return this;
       
  2473 	},
       
  2474 
       
  2475 	// @method setZoom(zoom: Number, options: Zoom/pan options): this
       
  2476 	// Sets the zoom of the map.
       
  2477 	setZoom: function (zoom, options) {
       
  2478 		if (!this._loaded) {
       
  2479 			this._zoom = zoom;
       
  2480 			return this;
       
  2481 		}
       
  2482 		return this.setView(this.getCenter(), zoom, {zoom: options});
       
  2483 	},
       
  2484 
       
  2485 	// @method zoomIn(delta?: Number, options?: Zoom options): this
       
  2486 	// Increases the zoom of the map by `delta` ([`zoomDelta`](#map-zoomdelta) by default).
       
  2487 	zoomIn: function (delta, options) {
       
  2488 		delta = delta || (L.Browser.any3d ? this.options.zoomDelta : 1);
       
  2489 		return this.setZoom(this._zoom + delta, options);
       
  2490 	},
       
  2491 
       
  2492 	// @method zoomOut(delta?: Number, options?: Zoom options): this
       
  2493 	// Decreases the zoom of the map by `delta` ([`zoomDelta`](#map-zoomdelta) by default).
       
  2494 	zoomOut: function (delta, options) {
       
  2495 		delta = delta || (L.Browser.any3d ? this.options.zoomDelta : 1);
       
  2496 		return this.setZoom(this._zoom - delta, options);
       
  2497 	},
       
  2498 
       
  2499 	// @method setZoomAround(latlng: LatLng, zoom: Number, options: Zoom options): this
       
  2500 	// Zooms the map while keeping a specified geographical point on the map
       
  2501 	// stationary (e.g. used internally for scroll zoom and double-click zoom).
       
  2502 	// @alternative
       
  2503 	// @method setZoomAround(offset: Point, zoom: Number, options: Zoom options): this
       
  2504 	// Zooms the map while keeping a specified pixel on the map (relative to the top-left corner) stationary.
       
  2505 	setZoomAround: function (latlng, zoom, options) {
       
  2506 		var scale = this.getZoomScale(zoom),
       
  2507 		    viewHalf = this.getSize().divideBy(2),
       
  2508 		    containerPoint = latlng instanceof L.Point ? latlng : this.latLngToContainerPoint(latlng),
       
  2509 
       
  2510 		    centerOffset = containerPoint.subtract(viewHalf).multiplyBy(1 - 1 / scale),
       
  2511 		    newCenter = this.containerPointToLatLng(viewHalf.add(centerOffset));
       
  2512 
       
  2513 		return this.setView(newCenter, zoom, {zoom: options});
       
  2514 	},
       
  2515 
       
  2516 	_getBoundsCenterZoom: function (bounds, options) {
       
  2517 
       
  2518 		options = options || {};
       
  2519 		bounds = bounds.getBounds ? bounds.getBounds() : L.latLngBounds(bounds);
       
  2520 
       
  2521 		var paddingTL = L.point(options.paddingTopLeft || options.padding || [0, 0]),
       
  2522 		    paddingBR = L.point(options.paddingBottomRight || options.padding || [0, 0]),
       
  2523 
       
  2524 		    zoom = this.getBoundsZoom(bounds, false, paddingTL.add(paddingBR));
       
  2525 
       
  2526 		zoom = (typeof options.maxZoom === 'number') ? Math.min(options.maxZoom, zoom) : zoom;
       
  2527 
       
  2528 		var paddingOffset = paddingBR.subtract(paddingTL).divideBy(2),
       
  2529 
       
  2530 		    swPoint = this.project(bounds.getSouthWest(), zoom),
       
  2531 		    nePoint = this.project(bounds.getNorthEast(), zoom),
       
  2532 		    center = this.unproject(swPoint.add(nePoint).divideBy(2).add(paddingOffset), zoom);
       
  2533 
       
  2534 		return {
       
  2535 			center: center,
       
  2536 			zoom: zoom
       
  2537 		};
       
  2538 	},
       
  2539 
       
  2540 	// @method fitBounds(bounds: LatLngBounds, options?: fitBounds options): this
       
  2541 	// Sets a map view that contains the given geographical bounds with the
       
  2542 	// maximum zoom level possible.
       
  2543 	fitBounds: function (bounds, options) {
       
  2544 
       
  2545 		bounds = L.latLngBounds(bounds);
       
  2546 
       
  2547 		if (!bounds.isValid()) {
       
  2548 			throw new Error('Bounds are not valid.');
       
  2549 		}
       
  2550 
       
  2551 		var target = this._getBoundsCenterZoom(bounds, options);
       
  2552 		return this.setView(target.center, target.zoom, options);
       
  2553 	},
       
  2554 
       
  2555 	// @method fitWorld(options?: fitBounds options): this
       
  2556 	// Sets a map view that mostly contains the whole world with the maximum
       
  2557 	// zoom level possible.
       
  2558 	fitWorld: function (options) {
       
  2559 		return this.fitBounds([[-90, -180], [90, 180]], options);
       
  2560 	},
       
  2561 
       
  2562 	// @method panTo(latlng: LatLng, options?: Pan options): this
       
  2563 	// Pans the map to a given center.
       
  2564 	panTo: function (center, options) { // (LatLng)
       
  2565 		return this.setView(center, this._zoom, {pan: options});
       
  2566 	},
       
  2567 
       
  2568 	// @method panBy(offset: Point): this
       
  2569 	// Pans the map by a given number of pixels (animated).
       
  2570 	panBy: function (offset, options) {
       
  2571 		offset = L.point(offset).round();
       
  2572 		options = options || {};
       
  2573 
       
  2574 		if (!offset.x && !offset.y) {
       
  2575 			return this.fire('moveend');
       
  2576 		}
       
  2577 		// If we pan too far, Chrome gets issues with tiles
       
  2578 		// and makes them disappear or appear in the wrong place (slightly offset) #2602
       
  2579 		if (options.animate !== true && !this.getSize().contains(offset)) {
       
  2580 			this._resetView(this.unproject(this.project(this.getCenter()).add(offset)), this.getZoom());
       
  2581 			return this;
       
  2582 		}
       
  2583 
       
  2584 		if (!this._panAnim) {
       
  2585 			this._panAnim = new L.PosAnimation();
       
  2586 
       
  2587 			this._panAnim.on({
       
  2588 				'step': this._onPanTransitionStep,
       
  2589 				'end': this._onPanTransitionEnd
       
  2590 			}, this);
       
  2591 		}
       
  2592 
       
  2593 		// don't fire movestart if animating inertia
       
  2594 		if (!options.noMoveStart) {
       
  2595 			this.fire('movestart');
       
  2596 		}
       
  2597 
       
  2598 		// animate pan unless animate: false specified
       
  2599 		if (options.animate !== false) {
       
  2600 			L.DomUtil.addClass(this._mapPane, 'leaflet-pan-anim');
       
  2601 
       
  2602 			var newPos = this._getMapPanePos().subtract(offset).round();
       
  2603 			this._panAnim.run(this._mapPane, newPos, options.duration || 0.25, options.easeLinearity);
       
  2604 		} else {
       
  2605 			this._rawPanBy(offset);
       
  2606 			this.fire('move').fire('moveend');
       
  2607 		}
       
  2608 
       
  2609 		return this;
       
  2610 	},
       
  2611 
       
  2612 	// @method flyTo(latlng: LatLng, zoom?: Number, options?: Zoom/pan options): this
       
  2613 	// Sets the view of the map (geographical center and zoom) performing a smooth
       
  2614 	// pan-zoom animation.
       
  2615 	flyTo: function (targetCenter, targetZoom, options) {
       
  2616 
       
  2617 		options = options || {};
       
  2618 		if (options.animate === false || !L.Browser.any3d) {
       
  2619 			return this.setView(targetCenter, targetZoom, options);
       
  2620 		}
       
  2621 
       
  2622 		this._stop();
       
  2623 
       
  2624 		var from = this.project(this.getCenter()),
       
  2625 		    to = this.project(targetCenter),
       
  2626 		    size = this.getSize(),
       
  2627 		    startZoom = this._zoom;
       
  2628 
       
  2629 		targetCenter = L.latLng(targetCenter);
       
  2630 		targetZoom = targetZoom === undefined ? startZoom : targetZoom;
       
  2631 
       
  2632 		var w0 = Math.max(size.x, size.y),
       
  2633 		    w1 = w0 * this.getZoomScale(startZoom, targetZoom),
       
  2634 		    u1 = (to.distanceTo(from)) || 1,
       
  2635 		    rho = 1.42,
       
  2636 		    rho2 = rho * rho;
       
  2637 
       
  2638 		function r(i) {
       
  2639 			var s1 = i ? -1 : 1,
       
  2640 			    s2 = i ? w1 : w0,
       
  2641 			    t1 = w1 * w1 - w0 * w0 + s1 * rho2 * rho2 * u1 * u1,
       
  2642 			    b1 = 2 * s2 * rho2 * u1,
       
  2643 			    b = t1 / b1,
       
  2644 			    sq = Math.sqrt(b * b + 1) - b;
       
  2645 
       
  2646 			    // workaround for floating point precision bug when sq = 0, log = -Infinite,
       
  2647 			    // thus triggering an infinite loop in flyTo
       
  2648 			    var log = sq < 0.000000001 ? -18 : Math.log(sq);
       
  2649 
       
  2650 			return log;
       
  2651 		}
       
  2652 
       
  2653 		function sinh(n) { return (Math.exp(n) - Math.exp(-n)) / 2; }
       
  2654 		function cosh(n) { return (Math.exp(n) + Math.exp(-n)) / 2; }
       
  2655 		function tanh(n) { return sinh(n) / cosh(n); }
       
  2656 
       
  2657 		var r0 = r(0);
       
  2658 
       
  2659 		function w(s) { return w0 * (cosh(r0) / cosh(r0 + rho * s)); }
       
  2660 		function u(s) { return w0 * (cosh(r0) * tanh(r0 + rho * s) - sinh(r0)) / rho2; }
       
  2661 
       
  2662 		function easeOut(t) { return 1 - Math.pow(1 - t, 1.5); }
       
  2663 
       
  2664 		var start = Date.now(),
       
  2665 		    S = (r(1) - r0) / rho,
       
  2666 		    duration = options.duration ? 1000 * options.duration : 1000 * S * 0.8;
       
  2667 
       
  2668 		function frame() {
       
  2669 			var t = (Date.now() - start) / duration,
       
  2670 			    s = easeOut(t) * S;
       
  2671 
       
  2672 			if (t <= 1) {
       
  2673 				this._flyToFrame = L.Util.requestAnimFrame(frame, this);
       
  2674 
       
  2675 				this._move(
       
  2676 					this.unproject(from.add(to.subtract(from).multiplyBy(u(s) / u1)), startZoom),
       
  2677 					this.getScaleZoom(w0 / w(s), startZoom),
       
  2678 					{flyTo: true});
       
  2679 
       
  2680 			} else {
       
  2681 				this
       
  2682 					._move(targetCenter, targetZoom)
       
  2683 					._moveEnd(true);
       
  2684 			}
       
  2685 		}
       
  2686 
       
  2687 		this._moveStart(true);
       
  2688 
       
  2689 		frame.call(this);
       
  2690 		return this;
       
  2691 	},
       
  2692 
       
  2693 	// @method flyToBounds(bounds: LatLngBounds, options?: fitBounds options): this
       
  2694 	// Sets the view of the map with a smooth animation like [`flyTo`](#map-flyto),
       
  2695 	// but takes a bounds parameter like [`fitBounds`](#map-fitbounds).
       
  2696 	flyToBounds: function (bounds, options) {
       
  2697 		var target = this._getBoundsCenterZoom(bounds, options);
       
  2698 		return this.flyTo(target.center, target.zoom, options);
       
  2699 	},
       
  2700 
       
  2701 	// @method setMaxBounds(bounds: Bounds): this
       
  2702 	// Restricts the map view to the given bounds (see the [maxBounds](#map-maxbounds) option).
       
  2703 	setMaxBounds: function (bounds) {
       
  2704 		bounds = L.latLngBounds(bounds);
       
  2705 
       
  2706 		if (!bounds.isValid()) {
       
  2707 			this.options.maxBounds = null;
       
  2708 			return this.off('moveend', this._panInsideMaxBounds);
       
  2709 		} else if (this.options.maxBounds) {
       
  2710 			this.off('moveend', this._panInsideMaxBounds);
       
  2711 		}
       
  2712 
       
  2713 		this.options.maxBounds = bounds;
       
  2714 
       
  2715 		if (this._loaded) {
       
  2716 			this._panInsideMaxBounds();
       
  2717 		}
       
  2718 
       
  2719 		return this.on('moveend', this._panInsideMaxBounds);
       
  2720 	},
       
  2721 
       
  2722 	// @method setMinZoom(zoom: Number): this
       
  2723 	// Sets the lower limit for the available zoom levels (see the [minZoom](#map-minzoom) option).
       
  2724 	setMinZoom: function (zoom) {
       
  2725 		this.options.minZoom = zoom;
       
  2726 
       
  2727 		if (this._loaded && this.getZoom() < this.options.minZoom) {
       
  2728 			return this.setZoom(zoom);
       
  2729 		}
       
  2730 
       
  2731 		return this;
       
  2732 	},
       
  2733 
       
  2734 	// @method setMaxZoom(zoom: Number): this
       
  2735 	// Sets the upper limit for the available zoom levels (see the [maxZoom](#map-maxzoom) option).
       
  2736 	setMaxZoom: function (zoom) {
       
  2737 		this.options.maxZoom = zoom;
       
  2738 
       
  2739 		if (this._loaded && (this.getZoom() > this.options.maxZoom)) {
       
  2740 			return this.setZoom(zoom);
       
  2741 		}
       
  2742 
       
  2743 		return this;
       
  2744 	},
       
  2745 
       
  2746 	// @method panInsideBounds(bounds: LatLngBounds, options?: Pan options): this
       
  2747 	// Pans the map to the closest view that would lie inside the given bounds (if it's not already), controlling the animation using the options specific, if any.
       
  2748 	panInsideBounds: function (bounds, options) {
       
  2749 		this._enforcingBounds = true;
       
  2750 		var center = this.getCenter(),
       
  2751 		    newCenter = this._limitCenter(center, this._zoom, L.latLngBounds(bounds));
       
  2752 
       
  2753 		if (!center.equals(newCenter)) {
       
  2754 			this.panTo(newCenter, options);
       
  2755 		}
       
  2756 
       
  2757 		this._enforcingBounds = false;
       
  2758 		return this;
       
  2759 	},
       
  2760 
       
  2761 	// @method invalidateSize(options: Zoom/Pan options): this
       
  2762 	// Checks if the map container size changed and updates the map if so —
       
  2763 	// call it after you've changed the map size dynamically, also animating
       
  2764 	// pan by default. If `options.pan` is `false`, panning will not occur.
       
  2765 	// If `options.debounceMoveend` is `true`, it will delay `moveend` event so
       
  2766 	// that it doesn't happen often even if the method is called many
       
  2767 	// times in a row.
       
  2768 
       
  2769 	// @alternative
       
  2770 	// @method invalidateSize(animate: Boolean): this
       
  2771 	// Checks if the map container size changed and updates the map if so —
       
  2772 	// call it after you've changed the map size dynamically, also animating
       
  2773 	// pan by default.
       
  2774 	invalidateSize: function (options) {
       
  2775 		if (!this._loaded) { return this; }
       
  2776 
       
  2777 		options = L.extend({
       
  2778 			animate: false,
       
  2779 			pan: true
       
  2780 		}, options === true ? {animate: true} : options);
       
  2781 
       
  2782 		var oldSize = this.getSize();
       
  2783 		this._sizeChanged = true;
       
  2784 		this._lastCenter = null;
       
  2785 
       
  2786 		var newSize = this.getSize(),
       
  2787 		    oldCenter = oldSize.divideBy(2).round(),
       
  2788 		    newCenter = newSize.divideBy(2).round(),
       
  2789 		    offset = oldCenter.subtract(newCenter);
       
  2790 
       
  2791 		if (!offset.x && !offset.y) { return this; }
       
  2792 
       
  2793 		if (options.animate && options.pan) {
       
  2794 			this.panBy(offset);
       
  2795 
       
  2796 		} else {
       
  2797 			if (options.pan) {
       
  2798 				this._rawPanBy(offset);
       
  2799 			}
       
  2800 
       
  2801 			this.fire('move');
       
  2802 
       
  2803 			if (options.debounceMoveend) {
       
  2804 				clearTimeout(this._sizeTimer);
       
  2805 				this._sizeTimer = setTimeout(L.bind(this.fire, this, 'moveend'), 200);
       
  2806 			} else {
       
  2807 				this.fire('moveend');
       
  2808 			}
       
  2809 		}
       
  2810 
       
  2811 		// @section Map state change events
       
  2812 		// @event resize: ResizeEvent
       
  2813 		// Fired when the map is resized.
       
  2814 		return this.fire('resize', {
       
  2815 			oldSize: oldSize,
       
  2816 			newSize: newSize
       
  2817 		});
       
  2818 	},
       
  2819 
       
  2820 	// @section Methods for modifying map state
       
  2821 	// @method stop(): this
       
  2822 	// Stops the currently running `panTo` or `flyTo` animation, if any.
       
  2823 	stop: function () {
       
  2824 		this.setZoom(this._limitZoom(this._zoom));
       
  2825 		if (!this.options.zoomSnap) {
       
  2826 			this.fire('viewreset');
       
  2827 		}
       
  2828 		return this._stop();
       
  2829 	},
       
  2830 
       
  2831 	// @section Geolocation methods
       
  2832 	// @method locate(options?: Locate options): this
       
  2833 	// Tries to locate the user using the Geolocation API, firing a [`locationfound`](#map-locationfound)
       
  2834 	// event with location data on success or a [`locationerror`](#map-locationerror) event on failure,
       
  2835 	// and optionally sets the map view to the user's location with respect to
       
  2836 	// detection accuracy (or to the world view if geolocation failed).
       
  2837 	// Note that, if your page doesn't use HTTPS, this method will fail in
       
  2838 	// modern browsers ([Chrome 50 and newer](https://sites.google.com/a/chromium.org/dev/Home/chromium-security/deprecating-powerful-features-on-insecure-origins))
       
  2839 	// See `Locate options` for more details.
       
  2840 	locate: function (options) {
       
  2841 
       
  2842 		options = this._locateOptions = L.extend({
       
  2843 			timeout: 10000,
       
  2844 			watch: false
       
  2845 			// setView: false
       
  2846 			// maxZoom: <Number>
       
  2847 			// maximumAge: 0
       
  2848 			// enableHighAccuracy: false
       
  2849 		}, options);
       
  2850 
       
  2851 		if (!('geolocation' in navigator)) {
       
  2852 			this._handleGeolocationError({
       
  2853 				code: 0,
       
  2854 				message: 'Geolocation not supported.'
       
  2855 			});
       
  2856 			return this;
       
  2857 		}
       
  2858 
       
  2859 		var onResponse = L.bind(this._handleGeolocationResponse, this),
       
  2860 		    onError = L.bind(this._handleGeolocationError, this);
       
  2861 
       
  2862 		if (options.watch) {
       
  2863 			this._locationWatchId =
       
  2864 			        navigator.geolocation.watchPosition(onResponse, onError, options);
       
  2865 		} else {
       
  2866 			navigator.geolocation.getCurrentPosition(onResponse, onError, options);
       
  2867 		}
       
  2868 		return this;
       
  2869 	},
       
  2870 
       
  2871 	// @method stopLocate(): this
       
  2872 	// Stops watching location previously initiated by `map.locate({watch: true})`
       
  2873 	// and aborts resetting the map view if map.locate was called with
       
  2874 	// `{setView: true}`.
       
  2875 	stopLocate: function () {
       
  2876 		if (navigator.geolocation && navigator.geolocation.clearWatch) {
       
  2877 			navigator.geolocation.clearWatch(this._locationWatchId);
       
  2878 		}
       
  2879 		if (this._locateOptions) {
       
  2880 			this._locateOptions.setView = false;
       
  2881 		}
       
  2882 		return this;
       
  2883 	},
       
  2884 
       
  2885 	_handleGeolocationError: function (error) {
       
  2886 		var c = error.code,
       
  2887 		    message = error.message ||
       
  2888 		            (c === 1 ? 'permission denied' :
       
  2889 		            (c === 2 ? 'position unavailable' : 'timeout'));
       
  2890 
       
  2891 		if (this._locateOptions.setView && !this._loaded) {
       
  2892 			this.fitWorld();
       
  2893 		}
       
  2894 
       
  2895 		// @section Location events
       
  2896 		// @event locationerror: ErrorEvent
       
  2897 		// Fired when geolocation (using the [`locate`](#map-locate) method) failed.
       
  2898 		this.fire('locationerror', {
       
  2899 			code: c,
       
  2900 			message: 'Geolocation error: ' + message + '.'
       
  2901 		});
       
  2902 	},
       
  2903 
       
  2904 	_handleGeolocationResponse: function (pos) {
       
  2905 		var lat = pos.coords.latitude,
       
  2906 		    lng = pos.coords.longitude,
       
  2907 		    latlng = new L.LatLng(lat, lng),
       
  2908 		    bounds = latlng.toBounds(pos.coords.accuracy),
       
  2909 		    options = this._locateOptions;
       
  2910 
       
  2911 		if (options.setView) {
       
  2912 			var zoom = this.getBoundsZoom(bounds);
       
  2913 			this.setView(latlng, options.maxZoom ? Math.min(zoom, options.maxZoom) : zoom);
       
  2914 		}
       
  2915 
       
  2916 		var data = {
       
  2917 			latlng: latlng,
       
  2918 			bounds: bounds,
       
  2919 			timestamp: pos.timestamp
       
  2920 		};
       
  2921 
       
  2922 		for (var i in pos.coords) {
       
  2923 			if (typeof pos.coords[i] === 'number') {
       
  2924 				data[i] = pos.coords[i];
       
  2925 			}
       
  2926 		}
       
  2927 
       
  2928 		// @event locationfound: LocationEvent
       
  2929 		// Fired when geolocation (using the [`locate`](#map-locate) method)
       
  2930 		// went successfully.
       
  2931 		this.fire('locationfound', data);
       
  2932 	},
       
  2933 
       
  2934 	// TODO handler.addTo
       
  2935 	// TODO Appropiate docs section?
       
  2936 	// @section Other Methods
       
  2937 	// @method addHandler(name: String, HandlerClass: Function): this
       
  2938 	// Adds a new `Handler` to the map, given its name and constructor function.
       
  2939 	addHandler: function (name, HandlerClass) {
       
  2940 		if (!HandlerClass) { return this; }
       
  2941 
       
  2942 		var handler = this[name] = new HandlerClass(this);
       
  2943 
       
  2944 		this._handlers.push(handler);
       
  2945 
       
  2946 		if (this.options[name]) {
       
  2947 			handler.enable();
       
  2948 		}
       
  2949 
       
  2950 		return this;
       
  2951 	},
       
  2952 
       
  2953 	// @method remove(): this
       
  2954 	// Destroys the map and clears all related event listeners.
       
  2955 	remove: function () {
       
  2956 
       
  2957 		this._initEvents(true);
       
  2958 
       
  2959 		if (this._containerId !== this._container._leaflet_id) {
       
  2960 			throw new Error('Map container is being reused by another instance');
       
  2961 		}
       
  2962 
       
  2963 		try {
       
  2964 			// throws error in IE6-8
       
  2965 			delete this._container._leaflet_id;
       
  2966 			delete this._containerId;
       
  2967 		} catch (e) {
       
  2968 			/*eslint-disable */
       
  2969 			this._container._leaflet_id = undefined;
       
  2970 			/*eslint-enable */
       
  2971 			this._containerId = undefined;
       
  2972 		}
       
  2973 
       
  2974 		L.DomUtil.remove(this._mapPane);
       
  2975 
       
  2976 		if (this._clearControlPos) {
       
  2977 			this._clearControlPos();
       
  2978 		}
       
  2979 
       
  2980 		this._clearHandlers();
       
  2981 
       
  2982 		if (this._loaded) {
       
  2983 			// @section Map state change events
       
  2984 			// @event unload: Event
       
  2985 			// Fired when the map is destroyed with [remove](#map-remove) method.
       
  2986 			this.fire('unload');
       
  2987 		}
       
  2988 
       
  2989 		for (var i in this._layers) {
       
  2990 			this._layers[i].remove();
       
  2991 		}
       
  2992 
       
  2993 		return this;
       
  2994 	},
       
  2995 
       
  2996 	// @section Other Methods
       
  2997 	// @method createPane(name: String, container?: HTMLElement): HTMLElement
       
  2998 	// Creates a new [map pane](#map-pane) with the given name if it doesn't exist already,
       
  2999 	// then returns it. The pane is created as a children of `container`, or
       
  3000 	// as a children of the main map pane if not set.
       
  3001 	createPane: function (name, container) {
       
  3002 		var className = 'leaflet-pane' + (name ? ' leaflet-' + name.replace('Pane', '') + '-pane' : ''),
       
  3003 		    pane = L.DomUtil.create('div', className, container || this._mapPane);
       
  3004 
       
  3005 		if (name) {
       
  3006 			this._panes[name] = pane;
       
  3007 		}
       
  3008 		return pane;
       
  3009 	},
       
  3010 
       
  3011 	// @section Methods for Getting Map State
       
  3012 
       
  3013 	// @method getCenter(): LatLng
       
  3014 	// Returns the geographical center of the map view
       
  3015 	getCenter: function () {
       
  3016 		this._checkIfLoaded();
       
  3017 
       
  3018 		if (this._lastCenter && !this._moved()) {
       
  3019 			return this._lastCenter;
       
  3020 		}
       
  3021 		return this.layerPointToLatLng(this._getCenterLayerPoint());
       
  3022 	},
       
  3023 
       
  3024 	// @method getZoom(): Number
       
  3025 	// Returns the current zoom level of the map view
       
  3026 	getZoom: function () {
       
  3027 		return this._zoom;
       
  3028 	},
       
  3029 
       
  3030 	// @method getBounds(): LatLngBounds
       
  3031 	// Returns the geographical bounds visible in the current map view
       
  3032 	getBounds: function () {
       
  3033 		var bounds = this.getPixelBounds(),
       
  3034 		    sw = this.unproject(bounds.getBottomLeft()),
       
  3035 		    ne = this.unproject(bounds.getTopRight());
       
  3036 
       
  3037 		return new L.LatLngBounds(sw, ne);
       
  3038 	},
       
  3039 
       
  3040 	// @method getMinZoom(): Number
       
  3041 	// Returns the minimum zoom level of the map (if set in the `minZoom` option of the map or of any layers), or `0` by default.
       
  3042 	getMinZoom: function () {
       
  3043 		return this.options.minZoom === undefined ? this._layersMinZoom || 0 : this.options.minZoom;
       
  3044 	},
       
  3045 
       
  3046 	// @method getMaxZoom(): Number
       
  3047 	// Returns the maximum zoom level of the map (if set in the `maxZoom` option of the map or of any layers).
       
  3048 	getMaxZoom: function () {
       
  3049 		return this.options.maxZoom === undefined ?
       
  3050 			(this._layersMaxZoom === undefined ? Infinity : this._layersMaxZoom) :
       
  3051 			this.options.maxZoom;
       
  3052 	},
       
  3053 
       
  3054 	// @method getBoundsZoom(bounds: LatLngBounds, inside?: Boolean): Number
       
  3055 	// Returns the maximum zoom level on which the given bounds fit to the map
       
  3056 	// view in its entirety. If `inside` (optional) is set to `true`, the method
       
  3057 	// instead returns the minimum zoom level on which the map view fits into
       
  3058 	// the given bounds in its entirety.
       
  3059 	getBoundsZoom: function (bounds, inside, padding) { // (LatLngBounds[, Boolean, Point]) -> Number
       
  3060 		bounds = L.latLngBounds(bounds);
       
  3061 		padding = L.point(padding || [0, 0]);
       
  3062 
       
  3063 		var zoom = this.getZoom() || 0,
       
  3064 		    min = this.getMinZoom(),
       
  3065 		    max = this.getMaxZoom(),
       
  3066 		    nw = bounds.getNorthWest(),
       
  3067 		    se = bounds.getSouthEast(),
       
  3068 		    size = this.getSize().subtract(padding),
       
  3069 		    boundsSize = L.bounds(this.project(se, zoom), this.project(nw, zoom)).getSize(),
       
  3070 		    snap = L.Browser.any3d ? this.options.zoomSnap : 1;
       
  3071 
       
  3072 		var scale = Math.min(size.x / boundsSize.x, size.y / boundsSize.y);
       
  3073 		zoom = this.getScaleZoom(scale, zoom);
       
  3074 
       
  3075 		if (snap) {
       
  3076 			zoom = Math.round(zoom / (snap / 100)) * (snap / 100); // don't jump if within 1% of a snap level
       
  3077 			zoom = inside ? Math.ceil(zoom / snap) * snap : Math.floor(zoom / snap) * snap;
       
  3078 		}
       
  3079 
       
  3080 		return Math.max(min, Math.min(max, zoom));
       
  3081 	},
       
  3082 
       
  3083 	// @method getSize(): Point
       
  3084 	// Returns the current size of the map container (in pixels).
       
  3085 	getSize: function () {
       
  3086 		if (!this._size || this._sizeChanged) {
       
  3087 			this._size = new L.Point(
       
  3088 				this._container.clientWidth || 0,
       
  3089 				this._container.clientHeight || 0);
       
  3090 
       
  3091 			this._sizeChanged = false;
       
  3092 		}
       
  3093 		return this._size.clone();
       
  3094 	},
       
  3095 
       
  3096 	// @method getPixelBounds(): Bounds
       
  3097 	// Returns the bounds of the current map view in projected pixel
       
  3098 	// coordinates (sometimes useful in layer and overlay implementations).
       
  3099 	getPixelBounds: function (center, zoom) {
       
  3100 		var topLeftPoint = this._getTopLeftPoint(center, zoom);
       
  3101 		return new L.Bounds(topLeftPoint, topLeftPoint.add(this.getSize()));
       
  3102 	},
       
  3103 
       
  3104 	// TODO: Check semantics - isn't the pixel origin the 0,0 coord relative to
       
  3105 	// the map pane? "left point of the map layer" can be confusing, specially
       
  3106 	// since there can be negative offsets.
       
  3107 	// @method getPixelOrigin(): Point
       
  3108 	// Returns the projected pixel coordinates of the top left point of
       
  3109 	// the map layer (useful in custom layer and overlay implementations).
       
  3110 	getPixelOrigin: function () {
       
  3111 		this._checkIfLoaded();
       
  3112 		return this._pixelOrigin;
       
  3113 	},
       
  3114 
       
  3115 	// @method getPixelWorldBounds(zoom?: Number): Bounds
       
  3116 	// Returns the world's bounds in pixel coordinates for zoom level `zoom`.
       
  3117 	// If `zoom` is omitted, the map's current zoom level is used.
       
  3118 	getPixelWorldBounds: function (zoom) {
       
  3119 		return this.options.crs.getProjectedBounds(zoom === undefined ? this.getZoom() : zoom);
       
  3120 	},
       
  3121 
       
  3122 	// @section Other Methods
       
  3123 
       
  3124 	// @method getPane(pane: String|HTMLElement): HTMLElement
       
  3125 	// Returns a [map pane](#map-pane), given its name or its HTML element (its identity).
       
  3126 	getPane: function (pane) {
       
  3127 		return typeof pane === 'string' ? this._panes[pane] : pane;
       
  3128 	},
       
  3129 
       
  3130 	// @method getPanes(): Object
       
  3131 	// Returns a plain object containing the names of all [panes](#map-pane) as keys and
       
  3132 	// the panes as values.
       
  3133 	getPanes: function () {
       
  3134 		return this._panes;
       
  3135 	},
       
  3136 
       
  3137 	// @method getContainer: HTMLElement
       
  3138 	// Returns the HTML element that contains the map.
       
  3139 	getContainer: function () {
       
  3140 		return this._container;
       
  3141 	},
       
  3142 
       
  3143 
       
  3144 	// @section Conversion Methods
       
  3145 
       
  3146 	// @method getZoomScale(toZoom: Number, fromZoom: Number): Number
       
  3147 	// Returns the scale factor to be applied to a map transition from zoom level
       
  3148 	// `fromZoom` to `toZoom`. Used internally to help with zoom animations.
       
  3149 	getZoomScale: function (toZoom, fromZoom) {
       
  3150 		// TODO replace with universal implementation after refactoring projections
       
  3151 		var crs = this.options.crs;
       
  3152 		fromZoom = fromZoom === undefined ? this._zoom : fromZoom;
       
  3153 		return crs.scale(toZoom) / crs.scale(fromZoom);
       
  3154 	},
       
  3155 
       
  3156 	// @method getScaleZoom(scale: Number, fromZoom: Number): Number
       
  3157 	// Returns the zoom level that the map would end up at, if it is at `fromZoom`
       
  3158 	// level and everything is scaled by a factor of `scale`. Inverse of
       
  3159 	// [`getZoomScale`](#map-getZoomScale).
       
  3160 	getScaleZoom: function (scale, fromZoom) {
       
  3161 		var crs = this.options.crs;
       
  3162 		fromZoom = fromZoom === undefined ? this._zoom : fromZoom;
       
  3163 		var zoom = crs.zoom(scale * crs.scale(fromZoom));
       
  3164 		return isNaN(zoom) ? Infinity : zoom;
       
  3165 	},
       
  3166 
       
  3167 	// @method project(latlng: LatLng, zoom: Number): Point
       
  3168 	// Projects a geographical coordinate `LatLng` according to the projection
       
  3169 	// of the map's CRS, then scales it according to `zoom` and the CRS's
       
  3170 	// `Transformation`. The result is pixel coordinate relative to
       
  3171 	// the CRS origin.
       
  3172 	project: function (latlng, zoom) {
       
  3173 		zoom = zoom === undefined ? this._zoom : zoom;
       
  3174 		return this.options.crs.latLngToPoint(L.latLng(latlng), zoom);
       
  3175 	},
       
  3176 
       
  3177 	// @method unproject(point: Point, zoom: Number): LatLng
       
  3178 	// Inverse of [`project`](#map-project).
       
  3179 	unproject: function (point, zoom) {
       
  3180 		zoom = zoom === undefined ? this._zoom : zoom;
       
  3181 		return this.options.crs.pointToLatLng(L.point(point), zoom);
       
  3182 	},
       
  3183 
       
  3184 	// @method layerPointToLatLng(point: Point): LatLng
       
  3185 	// Given a pixel coordinate relative to the [origin pixel](#map-getpixelorigin),
       
  3186 	// returns the corresponding geographical coordinate (for the current zoom level).
       
  3187 	layerPointToLatLng: function (point) {
       
  3188 		var projectedPoint = L.point(point).add(this.getPixelOrigin());
       
  3189 		return this.unproject(projectedPoint);
       
  3190 	},
       
  3191 
       
  3192 	// @method latLngToLayerPoint(latlng: LatLng): Point
       
  3193 	// Given a geographical coordinate, returns the corresponding pixel coordinate
       
  3194 	// relative to the [origin pixel](#map-getpixelorigin).
       
  3195 	latLngToLayerPoint: function (latlng) {
       
  3196 		var projectedPoint = this.project(L.latLng(latlng))._round();
       
  3197 		return projectedPoint._subtract(this.getPixelOrigin());
       
  3198 	},
       
  3199 
       
  3200 	// @method wrapLatLng(latlng: LatLng): LatLng
       
  3201 	// Returns a `LatLng` where `lat` and `lng` has been wrapped according to the
       
  3202 	// map's CRS's `wrapLat` and `wrapLng` properties, if they are outside the
       
  3203 	// CRS's bounds.
       
  3204 	// By default this means longitude is wrapped around the dateline so its
       
  3205 	// value is between -180 and +180 degrees.
       
  3206 	wrapLatLng: function (latlng) {
       
  3207 		return this.options.crs.wrapLatLng(L.latLng(latlng));
       
  3208 	},
       
  3209 
       
  3210 	// @method wrapLatLngBounds(bounds: LatLngBounds): LatLngBounds
       
  3211 	// Returns a `LatLngBounds` with the same size as the given one, ensuring that
       
  3212 	// its center is within the CRS's bounds.
       
  3213 	// By default this means the center longitude is wrapped around the dateline so its
       
  3214 	// value is between -180 and +180 degrees, and the majority of the bounds
       
  3215 	// overlaps the CRS's bounds.
       
  3216 	wrapLatLngBounds: function (latlng) {
       
  3217 		return this.options.crs.wrapLatLngBounds(L.latLngBounds(latlng));
       
  3218 	},
       
  3219 
       
  3220 	// @method distance(latlng1: LatLng, latlng2: LatLng): Number
       
  3221 	// Returns the distance between two geographical coordinates according to
       
  3222 	// the map's CRS. By default this measures distance in meters.
       
  3223 	distance: function (latlng1, latlng2) {
       
  3224 		return this.options.crs.distance(L.latLng(latlng1), L.latLng(latlng2));
       
  3225 	},
       
  3226 
       
  3227 	// @method containerPointToLayerPoint(point: Point): Point
       
  3228 	// Given a pixel coordinate relative to the map container, returns the corresponding
       
  3229 	// pixel coordinate relative to the [origin pixel](#map-getpixelorigin).
       
  3230 	containerPointToLayerPoint: function (point) { // (Point)
       
  3231 		return L.point(point).subtract(this._getMapPanePos());
       
  3232 	},
       
  3233 
       
  3234 	// @method layerPointToContainerPoint(point: Point): Point
       
  3235 	// Given a pixel coordinate relative to the [origin pixel](#map-getpixelorigin),
       
  3236 	// returns the corresponding pixel coordinate relative to the map container.
       
  3237 	layerPointToContainerPoint: function (point) { // (Point)
       
  3238 		return L.point(point).add(this._getMapPanePos());
       
  3239 	},
       
  3240 
       
  3241 	// @method containerPointToLatLng(point: Point): LatLng
       
  3242 	// Given a pixel coordinate relative to the map container, returns
       
  3243 	// the corresponding geographical coordinate (for the current zoom level).
       
  3244 	containerPointToLatLng: function (point) {
       
  3245 		var layerPoint = this.containerPointToLayerPoint(L.point(point));
       
  3246 		return this.layerPointToLatLng(layerPoint);
       
  3247 	},
       
  3248 
       
  3249 	// @method latLngToContainerPoint(latlng: LatLng): Point
       
  3250 	// Given a geographical coordinate, returns the corresponding pixel coordinate
       
  3251 	// relative to the map container.
       
  3252 	latLngToContainerPoint: function (latlng) {
       
  3253 		return this.layerPointToContainerPoint(this.latLngToLayerPoint(L.latLng(latlng)));
       
  3254 	},
       
  3255 
       
  3256 	// @method mouseEventToContainerPoint(ev: MouseEvent): Point
       
  3257 	// Given a MouseEvent object, returns the pixel coordinate relative to the
       
  3258 	// map container where the event took place.
       
  3259 	mouseEventToContainerPoint: function (e) {
       
  3260 		return L.DomEvent.getMousePosition(e, this._container);
       
  3261 	},
       
  3262 
       
  3263 	// @method mouseEventToLayerPoint(ev: MouseEvent): Point
       
  3264 	// Given a MouseEvent object, returns the pixel coordinate relative to
       
  3265 	// the [origin pixel](#map-getpixelorigin) where the event took place.
       
  3266 	mouseEventToLayerPoint: function (e) {
       
  3267 		return this.containerPointToLayerPoint(this.mouseEventToContainerPoint(e));
       
  3268 	},
       
  3269 
       
  3270 	// @method mouseEventToLatLng(ev: MouseEvent): LatLng
       
  3271 	// Given a MouseEvent object, returns geographical coordinate where the
       
  3272 	// event took place.
       
  3273 	mouseEventToLatLng: function (e) { // (MouseEvent)
       
  3274 		return this.layerPointToLatLng(this.mouseEventToLayerPoint(e));
       
  3275 	},
       
  3276 
       
  3277 
       
  3278 	// map initialization methods
       
  3279 
       
  3280 	_initContainer: function (id) {
       
  3281 		var container = this._container = L.DomUtil.get(id);
       
  3282 
       
  3283 		if (!container) {
       
  3284 			throw new Error('Map container not found.');
       
  3285 		} else if (container._leaflet_id) {
       
  3286 			throw new Error('Map container is already initialized.');
       
  3287 		}
       
  3288 
       
  3289 		L.DomEvent.addListener(container, 'scroll', this._onScroll, this);
       
  3290 		this._containerId = L.Util.stamp(container);
       
  3291 	},
       
  3292 
       
  3293 	_initLayout: function () {
       
  3294 		var container = this._container;
       
  3295 
       
  3296 		this._fadeAnimated = this.options.fadeAnimation && L.Browser.any3d;
       
  3297 
       
  3298 		L.DomUtil.addClass(container, 'leaflet-container' +
       
  3299 			(L.Browser.touch ? ' leaflet-touch' : '') +
       
  3300 			(L.Browser.retina ? ' leaflet-retina' : '') +
       
  3301 			(L.Browser.ielt9 ? ' leaflet-oldie' : '') +
       
  3302 			(L.Browser.safari ? ' leaflet-safari' : '') +
       
  3303 			(this._fadeAnimated ? ' leaflet-fade-anim' : ''));
       
  3304 
       
  3305 		var position = L.DomUtil.getStyle(container, 'position');
       
  3306 
       
  3307 		if (position !== 'absolute' && position !== 'relative' && position !== 'fixed') {
       
  3308 			container.style.position = 'relative';
       
  3309 		}
       
  3310 
       
  3311 		this._initPanes();
       
  3312 
       
  3313 		if (this._initControlPos) {
       
  3314 			this._initControlPos();
       
  3315 		}
       
  3316 	},
       
  3317 
       
  3318 	_initPanes: function () {
       
  3319 		var panes = this._panes = {};
       
  3320 		this._paneRenderers = {};
       
  3321 
       
  3322 		// @section
       
  3323 		//
       
  3324 		// Panes are DOM elements used to control the ordering of layers on the map. You
       
  3325 		// can access panes with [`map.getPane`](#map-getpane) or
       
  3326 		// [`map.getPanes`](#map-getpanes) methods. New panes can be created with the
       
  3327 		// [`map.createPane`](#map-createpane) method.
       
  3328 		//
       
  3329 		// Every map has the following default panes that differ only in zIndex.
       
  3330 		//
       
  3331 		// @pane mapPane: HTMLElement = 'auto'
       
  3332 		// Pane that contains all other map panes
       
  3333 
       
  3334 		this._mapPane = this.createPane('mapPane', this._container);
       
  3335 		L.DomUtil.setPosition(this._mapPane, new L.Point(0, 0));
       
  3336 
       
  3337 		// @pane tilePane: HTMLElement = 200
       
  3338 		// Pane for `GridLayer`s and `TileLayer`s
       
  3339 		this.createPane('tilePane');
       
  3340 		// @pane overlayPane: HTMLElement = 400
       
  3341 		// Pane for vector overlays (`Path`s), like `Polyline`s and `Polygon`s
       
  3342 		this.createPane('shadowPane');
       
  3343 		// @pane shadowPane: HTMLElement = 500
       
  3344 		// Pane for overlay shadows (e.g. `Marker` shadows)
       
  3345 		this.createPane('overlayPane');
       
  3346 		// @pane markerPane: HTMLElement = 600
       
  3347 		// Pane for `Icon`s of `Marker`s
       
  3348 		this.createPane('markerPane');
       
  3349 		// @pane tooltipPane: HTMLElement = 650
       
  3350 		// Pane for tooltip.
       
  3351 		this.createPane('tooltipPane');
       
  3352 		// @pane popupPane: HTMLElement = 700
       
  3353 		// Pane for `Popup`s.
       
  3354 		this.createPane('popupPane');
       
  3355 
       
  3356 		if (!this.options.markerZoomAnimation) {
       
  3357 			L.DomUtil.addClass(panes.markerPane, 'leaflet-zoom-hide');
       
  3358 			L.DomUtil.addClass(panes.shadowPane, 'leaflet-zoom-hide');
       
  3359 		}
       
  3360 	},
       
  3361 
       
  3362 
       
  3363 	// private methods that modify map state
       
  3364 
       
  3365 	// @section Map state change events
       
  3366 	_resetView: function (center, zoom) {
       
  3367 		L.DomUtil.setPosition(this._mapPane, new L.Point(0, 0));
       
  3368 
       
  3369 		var loading = !this._loaded;
       
  3370 		this._loaded = true;
       
  3371 		zoom = this._limitZoom(zoom);
       
  3372 
       
  3373 		this.fire('viewprereset');
       
  3374 
       
  3375 		var zoomChanged = this._zoom !== zoom;
       
  3376 		this
       
  3377 			._moveStart(zoomChanged)
       
  3378 			._move(center, zoom)
       
  3379 			._moveEnd(zoomChanged);
       
  3380 
       
  3381 		// @event viewreset: Event
       
  3382 		// Fired when the map needs to redraw its content (this usually happens
       
  3383 		// on map zoom or load). Very useful for creating custom overlays.
       
  3384 		this.fire('viewreset');
       
  3385 
       
  3386 		// @event load: Event
       
  3387 		// Fired when the map is initialized (when its center and zoom are set
       
  3388 		// for the first time).
       
  3389 		if (loading) {
       
  3390 			this.fire('load');
       
  3391 		}
       
  3392 	},
       
  3393 
       
  3394 	_moveStart: function (zoomChanged) {
       
  3395 		// @event zoomstart: Event
       
  3396 		// Fired when the map zoom is about to change (e.g. before zoom animation).
       
  3397 		// @event movestart: Event
       
  3398 		// Fired when the view of the map starts changing (e.g. user starts dragging the map).
       
  3399 		if (zoomChanged) {
       
  3400 			this.fire('zoomstart');
       
  3401 		}
       
  3402 		return this.fire('movestart');
       
  3403 	},
       
  3404 
       
  3405 	_move: function (center, zoom, data) {
       
  3406 		if (zoom === undefined) {
       
  3407 			zoom = this._zoom;
       
  3408 		}
       
  3409 		var zoomChanged = this._zoom !== zoom;
       
  3410 
       
  3411 		this._zoom = zoom;
       
  3412 		this._lastCenter = center;
       
  3413 		this._pixelOrigin = this._getNewPixelOrigin(center);
       
  3414 
       
  3415 		// @event zoom: Event
       
  3416 		// Fired repeatedly during any change in zoom level, including zoom
       
  3417 		// and fly animations.
       
  3418 		if (zoomChanged || (data && data.pinch)) {	// Always fire 'zoom' if pinching because #3530
       
  3419 			this.fire('zoom', data);
       
  3420 		}
       
  3421 
       
  3422 		// @event move: Event
       
  3423 		// Fired repeatedly during any movement of the map, including pan and
       
  3424 		// fly animations.
       
  3425 		return this.fire('move', data);
       
  3426 	},
       
  3427 
       
  3428 	_moveEnd: function (zoomChanged) {
       
  3429 		// @event zoomend: Event
       
  3430 		// Fired when the map has changed, after any animations.
       
  3431 		if (zoomChanged) {
       
  3432 			this.fire('zoomend');
       
  3433 		}
       
  3434 
       
  3435 		// @event moveend: Event
       
  3436 		// Fired when the center of the map stops changing (e.g. user stopped
       
  3437 		// dragging the map).
       
  3438 		return this.fire('moveend');
       
  3439 	},
       
  3440 
       
  3441 	_stop: function () {
       
  3442 		L.Util.cancelAnimFrame(this._flyToFrame);
       
  3443 		if (this._panAnim) {
       
  3444 			this._panAnim.stop();
       
  3445 		}
       
  3446 		return this;
       
  3447 	},
       
  3448 
       
  3449 	_rawPanBy: function (offset) {
       
  3450 		L.DomUtil.setPosition(this._mapPane, this._getMapPanePos().subtract(offset));
       
  3451 	},
       
  3452 
       
  3453 	_getZoomSpan: function () {
       
  3454 		return this.getMaxZoom() - this.getMinZoom();
       
  3455 	},
       
  3456 
       
  3457 	_panInsideMaxBounds: function () {
       
  3458 		if (!this._enforcingBounds) {
       
  3459 			this.panInsideBounds(this.options.maxBounds);
       
  3460 		}
       
  3461 	},
       
  3462 
       
  3463 	_checkIfLoaded: function () {
       
  3464 		if (!this._loaded) {
       
  3465 			throw new Error('Set map center and zoom first.');
       
  3466 		}
       
  3467 	},
       
  3468 
       
  3469 	// DOM event handling
       
  3470 
       
  3471 	// @section Interaction events
       
  3472 	_initEvents: function (remove) {
       
  3473 		if (!L.DomEvent) { return; }
       
  3474 
       
  3475 		this._targets = {};
       
  3476 		this._targets[L.stamp(this._container)] = this;
       
  3477 
       
  3478 		var onOff = remove ? 'off' : 'on';
       
  3479 
       
  3480 		// @event click: MouseEvent
       
  3481 		// Fired when the user clicks (or taps) the map.
       
  3482 		// @event dblclick: MouseEvent
       
  3483 		// Fired when the user double-clicks (or double-taps) the map.
       
  3484 		// @event mousedown: MouseEvent
       
  3485 		// Fired when the user pushes the mouse button on the map.
       
  3486 		// @event mouseup: MouseEvent
       
  3487 		// Fired when the user releases the mouse button on the map.
       
  3488 		// @event mouseover: MouseEvent
       
  3489 		// Fired when the mouse enters the map.
       
  3490 		// @event mouseout: MouseEvent
       
  3491 		// Fired when the mouse leaves the map.
       
  3492 		// @event mousemove: MouseEvent
       
  3493 		// Fired while the mouse moves over the map.
       
  3494 		// @event contextmenu: MouseEvent
       
  3495 		// Fired when the user pushes the right mouse button on the map, prevents
       
  3496 		// default browser context menu from showing if there are listeners on
       
  3497 		// this event. Also fired on mobile when the user holds a single touch
       
  3498 		// for a second (also called long press).
       
  3499 		// @event keypress: KeyboardEvent
       
  3500 		// Fired when the user presses a key from the keyboard while the map is focused.
       
  3501 		L.DomEvent[onOff](this._container, 'click dblclick mousedown mouseup ' +
       
  3502 			'mouseover mouseout mousemove contextmenu keypress', this._handleDOMEvent, this);
       
  3503 
       
  3504 		if (this.options.trackResize) {
       
  3505 			L.DomEvent[onOff](window, 'resize', this._onResize, this);
       
  3506 		}
       
  3507 
       
  3508 		if (L.Browser.any3d && this.options.transform3DLimit) {
       
  3509 			this[onOff]('moveend', this._onMoveEnd);
       
  3510 		}
       
  3511 	},
       
  3512 
       
  3513 	_onResize: function () {
       
  3514 		L.Util.cancelAnimFrame(this._resizeRequest);
       
  3515 		this._resizeRequest = L.Util.requestAnimFrame(
       
  3516 		        function () { this.invalidateSize({debounceMoveend: true}); }, this);
       
  3517 	},
       
  3518 
       
  3519 	_onScroll: function () {
       
  3520 		this._container.scrollTop  = 0;
       
  3521 		this._container.scrollLeft = 0;
       
  3522 	},
       
  3523 
       
  3524 	_onMoveEnd: function () {
       
  3525 		var pos = this._getMapPanePos();
       
  3526 		if (Math.max(Math.abs(pos.x), Math.abs(pos.y)) >= this.options.transform3DLimit) {
       
  3527 			// https://bugzilla.mozilla.org/show_bug.cgi?id=1203873 but Webkit also have
       
  3528 			// a pixel offset on very high values, see: http://jsfiddle.net/dg6r5hhb/
       
  3529 			this._resetView(this.getCenter(), this.getZoom());
       
  3530 		}
       
  3531 	},
       
  3532 
       
  3533 	_findEventTargets: function (e, type) {
       
  3534 		var targets = [],
       
  3535 		    target,
       
  3536 		    isHover = type === 'mouseout' || type === 'mouseover',
       
  3537 		    src = e.target || e.srcElement,
       
  3538 		    dragging = false;
       
  3539 
       
  3540 		while (src) {
       
  3541 			target = this._targets[L.stamp(src)];
       
  3542 			if (target && (type === 'click' || type === 'preclick') && !e._simulated && this._draggableMoved(target)) {
       
  3543 				// Prevent firing click after you just dragged an object.
       
  3544 				dragging = true;
       
  3545 				break;
       
  3546 			}
       
  3547 			if (target && target.listens(type, true)) {
       
  3548 				if (isHover && !L.DomEvent._isExternalTarget(src, e)) { break; }
       
  3549 				targets.push(target);
       
  3550 				if (isHover) { break; }
       
  3551 			}
       
  3552 			if (src === this._container) { break; }
       
  3553 			src = src.parentNode;
       
  3554 		}
       
  3555 		if (!targets.length && !dragging && !isHover && L.DomEvent._isExternalTarget(src, e)) {
       
  3556 			targets = [this];
       
  3557 		}
       
  3558 		return targets;
       
  3559 	},
       
  3560 
       
  3561 	_handleDOMEvent: function (e) {
       
  3562 		if (!this._loaded || L.DomEvent._skipped(e)) { return; }
       
  3563 
       
  3564 		var type = e.type === 'keypress' && e.keyCode === 13 ? 'click' : e.type;
       
  3565 
       
  3566 		if (type === 'mousedown') {
       
  3567 			// prevents outline when clicking on keyboard-focusable element
       
  3568 			L.DomUtil.preventOutline(e.target || e.srcElement);
       
  3569 		}
       
  3570 
       
  3571 		this._fireDOMEvent(e, type);
       
  3572 	},
       
  3573 
       
  3574 	_fireDOMEvent: function (e, type, targets) {
       
  3575 
       
  3576 		if (e.type === 'click') {
       
  3577 			// Fire a synthetic 'preclick' event which propagates up (mainly for closing popups).
       
  3578 			// @event preclick: MouseEvent
       
  3579 			// Fired before mouse click on the map (sometimes useful when you
       
  3580 			// want something to happen on click before any existing click
       
  3581 			// handlers start running).
       
  3582 			var synth = L.Util.extend({}, e);
       
  3583 			synth.type = 'preclick';
       
  3584 			this._fireDOMEvent(synth, synth.type, targets);
       
  3585 		}
       
  3586 
       
  3587 		if (e._stopped) { return; }
       
  3588 
       
  3589 		// Find the layer the event is propagating from and its parents.
       
  3590 		targets = (targets || []).concat(this._findEventTargets(e, type));
       
  3591 
       
  3592 		if (!targets.length) { return; }
       
  3593 
       
  3594 		var target = targets[0];
       
  3595 		if (type === 'contextmenu' && target.listens(type, true)) {
       
  3596 			L.DomEvent.preventDefault(e);
       
  3597 		}
       
  3598 
       
  3599 		var data = {
       
  3600 			originalEvent: e
       
  3601 		};
       
  3602 
       
  3603 		if (e.type !== 'keypress') {
       
  3604 			var isMarker = target instanceof L.Marker;
       
  3605 			data.containerPoint = isMarker ?
       
  3606 					this.latLngToContainerPoint(target.getLatLng()) : this.mouseEventToContainerPoint(e);
       
  3607 			data.layerPoint = this.containerPointToLayerPoint(data.containerPoint);
       
  3608 			data.latlng = isMarker ? target.getLatLng() : this.layerPointToLatLng(data.layerPoint);
       
  3609 		}
       
  3610 
       
  3611 		for (var i = 0; i < targets.length; i++) {
       
  3612 			targets[i].fire(type, data, true);
       
  3613 			if (data.originalEvent._stopped ||
       
  3614 				(targets[i].options.nonBubblingEvents && L.Util.indexOf(targets[i].options.nonBubblingEvents, type) !== -1)) { return; }
       
  3615 		}
       
  3616 	},
       
  3617 
       
  3618 	_draggableMoved: function (obj) {
       
  3619 		obj = obj.dragging && obj.dragging.enabled() ? obj : this;
       
  3620 		return (obj.dragging && obj.dragging.moved()) || (this.boxZoom && this.boxZoom.moved());
       
  3621 	},
       
  3622 
       
  3623 	_clearHandlers: function () {
       
  3624 		for (var i = 0, len = this._handlers.length; i < len; i++) {
       
  3625 			this._handlers[i].disable();
       
  3626 		}
       
  3627 	},
       
  3628 
       
  3629 	// @section Other Methods
       
  3630 
       
  3631 	// @method whenReady(fn: Function, context?: Object): this
       
  3632 	// Runs the given function `fn` when the map gets initialized with
       
  3633 	// a view (center and zoom) and at least one layer, or immediately
       
  3634 	// if it's already initialized, optionally passing a function context.
       
  3635 	whenReady: function (callback, context) {
       
  3636 		if (this._loaded) {
       
  3637 			callback.call(context || this, {target: this});
       
  3638 		} else {
       
  3639 			this.on('load', callback, context);
       
  3640 		}
       
  3641 		return this;
       
  3642 	},
       
  3643 
       
  3644 
       
  3645 	// private methods for getting map state
       
  3646 
       
  3647 	_getMapPanePos: function () {
       
  3648 		return L.DomUtil.getPosition(this._mapPane) || new L.Point(0, 0);
       
  3649 	},
       
  3650 
       
  3651 	_moved: function () {
       
  3652 		var pos = this._getMapPanePos();
       
  3653 		return pos && !pos.equals([0, 0]);
       
  3654 	},
       
  3655 
       
  3656 	_getTopLeftPoint: function (center, zoom) {
       
  3657 		var pixelOrigin = center && zoom !== undefined ?
       
  3658 			this._getNewPixelOrigin(center, zoom) :
       
  3659 			this.getPixelOrigin();
       
  3660 		return pixelOrigin.subtract(this._getMapPanePos());
       
  3661 	},
       
  3662 
       
  3663 	_getNewPixelOrigin: function (center, zoom) {
       
  3664 		var viewHalf = this.getSize()._divideBy(2);
       
  3665 		return this.project(center, zoom)._subtract(viewHalf)._add(this._getMapPanePos())._round();
       
  3666 	},
       
  3667 
       
  3668 	_latLngToNewLayerPoint: function (latlng, zoom, center) {
       
  3669 		var topLeft = this._getNewPixelOrigin(center, zoom);
       
  3670 		return this.project(latlng, zoom)._subtract(topLeft);
       
  3671 	},
       
  3672 
       
  3673 	_latLngBoundsToNewLayerBounds: function (latLngBounds, zoom, center) {
       
  3674 		var topLeft = this._getNewPixelOrigin(center, zoom);
       
  3675 		return L.bounds([
       
  3676 			this.project(latLngBounds.getSouthWest(), zoom)._subtract(topLeft),
       
  3677 			this.project(latLngBounds.getNorthWest(), zoom)._subtract(topLeft),
       
  3678 			this.project(latLngBounds.getSouthEast(), zoom)._subtract(topLeft),
       
  3679 			this.project(latLngBounds.getNorthEast(), zoom)._subtract(topLeft)
       
  3680 		]);
       
  3681 	},
       
  3682 
       
  3683 	// layer point of the current center
       
  3684 	_getCenterLayerPoint: function () {
       
  3685 		return this.containerPointToLayerPoint(this.getSize()._divideBy(2));
       
  3686 	},
       
  3687 
       
  3688 	// offset of the specified place to the current center in pixels
       
  3689 	_getCenterOffset: function (latlng) {
       
  3690 		return this.latLngToLayerPoint(latlng).subtract(this._getCenterLayerPoint());
       
  3691 	},
       
  3692 
       
  3693 	// adjust center for view to get inside bounds
       
  3694 	_limitCenter: function (center, zoom, bounds) {
       
  3695 
       
  3696 		if (!bounds) { return center; }
       
  3697 
       
  3698 		var centerPoint = this.project(center, zoom),
       
  3699 		    viewHalf = this.getSize().divideBy(2),
       
  3700 		    viewBounds = new L.Bounds(centerPoint.subtract(viewHalf), centerPoint.add(viewHalf)),
       
  3701 		    offset = this._getBoundsOffset(viewBounds, bounds, zoom);
       
  3702 
       
  3703 		// If offset is less than a pixel, ignore.
       
  3704 		// This prevents unstable projections from getting into
       
  3705 		// an infinite loop of tiny offsets.
       
  3706 		if (offset.round().equals([0, 0])) {
       
  3707 			return center;
       
  3708 		}
       
  3709 
       
  3710 		return this.unproject(centerPoint.add(offset), zoom);
       
  3711 	},
       
  3712 
       
  3713 	// adjust offset for view to get inside bounds
       
  3714 	_limitOffset: function (offset, bounds) {
       
  3715 		if (!bounds) { return offset; }
       
  3716 
       
  3717 		var viewBounds = this.getPixelBounds(),
       
  3718 		    newBounds = new L.Bounds(viewBounds.min.add(offset), viewBounds.max.add(offset));
       
  3719 
       
  3720 		return offset.add(this._getBoundsOffset(newBounds, bounds));
       
  3721 	},
       
  3722 
       
  3723 	// returns offset needed for pxBounds to get inside maxBounds at a specified zoom
       
  3724 	_getBoundsOffset: function (pxBounds, maxBounds, zoom) {
       
  3725 		var projectedMaxBounds = L.bounds(
       
  3726 		        this.project(maxBounds.getNorthEast(), zoom),
       
  3727 		        this.project(maxBounds.getSouthWest(), zoom)
       
  3728 		    ),
       
  3729 		    minOffset = projectedMaxBounds.min.subtract(pxBounds.min),
       
  3730 		    maxOffset = projectedMaxBounds.max.subtract(pxBounds.max),
       
  3731 
       
  3732 		    dx = this._rebound(minOffset.x, -maxOffset.x),
       
  3733 		    dy = this._rebound(minOffset.y, -maxOffset.y);
       
  3734 
       
  3735 		return new L.Point(dx, dy);
       
  3736 	},
       
  3737 
       
  3738 	_rebound: function (left, right) {
       
  3739 		return left + right > 0 ?
       
  3740 			Math.round(left - right) / 2 :
       
  3741 			Math.max(0, Math.ceil(left)) - Math.max(0, Math.floor(right));
       
  3742 	},
       
  3743 
       
  3744 	_limitZoom: function (zoom) {
       
  3745 		var min = this.getMinZoom(),
       
  3746 		    max = this.getMaxZoom(),
       
  3747 		    snap = L.Browser.any3d ? this.options.zoomSnap : 1;
       
  3748 		if (snap) {
       
  3749 			zoom = Math.round(zoom / snap) * snap;
       
  3750 		}
       
  3751 		return Math.max(min, Math.min(max, zoom));
       
  3752 	},
       
  3753 
       
  3754 	_onPanTransitionStep: function () {
       
  3755 		this.fire('move');
       
  3756 	},
       
  3757 
       
  3758 	_onPanTransitionEnd: function () {
       
  3759 		L.DomUtil.removeClass(this._mapPane, 'leaflet-pan-anim');
       
  3760 		this.fire('moveend');
       
  3761 	},
       
  3762 
       
  3763 	_tryAnimatedPan: function (center, options) {
       
  3764 		// difference between the new and current centers in pixels
       
  3765 		var offset = this._getCenterOffset(center)._floor();
       
  3766 
       
  3767 		// don't animate too far unless animate: true specified in options
       
  3768 		if ((options && options.animate) !== true && !this.getSize().contains(offset)) { return false; }
       
  3769 
       
  3770 		this.panBy(offset, options);
       
  3771 
       
  3772 		return true;
       
  3773 	},
       
  3774 
       
  3775 	_createAnimProxy: function () {
       
  3776 
       
  3777 		var proxy = this._proxy = L.DomUtil.create('div', 'leaflet-proxy leaflet-zoom-animated');
       
  3778 		this._panes.mapPane.appendChild(proxy);
       
  3779 
       
  3780 		this.on('zoomanim', function (e) {
       
  3781 			var prop = L.DomUtil.TRANSFORM,
       
  3782 			    transform = proxy.style[prop];
       
  3783 
       
  3784 			L.DomUtil.setTransform(proxy, this.project(e.center, e.zoom), this.getZoomScale(e.zoom, 1));
       
  3785 
       
  3786 			// workaround for case when transform is the same and so transitionend event is not fired
       
  3787 			if (transform === proxy.style[prop] && this._animatingZoom) {
       
  3788 				this._onZoomTransitionEnd();
       
  3789 			}
       
  3790 		}, this);
       
  3791 
       
  3792 		this.on('load moveend', function () {
       
  3793 			var c = this.getCenter(),
       
  3794 			    z = this.getZoom();
       
  3795 			L.DomUtil.setTransform(proxy, this.project(c, z), this.getZoomScale(z, 1));
       
  3796 		}, this);
       
  3797 	},
       
  3798 
       
  3799 	_catchTransitionEnd: function (e) {
       
  3800 		if (this._animatingZoom && e.propertyName.indexOf('transform') >= 0) {
       
  3801 			this._onZoomTransitionEnd();
       
  3802 		}
       
  3803 	},
       
  3804 
       
  3805 	_nothingToAnimate: function () {
       
  3806 		return !this._container.getElementsByClassName('leaflet-zoom-animated').length;
       
  3807 	},
       
  3808 
       
  3809 	_tryAnimatedZoom: function (center, zoom, options) {
       
  3810 
       
  3811 		if (this._animatingZoom) { return true; }
       
  3812 
       
  3813 		options = options || {};
       
  3814 
       
  3815 		// don't animate if disabled, not supported or zoom difference is too large
       
  3816 		if (!this._zoomAnimated || options.animate === false || this._nothingToAnimate() ||
       
  3817 		        Math.abs(zoom - this._zoom) > this.options.zoomAnimationThreshold) { return false; }
       
  3818 
       
  3819 		// offset is the pixel coords of the zoom origin relative to the current center
       
  3820 		var scale = this.getZoomScale(zoom),
       
  3821 		    offset = this._getCenterOffset(center)._divideBy(1 - 1 / scale);
       
  3822 
       
  3823 		// don't animate if the zoom origin isn't within one screen from the current center, unless forced
       
  3824 		if (options.animate !== true && !this.getSize().contains(offset)) { return false; }
       
  3825 
       
  3826 		L.Util.requestAnimFrame(function () {
       
  3827 			this
       
  3828 			    ._moveStart(true)
       
  3829 			    ._animateZoom(center, zoom, true);
       
  3830 		}, this);
       
  3831 
       
  3832 		return true;
       
  3833 	},
       
  3834 
       
  3835 	_animateZoom: function (center, zoom, startAnim, noUpdate) {
       
  3836 		if (startAnim) {
       
  3837 			this._animatingZoom = true;
       
  3838 
       
  3839 			// remember what center/zoom to set after animation
       
  3840 			this._animateToCenter = center;
       
  3841 			this._animateToZoom = zoom;
       
  3842 
       
  3843 			L.DomUtil.addClass(this._mapPane, 'leaflet-zoom-anim');
       
  3844 		}
       
  3845 
       
  3846 		// @event zoomanim: ZoomAnimEvent
       
  3847 		// Fired on every frame of a zoom animation
       
  3848 		this.fire('zoomanim', {
       
  3849 			center: center,
       
  3850 			zoom: zoom,
       
  3851 			noUpdate: noUpdate
       
  3852 		});
       
  3853 
       
  3854 		// Work around webkit not firing 'transitionend', see https://github.com/Leaflet/Leaflet/issues/3689, 2693
       
  3855 		setTimeout(L.bind(this._onZoomTransitionEnd, this), 250);
       
  3856 	},
       
  3857 
       
  3858 	_onZoomTransitionEnd: function () {
       
  3859 		if (!this._animatingZoom) { return; }
       
  3860 
       
  3861 		L.DomUtil.removeClass(this._mapPane, 'leaflet-zoom-anim');
       
  3862 
       
  3863 		this._animatingZoom = false;
       
  3864 
       
  3865 		this._move(this._animateToCenter, this._animateToZoom);
       
  3866 
       
  3867 		// This anim frame should prevent an obscure iOS webkit tile loading race condition.
       
  3868 		L.Util.requestAnimFrame(function () {
       
  3869 			this._moveEnd(true);
       
  3870 		}, this);
       
  3871 	}
       
  3872 });
       
  3873 
       
  3874 // @section
       
  3875 
       
  3876 // @factory L.map(id: String, options?: Map options)
       
  3877 // Instantiates a map object given the DOM ID of a `<div>` element
       
  3878 // and optionally an object literal with `Map options`.
       
  3879 //
       
  3880 // @alternative
       
  3881 // @factory L.map(el: HTMLElement, options?: Map options)
       
  3882 // Instantiates a map object given an instance of a `<div>` HTML element
       
  3883 // and optionally an object literal with `Map options`.
       
  3884 L.map = function (id, options) {
       
  3885 	return new L.Map(id, options);
       
  3886 };
       
  3887 
       
  3888 
       
  3889 
       
  3890 
       
  3891 /*
       
  3892  * @class Layer
       
  3893  * @inherits Evented
       
  3894  * @aka L.Layer
       
  3895  * @aka ILayer
       
  3896  *
       
  3897  * A set of methods from the Layer base class that all Leaflet layers use.
       
  3898  * Inherits all methods, options and events from `L.Evented`.
       
  3899  *
       
  3900  * @example
       
  3901  *
       
  3902  * ```js
       
  3903  * var layer = L.Marker(latlng).addTo(map);
       
  3904  * layer.addTo(map);
       
  3905  * layer.remove();
       
  3906  * ```
       
  3907  *
       
  3908  * @event add: Event
       
  3909  * Fired after the layer is added to a map
       
  3910  *
       
  3911  * @event remove: Event
       
  3912  * Fired after the layer is removed from a map
       
  3913  */
       
  3914 
       
  3915 
       
  3916 L.Layer = L.Evented.extend({
       
  3917 
       
  3918 	// Classes extending `L.Layer` will inherit the following options:
       
  3919 	options: {
       
  3920 		// @option pane: String = 'overlayPane'
       
  3921 		// By default the layer will be added to the map's [overlay pane](#map-overlaypane). Overriding this option will cause the layer to be placed on another pane by default.
       
  3922 		pane: 'overlayPane',
       
  3923 		nonBubblingEvents: [],  // Array of events that should not be bubbled to DOM parents (like the map),
       
  3924 
       
  3925 		// @option attribution: String = null
       
  3926 		// String to be shown in the attribution control, describes the layer data, e.g. "© Mapbox".
       
  3927 		attribution: null
       
  3928 	},
       
  3929 
       
  3930 	/* @section
       
  3931 	 * Classes extending `L.Layer` will inherit the following methods:
       
  3932 	 *
       
  3933 	 * @method addTo(map: Map): this
       
  3934 	 * Adds the layer to the given map
       
  3935 	 */
       
  3936 	addTo: function (map) {
       
  3937 		map.addLayer(this);
       
  3938 		return this;
       
  3939 	},
       
  3940 
       
  3941 	// @method remove: this
       
  3942 	// Removes the layer from the map it is currently active on.
       
  3943 	remove: function () {
       
  3944 		return this.removeFrom(this._map || this._mapToAdd);
       
  3945 	},
       
  3946 
       
  3947 	// @method removeFrom(map: Map): this
       
  3948 	// Removes the layer from the given map
       
  3949 	removeFrom: function (obj) {
       
  3950 		if (obj) {
       
  3951 			obj.removeLayer(this);
       
  3952 		}
       
  3953 		return this;
       
  3954 	},
       
  3955 
       
  3956 	// @method getPane(name? : String): HTMLElement
       
  3957 	// Returns the `HTMLElement` representing the named pane on the map. If `name` is omitted, returns the pane for this layer.
       
  3958 	getPane: function (name) {
       
  3959 		return this._map.getPane(name ? (this.options[name] || name) : this.options.pane);
       
  3960 	},
       
  3961 
       
  3962 	addInteractiveTarget: function (targetEl) {
       
  3963 		this._map._targets[L.stamp(targetEl)] = this;
       
  3964 		return this;
       
  3965 	},
       
  3966 
       
  3967 	removeInteractiveTarget: function (targetEl) {
       
  3968 		delete this._map._targets[L.stamp(targetEl)];
       
  3969 		return this;
       
  3970 	},
       
  3971 
       
  3972 	// @method getAttribution: String
       
  3973 	// Used by the `attribution control`, returns the [attribution option](#gridlayer-attribution).
       
  3974 	getAttribution: function () {
       
  3975 		return this.options.attribution;
       
  3976 	},
       
  3977 
       
  3978 	_layerAdd: function (e) {
       
  3979 		var map = e.target;
       
  3980 
       
  3981 		// check in case layer gets added and then removed before the map is ready
       
  3982 		if (!map.hasLayer(this)) { return; }
       
  3983 
       
  3984 		this._map = map;
       
  3985 		this._zoomAnimated = map._zoomAnimated;
       
  3986 
       
  3987 		if (this.getEvents) {
       
  3988 			var events = this.getEvents();
       
  3989 			map.on(events, this);
       
  3990 			this.once('remove', function () {
       
  3991 				map.off(events, this);
       
  3992 			}, this);
       
  3993 		}
       
  3994 
       
  3995 		this.onAdd(map);
       
  3996 
       
  3997 		if (this.getAttribution && map.attributionControl) {
       
  3998 			map.attributionControl.addAttribution(this.getAttribution());
       
  3999 		}
       
  4000 
       
  4001 		this.fire('add');
       
  4002 		map.fire('layeradd', {layer: this});
       
  4003 	}
       
  4004 });
       
  4005 
       
  4006 /* @section Extension methods
       
  4007  * @uninheritable
       
  4008  *
       
  4009  * Every layer should extend from `L.Layer` and (re-)implement the following methods.
       
  4010  *
       
  4011  * @method onAdd(map: Map): this
       
  4012  * Should contain code that creates DOM elements for the layer, adds them to `map panes` where they should belong and puts listeners on relevant map events. Called on [`map.addLayer(layer)`](#map-addlayer).
       
  4013  *
       
  4014  * @method onRemove(map: Map): this
       
  4015  * Should contain all clean up code that removes the layer's elements from the DOM and removes listeners previously added in [`onAdd`](#layer-onadd). Called on [`map.removeLayer(layer)`](#map-removelayer).
       
  4016  *
       
  4017  * @method getEvents(): Object
       
  4018  * This optional method should return an object like `{ viewreset: this._reset }` for [`addEventListener`](#evented-addeventlistener). The event handlers in this object will be automatically added and removed from the map with your layer.
       
  4019  *
       
  4020  * @method getAttribution(): String
       
  4021  * This optional method should return a string containing HTML to be shown on the `Attribution control` whenever the layer is visible.
       
  4022  *
       
  4023  * @method beforeAdd(map: Map): this
       
  4024  * Optional method. Called on [`map.addLayer(layer)`](#map-addlayer), before the layer is added to the map, before events are initialized, without waiting until the map is in a usable state. Use for early initialization only.
       
  4025  */
       
  4026 
       
  4027 
       
  4028 /* @namespace Map
       
  4029  * @section Layer events
       
  4030  *
       
  4031  * @event layeradd: LayerEvent
       
  4032  * Fired when a new layer is added to the map.
       
  4033  *
       
  4034  * @event layerremove: LayerEvent
       
  4035  * Fired when some layer is removed from the map
       
  4036  *
       
  4037  * @section Methods for Layers and Controls
       
  4038  */
       
  4039 L.Map.include({
       
  4040 	// @method addLayer(layer: Layer): this
       
  4041 	// Adds the given layer to the map
       
  4042 	addLayer: function (layer) {
       
  4043 		var id = L.stamp(layer);
       
  4044 		if (this._layers[id]) { return this; }
       
  4045 		this._layers[id] = layer;
       
  4046 
       
  4047 		layer._mapToAdd = this;
       
  4048 
       
  4049 		if (layer.beforeAdd) {
       
  4050 			layer.beforeAdd(this);
       
  4051 		}
       
  4052 
       
  4053 		this.whenReady(layer._layerAdd, layer);
       
  4054 
       
  4055 		return this;
       
  4056 	},
       
  4057 
       
  4058 	// @method removeLayer(layer: Layer): this
       
  4059 	// Removes the given layer from the map.
       
  4060 	removeLayer: function (layer) {
       
  4061 		var id = L.stamp(layer);
       
  4062 
       
  4063 		if (!this._layers[id]) { return this; }
       
  4064 
       
  4065 		if (this._loaded) {
       
  4066 			layer.onRemove(this);
       
  4067 		}
       
  4068 
       
  4069 		if (layer.getAttribution && this.attributionControl) {
       
  4070 			this.attributionControl.removeAttribution(layer.getAttribution());
       
  4071 		}
       
  4072 
       
  4073 		delete this._layers[id];
       
  4074 
       
  4075 		if (this._loaded) {
       
  4076 			this.fire('layerremove', {layer: layer});
       
  4077 			layer.fire('remove');
       
  4078 		}
       
  4079 
       
  4080 		layer._map = layer._mapToAdd = null;
       
  4081 
       
  4082 		return this;
       
  4083 	},
       
  4084 
       
  4085 	// @method hasLayer(layer: Layer): Boolean
       
  4086 	// Returns `true` if the given layer is currently added to the map
       
  4087 	hasLayer: function (layer) {
       
  4088 		return !!layer && (L.stamp(layer) in this._layers);
       
  4089 	},
       
  4090 
       
  4091 	/* @method eachLayer(fn: Function, context?: Object): this
       
  4092 	 * Iterates over the layers of the map, optionally specifying context of the iterator function.
       
  4093 	 * ```
       
  4094 	 * map.eachLayer(function(layer){
       
  4095 	 *     layer.bindPopup('Hello');
       
  4096 	 * });
       
  4097 	 * ```
       
  4098 	 */
       
  4099 	eachLayer: function (method, context) {
       
  4100 		for (var i in this._layers) {
       
  4101 			method.call(context, this._layers[i]);
       
  4102 		}
       
  4103 		return this;
       
  4104 	},
       
  4105 
       
  4106 	_addLayers: function (layers) {
       
  4107 		layers = layers ? (L.Util.isArray(layers) ? layers : [layers]) : [];
       
  4108 
       
  4109 		for (var i = 0, len = layers.length; i < len; i++) {
       
  4110 			this.addLayer(layers[i]);
       
  4111 		}
       
  4112 	},
       
  4113 
       
  4114 	_addZoomLimit: function (layer) {
       
  4115 		if (isNaN(layer.options.maxZoom) || !isNaN(layer.options.minZoom)) {
       
  4116 			this._zoomBoundLayers[L.stamp(layer)] = layer;
       
  4117 			this._updateZoomLevels();
       
  4118 		}
       
  4119 	},
       
  4120 
       
  4121 	_removeZoomLimit: function (layer) {
       
  4122 		var id = L.stamp(layer);
       
  4123 
       
  4124 		if (this._zoomBoundLayers[id]) {
       
  4125 			delete this._zoomBoundLayers[id];
       
  4126 			this._updateZoomLevels();
       
  4127 		}
       
  4128 	},
       
  4129 
       
  4130 	_updateZoomLevels: function () {
       
  4131 		var minZoom = Infinity,
       
  4132 		    maxZoom = -Infinity,
       
  4133 		    oldZoomSpan = this._getZoomSpan();
       
  4134 
       
  4135 		for (var i in this._zoomBoundLayers) {
       
  4136 			var options = this._zoomBoundLayers[i].options;
       
  4137 
       
  4138 			minZoom = options.minZoom === undefined ? minZoom : Math.min(minZoom, options.minZoom);
       
  4139 			maxZoom = options.maxZoom === undefined ? maxZoom : Math.max(maxZoom, options.maxZoom);
       
  4140 		}
       
  4141 
       
  4142 		this._layersMaxZoom = maxZoom === -Infinity ? undefined : maxZoom;
       
  4143 		this._layersMinZoom = minZoom === Infinity ? undefined : minZoom;
       
  4144 
       
  4145 		// @section Map state change events
       
  4146 		// @event zoomlevelschange: Event
       
  4147 		// Fired when the number of zoomlevels on the map is changed due
       
  4148 		// to adding or removing a layer.
       
  4149 		if (oldZoomSpan !== this._getZoomSpan()) {
       
  4150 			this.fire('zoomlevelschange');
       
  4151 		}
       
  4152 
       
  4153 		if (this.options.maxZoom === undefined && this._layersMaxZoom && this.getZoom() > this._layersMaxZoom) {
       
  4154 			this.setZoom(this._layersMaxZoom);
       
  4155 		}
       
  4156 		if (this.options.minZoom === undefined && this._layersMinZoom && this.getZoom() < this._layersMinZoom) {
       
  4157 			this.setZoom(this._layersMinZoom);
       
  4158 		}
       
  4159 	}
       
  4160 });
       
  4161 
       
  4162 
       
  4163 
       
  4164 /*
       
  4165  * @namespace DomEvent
       
  4166  * Utility functions to work with the [DOM events](https://developer.mozilla.org/docs/Web/API/Event), used by Leaflet internally.
       
  4167  */
       
  4168 
       
  4169 // Inspired by John Resig, Dean Edwards and YUI addEvent implementations.
       
  4170 
       
  4171 
       
  4172 
       
  4173 var eventsKey = '_leaflet_events';
       
  4174 
       
  4175 L.DomEvent = {
       
  4176 
       
  4177 	// @function on(el: HTMLElement, types: String, fn: Function, context?: Object): this
       
  4178 	// Adds a listener function (`fn`) to a particular DOM event type of the
       
  4179 	// element `el`. You can optionally specify the context of the listener
       
  4180 	// (object the `this` keyword will point to). You can also pass several
       
  4181 	// space-separated types (e.g. `'click dblclick'`).
       
  4182 
       
  4183 	// @alternative
       
  4184 	// @function on(el: HTMLElement, eventMap: Object, context?: Object): this
       
  4185 	// Adds a set of type/listener pairs, e.g. `{click: onClick, mousemove: onMouseMove}`
       
  4186 	on: function (obj, types, fn, context) {
       
  4187 
       
  4188 		if (typeof types === 'object') {
       
  4189 			for (var type in types) {
       
  4190 				this._on(obj, type, types[type], fn);
       
  4191 			}
       
  4192 		} else {
       
  4193 			types = L.Util.splitWords(types);
       
  4194 
       
  4195 			for (var i = 0, len = types.length; i < len; i++) {
       
  4196 				this._on(obj, types[i], fn, context);
       
  4197 			}
       
  4198 		}
       
  4199 
       
  4200 		return this;
       
  4201 	},
       
  4202 
       
  4203 	// @function off(el: HTMLElement, types: String, fn: Function, context?: Object): this
       
  4204 	// Removes a previously added listener function. If no function is specified,
       
  4205 	// it will remove all the listeners of that particular DOM event from the element.
       
  4206 	// Note that if you passed a custom context to on, you must pass the same
       
  4207 	// context to `off` in order to remove the listener.
       
  4208 
       
  4209 	// @alternative
       
  4210 	// @function off(el: HTMLElement, eventMap: Object, context?: Object): this
       
  4211 	// Removes a set of type/listener pairs, e.g. `{click: onClick, mousemove: onMouseMove}`
       
  4212 	off: function (obj, types, fn, context) {
       
  4213 
       
  4214 		if (typeof types === 'object') {
       
  4215 			for (var type in types) {
       
  4216 				this._off(obj, type, types[type], fn);
       
  4217 			}
       
  4218 		} else {
       
  4219 			types = L.Util.splitWords(types);
       
  4220 
       
  4221 			for (var i = 0, len = types.length; i < len; i++) {
       
  4222 				this._off(obj, types[i], fn, context);
       
  4223 			}
       
  4224 		}
       
  4225 
       
  4226 		return this;
       
  4227 	},
       
  4228 
       
  4229 	_on: function (obj, type, fn, context) {
       
  4230 		var id = type + L.stamp(fn) + (context ? '_' + L.stamp(context) : '');
       
  4231 
       
  4232 		if (obj[eventsKey] && obj[eventsKey][id]) { return this; }
       
  4233 
       
  4234 		var handler = function (e) {
       
  4235 			return fn.call(context || obj, e || window.event);
       
  4236 		};
       
  4237 
       
  4238 		var originalHandler = handler;
       
  4239 
       
  4240 		if (L.Browser.pointer && type.indexOf('touch') === 0) {
       
  4241 			this.addPointerListener(obj, type, handler, id);
       
  4242 
       
  4243 		} else if (L.Browser.touch && (type === 'dblclick') && this.addDoubleTapListener &&
       
  4244 		           !(L.Browser.pointer && L.Browser.chrome)) {
       
  4245 			// Chrome >55 does not need the synthetic dblclicks from addDoubleTapListener
       
  4246 			// See #5180
       
  4247 			this.addDoubleTapListener(obj, handler, id);
       
  4248 
       
  4249 		} else if ('addEventListener' in obj) {
       
  4250 
       
  4251 			if (type === 'mousewheel') {
       
  4252 				obj.addEventListener('onwheel' in obj ? 'wheel' : 'mousewheel', handler, false);
       
  4253 
       
  4254 			} else if ((type === 'mouseenter') || (type === 'mouseleave')) {
       
  4255 				handler = function (e) {
       
  4256 					e = e || window.event;
       
  4257 					if (L.DomEvent._isExternalTarget(obj, e)) {
       
  4258 						originalHandler(e);
       
  4259 					}
       
  4260 				};
       
  4261 				obj.addEventListener(type === 'mouseenter' ? 'mouseover' : 'mouseout', handler, false);
       
  4262 
       
  4263 			} else {
       
  4264 				if (type === 'click' && L.Browser.android) {
       
  4265 					handler = function (e) {
       
  4266 						return L.DomEvent._filterClick(e, originalHandler);
       
  4267 					};
       
  4268 				}
       
  4269 				obj.addEventListener(type, handler, false);
       
  4270 			}
       
  4271 
       
  4272 		} else if ('attachEvent' in obj) {
       
  4273 			obj.attachEvent('on' + type, handler);
       
  4274 		}
       
  4275 
       
  4276 		obj[eventsKey] = obj[eventsKey] || {};
       
  4277 		obj[eventsKey][id] = handler;
       
  4278 
       
  4279 		return this;
       
  4280 	},
       
  4281 
       
  4282 	_off: function (obj, type, fn, context) {
       
  4283 
       
  4284 		var id = type + L.stamp(fn) + (context ? '_' + L.stamp(context) : ''),
       
  4285 		    handler = obj[eventsKey] && obj[eventsKey][id];
       
  4286 
       
  4287 		if (!handler) { return this; }
       
  4288 
       
  4289 		if (L.Browser.pointer && type.indexOf('touch') === 0) {
       
  4290 			this.removePointerListener(obj, type, id);
       
  4291 
       
  4292 		} else if (L.Browser.touch && (type === 'dblclick') && this.removeDoubleTapListener) {
       
  4293 			this.removeDoubleTapListener(obj, id);
       
  4294 
       
  4295 		} else if ('removeEventListener' in obj) {
       
  4296 
       
  4297 			if (type === 'mousewheel') {
       
  4298 				obj.removeEventListener('onwheel' in obj ? 'wheel' : 'mousewheel', handler, false);
       
  4299 
       
  4300 			} else {
       
  4301 				obj.removeEventListener(
       
  4302 					type === 'mouseenter' ? 'mouseover' :
       
  4303 					type === 'mouseleave' ? 'mouseout' : type, handler, false);
       
  4304 			}
       
  4305 
       
  4306 		} else if ('detachEvent' in obj) {
       
  4307 			obj.detachEvent('on' + type, handler);
       
  4308 		}
       
  4309 
       
  4310 		obj[eventsKey][id] = null;
       
  4311 
       
  4312 		return this;
       
  4313 	},
       
  4314 
       
  4315 	// @function stopPropagation(ev: DOMEvent): this
       
  4316 	// Stop the given event from propagation to parent elements. Used inside the listener functions:
       
  4317 	// ```js
       
  4318 	// L.DomEvent.on(div, 'click', function (ev) {
       
  4319 	// 	L.DomEvent.stopPropagation(ev);
       
  4320 	// });
       
  4321 	// ```
       
  4322 	stopPropagation: function (e) {
       
  4323 
       
  4324 		if (e.stopPropagation) {
       
  4325 			e.stopPropagation();
       
  4326 		} else if (e.originalEvent) {  // In case of Leaflet event.
       
  4327 			e.originalEvent._stopped = true;
       
  4328 		} else {
       
  4329 			e.cancelBubble = true;
       
  4330 		}
       
  4331 		L.DomEvent._skipped(e);
       
  4332 
       
  4333 		return this;
       
  4334 	},
       
  4335 
       
  4336 	// @function disableScrollPropagation(el: HTMLElement): this
       
  4337 	// Adds `stopPropagation` to the element's `'mousewheel'` events (plus browser variants).
       
  4338 	disableScrollPropagation: function (el) {
       
  4339 		return L.DomEvent.on(el, 'mousewheel', L.DomEvent.stopPropagation);
       
  4340 	},
       
  4341 
       
  4342 	// @function disableClickPropagation(el: HTMLElement): this
       
  4343 	// Adds `stopPropagation` to the element's `'click'`, `'doubleclick'`,
       
  4344 	// `'mousedown'` and `'touchstart'` events (plus browser variants).
       
  4345 	disableClickPropagation: function (el) {
       
  4346 		var stop = L.DomEvent.stopPropagation;
       
  4347 
       
  4348 		L.DomEvent.on(el, L.Draggable.START.join(' '), stop);
       
  4349 
       
  4350 		return L.DomEvent.on(el, {
       
  4351 			click: L.DomEvent._fakeStop,
       
  4352 			dblclick: stop
       
  4353 		});
       
  4354 	},
       
  4355 
       
  4356 	// @function preventDefault(ev: DOMEvent): this
       
  4357 	// Prevents the default action of the DOM Event `ev` from happening (such as
       
  4358 	// following a link in the href of the a element, or doing a POST request
       
  4359 	// with page reload when a `<form>` is submitted).
       
  4360 	// Use it inside listener functions.
       
  4361 	preventDefault: function (e) {
       
  4362 
       
  4363 		if (e.preventDefault) {
       
  4364 			e.preventDefault();
       
  4365 		} else {
       
  4366 			e.returnValue = false;
       
  4367 		}
       
  4368 		return this;
       
  4369 	},
       
  4370 
       
  4371 	// @function stop(ev): this
       
  4372 	// Does `stopPropagation` and `preventDefault` at the same time.
       
  4373 	stop: function (e) {
       
  4374 		return L.DomEvent
       
  4375 			.preventDefault(e)
       
  4376 			.stopPropagation(e);
       
  4377 	},
       
  4378 
       
  4379 	// @function getMousePosition(ev: DOMEvent, container?: HTMLElement): Point
       
  4380 	// Gets normalized mouse position from a DOM event relative to the
       
  4381 	// `container` or to the whole page if not specified.
       
  4382 	getMousePosition: function (e, container) {
       
  4383 		if (!container) {
       
  4384 			return new L.Point(e.clientX, e.clientY);
       
  4385 		}
       
  4386 
       
  4387 		var rect = container.getBoundingClientRect();
       
  4388 
       
  4389 		return new L.Point(
       
  4390 			e.clientX - rect.left - container.clientLeft,
       
  4391 			e.clientY - rect.top - container.clientTop);
       
  4392 	},
       
  4393 
       
  4394 	// Chrome on Win scrolls double the pixels as in other platforms (see #4538),
       
  4395 	// and Firefox scrolls device pixels, not CSS pixels
       
  4396 	_wheelPxFactor: (L.Browser.win && L.Browser.chrome) ? 2 :
       
  4397 	                L.Browser.gecko ? window.devicePixelRatio :
       
  4398 	                1,
       
  4399 
       
  4400 	// @function getWheelDelta(ev: DOMEvent): Number
       
  4401 	// Gets normalized wheel delta from a mousewheel DOM event, in vertical
       
  4402 	// pixels scrolled (negative if scrolling down).
       
  4403 	// Events from pointing devices without precise scrolling are mapped to
       
  4404 	// a best guess of 60 pixels.
       
  4405 	getWheelDelta: function (e) {
       
  4406 		return (L.Browser.edge) ? e.wheelDeltaY / 2 : // Don't trust window-geometry-based delta
       
  4407 		       (e.deltaY && e.deltaMode === 0) ? -e.deltaY / L.DomEvent._wheelPxFactor : // Pixels
       
  4408 		       (e.deltaY && e.deltaMode === 1) ? -e.deltaY * 20 : // Lines
       
  4409 		       (e.deltaY && e.deltaMode === 2) ? -e.deltaY * 60 : // Pages
       
  4410 		       (e.deltaX || e.deltaZ) ? 0 :	// Skip horizontal/depth wheel events
       
  4411 		       e.wheelDelta ? (e.wheelDeltaY || e.wheelDelta) / 2 : // Legacy IE pixels
       
  4412 		       (e.detail && Math.abs(e.detail) < 32765) ? -e.detail * 20 : // Legacy Moz lines
       
  4413 		       e.detail ? e.detail / -32765 * 60 : // Legacy Moz pages
       
  4414 		       0;
       
  4415 	},
       
  4416 
       
  4417 	_skipEvents: {},
       
  4418 
       
  4419 	_fakeStop: function (e) {
       
  4420 		// fakes stopPropagation by setting a special event flag, checked/reset with L.DomEvent._skipped(e)
       
  4421 		L.DomEvent._skipEvents[e.type] = true;
       
  4422 	},
       
  4423 
       
  4424 	_skipped: function (e) {
       
  4425 		var skipped = this._skipEvents[e.type];
       
  4426 		// reset when checking, as it's only used in map container and propagates outside of the map
       
  4427 		this._skipEvents[e.type] = false;
       
  4428 		return skipped;
       
  4429 	},
       
  4430 
       
  4431 	// check if element really left/entered the event target (for mouseenter/mouseleave)
       
  4432 	_isExternalTarget: function (el, e) {
       
  4433 
       
  4434 		var related = e.relatedTarget;
       
  4435 
       
  4436 		if (!related) { return true; }
       
  4437 
       
  4438 		try {
       
  4439 			while (related && (related !== el)) {
       
  4440 				related = related.parentNode;
       
  4441 			}
       
  4442 		} catch (err) {
       
  4443 			return false;
       
  4444 		}
       
  4445 		return (related !== el);
       
  4446 	},
       
  4447 
       
  4448 	// this is a horrible workaround for a bug in Android where a single touch triggers two click events
       
  4449 	_filterClick: function (e, handler) {
       
  4450 		var timeStamp = (e.timeStamp || (e.originalEvent && e.originalEvent.timeStamp)),
       
  4451 		    elapsed = L.DomEvent._lastClick && (timeStamp - L.DomEvent._lastClick);
       
  4452 
       
  4453 		// are they closer together than 500ms yet more than 100ms?
       
  4454 		// Android typically triggers them ~300ms apart while multiple listeners
       
  4455 		// on the same event should be triggered far faster;
       
  4456 		// or check if click is simulated on the element, and if it is, reject any non-simulated events
       
  4457 
       
  4458 		if ((elapsed && elapsed > 100 && elapsed < 500) || (e.target._simulatedClick && !e._simulated)) {
       
  4459 			L.DomEvent.stop(e);
       
  4460 			return;
       
  4461 		}
       
  4462 		L.DomEvent._lastClick = timeStamp;
       
  4463 
       
  4464 		handler(e);
       
  4465 	}
       
  4466 };
       
  4467 
       
  4468 // @function addListener(…): this
       
  4469 // Alias to [`L.DomEvent.on`](#domevent-on)
       
  4470 L.DomEvent.addListener = L.DomEvent.on;
       
  4471 
       
  4472 // @function removeListener(…): this
       
  4473 // Alias to [`L.DomEvent.off`](#domevent-off)
       
  4474 L.DomEvent.removeListener = L.DomEvent.off;
       
  4475 
       
  4476 
       
  4477 
       
  4478 /*
       
  4479  * @class PosAnimation
       
  4480  * @aka L.PosAnimation
       
  4481  * @inherits Evented
       
  4482  * Used internally for panning animations, utilizing CSS3 Transitions for modern browsers and a timer fallback for IE6-9.
       
  4483  *
       
  4484  * @example
       
  4485  * ```js
       
  4486  * var fx = new L.PosAnimation();
       
  4487  * fx.run(el, [300, 500], 0.5);
       
  4488  * ```
       
  4489  *
       
  4490  * @constructor L.PosAnimation()
       
  4491  * Creates a `PosAnimation` object.
       
  4492  *
       
  4493  */
       
  4494 
       
  4495 L.PosAnimation = L.Evented.extend({
       
  4496 
       
  4497 	// @method run(el: HTMLElement, newPos: Point, duration?: Number, easeLinearity?: Number)
       
  4498 	// Run an animation of a given element to a new position, optionally setting
       
  4499 	// duration in seconds (`0.25` by default) and easing linearity factor (3rd
       
  4500 	// argument of the [cubic bezier curve](http://cubic-bezier.com/#0,0,.5,1),
       
  4501 	// `0.5` by default).
       
  4502 	run: function (el, newPos, duration, easeLinearity) {
       
  4503 		this.stop();
       
  4504 
       
  4505 		this._el = el;
       
  4506 		this._inProgress = true;
       
  4507 		this._duration = duration || 0.25;
       
  4508 		this._easeOutPower = 1 / Math.max(easeLinearity || 0.5, 0.2);
       
  4509 
       
  4510 		this._startPos = L.DomUtil.getPosition(el);
       
  4511 		this._offset = newPos.subtract(this._startPos);
       
  4512 		this._startTime = +new Date();
       
  4513 
       
  4514 		// @event start: Event
       
  4515 		// Fired when the animation starts
       
  4516 		this.fire('start');
       
  4517 
       
  4518 		this._animate();
       
  4519 	},
       
  4520 
       
  4521 	// @method stop()
       
  4522 	// Stops the animation (if currently running).
       
  4523 	stop: function () {
       
  4524 		if (!this._inProgress) { return; }
       
  4525 
       
  4526 		this._step(true);
       
  4527 		this._complete();
       
  4528 	},
       
  4529 
       
  4530 	_animate: function () {
       
  4531 		// animation loop
       
  4532 		this._animId = L.Util.requestAnimFrame(this._animate, this);
       
  4533 		this._step();
       
  4534 	},
       
  4535 
       
  4536 	_step: function (round) {
       
  4537 		var elapsed = (+new Date()) - this._startTime,
       
  4538 		    duration = this._duration * 1000;
       
  4539 
       
  4540 		if (elapsed < duration) {
       
  4541 			this._runFrame(this._easeOut(elapsed / duration), round);
       
  4542 		} else {
       
  4543 			this._runFrame(1);
       
  4544 			this._complete();
       
  4545 		}
       
  4546 	},
       
  4547 
       
  4548 	_runFrame: function (progress, round) {
       
  4549 		var pos = this._startPos.add(this._offset.multiplyBy(progress));
       
  4550 		if (round) {
       
  4551 			pos._round();
       
  4552 		}
       
  4553 		L.DomUtil.setPosition(this._el, pos);
       
  4554 
       
  4555 		// @event step: Event
       
  4556 		// Fired continuously during the animation.
       
  4557 		this.fire('step');
       
  4558 	},
       
  4559 
       
  4560 	_complete: function () {
       
  4561 		L.Util.cancelAnimFrame(this._animId);
       
  4562 
       
  4563 		this._inProgress = false;
       
  4564 		// @event end: Event
       
  4565 		// Fired when the animation ends.
       
  4566 		this.fire('end');
       
  4567 	},
       
  4568 
       
  4569 	_easeOut: function (t) {
       
  4570 		return 1 - Math.pow(1 - t, this._easeOutPower);
       
  4571 	}
       
  4572 });
       
  4573 
       
  4574 
       
  4575 
       
  4576 /*
       
  4577  * @namespace Projection
       
  4578  * @projection L.Projection.Mercator
       
  4579  *
       
  4580  * Elliptical Mercator projection — more complex than Spherical Mercator. Takes into account that Earth is a geoid, not a perfect sphere. Used by the EPSG:3395 CRS.
       
  4581  */
       
  4582 
       
  4583 L.Projection.Mercator = {
       
  4584 	R: 6378137,
       
  4585 	R_MINOR: 6356752.314245179,
       
  4586 
       
  4587 	bounds: L.bounds([-20037508.34279, -15496570.73972], [20037508.34279, 18764656.23138]),
       
  4588 
       
  4589 	project: function (latlng) {
       
  4590 		var d = Math.PI / 180,
       
  4591 		    r = this.R,
       
  4592 		    y = latlng.lat * d,
       
  4593 		    tmp = this.R_MINOR / r,
       
  4594 		    e = Math.sqrt(1 - tmp * tmp),
       
  4595 		    con = e * Math.sin(y);
       
  4596 
       
  4597 		var ts = Math.tan(Math.PI / 4 - y / 2) / Math.pow((1 - con) / (1 + con), e / 2);
       
  4598 		y = -r * Math.log(Math.max(ts, 1E-10));
       
  4599 
       
  4600 		return new L.Point(latlng.lng * d * r, y);
       
  4601 	},
       
  4602 
       
  4603 	unproject: function (point) {
       
  4604 		var d = 180 / Math.PI,
       
  4605 		    r = this.R,
       
  4606 		    tmp = this.R_MINOR / r,
       
  4607 		    e = Math.sqrt(1 - tmp * tmp),
       
  4608 		    ts = Math.exp(-point.y / r),
       
  4609 		    phi = Math.PI / 2 - 2 * Math.atan(ts);
       
  4610 
       
  4611 		for (var i = 0, dphi = 0.1, con; i < 15 && Math.abs(dphi) > 1e-7; i++) {
       
  4612 			con = e * Math.sin(phi);
       
  4613 			con = Math.pow((1 - con) / (1 + con), e / 2);
       
  4614 			dphi = Math.PI / 2 - 2 * Math.atan(ts * con) - phi;
       
  4615 			phi += dphi;
       
  4616 		}
       
  4617 
       
  4618 		return new L.LatLng(phi * d, point.x * d / r);
       
  4619 	}
       
  4620 };
       
  4621 
       
  4622 
       
  4623 
       
  4624 /*
       
  4625  * @namespace CRS
       
  4626  * @crs L.CRS.EPSG3395
       
  4627  *
       
  4628  * Rarely used by some commercial tile providers. Uses Elliptical Mercator projection.
       
  4629  */
       
  4630 
       
  4631 L.CRS.EPSG3395 = L.extend({}, L.CRS.Earth, {
       
  4632 	code: 'EPSG:3395',
       
  4633 	projection: L.Projection.Mercator,
       
  4634 
       
  4635 	transformation: (function () {
       
  4636 		var scale = 0.5 / (Math.PI * L.Projection.Mercator.R);
       
  4637 		return new L.Transformation(scale, 0.5, -scale, 0.5);
       
  4638 	}())
       
  4639 });
       
  4640 
       
  4641 
       
  4642 
       
  4643 /*
       
  4644  * @class GridLayer
       
  4645  * @inherits Layer
       
  4646  * @aka L.GridLayer
       
  4647  *
       
  4648  * Generic class for handling a tiled grid of HTML elements. This is the base class for all tile layers and replaces `TileLayer.Canvas`.
       
  4649  * GridLayer can be extended to create a tiled grid of HTML elements like `<canvas>`, `<img>` or `<div>`. GridLayer will handle creating and animating these DOM elements for you.
       
  4650  *
       
  4651  *
       
  4652  * @section Synchronous usage
       
  4653  * @example
       
  4654  *
       
  4655  * To create a custom layer, extend GridLayer and implement the `createTile()` method, which will be passed a `Point` object with the `x`, `y`, and `z` (zoom level) coordinates to draw your tile.
       
  4656  *
       
  4657  * ```js
       
  4658  * var CanvasLayer = L.GridLayer.extend({
       
  4659  *     createTile: function(coords){
       
  4660  *         // create a <canvas> element for drawing
       
  4661  *         var tile = L.DomUtil.create('canvas', 'leaflet-tile');
       
  4662  *
       
  4663  *         // setup tile width and height according to the options
       
  4664  *         var size = this.getTileSize();
       
  4665  *         tile.width = size.x;
       
  4666  *         tile.height = size.y;
       
  4667  *
       
  4668  *         // get a canvas context and draw something on it using coords.x, coords.y and coords.z
       
  4669  *         var ctx = tile.getContext('2d');
       
  4670  *
       
  4671  *         // return the tile so it can be rendered on screen
       
  4672  *         return tile;
       
  4673  *     }
       
  4674  * });
       
  4675  * ```
       
  4676  *
       
  4677  * @section Asynchronous usage
       
  4678  * @example
       
  4679  *
       
  4680  * Tile creation can also be asynchronous, this is useful when using a third-party drawing library. Once the tile is finished drawing it can be passed to the `done()` callback.
       
  4681  *
       
  4682  * ```js
       
  4683  * var CanvasLayer = L.GridLayer.extend({
       
  4684  *     createTile: function(coords, done){
       
  4685  *         var error;
       
  4686  *
       
  4687  *         // create a <canvas> element for drawing
       
  4688  *         var tile = L.DomUtil.create('canvas', 'leaflet-tile');
       
  4689  *
       
  4690  *         // setup tile width and height according to the options
       
  4691  *         var size = this.getTileSize();
       
  4692  *         tile.width = size.x;
       
  4693  *         tile.height = size.y;
       
  4694  *
       
  4695  *         // draw something asynchronously and pass the tile to the done() callback
       
  4696  *         setTimeout(function() {
       
  4697  *             done(error, tile);
       
  4698  *         }, 1000);
       
  4699  *
       
  4700  *         return tile;
       
  4701  *     }
       
  4702  * });
       
  4703  * ```
       
  4704  *
       
  4705  * @section
       
  4706  */
       
  4707 
       
  4708 
       
  4709 L.GridLayer = L.Layer.extend({
       
  4710 
       
  4711 	// @section
       
  4712 	// @aka GridLayer options
       
  4713 	options: {
       
  4714 		// @option tileSize: Number|Point = 256
       
  4715 		// Width and height of tiles in the grid. Use a number if width and height are equal, or `L.point(width, height)` otherwise.
       
  4716 		tileSize: 256,
       
  4717 
       
  4718 		// @option opacity: Number = 1.0
       
  4719 		// Opacity of the tiles. Can be used in the `createTile()` function.
       
  4720 		opacity: 1,
       
  4721 
       
  4722 		// @option updateWhenIdle: Boolean = depends
       
  4723 		// If `false`, new tiles are loaded during panning, otherwise only after it (for better performance). `true` by default on mobile browsers, otherwise `false`.
       
  4724 		updateWhenIdle: L.Browser.mobile,
       
  4725 
       
  4726 		// @option updateWhenZooming: Boolean = true
       
  4727 		// By default, a smooth zoom animation (during a [touch zoom](#map-touchzoom) or a [`flyTo()`](#map-flyto)) will update grid layers every integer zoom level. Setting this option to `false` will update the grid layer only when the smooth animation ends.
       
  4728 		updateWhenZooming: true,
       
  4729 
       
  4730 		// @option updateInterval: Number = 200
       
  4731 		// Tiles will not update more than once every `updateInterval` milliseconds when panning.
       
  4732 		updateInterval: 200,
       
  4733 
       
  4734 		// @option zIndex: Number = 1
       
  4735 		// The explicit zIndex of the tile layer.
       
  4736 		zIndex: 1,
       
  4737 
       
  4738 		// @option bounds: LatLngBounds = undefined
       
  4739 		// If set, tiles will only be loaded inside the set `LatLngBounds`.
       
  4740 		bounds: null,
       
  4741 
       
  4742 		// @option minZoom: Number = 0
       
  4743 		// The minimum zoom level that tiles will be loaded at. By default the entire map.
       
  4744 		minZoom: 0,
       
  4745 
       
  4746 		// @option maxZoom: Number = undefined
       
  4747 		// The maximum zoom level that tiles will be loaded at.
       
  4748 		maxZoom: undefined,
       
  4749 
       
  4750 		// @option noWrap: Boolean = false
       
  4751 		// Whether the layer is wrapped around the antimeridian. If `true`, the
       
  4752 		// GridLayer will only be displayed once at low zoom levels. Has no
       
  4753 		// effect when the [map CRS](#map-crs) doesn't wrap around. Can be used
       
  4754 		// in combination with [`bounds`](#gridlayer-bounds) to prevent requesting
       
  4755 		// tiles outside the CRS limits.
       
  4756 		noWrap: false,
       
  4757 
       
  4758 		// @option pane: String = 'tilePane'
       
  4759 		// `Map pane` where the grid layer will be added.
       
  4760 		pane: 'tilePane',
       
  4761 
       
  4762 		// @option className: String = ''
       
  4763 		// A custom class name to assign to the tile layer. Empty by default.
       
  4764 		className: '',
       
  4765 
       
  4766 		// @option keepBuffer: Number = 2
       
  4767 		// When panning the map, keep this many rows and columns of tiles before unloading them.
       
  4768 		keepBuffer: 2
       
  4769 	},
       
  4770 
       
  4771 	initialize: function (options) {
       
  4772 		L.setOptions(this, options);
       
  4773 	},
       
  4774 
       
  4775 	onAdd: function () {
       
  4776 		this._initContainer();
       
  4777 
       
  4778 		this._levels = {};
       
  4779 		this._tiles = {};
       
  4780 
       
  4781 		this._resetView();
       
  4782 		this._update();
       
  4783 	},
       
  4784 
       
  4785 	beforeAdd: function (map) {
       
  4786 		map._addZoomLimit(this);
       
  4787 	},
       
  4788 
       
  4789 	onRemove: function (map) {
       
  4790 		this._removeAllTiles();
       
  4791 		L.DomUtil.remove(this._container);
       
  4792 		map._removeZoomLimit(this);
       
  4793 		this._container = null;
       
  4794 		this._tileZoom = null;
       
  4795 	},
       
  4796 
       
  4797 	// @method bringToFront: this
       
  4798 	// Brings the tile layer to the top of all tile layers.
       
  4799 	bringToFront: function () {
       
  4800 		if (this._map) {
       
  4801 			L.DomUtil.toFront(this._container);
       
  4802 			this._setAutoZIndex(Math.max);
       
  4803 		}
       
  4804 		return this;
       
  4805 	},
       
  4806 
       
  4807 	// @method bringToBack: this
       
  4808 	// Brings the tile layer to the bottom of all tile layers.
       
  4809 	bringToBack: function () {
       
  4810 		if (this._map) {
       
  4811 			L.DomUtil.toBack(this._container);
       
  4812 			this._setAutoZIndex(Math.min);
       
  4813 		}
       
  4814 		return this;
       
  4815 	},
       
  4816 
       
  4817 	// @method getContainer: HTMLElement
       
  4818 	// Returns the HTML element that contains the tiles for this layer.
       
  4819 	getContainer: function () {
       
  4820 		return this._container;
       
  4821 	},
       
  4822 
       
  4823 	// @method setOpacity(opacity: Number): this
       
  4824 	// Changes the [opacity](#gridlayer-opacity) of the grid layer.
       
  4825 	setOpacity: function (opacity) {
       
  4826 		this.options.opacity = opacity;
       
  4827 		this._updateOpacity();
       
  4828 		return this;
       
  4829 	},
       
  4830 
       
  4831 	// @method setZIndex(zIndex: Number): this
       
  4832 	// Changes the [zIndex](#gridlayer-zindex) of the grid layer.
       
  4833 	setZIndex: function (zIndex) {
       
  4834 		this.options.zIndex = zIndex;
       
  4835 		this._updateZIndex();
       
  4836 
       
  4837 		return this;
       
  4838 	},
       
  4839 
       
  4840 	// @method isLoading: Boolean
       
  4841 	// Returns `true` if any tile in the grid layer has not finished loading.
       
  4842 	isLoading: function () {
       
  4843 		return this._loading;
       
  4844 	},
       
  4845 
       
  4846 	// @method redraw: this
       
  4847 	// Causes the layer to clear all the tiles and request them again.
       
  4848 	redraw: function () {
       
  4849 		if (this._map) {
       
  4850 			this._removeAllTiles();
       
  4851 			this._update();
       
  4852 		}
       
  4853 		return this;
       
  4854 	},
       
  4855 
       
  4856 	getEvents: function () {
       
  4857 		var events = {
       
  4858 			viewprereset: this._invalidateAll,
       
  4859 			viewreset: this._resetView,
       
  4860 			zoom: this._resetView,
       
  4861 			moveend: this._onMoveEnd
       
  4862 		};
       
  4863 
       
  4864 		if (!this.options.updateWhenIdle) {
       
  4865 			// update tiles on move, but not more often than once per given interval
       
  4866 			if (!this._onMove) {
       
  4867 				this._onMove = L.Util.throttle(this._onMoveEnd, this.options.updateInterval, this);
       
  4868 			}
       
  4869 
       
  4870 			events.move = this._onMove;
       
  4871 		}
       
  4872 
       
  4873 		if (this._zoomAnimated) {
       
  4874 			events.zoomanim = this._animateZoom;
       
  4875 		}
       
  4876 
       
  4877 		return events;
       
  4878 	},
       
  4879 
       
  4880 	// @section Extension methods
       
  4881 	// Layers extending `GridLayer` shall reimplement the following method.
       
  4882 	// @method createTile(coords: Object, done?: Function): HTMLElement
       
  4883 	// Called only internally, must be overriden by classes extending `GridLayer`.
       
  4884 	// Returns the `HTMLElement` corresponding to the given `coords`. If the `done` callback
       
  4885 	// is specified, it must be called when the tile has finished loading and drawing.
       
  4886 	createTile: function () {
       
  4887 		return document.createElement('div');
       
  4888 	},
       
  4889 
       
  4890 	// @section
       
  4891 	// @method getTileSize: Point
       
  4892 	// Normalizes the [tileSize option](#gridlayer-tilesize) into a point. Used by the `createTile()` method.
       
  4893 	getTileSize: function () {
       
  4894 		var s = this.options.tileSize;
       
  4895 		return s instanceof L.Point ? s : new L.Point(s, s);
       
  4896 	},
       
  4897 
       
  4898 	_updateZIndex: function () {
       
  4899 		if (this._container && this.options.zIndex !== undefined && this.options.zIndex !== null) {
       
  4900 			this._container.style.zIndex = this.options.zIndex;
       
  4901 		}
       
  4902 	},
       
  4903 
       
  4904 	_setAutoZIndex: function (compare) {
       
  4905 		// go through all other layers of the same pane, set zIndex to max + 1 (front) or min - 1 (back)
       
  4906 
       
  4907 		var layers = this.getPane().children,
       
  4908 		    edgeZIndex = -compare(-Infinity, Infinity); // -Infinity for max, Infinity for min
       
  4909 
       
  4910 		for (var i = 0, len = layers.length, zIndex; i < len; i++) {
       
  4911 
       
  4912 			zIndex = layers[i].style.zIndex;
       
  4913 
       
  4914 			if (layers[i] !== this._container && zIndex) {
       
  4915 				edgeZIndex = compare(edgeZIndex, +zIndex);
       
  4916 			}
       
  4917 		}
       
  4918 
       
  4919 		if (isFinite(edgeZIndex)) {
       
  4920 			this.options.zIndex = edgeZIndex + compare(-1, 1);
       
  4921 			this._updateZIndex();
       
  4922 		}
       
  4923 	},
       
  4924 
       
  4925 	_updateOpacity: function () {
       
  4926 		if (!this._map) { return; }
       
  4927 
       
  4928 		// IE doesn't inherit filter opacity properly, so we're forced to set it on tiles
       
  4929 		if (L.Browser.ielt9) { return; }
       
  4930 
       
  4931 		L.DomUtil.setOpacity(this._container, this.options.opacity);
       
  4932 
       
  4933 		var now = +new Date(),
       
  4934 		    nextFrame = false,
       
  4935 		    willPrune = false;
       
  4936 
       
  4937 		for (var key in this._tiles) {
       
  4938 			var tile = this._tiles[key];
       
  4939 			if (!tile.current || !tile.loaded) { continue; }
       
  4940 
       
  4941 			var fade = Math.min(1, (now - tile.loaded) / 200);
       
  4942 
       
  4943 			L.DomUtil.setOpacity(tile.el, fade);
       
  4944 			if (fade < 1) {
       
  4945 				nextFrame = true;
       
  4946 			} else {
       
  4947 				if (tile.active) { willPrune = true; }
       
  4948 				tile.active = true;
       
  4949 			}
       
  4950 		}
       
  4951 
       
  4952 		if (willPrune && !this._noPrune) { this._pruneTiles(); }
       
  4953 
       
  4954 		if (nextFrame) {
       
  4955 			L.Util.cancelAnimFrame(this._fadeFrame);
       
  4956 			this._fadeFrame = L.Util.requestAnimFrame(this._updateOpacity, this);
       
  4957 		}
       
  4958 	},
       
  4959 
       
  4960 	_initContainer: function () {
       
  4961 		if (this._container) { return; }
       
  4962 
       
  4963 		this._container = L.DomUtil.create('div', 'leaflet-layer ' + (this.options.className || ''));
       
  4964 		this._updateZIndex();
       
  4965 
       
  4966 		if (this.options.opacity < 1) {
       
  4967 			this._updateOpacity();
       
  4968 		}
       
  4969 
       
  4970 		this.getPane().appendChild(this._container);
       
  4971 	},
       
  4972 
       
  4973 	_updateLevels: function () {
       
  4974 
       
  4975 		var zoom = this._tileZoom,
       
  4976 		    maxZoom = this.options.maxZoom;
       
  4977 
       
  4978 		if (zoom === undefined) { return undefined; }
       
  4979 
       
  4980 		for (var z in this._levels) {
       
  4981 			if (this._levels[z].el.children.length || z === zoom) {
       
  4982 				this._levels[z].el.style.zIndex = maxZoom - Math.abs(zoom - z);
       
  4983 			} else {
       
  4984 				L.DomUtil.remove(this._levels[z].el);
       
  4985 				this._removeTilesAtZoom(z);
       
  4986 				delete this._levels[z];
       
  4987 			}
       
  4988 		}
       
  4989 
       
  4990 		var level = this._levels[zoom],
       
  4991 		    map = this._map;
       
  4992 
       
  4993 		if (!level) {
       
  4994 			level = this._levels[zoom] = {};
       
  4995 
       
  4996 			level.el = L.DomUtil.create('div', 'leaflet-tile-container leaflet-zoom-animated', this._container);
       
  4997 			level.el.style.zIndex = maxZoom;
       
  4998 
       
  4999 			level.origin = map.project(map.unproject(map.getPixelOrigin()), zoom).round();
       
  5000 			level.zoom = zoom;
       
  5001 
       
  5002 			this._setZoomTransform(level, map.getCenter(), map.getZoom());
       
  5003 
       
  5004 			// force the browser to consider the newly added element for transition
       
  5005 			L.Util.falseFn(level.el.offsetWidth);
       
  5006 		}
       
  5007 
       
  5008 		this._level = level;
       
  5009 
       
  5010 		return level;
       
  5011 	},
       
  5012 
       
  5013 	_pruneTiles: function () {
       
  5014 		if (!this._map) {
       
  5015 			return;
       
  5016 		}
       
  5017 
       
  5018 		var key, tile;
       
  5019 
       
  5020 		var zoom = this._map.getZoom();
       
  5021 		if (zoom > this.options.maxZoom ||
       
  5022 			zoom < this.options.minZoom) {
       
  5023 			this._removeAllTiles();
       
  5024 			return;
       
  5025 		}
       
  5026 
       
  5027 		for (key in this._tiles) {
       
  5028 			tile = this._tiles[key];
       
  5029 			tile.retain = tile.current;
       
  5030 		}
       
  5031 
       
  5032 		for (key in this._tiles) {
       
  5033 			tile = this._tiles[key];
       
  5034 			if (tile.current && !tile.active) {
       
  5035 				var coords = tile.coords;
       
  5036 				if (!this._retainParent(coords.x, coords.y, coords.z, coords.z - 5)) {
       
  5037 					this._retainChildren(coords.x, coords.y, coords.z, coords.z + 2);
       
  5038 				}
       
  5039 			}
       
  5040 		}
       
  5041 
       
  5042 		for (key in this._tiles) {
       
  5043 			if (!this._tiles[key].retain) {
       
  5044 				this._removeTile(key);
       
  5045 			}
       
  5046 		}
       
  5047 	},
       
  5048 
       
  5049 	_removeTilesAtZoom: function (zoom) {
       
  5050 		for (var key in this._tiles) {
       
  5051 			if (this._tiles[key].coords.z !== zoom) {
       
  5052 				continue;
       
  5053 			}
       
  5054 			this._removeTile(key);
       
  5055 		}
       
  5056 	},
       
  5057 
       
  5058 	_removeAllTiles: function () {
       
  5059 		for (var key in this._tiles) {
       
  5060 			this._removeTile(key);
       
  5061 		}
       
  5062 	},
       
  5063 
       
  5064 	_invalidateAll: function () {
       
  5065 		for (var z in this._levels) {
       
  5066 			L.DomUtil.remove(this._levels[z].el);
       
  5067 			delete this._levels[z];
       
  5068 		}
       
  5069 		this._removeAllTiles();
       
  5070 
       
  5071 		this._tileZoom = null;
       
  5072 	},
       
  5073 
       
  5074 	_retainParent: function (x, y, z, minZoom) {
       
  5075 		var x2 = Math.floor(x / 2),
       
  5076 		    y2 = Math.floor(y / 2),
       
  5077 		    z2 = z - 1,
       
  5078 		    coords2 = new L.Point(+x2, +y2);
       
  5079 		coords2.z = +z2;
       
  5080 
       
  5081 		var key = this._tileCoordsToKey(coords2),
       
  5082 		    tile = this._tiles[key];
       
  5083 
       
  5084 		if (tile && tile.active) {
       
  5085 			tile.retain = true;
       
  5086 			return true;
       
  5087 
       
  5088 		} else if (tile && tile.loaded) {
       
  5089 			tile.retain = true;
       
  5090 		}
       
  5091 
       
  5092 		if (z2 > minZoom) {
       
  5093 			return this._retainParent(x2, y2, z2, minZoom);
       
  5094 		}
       
  5095 
       
  5096 		return false;
       
  5097 	},
       
  5098 
       
  5099 	_retainChildren: function (x, y, z, maxZoom) {
       
  5100 
       
  5101 		for (var i = 2 * x; i < 2 * x + 2; i++) {
       
  5102 			for (var j = 2 * y; j < 2 * y + 2; j++) {
       
  5103 
       
  5104 				var coords = new L.Point(i, j);
       
  5105 				coords.z = z + 1;
       
  5106 
       
  5107 				var key = this._tileCoordsToKey(coords),
       
  5108 				    tile = this._tiles[key];
       
  5109 
       
  5110 				if (tile && tile.active) {
       
  5111 					tile.retain = true;
       
  5112 					continue;
       
  5113 
       
  5114 				} else if (tile && tile.loaded) {
       
  5115 					tile.retain = true;
       
  5116 				}
       
  5117 
       
  5118 				if (z + 1 < maxZoom) {
       
  5119 					this._retainChildren(i, j, z + 1, maxZoom);
       
  5120 				}
       
  5121 			}
       
  5122 		}
       
  5123 	},
       
  5124 
       
  5125 	_resetView: function (e) {
       
  5126 		var animating = e && (e.pinch || e.flyTo);
       
  5127 		this._setView(this._map.getCenter(), this._map.getZoom(), animating, animating);
       
  5128 	},
       
  5129 
       
  5130 	_animateZoom: function (e) {
       
  5131 		this._setView(e.center, e.zoom, true, e.noUpdate);
       
  5132 	},
       
  5133 
       
  5134 	_setView: function (center, zoom, noPrune, noUpdate) {
       
  5135 		var tileZoom = Math.round(zoom);
       
  5136 		if ((this.options.maxZoom !== undefined && tileZoom > this.options.maxZoom) ||
       
  5137 		    (this.options.minZoom !== undefined && tileZoom < this.options.minZoom)) {
       
  5138 			tileZoom = undefined;
       
  5139 		}
       
  5140 
       
  5141 		var tileZoomChanged = this.options.updateWhenZooming && (tileZoom !== this._tileZoom);
       
  5142 
       
  5143 		if (!noUpdate || tileZoomChanged) {
       
  5144 
       
  5145 			this._tileZoom = tileZoom;
       
  5146 
       
  5147 			if (this._abortLoading) {
       
  5148 				this._abortLoading();
       
  5149 			}
       
  5150 
       
  5151 			this._updateLevels();
       
  5152 			this._resetGrid();
       
  5153 
       
  5154 			if (tileZoom !== undefined) {
       
  5155 				this._update(center);
       
  5156 			}
       
  5157 
       
  5158 			if (!noPrune) {
       
  5159 				this._pruneTiles();
       
  5160 			}
       
  5161 
       
  5162 			// Flag to prevent _updateOpacity from pruning tiles during
       
  5163 			// a zoom anim or a pinch gesture
       
  5164 			this._noPrune = !!noPrune;
       
  5165 		}
       
  5166 
       
  5167 		this._setZoomTransforms(center, zoom);
       
  5168 	},
       
  5169 
       
  5170 	_setZoomTransforms: function (center, zoom) {
       
  5171 		for (var i in this._levels) {
       
  5172 			this._setZoomTransform(this._levels[i], center, zoom);
       
  5173 		}
       
  5174 	},
       
  5175 
       
  5176 	_setZoomTransform: function (level, center, zoom) {
       
  5177 		var scale = this._map.getZoomScale(zoom, level.zoom),
       
  5178 		    translate = level.origin.multiplyBy(scale)
       
  5179 		        .subtract(this._map._getNewPixelOrigin(center, zoom)).round();
       
  5180 
       
  5181 		if (L.Browser.any3d) {
       
  5182 			L.DomUtil.setTransform(level.el, translate, scale);
       
  5183 		} else {
       
  5184 			L.DomUtil.setPosition(level.el, translate);
       
  5185 		}
       
  5186 	},
       
  5187 
       
  5188 	_resetGrid: function () {
       
  5189 		var map = this._map,
       
  5190 		    crs = map.options.crs,
       
  5191 		    tileSize = this._tileSize = this.getTileSize(),
       
  5192 		    tileZoom = this._tileZoom;
       
  5193 
       
  5194 		var bounds = this._map.getPixelWorldBounds(this._tileZoom);
       
  5195 		if (bounds) {
       
  5196 			this._globalTileRange = this._pxBoundsToTileRange(bounds);
       
  5197 		}
       
  5198 
       
  5199 		this._wrapX = crs.wrapLng && !this.options.noWrap && [
       
  5200 			Math.floor(map.project([0, crs.wrapLng[0]], tileZoom).x / tileSize.x),
       
  5201 			Math.ceil(map.project([0, crs.wrapLng[1]], tileZoom).x / tileSize.y)
       
  5202 		];
       
  5203 		this._wrapY = crs.wrapLat && !this.options.noWrap && [
       
  5204 			Math.floor(map.project([crs.wrapLat[0], 0], tileZoom).y / tileSize.x),
       
  5205 			Math.ceil(map.project([crs.wrapLat[1], 0], tileZoom).y / tileSize.y)
       
  5206 		];
       
  5207 	},
       
  5208 
       
  5209 	_onMoveEnd: function () {
       
  5210 		if (!this._map || this._map._animatingZoom) { return; }
       
  5211 
       
  5212 		this._update();
       
  5213 	},
       
  5214 
       
  5215 	_getTiledPixelBounds: function (center) {
       
  5216 		var map = this._map,
       
  5217 		    mapZoom = map._animatingZoom ? Math.max(map._animateToZoom, map.getZoom()) : map.getZoom(),
       
  5218 		    scale = map.getZoomScale(mapZoom, this._tileZoom),
       
  5219 		    pixelCenter = map.project(center, this._tileZoom).floor(),
       
  5220 		    halfSize = map.getSize().divideBy(scale * 2);
       
  5221 
       
  5222 		return new L.Bounds(pixelCenter.subtract(halfSize), pixelCenter.add(halfSize));
       
  5223 	},
       
  5224 
       
  5225 	// Private method to load tiles in the grid's active zoom level according to map bounds
       
  5226 	_update: function (center) {
       
  5227 		var map = this._map;
       
  5228 		if (!map) { return; }
       
  5229 		var zoom = map.getZoom();
       
  5230 
       
  5231 		if (center === undefined) { center = map.getCenter(); }
       
  5232 		if (this._tileZoom === undefined) { return; }	// if out of minzoom/maxzoom
       
  5233 
       
  5234 		var pixelBounds = this._getTiledPixelBounds(center),
       
  5235 		    tileRange = this._pxBoundsToTileRange(pixelBounds),
       
  5236 		    tileCenter = tileRange.getCenter(),
       
  5237 		    queue = [],
       
  5238 		    margin = this.options.keepBuffer,
       
  5239 		    noPruneRange = new L.Bounds(tileRange.getBottomLeft().subtract([margin, -margin]),
       
  5240 		                              tileRange.getTopRight().add([margin, -margin]));
       
  5241 
       
  5242 		for (var key in this._tiles) {
       
  5243 			var c = this._tiles[key].coords;
       
  5244 			if (c.z !== this._tileZoom || !noPruneRange.contains(L.point(c.x, c.y))) {
       
  5245 				this._tiles[key].current = false;
       
  5246 			}
       
  5247 		}
       
  5248 
       
  5249 		// _update just loads more tiles. If the tile zoom level differs too much
       
  5250 		// from the map's, let _setView reset levels and prune old tiles.
       
  5251 		if (Math.abs(zoom - this._tileZoom) > 1) { this._setView(center, zoom); return; }
       
  5252 
       
  5253 		// create a queue of coordinates to load tiles from
       
  5254 		for (var j = tileRange.min.y; j <= tileRange.max.y; j++) {
       
  5255 			for (var i = tileRange.min.x; i <= tileRange.max.x; i++) {
       
  5256 				var coords = new L.Point(i, j);
       
  5257 				coords.z = this._tileZoom;
       
  5258 
       
  5259 				if (!this._isValidTile(coords)) { continue; }
       
  5260 
       
  5261 				var tile = this._tiles[this._tileCoordsToKey(coords)];
       
  5262 				if (tile) {
       
  5263 					tile.current = true;
       
  5264 				} else {
       
  5265 					queue.push(coords);
       
  5266 				}
       
  5267 			}
       
  5268 		}
       
  5269 
       
  5270 		// sort tile queue to load tiles in order of their distance to center
       
  5271 		queue.sort(function (a, b) {
       
  5272 			return a.distanceTo(tileCenter) - b.distanceTo(tileCenter);
       
  5273 		});
       
  5274 
       
  5275 		if (queue.length !== 0) {
       
  5276 			// if it's the first batch of tiles to load
       
  5277 			if (!this._loading) {
       
  5278 				this._loading = true;
       
  5279 				// @event loading: Event
       
  5280 				// Fired when the grid layer starts loading tiles.
       
  5281 				this.fire('loading');
       
  5282 			}
       
  5283 
       
  5284 			// create DOM fragment to append tiles in one batch
       
  5285 			var fragment = document.createDocumentFragment();
       
  5286 
       
  5287 			for (i = 0; i < queue.length; i++) {
       
  5288 				this._addTile(queue[i], fragment);
       
  5289 			}
       
  5290 
       
  5291 			this._level.el.appendChild(fragment);
       
  5292 		}
       
  5293 	},
       
  5294 
       
  5295 	_isValidTile: function (coords) {
       
  5296 		var crs = this._map.options.crs;
       
  5297 
       
  5298 		if (!crs.infinite) {
       
  5299 			// don't load tile if it's out of bounds and not wrapped
       
  5300 			var bounds = this._globalTileRange;
       
  5301 			if ((!crs.wrapLng && (coords.x < bounds.min.x || coords.x > bounds.max.x)) ||
       
  5302 			    (!crs.wrapLat && (coords.y < bounds.min.y || coords.y > bounds.max.y))) { return false; }
       
  5303 		}
       
  5304 
       
  5305 		if (!this.options.bounds) { return true; }
       
  5306 
       
  5307 		// don't load tile if it doesn't intersect the bounds in options
       
  5308 		var tileBounds = this._tileCoordsToBounds(coords);
       
  5309 		return L.latLngBounds(this.options.bounds).overlaps(tileBounds);
       
  5310 	},
       
  5311 
       
  5312 	_keyToBounds: function (key) {
       
  5313 		return this._tileCoordsToBounds(this._keyToTileCoords(key));
       
  5314 	},
       
  5315 
       
  5316 	// converts tile coordinates to its geographical bounds
       
  5317 	_tileCoordsToBounds: function (coords) {
       
  5318 
       
  5319 		var map = this._map,
       
  5320 		    tileSize = this.getTileSize(),
       
  5321 
       
  5322 		    nwPoint = coords.scaleBy(tileSize),
       
  5323 		    sePoint = nwPoint.add(tileSize),
       
  5324 
       
  5325 		    nw = map.unproject(nwPoint, coords.z),
       
  5326 		    se = map.unproject(sePoint, coords.z),
       
  5327 		    bounds = new L.LatLngBounds(nw, se);
       
  5328 
       
  5329 		if (!this.options.noWrap) {
       
  5330 			map.wrapLatLngBounds(bounds);
       
  5331 		}
       
  5332 
       
  5333 		return bounds;
       
  5334 	},
       
  5335 
       
  5336 	// converts tile coordinates to key for the tile cache
       
  5337 	_tileCoordsToKey: function (coords) {
       
  5338 		return coords.x + ':' + coords.y + ':' + coords.z;
       
  5339 	},
       
  5340 
       
  5341 	// converts tile cache key to coordinates
       
  5342 	_keyToTileCoords: function (key) {
       
  5343 		var k = key.split(':'),
       
  5344 		    coords = new L.Point(+k[0], +k[1]);
       
  5345 		coords.z = +k[2];
       
  5346 		return coords;
       
  5347 	},
       
  5348 
       
  5349 	_removeTile: function (key) {
       
  5350 		var tile = this._tiles[key];
       
  5351 		if (!tile) { return; }
       
  5352 
       
  5353 		L.DomUtil.remove(tile.el);
       
  5354 
       
  5355 		delete this._tiles[key];
       
  5356 
       
  5357 		// @event tileunload: TileEvent
       
  5358 		// Fired when a tile is removed (e.g. when a tile goes off the screen).
       
  5359 		this.fire('tileunload', {
       
  5360 			tile: tile.el,
       
  5361 			coords: this._keyToTileCoords(key)
       
  5362 		});
       
  5363 	},
       
  5364 
       
  5365 	_initTile: function (tile) {
       
  5366 		L.DomUtil.addClass(tile, 'leaflet-tile');
       
  5367 
       
  5368 		var tileSize = this.getTileSize();
       
  5369 		tile.style.width = tileSize.x + 'px';
       
  5370 		tile.style.height = tileSize.y + 'px';
       
  5371 
       
  5372 		tile.onselectstart = L.Util.falseFn;
       
  5373 		tile.onmousemove = L.Util.falseFn;
       
  5374 
       
  5375 		// update opacity on tiles in IE7-8 because of filter inheritance problems
       
  5376 		if (L.Browser.ielt9 && this.options.opacity < 1) {
       
  5377 			L.DomUtil.setOpacity(tile, this.options.opacity);
       
  5378 		}
       
  5379 
       
  5380 		// without this hack, tiles disappear after zoom on Chrome for Android
       
  5381 		// https://github.com/Leaflet/Leaflet/issues/2078
       
  5382 		if (L.Browser.android && !L.Browser.android23) {
       
  5383 			tile.style.WebkitBackfaceVisibility = 'hidden';
       
  5384 		}
       
  5385 	},
       
  5386 
       
  5387 	_addTile: function (coords, container) {
       
  5388 		var tilePos = this._getTilePos(coords),
       
  5389 		    key = this._tileCoordsToKey(coords);
       
  5390 
       
  5391 		var tile = this.createTile(this._wrapCoords(coords), L.bind(this._tileReady, this, coords));
       
  5392 
       
  5393 		this._initTile(tile);
       
  5394 
       
  5395 		// if createTile is defined with a second argument ("done" callback),
       
  5396 		// we know that tile is async and will be ready later; otherwise
       
  5397 		if (this.createTile.length < 2) {
       
  5398 			// mark tile as ready, but delay one frame for opacity animation to happen
       
  5399 			L.Util.requestAnimFrame(L.bind(this._tileReady, this, coords, null, tile));
       
  5400 		}
       
  5401 
       
  5402 		L.DomUtil.setPosition(tile, tilePos);
       
  5403 
       
  5404 		// save tile in cache
       
  5405 		this._tiles[key] = {
       
  5406 			el: tile,
       
  5407 			coords: coords,
       
  5408 			current: true
       
  5409 		};
       
  5410 
       
  5411 		container.appendChild(tile);
       
  5412 		// @event tileloadstart: TileEvent
       
  5413 		// Fired when a tile is requested and starts loading.
       
  5414 		this.fire('tileloadstart', {
       
  5415 			tile: tile,
       
  5416 			coords: coords
       
  5417 		});
       
  5418 	},
       
  5419 
       
  5420 	_tileReady: function (coords, err, tile) {
       
  5421 		if (!this._map) { return; }
       
  5422 
       
  5423 		if (err) {
       
  5424 			// @event tileerror: TileErrorEvent
       
  5425 			// Fired when there is an error loading a tile.
       
  5426 			this.fire('tileerror', {
       
  5427 				error: err,
       
  5428 				tile: tile,
       
  5429 				coords: coords
       
  5430 			});
       
  5431 		}
       
  5432 
       
  5433 		var key = this._tileCoordsToKey(coords);
       
  5434 
       
  5435 		tile = this._tiles[key];
       
  5436 		if (!tile) { return; }
       
  5437 
       
  5438 		tile.loaded = +new Date();
       
  5439 		if (this._map._fadeAnimated) {
       
  5440 			L.DomUtil.setOpacity(tile.el, 0);
       
  5441 			L.Util.cancelAnimFrame(this._fadeFrame);
       
  5442 			this._fadeFrame = L.Util.requestAnimFrame(this._updateOpacity, this);
       
  5443 		} else {
       
  5444 			tile.active = true;
       
  5445 			this._pruneTiles();
       
  5446 		}
       
  5447 
       
  5448 		if (!err) {
       
  5449 			L.DomUtil.addClass(tile.el, 'leaflet-tile-loaded');
       
  5450 
       
  5451 			// @event tileload: TileEvent
       
  5452 			// Fired when a tile loads.
       
  5453 			this.fire('tileload', {
       
  5454 				tile: tile.el,
       
  5455 				coords: coords
       
  5456 			});
       
  5457 		}
       
  5458 
       
  5459 		if (this._noTilesToLoad()) {
       
  5460 			this._loading = false;
       
  5461 			// @event load: Event
       
  5462 			// Fired when the grid layer loaded all visible tiles.
       
  5463 			this.fire('load');
       
  5464 
       
  5465 			if (L.Browser.ielt9 || !this._map._fadeAnimated) {
       
  5466 				L.Util.requestAnimFrame(this._pruneTiles, this);
       
  5467 			} else {
       
  5468 				// Wait a bit more than 0.2 secs (the duration of the tile fade-in)
       
  5469 				// to trigger a pruning.
       
  5470 				setTimeout(L.bind(this._pruneTiles, this), 250);
       
  5471 			}
       
  5472 		}
       
  5473 	},
       
  5474 
       
  5475 	_getTilePos: function (coords) {
       
  5476 		return coords.scaleBy(this.getTileSize()).subtract(this._level.origin);
       
  5477 	},
       
  5478 
       
  5479 	_wrapCoords: function (coords) {
       
  5480 		var newCoords = new L.Point(
       
  5481 			this._wrapX ? L.Util.wrapNum(coords.x, this._wrapX) : coords.x,
       
  5482 			this._wrapY ? L.Util.wrapNum(coords.y, this._wrapY) : coords.y);
       
  5483 		newCoords.z = coords.z;
       
  5484 		return newCoords;
       
  5485 	},
       
  5486 
       
  5487 	_pxBoundsToTileRange: function (bounds) {
       
  5488 		var tileSize = this.getTileSize();
       
  5489 		return new L.Bounds(
       
  5490 			bounds.min.unscaleBy(tileSize).floor(),
       
  5491 			bounds.max.unscaleBy(tileSize).ceil().subtract([1, 1]));
       
  5492 	},
       
  5493 
       
  5494 	_noTilesToLoad: function () {
       
  5495 		for (var key in this._tiles) {
       
  5496 			if (!this._tiles[key].loaded) { return false; }
       
  5497 		}
       
  5498 		return true;
       
  5499 	}
       
  5500 });
       
  5501 
       
  5502 // @factory L.gridLayer(options?: GridLayer options)
       
  5503 // Creates a new instance of GridLayer with the supplied options.
       
  5504 L.gridLayer = function (options) {
       
  5505 	return new L.GridLayer(options);
       
  5506 };
       
  5507 
       
  5508 
       
  5509 
       
  5510 /*
       
  5511  * @class TileLayer
       
  5512  * @inherits GridLayer
       
  5513  * @aka L.TileLayer
       
  5514  * Used to load and display tile layers on the map. Extends `GridLayer`.
       
  5515  *
       
  5516  * @example
       
  5517  *
       
  5518  * ```js
       
  5519  * L.tileLayer('http://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png?{foo}', {foo: 'bar'}).addTo(map);
       
  5520  * ```
       
  5521  *
       
  5522  * @section URL template
       
  5523  * @example
       
  5524  *
       
  5525  * A string of the following form:
       
  5526  *
       
  5527  * ```
       
  5528  * 'http://{s}.somedomain.com/blabla/{z}/{x}/{y}{r}.png'
       
  5529  * ```
       
  5530  *
       
  5531  * `{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 @2x to the URL to load retina tiles.
       
  5532  *
       
  5533  * You can use custom keys in the template, which will be [evaluated](#util-template) from TileLayer options, like this:
       
  5534  *
       
  5535  * ```
       
  5536  * L.tileLayer('http://{s}.somedomain.com/{foo}/{z}/{x}/{y}.png', {foo: 'bar'});
       
  5537  * ```
       
  5538  */
       
  5539 
       
  5540 
       
  5541 L.TileLayer = L.GridLayer.extend({
       
  5542 
       
  5543 	// @section
       
  5544 	// @aka TileLayer options
       
  5545 	options: {
       
  5546 		// @option minZoom: Number = 0
       
  5547 		// Minimum zoom number.
       
  5548 		minZoom: 0,
       
  5549 
       
  5550 		// @option maxZoom: Number = 18
       
  5551 		// Maximum zoom number.
       
  5552 		maxZoom: 18,
       
  5553 
       
  5554 		// @option maxNativeZoom: Number = null
       
  5555 		// Maximum zoom number the tile source has available. If it is specified,
       
  5556 		// the tiles on all zoom levels higher than `maxNativeZoom` will be loaded
       
  5557 		// from `maxNativeZoom` level and auto-scaled.
       
  5558 		maxNativeZoom: null,
       
  5559 
       
  5560 		// @option minNativeZoom: Number = null
       
  5561 		// Minimum zoom number the tile source has available. If it is specified,
       
  5562 		// the tiles on all zoom levels lower than `minNativeZoom` will be loaded
       
  5563 		// from `minNativeZoom` level and auto-scaled.
       
  5564 		minNativeZoom: null,
       
  5565 
       
  5566 		// @option subdomains: String|String[] = 'abc'
       
  5567 		// 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.
       
  5568 		subdomains: 'abc',
       
  5569 
       
  5570 		// @option errorTileUrl: String = ''
       
  5571 		// URL to the tile image to show in place of the tile that failed to load.
       
  5572 		errorTileUrl: '',
       
  5573 
       
  5574 		// @option zoomOffset: Number = 0
       
  5575 		// The zoom number used in tile URLs will be offset with this value.
       
  5576 		zoomOffset: 0,
       
  5577 
       
  5578 		// @option tms: Boolean = false
       
  5579 		// If `true`, inverses Y axis numbering for tiles (turn this on for [TMS](https://en.wikipedia.org/wiki/Tile_Map_Service) services).
       
  5580 		tms: false,
       
  5581 
       
  5582 		// @option zoomReverse: Boolean = false
       
  5583 		// If set to true, the zoom number used in tile URLs will be reversed (`maxZoom - zoom` instead of `zoom`)
       
  5584 		zoomReverse: false,
       
  5585 
       
  5586 		// @option detectRetina: Boolean = false
       
  5587 		// 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.
       
  5588 		detectRetina: false,
       
  5589 
       
  5590 		// @option crossOrigin: Boolean = false
       
  5591 		// If true, all tiles will have their crossOrigin attribute set to ''. This is needed if you want to access tile pixel data.
       
  5592 		crossOrigin: false
       
  5593 	},
       
  5594 
       
  5595 	initialize: function (url, options) {
       
  5596 
       
  5597 		this._url = url;
       
  5598 
       
  5599 		options = L.setOptions(this, options);
       
  5600 
       
  5601 		// detecting retina displays, adjusting tileSize and zoom levels
       
  5602 		if (options.detectRetina && L.Browser.retina && options.maxZoom > 0) {
       
  5603 
       
  5604 			options.tileSize = Math.floor(options.tileSize / 2);
       
  5605 
       
  5606 			if (!options.zoomReverse) {
       
  5607 				options.zoomOffset++;
       
  5608 				options.maxZoom--;
       
  5609 			} else {
       
  5610 				options.zoomOffset--;
       
  5611 				options.minZoom++;
       
  5612 			}
       
  5613 
       
  5614 			options.minZoom = Math.max(0, options.minZoom);
       
  5615 		}
       
  5616 
       
  5617 		if (typeof options.subdomains === 'string') {
       
  5618 			options.subdomains = options.subdomains.split('');
       
  5619 		}
       
  5620 
       
  5621 		// for https://github.com/Leaflet/Leaflet/issues/137
       
  5622 		if (!L.Browser.android) {
       
  5623 			this.on('tileunload', this._onTileRemove);
       
  5624 		}
       
  5625 	},
       
  5626 
       
  5627 	// @method setUrl(url: String, noRedraw?: Boolean): this
       
  5628 	// Updates the layer's URL template and redraws it (unless `noRedraw` is set to `true`).
       
  5629 	setUrl: function (url, noRedraw) {
       
  5630 		this._url = url;
       
  5631 
       
  5632 		if (!noRedraw) {
       
  5633 			this.redraw();
       
  5634 		}
       
  5635 		return this;
       
  5636 	},
       
  5637 
       
  5638 	// @method createTile(coords: Object, done?: Function): HTMLElement
       
  5639 	// Called only internally, overrides GridLayer's [`createTile()`](#gridlayer-createtile)
       
  5640 	// to return an `<img>` HTML element with the appropiate image URL given `coords`. The `done`
       
  5641 	// callback is called when the tile has been loaded.
       
  5642 	createTile: function (coords, done) {
       
  5643 		var tile = document.createElement('img');
       
  5644 
       
  5645 		L.DomEvent.on(tile, 'load', L.bind(this._tileOnLoad, this, done, tile));
       
  5646 		L.DomEvent.on(tile, 'error', L.bind(this._tileOnError, this, done, tile));
       
  5647 
       
  5648 		if (this.options.crossOrigin) {
       
  5649 			tile.crossOrigin = '';
       
  5650 		}
       
  5651 
       
  5652 		/*
       
  5653 		 Alt tag is set to empty string to keep screen readers from reading URL and for compliance reasons
       
  5654 		 http://www.w3.org/TR/WCAG20-TECHS/H67
       
  5655 		*/
       
  5656 		tile.alt = '';
       
  5657 
       
  5658 		/*
       
  5659 		 Set role="presentation" to force screen readers to ignore this
       
  5660 		 https://www.w3.org/TR/wai-aria/roles#textalternativecomputation
       
  5661 		*/
       
  5662 		tile.setAttribute('role', 'presentation');
       
  5663 
       
  5664 		tile.src = this.getTileUrl(coords);
       
  5665 
       
  5666 		return tile;
       
  5667 	},
       
  5668 
       
  5669 	// @section Extension methods
       
  5670 	// @uninheritable
       
  5671 	// Layers extending `TileLayer` might reimplement the following method.
       
  5672 	// @method getTileUrl(coords: Object): String
       
  5673 	// Called only internally, returns the URL for a tile given its coordinates.
       
  5674 	// Classes extending `TileLayer` can override this function to provide custom tile URL naming schemes.
       
  5675 	getTileUrl: function (coords) {
       
  5676 		var data = {
       
  5677 			r: L.Browser.retina ? '@2x' : '',
       
  5678 			s: this._getSubdomain(coords),
       
  5679 			x: coords.x,
       
  5680 			y: coords.y,
       
  5681 			z: this._getZoomForUrl()
       
  5682 		};
       
  5683 		if (this._map && !this._map.options.crs.infinite) {
       
  5684 			var invertedY = this._globalTileRange.max.y - coords.y;
       
  5685 			if (this.options.tms) {
       
  5686 				data['y'] = invertedY;
       
  5687 			}
       
  5688 			data['-y'] = invertedY;
       
  5689 		}
       
  5690 
       
  5691 		return L.Util.template(this._url, L.extend(data, this.options));
       
  5692 	},
       
  5693 
       
  5694 	_tileOnLoad: function (done, tile) {
       
  5695 		// For https://github.com/Leaflet/Leaflet/issues/3332
       
  5696 		if (L.Browser.ielt9) {
       
  5697 			setTimeout(L.bind(done, this, null, tile), 0);
       
  5698 		} else {
       
  5699 			done(null, tile);
       
  5700 		}
       
  5701 	},
       
  5702 
       
  5703 	_tileOnError: function (done, tile, e) {
       
  5704 		var errorUrl = this.options.errorTileUrl;
       
  5705 		if (errorUrl && tile.src !== errorUrl) {
       
  5706 			tile.src = errorUrl;
       
  5707 		}
       
  5708 		done(e, tile);
       
  5709 	},
       
  5710 
       
  5711 	getTileSize: function () {
       
  5712 		var map = this._map,
       
  5713 		tileSize = L.GridLayer.prototype.getTileSize.call(this),
       
  5714 		zoom = this._tileZoom + this.options.zoomOffset,
       
  5715 		minNativeZoom = this.options.minNativeZoom,
       
  5716 		maxNativeZoom = this.options.maxNativeZoom;
       
  5717 
       
  5718 		// decrease tile size when scaling below minNativeZoom
       
  5719 		if (minNativeZoom !== null && zoom < minNativeZoom) {
       
  5720 			return tileSize.divideBy(map.getZoomScale(minNativeZoom, zoom)).round();
       
  5721 		}
       
  5722 
       
  5723 		// increase tile size when scaling above maxNativeZoom
       
  5724 		if (maxNativeZoom !== null && zoom > maxNativeZoom) {
       
  5725 			return tileSize.divideBy(map.getZoomScale(maxNativeZoom, zoom)).round();
       
  5726 		}
       
  5727 
       
  5728 		return tileSize;
       
  5729 	},
       
  5730 
       
  5731 	_onTileRemove: function (e) {
       
  5732 		e.tile.onload = null;
       
  5733 	},
       
  5734 
       
  5735 	_getZoomForUrl: function () {
       
  5736 		var zoom = this._tileZoom,
       
  5737 		maxZoom = this.options.maxZoom,
       
  5738 		zoomReverse = this.options.zoomReverse,
       
  5739 		zoomOffset = this.options.zoomOffset,
       
  5740 		minNativeZoom = this.options.minNativeZoom,
       
  5741 		maxNativeZoom = this.options.maxNativeZoom;
       
  5742 
       
  5743 		if (zoomReverse) {
       
  5744 			zoom = maxZoom - zoom;
       
  5745 		}
       
  5746 
       
  5747 		zoom += zoomOffset;
       
  5748 
       
  5749 		if (minNativeZoom !== null && zoom < minNativeZoom) {
       
  5750 			return minNativeZoom;
       
  5751 		}
       
  5752 
       
  5753 		if (maxNativeZoom !== null && zoom > maxNativeZoom) {
       
  5754 			return maxNativeZoom;
       
  5755 		}
       
  5756 
       
  5757 		return zoom;
       
  5758 	},
       
  5759 
       
  5760 	_getSubdomain: function (tilePoint) {
       
  5761 		var index = Math.abs(tilePoint.x + tilePoint.y) % this.options.subdomains.length;
       
  5762 		return this.options.subdomains[index];
       
  5763 	},
       
  5764 
       
  5765 	// stops loading all tiles in the background layer
       
  5766 	_abortLoading: function () {
       
  5767 		var i, tile;
       
  5768 		for (i in this._tiles) {
       
  5769 			if (this._tiles[i].coords.z !== this._tileZoom) {
       
  5770 				tile = this._tiles[i].el;
       
  5771 
       
  5772 				tile.onload = L.Util.falseFn;
       
  5773 				tile.onerror = L.Util.falseFn;
       
  5774 
       
  5775 				if (!tile.complete) {
       
  5776 					tile.src = L.Util.emptyImageUrl;
       
  5777 					L.DomUtil.remove(tile);
       
  5778 				}
       
  5779 			}
       
  5780 		}
       
  5781 	}
       
  5782 });
       
  5783 
       
  5784 
       
  5785 // @factory L.tilelayer(urlTemplate: String, options?: TileLayer options)
       
  5786 // Instantiates a tile layer object given a `URL template` and optionally an options object.
       
  5787 
       
  5788 L.tileLayer = function (url, options) {
       
  5789 	return new L.TileLayer(url, options);
       
  5790 };
       
  5791 
       
  5792 
       
  5793 
       
  5794 /*
       
  5795  * @class TileLayer.WMS
       
  5796  * @inherits TileLayer
       
  5797  * @aka L.TileLayer.WMS
       
  5798  * Used to display [WMS](https://en.wikipedia.org/wiki/Web_Map_Service) services as tile layers on the map. Extends `TileLayer`.
       
  5799  *
       
  5800  * @example
       
  5801  *
       
  5802  * ```js
       
  5803  * var nexrad = L.tileLayer.wms("http://mesonet.agron.iastate.edu/cgi-bin/wms/nexrad/n0r.cgi", {
       
  5804  * 	layers: 'nexrad-n0r-900913',
       
  5805  * 	format: 'image/png',
       
  5806  * 	transparent: true,
       
  5807  * 	attribution: "Weather data © 2012 IEM Nexrad"
       
  5808  * });
       
  5809  * ```
       
  5810  */
       
  5811 
       
  5812 L.TileLayer.WMS = L.TileLayer.extend({
       
  5813 
       
  5814 	// @section
       
  5815 	// @aka TileLayer.WMS options
       
  5816 	// If any custom options not documented here are used, they will be sent to the
       
  5817 	// WMS server as extra parameters in each request URL. This can be useful for
       
  5818 	// [non-standard vendor WMS parameters](http://docs.geoserver.org/stable/en/user/services/wms/vendor.html).
       
  5819 	defaultWmsParams: {
       
  5820 		service: 'WMS',
       
  5821 		request: 'GetMap',
       
  5822 
       
  5823 		// @option layers: String = ''
       
  5824 		// **(required)** Comma-separated list of WMS layers to show.
       
  5825 		layers: '',
       
  5826 
       
  5827 		// @option styles: String = ''
       
  5828 		// Comma-separated list of WMS styles.
       
  5829 		styles: '',
       
  5830 
       
  5831 		// @option format: String = 'image/jpeg'
       
  5832 		// WMS image format (use `'image/png'` for layers with transparency).
       
  5833 		format: 'image/jpeg',
       
  5834 
       
  5835 		// @option transparent: Boolean = false
       
  5836 		// If `true`, the WMS service will return images with transparency.
       
  5837 		transparent: false,
       
  5838 
       
  5839 		// @option version: String = '1.1.1'
       
  5840 		// Version of the WMS service to use
       
  5841 		version: '1.1.1'
       
  5842 	},
       
  5843 
       
  5844 	options: {
       
  5845 		// @option crs: CRS = null
       
  5846 		// Coordinate Reference System to use for the WMS requests, defaults to
       
  5847 		// map CRS. Don't change this if you're not sure what it means.
       
  5848 		crs: null,
       
  5849 
       
  5850 		// @option uppercase: Boolean = false
       
  5851 		// If `true`, WMS request parameter keys will be uppercase.
       
  5852 		uppercase: false
       
  5853 	},
       
  5854 
       
  5855 	initialize: function (url, options) {
       
  5856 
       
  5857 		this._url = url;
       
  5858 
       
  5859 		var wmsParams = L.extend({}, this.defaultWmsParams);
       
  5860 
       
  5861 		// all keys that are not TileLayer options go to WMS params
       
  5862 		for (var i in options) {
       
  5863 			if (!(i in this.options)) {
       
  5864 				wmsParams[i] = options[i];
       
  5865 			}
       
  5866 		}
       
  5867 
       
  5868 		options = L.setOptions(this, options);
       
  5869 
       
  5870 		wmsParams.width = wmsParams.height = options.tileSize * (options.detectRetina && L.Browser.retina ? 2 : 1);
       
  5871 
       
  5872 		this.wmsParams = wmsParams;
       
  5873 	},
       
  5874 
       
  5875 	onAdd: function (map) {
       
  5876 
       
  5877 		this._crs = this.options.crs || map.options.crs;
       
  5878 		this._wmsVersion = parseFloat(this.wmsParams.version);
       
  5879 
       
  5880 		var projectionKey = this._wmsVersion >= 1.3 ? 'crs' : 'srs';
       
  5881 		this.wmsParams[projectionKey] = this._crs.code;
       
  5882 
       
  5883 		L.TileLayer.prototype.onAdd.call(this, map);
       
  5884 	},
       
  5885 
       
  5886 	getTileUrl: function (coords) {
       
  5887 
       
  5888 		var tileBounds = this._tileCoordsToBounds(coords),
       
  5889 		    nw = this._crs.project(tileBounds.getNorthWest()),
       
  5890 		    se = this._crs.project(tileBounds.getSouthEast()),
       
  5891 
       
  5892 		    bbox = (this._wmsVersion >= 1.3 && this._crs === L.CRS.EPSG4326 ?
       
  5893 			    [se.y, nw.x, nw.y, se.x] :
       
  5894 			    [nw.x, se.y, se.x, nw.y]).join(','),
       
  5895 
       
  5896 		    url = L.TileLayer.prototype.getTileUrl.call(this, coords);
       
  5897 
       
  5898 		return url +
       
  5899 			L.Util.getParamString(this.wmsParams, url, this.options.uppercase) +
       
  5900 			(this.options.uppercase ? '&BBOX=' : '&bbox=') + bbox;
       
  5901 	},
       
  5902 
       
  5903 	// @method setParams(params: Object, noRedraw?: Boolean): this
       
  5904 	// Merges an object with the new parameters and re-requests tiles on the current screen (unless `noRedraw` was set to true).
       
  5905 	setParams: function (params, noRedraw) {
       
  5906 
       
  5907 		L.extend(this.wmsParams, params);
       
  5908 
       
  5909 		if (!noRedraw) {
       
  5910 			this.redraw();
       
  5911 		}
       
  5912 
       
  5913 		return this;
       
  5914 	}
       
  5915 });
       
  5916 
       
  5917 
       
  5918 // @factory L.tileLayer.wms(baseUrl: String, options: TileLayer.WMS options)
       
  5919 // Instantiates a WMS tile layer object given a base URL of the WMS service and a WMS parameters/options object.
       
  5920 L.tileLayer.wms = function (url, options) {
       
  5921 	return new L.TileLayer.WMS(url, options);
       
  5922 };
       
  5923 
       
  5924 
       
  5925 
       
  5926 /*
       
  5927  * @class ImageOverlay
       
  5928  * @aka L.ImageOverlay
       
  5929  * @inherits Interactive layer
       
  5930  *
       
  5931  * Used to load and display a single image over specific bounds of the map. Extends `Layer`.
       
  5932  *
       
  5933  * @example
       
  5934  *
       
  5935  * ```js
       
  5936  * var imageUrl = 'http://www.lib.utexas.edu/maps/historical/newark_nj_1922.jpg',
       
  5937  * 	imageBounds = [[40.712216, -74.22655], [40.773941, -74.12544]];
       
  5938  * L.imageOverlay(imageUrl, imageBounds).addTo(map);
       
  5939  * ```
       
  5940  */
       
  5941 
       
  5942 L.ImageOverlay = L.Layer.extend({
       
  5943 
       
  5944 	// @section
       
  5945 	// @aka ImageOverlay options
       
  5946 	options: {
       
  5947 		// @option opacity: Number = 1.0
       
  5948 		// The opacity of the image overlay.
       
  5949 		opacity: 1,
       
  5950 
       
  5951 		// @option alt: String = ''
       
  5952 		// Text for the `alt` attribute of the image (useful for accessibility).
       
  5953 		alt: '',
       
  5954 
       
  5955 		// @option interactive: Boolean = false
       
  5956 		// If `true`, the image overlay will emit [mouse events](#interactive-layer) when clicked or hovered.
       
  5957 		interactive: false,
       
  5958 
       
  5959 		// @option crossOrigin: Boolean = false
       
  5960 		// If true, the image will have its crossOrigin attribute set to ''. This is needed if you want to access image pixel data.
       
  5961 		crossOrigin: false
       
  5962 	},
       
  5963 
       
  5964 	initialize: function (url, bounds, options) { // (String, LatLngBounds, Object)
       
  5965 		this._url = url;
       
  5966 		this._bounds = L.latLngBounds(bounds);
       
  5967 
       
  5968 		L.setOptions(this, options);
       
  5969 	},
       
  5970 
       
  5971 	onAdd: function () {
       
  5972 		if (!this._image) {
       
  5973 			this._initImage();
       
  5974 
       
  5975 			if (this.options.opacity < 1) {
       
  5976 				this._updateOpacity();
       
  5977 			}
       
  5978 		}
       
  5979 
       
  5980 		if (this.options.interactive) {
       
  5981 			L.DomUtil.addClass(this._image, 'leaflet-interactive');
       
  5982 			this.addInteractiveTarget(this._image);
       
  5983 		}
       
  5984 
       
  5985 		this.getPane().appendChild(this._image);
       
  5986 		this._reset();
       
  5987 	},
       
  5988 
       
  5989 	onRemove: function () {
       
  5990 		L.DomUtil.remove(this._image);
       
  5991 		if (this.options.interactive) {
       
  5992 			this.removeInteractiveTarget(this._image);
       
  5993 		}
       
  5994 	},
       
  5995 
       
  5996 	// @method setOpacity(opacity: Number): this
       
  5997 	// Sets the opacity of the overlay.
       
  5998 	setOpacity: function (opacity) {
       
  5999 		this.options.opacity = opacity;
       
  6000 
       
  6001 		if (this._image) {
       
  6002 			this._updateOpacity();
       
  6003 		}
       
  6004 		return this;
       
  6005 	},
       
  6006 
       
  6007 	setStyle: function (styleOpts) {
       
  6008 		if (styleOpts.opacity) {
       
  6009 			this.setOpacity(styleOpts.opacity);
       
  6010 		}
       
  6011 		return this;
       
  6012 	},
       
  6013 
       
  6014 	// @method bringToFront(): this
       
  6015 	// Brings the layer to the top of all overlays.
       
  6016 	bringToFront: function () {
       
  6017 		if (this._map) {
       
  6018 			L.DomUtil.toFront(this._image);
       
  6019 		}
       
  6020 		return this;
       
  6021 	},
       
  6022 
       
  6023 	// @method bringToBack(): this
       
  6024 	// Brings the layer to the bottom of all overlays.
       
  6025 	bringToBack: function () {
       
  6026 		if (this._map) {
       
  6027 			L.DomUtil.toBack(this._image);
       
  6028 		}
       
  6029 		return this;
       
  6030 	},
       
  6031 
       
  6032 	// @method setUrl(url: String): this
       
  6033 	// Changes the URL of the image.
       
  6034 	setUrl: function (url) {
       
  6035 		this._url = url;
       
  6036 
       
  6037 		if (this._image) {
       
  6038 			this._image.src = url;
       
  6039 		}
       
  6040 		return this;
       
  6041 	},
       
  6042 
       
  6043 	// @method setBounds(bounds: LatLngBounds): this
       
  6044 	// Update the bounds that this ImageOverlay covers
       
  6045 	setBounds: function (bounds) {
       
  6046 		this._bounds = bounds;
       
  6047 
       
  6048 		if (this._map) {
       
  6049 			this._reset();
       
  6050 		}
       
  6051 		return this;
       
  6052 	},
       
  6053 
       
  6054 	getEvents: function () {
       
  6055 		var events = {
       
  6056 			zoom: this._reset,
       
  6057 			viewreset: this._reset
       
  6058 		};
       
  6059 
       
  6060 		if (this._zoomAnimated) {
       
  6061 			events.zoomanim = this._animateZoom;
       
  6062 		}
       
  6063 
       
  6064 		return events;
       
  6065 	},
       
  6066 
       
  6067 	// @method getBounds(): LatLngBounds
       
  6068 	// Get the bounds that this ImageOverlay covers
       
  6069 	getBounds: function () {
       
  6070 		return this._bounds;
       
  6071 	},
       
  6072 
       
  6073 	// @method getElement(): HTMLElement
       
  6074 	// Get the img element that represents the ImageOverlay on the map
       
  6075 	getElement: function () {
       
  6076 		return this._image;
       
  6077 	},
       
  6078 
       
  6079 	_initImage: function () {
       
  6080 		var img = this._image = L.DomUtil.create('img',
       
  6081 				'leaflet-image-layer ' + (this._zoomAnimated ? 'leaflet-zoom-animated' : ''));
       
  6082 
       
  6083 		img.onselectstart = L.Util.falseFn;
       
  6084 		img.onmousemove = L.Util.falseFn;
       
  6085 
       
  6086 		img.onload = L.bind(this.fire, this, 'load');
       
  6087 
       
  6088 		if (this.options.crossOrigin) {
       
  6089 			img.crossOrigin = '';
       
  6090 		}
       
  6091 
       
  6092 		img.src = this._url;
       
  6093 		img.alt = this.options.alt;
       
  6094 	},
       
  6095 
       
  6096 	_animateZoom: function (e) {
       
  6097 		var scale = this._map.getZoomScale(e.zoom),
       
  6098 		    offset = this._map._latLngBoundsToNewLayerBounds(this._bounds, e.zoom, e.center).min;
       
  6099 
       
  6100 		L.DomUtil.setTransform(this._image, offset, scale);
       
  6101 	},
       
  6102 
       
  6103 	_reset: function () {
       
  6104 		var image = this._image,
       
  6105 		    bounds = new L.Bounds(
       
  6106 		        this._map.latLngToLayerPoint(this._bounds.getNorthWest()),
       
  6107 		        this._map.latLngToLayerPoint(this._bounds.getSouthEast())),
       
  6108 		    size = bounds.getSize();
       
  6109 
       
  6110 		L.DomUtil.setPosition(image, bounds.min);
       
  6111 
       
  6112 		image.style.width  = size.x + 'px';
       
  6113 		image.style.height = size.y + 'px';
       
  6114 	},
       
  6115 
       
  6116 	_updateOpacity: function () {
       
  6117 		L.DomUtil.setOpacity(this._image, this.options.opacity);
       
  6118 	}
       
  6119 });
       
  6120 
       
  6121 // @factory L.imageOverlay(imageUrl: String, bounds: LatLngBounds, options?: ImageOverlay options)
       
  6122 // Instantiates an image overlay object given the URL of the image and the
       
  6123 // geographical bounds it is tied to.
       
  6124 L.imageOverlay = function (url, bounds, options) {
       
  6125 	return new L.ImageOverlay(url, bounds, options);
       
  6126 };
       
  6127 
       
  6128 
       
  6129 
       
  6130 /*
       
  6131  * @class Icon
       
  6132  * @aka L.Icon
       
  6133  * @inherits Layer
       
  6134  *
       
  6135  * Represents an icon to provide when creating a marker.
       
  6136  *
       
  6137  * @example
       
  6138  *
       
  6139  * ```js
       
  6140  * var myIcon = L.icon({
       
  6141  *     iconUrl: 'my-icon.png',
       
  6142  *     iconRetinaUrl: 'my-icon@2x.png',
       
  6143  *     iconSize: [38, 95],
       
  6144  *     iconAnchor: [22, 94],
       
  6145  *     popupAnchor: [-3, -76],
       
  6146  *     shadowUrl: 'my-icon-shadow.png',
       
  6147  *     shadowRetinaUrl: 'my-icon-shadow@2x.png',
       
  6148  *     shadowSize: [68, 95],
       
  6149  *     shadowAnchor: [22, 94]
       
  6150  * });
       
  6151  *
       
  6152  * L.marker([50.505, 30.57], {icon: myIcon}).addTo(map);
       
  6153  * ```
       
  6154  *
       
  6155  * `L.Icon.Default` extends `L.Icon` and is the blue icon Leaflet uses for markers by default.
       
  6156  *
       
  6157  */
       
  6158 
       
  6159 L.Icon = L.Class.extend({
       
  6160 
       
  6161 	/* @section
       
  6162 	 * @aka Icon options
       
  6163 	 *
       
  6164 	 * @option iconUrl: String = null
       
  6165 	 * **(required)** The URL to the icon image (absolute or relative to your script path).
       
  6166 	 *
       
  6167 	 * @option iconRetinaUrl: String = null
       
  6168 	 * The URL to a retina sized version of the icon image (absolute or relative to your
       
  6169 	 * script path). Used for Retina screen devices.
       
  6170 	 *
       
  6171 	 * @option iconSize: Point = null
       
  6172 	 * Size of the icon image in pixels.
       
  6173 	 *
       
  6174 	 * @option iconAnchor: Point = null
       
  6175 	 * The coordinates of the "tip" of the icon (relative to its top left corner). The icon
       
  6176 	 * will be aligned so that this point is at the marker's geographical location. Centered
       
  6177 	 * by default if size is specified, also can be set in CSS with negative margins.
       
  6178 	 *
       
  6179 	 * @option popupAnchor: Point = null
       
  6180 	 * The coordinates of the point from which popups will "open", relative to the icon anchor.
       
  6181 	 *
       
  6182 	 * @option shadowUrl: String = null
       
  6183 	 * The URL to the icon shadow image. If not specified, no shadow image will be created.
       
  6184 	 *
       
  6185 	 * @option shadowRetinaUrl: String = null
       
  6186 	 *
       
  6187 	 * @option shadowSize: Point = null
       
  6188 	 * Size of the shadow image in pixels.
       
  6189 	 *
       
  6190 	 * @option shadowAnchor: Point = null
       
  6191 	 * The coordinates of the "tip" of the shadow (relative to its top left corner) (the same
       
  6192 	 * as iconAnchor if not specified).
       
  6193 	 *
       
  6194 	 * @option className: String = ''
       
  6195 	 * A custom class name to assign to both icon and shadow images. Empty by default.
       
  6196 	 */
       
  6197 
       
  6198 	initialize: function (options) {
       
  6199 		L.setOptions(this, options);
       
  6200 	},
       
  6201 
       
  6202 	// @method createIcon(oldIcon?: HTMLElement): HTMLElement
       
  6203 	// Called internally when the icon has to be shown, returns a `<img>` HTML element
       
  6204 	// styled according to the options.
       
  6205 	createIcon: function (oldIcon) {
       
  6206 		return this._createIcon('icon', oldIcon);
       
  6207 	},
       
  6208 
       
  6209 	// @method createShadow(oldIcon?: HTMLElement): HTMLElement
       
  6210 	// As `createIcon`, but for the shadow beneath it.
       
  6211 	createShadow: function (oldIcon) {
       
  6212 		return this._createIcon('shadow', oldIcon);
       
  6213 	},
       
  6214 
       
  6215 	_createIcon: function (name, oldIcon) {
       
  6216 		var src = this._getIconUrl(name);
       
  6217 
       
  6218 		if (!src) {
       
  6219 			if (name === 'icon') {
       
  6220 				throw new Error('iconUrl not set in Icon options (see the docs).');
       
  6221 			}
       
  6222 			return null;
       
  6223 		}
       
  6224 
       
  6225 		var img = this._createImg(src, oldIcon && oldIcon.tagName === 'IMG' ? oldIcon : null);
       
  6226 		this._setIconStyles(img, name);
       
  6227 
       
  6228 		return img;
       
  6229 	},
       
  6230 
       
  6231 	_setIconStyles: function (img, name) {
       
  6232 		var options = this.options;
       
  6233 		var sizeOption = options[name + 'Size'];
       
  6234 
       
  6235 		if (typeof sizeOption === 'number') {
       
  6236 			sizeOption = [sizeOption, sizeOption];
       
  6237 		}
       
  6238 
       
  6239 		var size = L.point(sizeOption),
       
  6240 		    anchor = L.point(name === 'shadow' && options.shadowAnchor || options.iconAnchor ||
       
  6241 		            size && size.divideBy(2, true));
       
  6242 
       
  6243 		img.className = 'leaflet-marker-' + name + ' ' + (options.className || '');
       
  6244 
       
  6245 		if (anchor) {
       
  6246 			img.style.marginLeft = (-anchor.x) + 'px';
       
  6247 			img.style.marginTop  = (-anchor.y) + 'px';
       
  6248 		}
       
  6249 
       
  6250 		if (size) {
       
  6251 			img.style.width  = size.x + 'px';
       
  6252 			img.style.height = size.y + 'px';
       
  6253 		}
       
  6254 	},
       
  6255 
       
  6256 	_createImg: function (src, el) {
       
  6257 		el = el || document.createElement('img');
       
  6258 		el.src = src;
       
  6259 		return el;
       
  6260 	},
       
  6261 
       
  6262 	_getIconUrl: function (name) {
       
  6263 		return L.Browser.retina && this.options[name + 'RetinaUrl'] || this.options[name + 'Url'];
       
  6264 	}
       
  6265 });
       
  6266 
       
  6267 
       
  6268 // @factory L.icon(options: Icon options)
       
  6269 // Creates an icon instance with the given options.
       
  6270 L.icon = function (options) {
       
  6271 	return new L.Icon(options);
       
  6272 };
       
  6273 
       
  6274 
       
  6275 
       
  6276 /*
       
  6277  * @miniclass Icon.Default (Icon)
       
  6278  * @aka L.Icon.Default
       
  6279  * @section
       
  6280  *
       
  6281  * A trivial subclass of `Icon`, represents the icon to use in `Marker`s when
       
  6282  * no icon is specified. Points to the blue marker image distributed with Leaflet
       
  6283  * releases.
       
  6284  *
       
  6285  * In order to customize the default icon, just change the properties of `L.Icon.Default.prototype.options`
       
  6286  * (which is a set of `Icon options`).
       
  6287  *
       
  6288  * If you want to _completely_ replace the default icon, override the
       
  6289  * `L.Marker.prototype.options.icon` with your own icon instead.
       
  6290  */
       
  6291 
       
  6292 L.Icon.Default = L.Icon.extend({
       
  6293 
       
  6294 	options: {
       
  6295 		iconUrl:       'marker-icon.png',
       
  6296 		iconRetinaUrl: 'marker-icon-2x.png',
       
  6297 		shadowUrl:     'marker-shadow.png',
       
  6298 		iconSize:    [25, 41],
       
  6299 		iconAnchor:  [12, 41],
       
  6300 		popupAnchor: [1, -34],
       
  6301 		tooltipAnchor: [16, -28],
       
  6302 		shadowSize:  [41, 41]
       
  6303 	},
       
  6304 
       
  6305 	_getIconUrl: function (name) {
       
  6306 		if (!L.Icon.Default.imagePath) {	// Deprecated, backwards-compatibility only
       
  6307 			L.Icon.Default.imagePath = this._detectIconPath();
       
  6308 		}
       
  6309 
       
  6310 		// @option imagePath: String
       
  6311 		// `L.Icon.Default` will try to auto-detect the absolute location of the
       
  6312 		// blue icon images. If you are placing these images in a non-standard
       
  6313 		// way, set this option to point to the right absolute path.
       
  6314 		return (this.options.imagePath || L.Icon.Default.imagePath) + L.Icon.prototype._getIconUrl.call(this, name);
       
  6315 	},
       
  6316 
       
  6317 	_detectIconPath: function () {
       
  6318 		var el = L.DomUtil.create('div',  'leaflet-default-icon-path', document.body);
       
  6319 		var path = L.DomUtil.getStyle(el, 'background-image') ||
       
  6320 		           L.DomUtil.getStyle(el, 'backgroundImage');	// IE8
       
  6321 
       
  6322 		document.body.removeChild(el);
       
  6323 
       
  6324 		return path.indexOf('url') === 0 ?
       
  6325 			path.replace(/^url\([\"\']?/, '').replace(/marker-icon\.png[\"\']?\)$/, '') : '';
       
  6326 	}
       
  6327 });
       
  6328 
       
  6329 
       
  6330 
       
  6331 /*
       
  6332  * @class Marker
       
  6333  * @inherits Interactive layer
       
  6334  * @aka L.Marker
       
  6335  * L.Marker is used to display clickable/draggable icons on the map. Extends `Layer`.
       
  6336  *
       
  6337  * @example
       
  6338  *
       
  6339  * ```js
       
  6340  * L.marker([50.5, 30.5]).addTo(map);
       
  6341  * ```
       
  6342  */
       
  6343 
       
  6344 L.Marker = L.Layer.extend({
       
  6345 
       
  6346 	// @section
       
  6347 	// @aka Marker options
       
  6348 	options: {
       
  6349 		// @option icon: Icon = *
       
  6350 		// Icon class to use for rendering the marker. See [Icon documentation](#L.Icon) for details on how to customize the marker icon. If not specified, a new `L.Icon.Default` is used.
       
  6351 		icon: new L.Icon.Default(),
       
  6352 
       
  6353 		// Option inherited from "Interactive layer" abstract class
       
  6354 		interactive: true,
       
  6355 
       
  6356 		// @option draggable: Boolean = false
       
  6357 		// Whether the marker is draggable with mouse/touch or not.
       
  6358 		draggable: false,
       
  6359 
       
  6360 		// @option keyboard: Boolean = true
       
  6361 		// Whether the marker can be tabbed to with a keyboard and clicked by pressing enter.
       
  6362 		keyboard: true,
       
  6363 
       
  6364 		// @option title: String = ''
       
  6365 		// Text for the browser tooltip that appear on marker hover (no tooltip by default).
       
  6366 		title: '',
       
  6367 
       
  6368 		// @option alt: String = ''
       
  6369 		// Text for the `alt` attribute of the icon image (useful for accessibility).
       
  6370 		alt: '',
       
  6371 
       
  6372 		// @option zIndexOffset: Number = 0
       
  6373 		// By default, marker images zIndex is set automatically based on its latitude. Use this option if you want to put the marker on top of all others (or below), specifying a high value like `1000` (or high negative value, respectively).
       
  6374 		zIndexOffset: 0,
       
  6375 
       
  6376 		// @option opacity: Number = 1.0
       
  6377 		// The opacity of the marker.
       
  6378 		opacity: 1,
       
  6379 
       
  6380 		// @option riseOnHover: Boolean = false
       
  6381 		// If `true`, the marker will get on top of others when you hover the mouse over it.
       
  6382 		riseOnHover: false,
       
  6383 
       
  6384 		// @option riseOffset: Number = 250
       
  6385 		// The z-index offset used for the `riseOnHover` feature.
       
  6386 		riseOffset: 250,
       
  6387 
       
  6388 		// @option pane: String = 'markerPane'
       
  6389 		// `Map pane` where the markers icon will be added.
       
  6390 		pane: 'markerPane',
       
  6391 
       
  6392 		// FIXME: shadowPane is no longer a valid option
       
  6393 		nonBubblingEvents: ['click', 'dblclick', 'mouseover', 'mouseout', 'contextmenu']
       
  6394 	},
       
  6395 
       
  6396 	/* @section
       
  6397 	 *
       
  6398 	 * In addition to [shared layer methods](#Layer) like `addTo()` and `remove()` and [popup methods](#Popup) like bindPopup() you can also use the following methods:
       
  6399 	 */
       
  6400 
       
  6401 	initialize: function (latlng, options) {
       
  6402 		L.setOptions(this, options);
       
  6403 		this._latlng = L.latLng(latlng);
       
  6404 	},
       
  6405 
       
  6406 	onAdd: function (map) {
       
  6407 		this._zoomAnimated = this._zoomAnimated && map.options.markerZoomAnimation;
       
  6408 
       
  6409 		if (this._zoomAnimated) {
       
  6410 			map.on('zoomanim', this._animateZoom, this);
       
  6411 		}
       
  6412 
       
  6413 		this._initIcon();
       
  6414 		this.update();
       
  6415 	},
       
  6416 
       
  6417 	onRemove: function (map) {
       
  6418 		if (this.dragging && this.dragging.enabled()) {
       
  6419 			this.options.draggable = true;
       
  6420 			this.dragging.removeHooks();
       
  6421 		}
       
  6422 
       
  6423 		if (this._zoomAnimated) {
       
  6424 			map.off('zoomanim', this._animateZoom, this);
       
  6425 		}
       
  6426 
       
  6427 		this._removeIcon();
       
  6428 		this._removeShadow();
       
  6429 	},
       
  6430 
       
  6431 	getEvents: function () {
       
  6432 		return {
       
  6433 			zoom: this.update,
       
  6434 			viewreset: this.update
       
  6435 		};
       
  6436 	},
       
  6437 
       
  6438 	// @method getLatLng: LatLng
       
  6439 	// Returns the current geographical position of the marker.
       
  6440 	getLatLng: function () {
       
  6441 		return this._latlng;
       
  6442 	},
       
  6443 
       
  6444 	// @method setLatLng(latlng: LatLng): this
       
  6445 	// Changes the marker position to the given point.
       
  6446 	setLatLng: function (latlng) {
       
  6447 		var oldLatLng = this._latlng;
       
  6448 		this._latlng = L.latLng(latlng);
       
  6449 		this.update();
       
  6450 
       
  6451 		// @event move: Event
       
  6452 		// Fired when the marker is moved via [`setLatLng`](#marker-setlatlng) or by [dragging](#marker-dragging). Old and new coordinates are included in event arguments as `oldLatLng`, `latlng`.
       
  6453 		return this.fire('move', {oldLatLng: oldLatLng, latlng: this._latlng});
       
  6454 	},
       
  6455 
       
  6456 	// @method setZIndexOffset(offset: Number): this
       
  6457 	// Changes the [zIndex offset](#marker-zindexoffset) of the marker.
       
  6458 	setZIndexOffset: function (offset) {
       
  6459 		this.options.zIndexOffset = offset;
       
  6460 		return this.update();
       
  6461 	},
       
  6462 
       
  6463 	// @method setIcon(icon: Icon): this
       
  6464 	// Changes the marker icon.
       
  6465 	setIcon: function (icon) {
       
  6466 
       
  6467 		this.options.icon = icon;
       
  6468 
       
  6469 		if (this._map) {
       
  6470 			this._initIcon();
       
  6471 			this.update();
       
  6472 		}
       
  6473 
       
  6474 		if (this._popup) {
       
  6475 			this.bindPopup(this._popup, this._popup.options);
       
  6476 		}
       
  6477 
       
  6478 		return this;
       
  6479 	},
       
  6480 
       
  6481 	getElement: function () {
       
  6482 		return this._icon;
       
  6483 	},
       
  6484 
       
  6485 	update: function () {
       
  6486 
       
  6487 		if (this._icon) {
       
  6488 			var pos = this._map.latLngToLayerPoint(this._latlng).round();
       
  6489 			this._setPos(pos);
       
  6490 		}
       
  6491 
       
  6492 		return this;
       
  6493 	},
       
  6494 
       
  6495 	_initIcon: function () {
       
  6496 		var options = this.options,
       
  6497 		    classToAdd = 'leaflet-zoom-' + (this._zoomAnimated ? 'animated' : 'hide');
       
  6498 
       
  6499 		var icon = options.icon.createIcon(this._icon),
       
  6500 		    addIcon = false;
       
  6501 
       
  6502 		// if we're not reusing the icon, remove the old one and init new one
       
  6503 		if (icon !== this._icon) {
       
  6504 			if (this._icon) {
       
  6505 				this._removeIcon();
       
  6506 			}
       
  6507 			addIcon = true;
       
  6508 
       
  6509 			if (options.title) {
       
  6510 				icon.title = options.title;
       
  6511 			}
       
  6512 			if (options.alt) {
       
  6513 				icon.alt = options.alt;
       
  6514 			}
       
  6515 		}
       
  6516 
       
  6517 		L.DomUtil.addClass(icon, classToAdd);
       
  6518 
       
  6519 		if (options.keyboard) {
       
  6520 			icon.tabIndex = '0';
       
  6521 		}
       
  6522 
       
  6523 		this._icon = icon;
       
  6524 
       
  6525 		if (options.riseOnHover) {
       
  6526 			this.on({
       
  6527 				mouseover: this._bringToFront,
       
  6528 				mouseout: this._resetZIndex
       
  6529 			});
       
  6530 		}
       
  6531 
       
  6532 		var newShadow = options.icon.createShadow(this._shadow),
       
  6533 		    addShadow = false;
       
  6534 
       
  6535 		if (newShadow !== this._shadow) {
       
  6536 			this._removeShadow();
       
  6537 			addShadow = true;
       
  6538 		}
       
  6539 
       
  6540 		if (newShadow) {
       
  6541 			L.DomUtil.addClass(newShadow, classToAdd);
       
  6542 			newShadow.alt = '';
       
  6543 		}
       
  6544 		this._shadow = newShadow;
       
  6545 
       
  6546 
       
  6547 		if (options.opacity < 1) {
       
  6548 			this._updateOpacity();
       
  6549 		}
       
  6550 
       
  6551 
       
  6552 		if (addIcon) {
       
  6553 			this.getPane().appendChild(this._icon);
       
  6554 		}
       
  6555 		this._initInteraction();
       
  6556 		if (newShadow && addShadow) {
       
  6557 			this.getPane('shadowPane').appendChild(this._shadow);
       
  6558 		}
       
  6559 	},
       
  6560 
       
  6561 	_removeIcon: function () {
       
  6562 		if (this.options.riseOnHover) {
       
  6563 			this.off({
       
  6564 				mouseover: this._bringToFront,
       
  6565 				mouseout: this._resetZIndex
       
  6566 			});
       
  6567 		}
       
  6568 
       
  6569 		L.DomUtil.remove(this._icon);
       
  6570 		this.removeInteractiveTarget(this._icon);
       
  6571 
       
  6572 		this._icon = null;
       
  6573 	},
       
  6574 
       
  6575 	_removeShadow: function () {
       
  6576 		if (this._shadow) {
       
  6577 			L.DomUtil.remove(this._shadow);
       
  6578 		}
       
  6579 		this._shadow = null;
       
  6580 	},
       
  6581 
       
  6582 	_setPos: function (pos) {
       
  6583 		L.DomUtil.setPosition(this._icon, pos);
       
  6584 
       
  6585 		if (this._shadow) {
       
  6586 			L.DomUtil.setPosition(this._shadow, pos);
       
  6587 		}
       
  6588 
       
  6589 		this._zIndex = pos.y + this.options.zIndexOffset;
       
  6590 
       
  6591 		this._resetZIndex();
       
  6592 	},
       
  6593 
       
  6594 	_updateZIndex: function (offset) {
       
  6595 		this._icon.style.zIndex = this._zIndex + offset;
       
  6596 	},
       
  6597 
       
  6598 	_animateZoom: function (opt) {
       
  6599 		var pos = this._map._latLngToNewLayerPoint(this._latlng, opt.zoom, opt.center).round();
       
  6600 
       
  6601 		this._setPos(pos);
       
  6602 	},
       
  6603 
       
  6604 	_initInteraction: function () {
       
  6605 
       
  6606 		if (!this.options.interactive) { return; }
       
  6607 
       
  6608 		L.DomUtil.addClass(this._icon, 'leaflet-interactive');
       
  6609 
       
  6610 		this.addInteractiveTarget(this._icon);
       
  6611 
       
  6612 		if (L.Handler.MarkerDrag) {
       
  6613 			var draggable = this.options.draggable;
       
  6614 			if (this.dragging) {
       
  6615 				draggable = this.dragging.enabled();
       
  6616 				this.dragging.disable();
       
  6617 			}
       
  6618 
       
  6619 			this.dragging = new L.Handler.MarkerDrag(this);
       
  6620 
       
  6621 			if (draggable) {
       
  6622 				this.dragging.enable();
       
  6623 			}
       
  6624 		}
       
  6625 	},
       
  6626 
       
  6627 	// @method setOpacity(opacity: Number): this
       
  6628 	// Changes the opacity of the marker.
       
  6629 	setOpacity: function (opacity) {
       
  6630 		this.options.opacity = opacity;
       
  6631 		if (this._map) {
       
  6632 			this._updateOpacity();
       
  6633 		}
       
  6634 
       
  6635 		return this;
       
  6636 	},
       
  6637 
       
  6638 	_updateOpacity: function () {
       
  6639 		var opacity = this.options.opacity;
       
  6640 
       
  6641 		L.DomUtil.setOpacity(this._icon, opacity);
       
  6642 
       
  6643 		if (this._shadow) {
       
  6644 			L.DomUtil.setOpacity(this._shadow, opacity);
       
  6645 		}
       
  6646 	},
       
  6647 
       
  6648 	_bringToFront: function () {
       
  6649 		this._updateZIndex(this.options.riseOffset);
       
  6650 	},
       
  6651 
       
  6652 	_resetZIndex: function () {
       
  6653 		this._updateZIndex(0);
       
  6654 	},
       
  6655 
       
  6656 	_getPopupAnchor: function () {
       
  6657 		return this.options.icon.options.popupAnchor || [0, 0];
       
  6658 	},
       
  6659 
       
  6660 	_getTooltipAnchor: function () {
       
  6661 		return this.options.icon.options.tooltipAnchor || [0, 0];
       
  6662 	}
       
  6663 });
       
  6664 
       
  6665 
       
  6666 // factory L.marker(latlng: LatLng, options? : Marker options)
       
  6667 
       
  6668 // @factory L.marker(latlng: LatLng, options? : Marker options)
       
  6669 // Instantiates a Marker object given a geographical point and optionally an options object.
       
  6670 L.marker = function (latlng, options) {
       
  6671 	return new L.Marker(latlng, options);
       
  6672 };
       
  6673 
       
  6674 
       
  6675 
       
  6676 /*
       
  6677  * @class DivIcon
       
  6678  * @aka L.DivIcon
       
  6679  * @inherits Icon
       
  6680  *
       
  6681  * Represents a lightweight icon for markers that uses a simple `<div>`
       
  6682  * element instead of an image. Inherits from `Icon` but ignores the `iconUrl` and shadow options.
       
  6683  *
       
  6684  * @example
       
  6685  * ```js
       
  6686  * var myIcon = L.divIcon({className: 'my-div-icon'});
       
  6687  * // you can set .my-div-icon styles in CSS
       
  6688  *
       
  6689  * L.marker([50.505, 30.57], {icon: myIcon}).addTo(map);
       
  6690  * ```
       
  6691  *
       
  6692  * By default, it has a 'leaflet-div-icon' CSS class and is styled as a little white square with a shadow.
       
  6693  */
       
  6694 
       
  6695 L.DivIcon = L.Icon.extend({
       
  6696 	options: {
       
  6697 		// @section
       
  6698 		// @aka DivIcon options
       
  6699 		iconSize: [12, 12], // also can be set through CSS
       
  6700 
       
  6701 		// iconAnchor: (Point),
       
  6702 		// popupAnchor: (Point),
       
  6703 
       
  6704 		// @option html: String = ''
       
  6705 		// Custom HTML code to put inside the div element, empty by default.
       
  6706 		html: false,
       
  6707 
       
  6708 		// @option bgPos: Point = [0, 0]
       
  6709 		// Optional relative position of the background, in pixels
       
  6710 		bgPos: null,
       
  6711 
       
  6712 		className: 'leaflet-div-icon'
       
  6713 	},
       
  6714 
       
  6715 	createIcon: function (oldIcon) {
       
  6716 		var div = (oldIcon && oldIcon.tagName === 'DIV') ? oldIcon : document.createElement('div'),
       
  6717 		    options = this.options;
       
  6718 
       
  6719 		div.innerHTML = options.html !== false ? options.html : '';
       
  6720 
       
  6721 		if (options.bgPos) {
       
  6722 			var bgPos = L.point(options.bgPos);
       
  6723 			div.style.backgroundPosition = (-bgPos.x) + 'px ' + (-bgPos.y) + 'px';
       
  6724 		}
       
  6725 		this._setIconStyles(div, 'icon');
       
  6726 
       
  6727 		return div;
       
  6728 	},
       
  6729 
       
  6730 	createShadow: function () {
       
  6731 		return null;
       
  6732 	}
       
  6733 });
       
  6734 
       
  6735 // @factory L.divIcon(options: DivIcon options)
       
  6736 // Creates a `DivIcon` instance with the given options.
       
  6737 L.divIcon = function (options) {
       
  6738 	return new L.DivIcon(options);
       
  6739 };
       
  6740 
       
  6741 
       
  6742 
       
  6743 /*
       
  6744  * @class DivOverlay
       
  6745  * @inherits Layer
       
  6746  * @aka L.DivOverlay
       
  6747  * Base model for L.Popup and L.Tooltip. Inherit from it for custom popup like plugins.
       
  6748  */
       
  6749 
       
  6750 // @namespace DivOverlay
       
  6751 L.DivOverlay = L.Layer.extend({
       
  6752 
       
  6753 	// @section
       
  6754 	// @aka DivOverlay options
       
  6755 	options: {
       
  6756 		// @option offset: Point = Point(0, 7)
       
  6757 		// The offset of the popup position. Useful to control the anchor
       
  6758 		// of the popup when opening it on some overlays.
       
  6759 		offset: [0, 7],
       
  6760 
       
  6761 		// @option className: String = ''
       
  6762 		// A custom CSS class name to assign to the popup.
       
  6763 		className: '',
       
  6764 
       
  6765 		// @option pane: String = 'popupPane'
       
  6766 		// `Map pane` where the popup will be added.
       
  6767 		pane: 'popupPane'
       
  6768 	},
       
  6769 
       
  6770 	initialize: function (options, source) {
       
  6771 		L.setOptions(this, options);
       
  6772 
       
  6773 		this._source = source;
       
  6774 	},
       
  6775 
       
  6776 	onAdd: function (map) {
       
  6777 		this._zoomAnimated = map._zoomAnimated;
       
  6778 
       
  6779 		if (!this._container) {
       
  6780 			this._initLayout();
       
  6781 		}
       
  6782 
       
  6783 		if (map._fadeAnimated) {
       
  6784 			L.DomUtil.setOpacity(this._container, 0);
       
  6785 		}
       
  6786 
       
  6787 		clearTimeout(this._removeTimeout);
       
  6788 		this.getPane().appendChild(this._container);
       
  6789 		this.update();
       
  6790 
       
  6791 		if (map._fadeAnimated) {
       
  6792 			L.DomUtil.setOpacity(this._container, 1);
       
  6793 		}
       
  6794 
       
  6795 		this.bringToFront();
       
  6796 	},
       
  6797 
       
  6798 	onRemove: function (map) {
       
  6799 		if (map._fadeAnimated) {
       
  6800 			L.DomUtil.setOpacity(this._container, 0);
       
  6801 			this._removeTimeout = setTimeout(L.bind(L.DomUtil.remove, L.DomUtil, this._container), 200);
       
  6802 		} else {
       
  6803 			L.DomUtil.remove(this._container);
       
  6804 		}
       
  6805 	},
       
  6806 
       
  6807 	// @namespace Popup
       
  6808 	// @method getLatLng: LatLng
       
  6809 	// Returns the geographical point of popup.
       
  6810 	getLatLng: function () {
       
  6811 		return this._latlng;
       
  6812 	},
       
  6813 
       
  6814 	// @method setLatLng(latlng: LatLng): this
       
  6815 	// Sets the geographical point where the popup will open.
       
  6816 	setLatLng: function (latlng) {
       
  6817 		this._latlng = L.latLng(latlng);
       
  6818 		if (this._map) {
       
  6819 			this._updatePosition();
       
  6820 			this._adjustPan();
       
  6821 		}
       
  6822 		return this;
       
  6823 	},
       
  6824 
       
  6825 	// @method getContent: String|HTMLElement
       
  6826 	// Returns the content of the popup.
       
  6827 	getContent: function () {
       
  6828 		return this._content;
       
  6829 	},
       
  6830 
       
  6831 	// @method setContent(htmlContent: String|HTMLElement|Function): this
       
  6832 	// Sets the HTML content of the popup. If a function is passed the source layer will be passed to the function. The function should return a `String` or `HTMLElement` to be used in the popup.
       
  6833 	setContent: function (content) {
       
  6834 		this._content = content;
       
  6835 		this.update();
       
  6836 		return this;
       
  6837 	},
       
  6838 
       
  6839 	// @method getElement: String|HTMLElement
       
  6840 	// Alias for [getContent()](#popup-getcontent)
       
  6841 	getElement: function () {
       
  6842 		return this._container;
       
  6843 	},
       
  6844 
       
  6845 	// @method update: null
       
  6846 	// Updates the popup content, layout and position. Useful for updating the popup after something inside changed, e.g. image loaded.
       
  6847 	update: function () {
       
  6848 		if (!this._map) { return; }
       
  6849 
       
  6850 		this._container.style.visibility = 'hidden';
       
  6851 
       
  6852 		this._updateContent();
       
  6853 		this._updateLayout();
       
  6854 		this._updatePosition();
       
  6855 
       
  6856 		this._container.style.visibility = '';
       
  6857 
       
  6858 		this._adjustPan();
       
  6859 	},
       
  6860 
       
  6861 	getEvents: function () {
       
  6862 		var events = {
       
  6863 			zoom: this._updatePosition,
       
  6864 			viewreset: this._updatePosition
       
  6865 		};
       
  6866 
       
  6867 		if (this._zoomAnimated) {
       
  6868 			events.zoomanim = this._animateZoom;
       
  6869 		}
       
  6870 		return events;
       
  6871 	},
       
  6872 
       
  6873 	// @method isOpen: Boolean
       
  6874 	// Returns `true` when the popup is visible on the map.
       
  6875 	isOpen: function () {
       
  6876 		return !!this._map && this._map.hasLayer(this);
       
  6877 	},
       
  6878 
       
  6879 	// @method bringToFront: this
       
  6880 	// Brings this popup in front of other popups (in the same map pane).
       
  6881 	bringToFront: function () {
       
  6882 		if (this._map) {
       
  6883 			L.DomUtil.toFront(this._container);
       
  6884 		}
       
  6885 		return this;
       
  6886 	},
       
  6887 
       
  6888 	// @method bringToBack: this
       
  6889 	// Brings this popup to the back of other popups (in the same map pane).
       
  6890 	bringToBack: function () {
       
  6891 		if (this._map) {
       
  6892 			L.DomUtil.toBack(this._container);
       
  6893 		}
       
  6894 		return this;
       
  6895 	},
       
  6896 
       
  6897 	_updateContent: function () {
       
  6898 		if (!this._content) { return; }
       
  6899 
       
  6900 		var node = this._contentNode;
       
  6901 		var content = (typeof this._content === 'function') ? this._content(this._source || this) : this._content;
       
  6902 
       
  6903 		if (typeof content === 'string') {
       
  6904 			node.innerHTML = content;
       
  6905 		} else {
       
  6906 			while (node.hasChildNodes()) {
       
  6907 				node.removeChild(node.firstChild);
       
  6908 			}
       
  6909 			node.appendChild(content);
       
  6910 		}
       
  6911 		this.fire('contentupdate');
       
  6912 	},
       
  6913 
       
  6914 	_updatePosition: function () {
       
  6915 		if (!this._map) { return; }
       
  6916 
       
  6917 		var pos = this._map.latLngToLayerPoint(this._latlng),
       
  6918 		    offset = L.point(this.options.offset),
       
  6919 		    anchor = this._getAnchor();
       
  6920 
       
  6921 		if (this._zoomAnimated) {
       
  6922 			L.DomUtil.setPosition(this._container, pos.add(anchor));
       
  6923 		} else {
       
  6924 			offset = offset.add(pos).add(anchor);
       
  6925 		}
       
  6926 
       
  6927 		var bottom = this._containerBottom = -offset.y,
       
  6928 		    left = this._containerLeft = -Math.round(this._containerWidth / 2) + offset.x;
       
  6929 
       
  6930 		// bottom position the popup in case the height of the popup changes (images loading etc)
       
  6931 		this._container.style.bottom = bottom + 'px';
       
  6932 		this._container.style.left = left + 'px';
       
  6933 	},
       
  6934 
       
  6935 	_getAnchor: function () {
       
  6936 		return [0, 0];
       
  6937 	}
       
  6938 
       
  6939 });
       
  6940 
       
  6941 
       
  6942 
       
  6943 /*
       
  6944  * @class Popup
       
  6945  * @inherits DivOverlay
       
  6946  * @aka L.Popup
       
  6947  * Used to open popups in certain places of the map. Use [Map.openPopup](#map-openpopup) to
       
  6948  * open popups while making sure that only one popup is open at one time
       
  6949  * (recommended for usability), or use [Map.addLayer](#map-addlayer) to open as many as you want.
       
  6950  *
       
  6951  * @example
       
  6952  *
       
  6953  * If you want to just bind a popup to marker click and then open it, it's really easy:
       
  6954  *
       
  6955  * ```js
       
  6956  * marker.bindPopup(popupContent).openPopup();
       
  6957  * ```
       
  6958  * Path overlays like polylines also have a `bindPopup` method.
       
  6959  * Here's a more complicated way to open a popup on a map:
       
  6960  *
       
  6961  * ```js
       
  6962  * var popup = L.popup()
       
  6963  * 	.setLatLng(latlng)
       
  6964  * 	.setContent('<p>Hello world!<br />This is a nice popup.</p>')
       
  6965  * 	.openOn(map);
       
  6966  * ```
       
  6967  */
       
  6968 
       
  6969 
       
  6970 // @namespace Popup
       
  6971 L.Popup = L.DivOverlay.extend({
       
  6972 
       
  6973 	// @section
       
  6974 	// @aka Popup options
       
  6975 	options: {
       
  6976 		// @option maxWidth: Number = 300
       
  6977 		// Max width of the popup, in pixels.
       
  6978 		maxWidth: 300,
       
  6979 
       
  6980 		// @option minWidth: Number = 50
       
  6981 		// Min width of the popup, in pixels.
       
  6982 		minWidth: 50,
       
  6983 
       
  6984 		// @option maxHeight: Number = null
       
  6985 		// If set, creates a scrollable container of the given height
       
  6986 		// inside a popup if its content exceeds it.
       
  6987 		maxHeight: null,
       
  6988 
       
  6989 		// @option autoPan: Boolean = true
       
  6990 		// Set it to `false` if you don't want the map to do panning animation
       
  6991 		// to fit the opened popup.
       
  6992 		autoPan: true,
       
  6993 
       
  6994 		// @option autoPanPaddingTopLeft: Point = null
       
  6995 		// The margin between the popup and the top left corner of the map
       
  6996 		// view after autopanning was performed.
       
  6997 		autoPanPaddingTopLeft: null,
       
  6998 
       
  6999 		// @option autoPanPaddingBottomRight: Point = null
       
  7000 		// The margin between the popup and the bottom right corner of the map
       
  7001 		// view after autopanning was performed.
       
  7002 		autoPanPaddingBottomRight: null,
       
  7003 
       
  7004 		// @option autoPanPadding: Point = Point(5, 5)
       
  7005 		// Equivalent of setting both top left and bottom right autopan padding to the same value.
       
  7006 		autoPanPadding: [5, 5],
       
  7007 
       
  7008 		// @option keepInView: Boolean = false
       
  7009 		// Set it to `true` if you want to prevent users from panning the popup
       
  7010 		// off of the screen while it is open.
       
  7011 		keepInView: false,
       
  7012 
       
  7013 		// @option closeButton: Boolean = true
       
  7014 		// Controls the presence of a close button in the popup.
       
  7015 		closeButton: true,
       
  7016 
       
  7017 		// @option autoClose: Boolean = true
       
  7018 		// Set it to `false` if you want to override the default behavior of
       
  7019 		// the popup closing when user clicks the map (set globally by
       
  7020 		// the Map's [closePopupOnClick](#map-closepopuponclick) option).
       
  7021 		autoClose: true,
       
  7022 
       
  7023 		// @option className: String = ''
       
  7024 		// A custom CSS class name to assign to the popup.
       
  7025 		className: ''
       
  7026 	},
       
  7027 
       
  7028 	// @namespace Popup
       
  7029 	// @method openOn(map: Map): this
       
  7030 	// Adds the popup to the map and closes the previous one. The same as `map.openPopup(popup)`.
       
  7031 	openOn: function (map) {
       
  7032 		map.openPopup(this);
       
  7033 		return this;
       
  7034 	},
       
  7035 
       
  7036 	onAdd: function (map) {
       
  7037 		L.DivOverlay.prototype.onAdd.call(this, map);
       
  7038 
       
  7039 		// @namespace Map
       
  7040 		// @section Popup events
       
  7041 		// @event popupopen: PopupEvent
       
  7042 		// Fired when a popup is opened in the map
       
  7043 		map.fire('popupopen', {popup: this});
       
  7044 
       
  7045 		if (this._source) {
       
  7046 			// @namespace Layer
       
  7047 			// @section Popup events
       
  7048 			// @event popupopen: PopupEvent
       
  7049 			// Fired when a popup bound to this layer is opened
       
  7050 			this._source.fire('popupopen', {popup: this}, true);
       
  7051 			// For non-path layers, we toggle the popup when clicking
       
  7052 			// again the layer, so prevent the map to reopen it.
       
  7053 			if (!(this._source instanceof L.Path)) {
       
  7054 				this._source.on('preclick', L.DomEvent.stopPropagation);
       
  7055 			}
       
  7056 		}
       
  7057 	},
       
  7058 
       
  7059 	onRemove: function (map) {
       
  7060 		L.DivOverlay.prototype.onRemove.call(this, map);
       
  7061 
       
  7062 		// @namespace Map
       
  7063 		// @section Popup events
       
  7064 		// @event popupclose: PopupEvent
       
  7065 		// Fired when a popup in the map is closed
       
  7066 		map.fire('popupclose', {popup: this});
       
  7067 
       
  7068 		if (this._source) {
       
  7069 			// @namespace Layer
       
  7070 			// @section Popup events
       
  7071 			// @event popupclose: PopupEvent
       
  7072 			// Fired when a popup bound to this layer is closed
       
  7073 			this._source.fire('popupclose', {popup: this}, true);
       
  7074 			if (!(this._source instanceof L.Path)) {
       
  7075 				this._source.off('preclick', L.DomEvent.stopPropagation);
       
  7076 			}
       
  7077 		}
       
  7078 	},
       
  7079 
       
  7080 	getEvents: function () {
       
  7081 		var events = L.DivOverlay.prototype.getEvents.call(this);
       
  7082 
       
  7083 		if ('closeOnClick' in this.options ? this.options.closeOnClick : this._map.options.closePopupOnClick) {
       
  7084 			events.preclick = this._close;
       
  7085 		}
       
  7086 
       
  7087 		if (this.options.keepInView) {
       
  7088 			events.moveend = this._adjustPan;
       
  7089 		}
       
  7090 
       
  7091 		return events;
       
  7092 	},
       
  7093 
       
  7094 	_close: function () {
       
  7095 		if (this._map) {
       
  7096 			this._map.closePopup(this);
       
  7097 		}
       
  7098 	},
       
  7099 
       
  7100 	_initLayout: function () {
       
  7101 		var prefix = 'leaflet-popup',
       
  7102 		    container = this._container = L.DomUtil.create('div',
       
  7103 			prefix + ' ' + (this.options.className || '') +
       
  7104 			' leaflet-zoom-animated');
       
  7105 
       
  7106 		if (this.options.closeButton) {
       
  7107 			var closeButton = this._closeButton = L.DomUtil.create('a', prefix + '-close-button', container);
       
  7108 			closeButton.href = '#close';
       
  7109 			closeButton.innerHTML = '&#215;';
       
  7110 
       
  7111 			L.DomEvent.on(closeButton, 'click', this._onCloseButtonClick, this);
       
  7112 		}
       
  7113 
       
  7114 		var wrapper = this._wrapper = L.DomUtil.create('div', prefix + '-content-wrapper', container);
       
  7115 		this._contentNode = L.DomUtil.create('div', prefix + '-content', wrapper);
       
  7116 
       
  7117 		L.DomEvent
       
  7118 			.disableClickPropagation(wrapper)
       
  7119 			.disableScrollPropagation(this._contentNode)
       
  7120 			.on(wrapper, 'contextmenu', L.DomEvent.stopPropagation);
       
  7121 
       
  7122 		this._tipContainer = L.DomUtil.create('div', prefix + '-tip-container', container);
       
  7123 		this._tip = L.DomUtil.create('div', prefix + '-tip', this._tipContainer);
       
  7124 	},
       
  7125 
       
  7126 	_updateLayout: function () {
       
  7127 		var container = this._contentNode,
       
  7128 		    style = container.style;
       
  7129 
       
  7130 		style.width = '';
       
  7131 		style.whiteSpace = 'nowrap';
       
  7132 
       
  7133 		var width = container.offsetWidth;
       
  7134 		width = Math.min(width, this.options.maxWidth);
       
  7135 		width = Math.max(width, this.options.minWidth);
       
  7136 
       
  7137 		style.width = (width + 1) + 'px';
       
  7138 		style.whiteSpace = '';
       
  7139 
       
  7140 		style.height = '';
       
  7141 
       
  7142 		var height = container.offsetHeight,
       
  7143 		    maxHeight = this.options.maxHeight,
       
  7144 		    scrolledClass = 'leaflet-popup-scrolled';
       
  7145 
       
  7146 		if (maxHeight && height > maxHeight) {
       
  7147 			style.height = maxHeight + 'px';
       
  7148 			L.DomUtil.addClass(container, scrolledClass);
       
  7149 		} else {
       
  7150 			L.DomUtil.removeClass(container, scrolledClass);
       
  7151 		}
       
  7152 
       
  7153 		this._containerWidth = this._container.offsetWidth;
       
  7154 	},
       
  7155 
       
  7156 	_animateZoom: function (e) {
       
  7157 		var pos = this._map._latLngToNewLayerPoint(this._latlng, e.zoom, e.center),
       
  7158 		    anchor = this._getAnchor();
       
  7159 		L.DomUtil.setPosition(this._container, pos.add(anchor));
       
  7160 	},
       
  7161 
       
  7162 	_adjustPan: function () {
       
  7163 		if (!this.options.autoPan || (this._map._panAnim && this._map._panAnim._inProgress)) { return; }
       
  7164 
       
  7165 		var map = this._map,
       
  7166 		    marginBottom = parseInt(L.DomUtil.getStyle(this._container, 'marginBottom'), 10) || 0,
       
  7167 		    containerHeight = this._container.offsetHeight + marginBottom,
       
  7168 		    containerWidth = this._containerWidth,
       
  7169 		    layerPos = new L.Point(this._containerLeft, -containerHeight - this._containerBottom);
       
  7170 
       
  7171 		layerPos._add(L.DomUtil.getPosition(this._container));
       
  7172 
       
  7173 		var containerPos = map.layerPointToContainerPoint(layerPos),
       
  7174 		    padding = L.point(this.options.autoPanPadding),
       
  7175 		    paddingTL = L.point(this.options.autoPanPaddingTopLeft || padding),
       
  7176 		    paddingBR = L.point(this.options.autoPanPaddingBottomRight || padding),
       
  7177 		    size = map.getSize(),
       
  7178 		    dx = 0,
       
  7179 		    dy = 0;
       
  7180 
       
  7181 		if (containerPos.x + containerWidth + paddingBR.x > size.x) { // right
       
  7182 			dx = containerPos.x + containerWidth - size.x + paddingBR.x;
       
  7183 		}
       
  7184 		if (containerPos.x - dx - paddingTL.x < 0) { // left
       
  7185 			dx = containerPos.x - paddingTL.x;
       
  7186 		}
       
  7187 		if (containerPos.y + containerHeight + paddingBR.y > size.y) { // bottom
       
  7188 			dy = containerPos.y + containerHeight - size.y + paddingBR.y;
       
  7189 		}
       
  7190 		if (containerPos.y - dy - paddingTL.y < 0) { // top
       
  7191 			dy = containerPos.y - paddingTL.y;
       
  7192 		}
       
  7193 
       
  7194 		// @namespace Map
       
  7195 		// @section Popup events
       
  7196 		// @event autopanstart: Event
       
  7197 		// Fired when the map starts autopanning when opening a popup.
       
  7198 		if (dx || dy) {
       
  7199 			map
       
  7200 			    .fire('autopanstart')
       
  7201 			    .panBy([dx, dy]);
       
  7202 		}
       
  7203 	},
       
  7204 
       
  7205 	_onCloseButtonClick: function (e) {
       
  7206 		this._close();
       
  7207 		L.DomEvent.stop(e);
       
  7208 	},
       
  7209 
       
  7210 	_getAnchor: function () {
       
  7211 		// Where should we anchor the popup on the source layer?
       
  7212 		return L.point(this._source && this._source._getPopupAnchor ? this._source._getPopupAnchor() : [0, 0]);
       
  7213 	}
       
  7214 
       
  7215 });
       
  7216 
       
  7217 // @namespace Popup
       
  7218 // @factory L.popup(options?: Popup options, source?: Layer)
       
  7219 // Instantiates a `Popup` object given an optional `options` object that describes its appearance and location and an optional `source` object that is used to tag the popup with a reference to the Layer to which it refers.
       
  7220 L.popup = function (options, source) {
       
  7221 	return new L.Popup(options, source);
       
  7222 };
       
  7223 
       
  7224 
       
  7225 /* @namespace Map
       
  7226  * @section Interaction Options
       
  7227  * @option closePopupOnClick: Boolean = true
       
  7228  * Set it to `false` if you don't want popups to close when user clicks the map.
       
  7229  */
       
  7230 L.Map.mergeOptions({
       
  7231 	closePopupOnClick: true
       
  7232 });
       
  7233 
       
  7234 
       
  7235 // @namespace Map
       
  7236 // @section Methods for Layers and Controls
       
  7237 L.Map.include({
       
  7238 	// @method openPopup(popup: Popup): this
       
  7239 	// Opens the specified popup while closing the previously opened (to make sure only one is opened at one time for usability).
       
  7240 	// @alternative
       
  7241 	// @method openPopup(content: String|HTMLElement, latlng: LatLng, options?: Popup options): this
       
  7242 	// Creates a popup with the specified content and options and opens it in the given point on a map.
       
  7243 	openPopup: function (popup, latlng, options) {
       
  7244 		if (!(popup instanceof L.Popup)) {
       
  7245 			popup = new L.Popup(options).setContent(popup);
       
  7246 		}
       
  7247 
       
  7248 		if (latlng) {
       
  7249 			popup.setLatLng(latlng);
       
  7250 		}
       
  7251 
       
  7252 		if (this.hasLayer(popup)) {
       
  7253 			return this;
       
  7254 		}
       
  7255 
       
  7256 		if (this._popup && this._popup.options.autoClose) {
       
  7257 			this.closePopup();
       
  7258 		}
       
  7259 
       
  7260 		this._popup = popup;
       
  7261 		return this.addLayer(popup);
       
  7262 	},
       
  7263 
       
  7264 	// @method closePopup(popup?: Popup): this
       
  7265 	// Closes the popup previously opened with [openPopup](#map-openpopup) (or the given one).
       
  7266 	closePopup: function (popup) {
       
  7267 		if (!popup || popup === this._popup) {
       
  7268 			popup = this._popup;
       
  7269 			this._popup = null;
       
  7270 		}
       
  7271 		if (popup) {
       
  7272 			this.removeLayer(popup);
       
  7273 		}
       
  7274 		return this;
       
  7275 	}
       
  7276 });
       
  7277 
       
  7278 /*
       
  7279  * @namespace Layer
       
  7280  * @section Popup methods example
       
  7281  *
       
  7282  * All layers share a set of methods convenient for binding popups to it.
       
  7283  *
       
  7284  * ```js
       
  7285  * var layer = L.Polygon(latlngs).bindPopup('Hi There!').addTo(map);
       
  7286  * layer.openPopup();
       
  7287  * layer.closePopup();
       
  7288  * ```
       
  7289  *
       
  7290  * Popups will also be automatically opened when the layer is clicked on and closed when the layer is removed from the map or another popup is opened.
       
  7291  */
       
  7292 
       
  7293 // @section Popup methods
       
  7294 L.Layer.include({
       
  7295 
       
  7296 	// @method bindPopup(content: String|HTMLElement|Function|Popup, options?: Popup options): this
       
  7297 	// Binds a popup to the layer with the passed `content` and sets up the
       
  7298 	// neccessary event listeners. If a `Function` is passed it will receive
       
  7299 	// the layer as the first argument and should return a `String` or `HTMLElement`.
       
  7300 	bindPopup: function (content, options) {
       
  7301 
       
  7302 		if (content instanceof L.Popup) {
       
  7303 			L.setOptions(content, options);
       
  7304 			this._popup = content;
       
  7305 			content._source = this;
       
  7306 		} else {
       
  7307 			if (!this._popup || options) {
       
  7308 				this._popup = new L.Popup(options, this);
       
  7309 			}
       
  7310 			this._popup.setContent(content);
       
  7311 		}
       
  7312 
       
  7313 		if (!this._popupHandlersAdded) {
       
  7314 			this.on({
       
  7315 				click: this._openPopup,
       
  7316 				remove: this.closePopup,
       
  7317 				move: this._movePopup
       
  7318 			});
       
  7319 			this._popupHandlersAdded = true;
       
  7320 		}
       
  7321 
       
  7322 		return this;
       
  7323 	},
       
  7324 
       
  7325 	// @method unbindPopup(): this
       
  7326 	// Removes the popup previously bound with `bindPopup`.
       
  7327 	unbindPopup: function () {
       
  7328 		if (this._popup) {
       
  7329 			this.off({
       
  7330 				click: this._openPopup,
       
  7331 				remove: this.closePopup,
       
  7332 				move: this._movePopup
       
  7333 			});
       
  7334 			this._popupHandlersAdded = false;
       
  7335 			this._popup = null;
       
  7336 		}
       
  7337 		return this;
       
  7338 	},
       
  7339 
       
  7340 	// @method openPopup(latlng?: LatLng): this
       
  7341 	// Opens the bound popup at the specificed `latlng` or at the default popup anchor if no `latlng` is passed.
       
  7342 	openPopup: function (layer, latlng) {
       
  7343 		if (!(layer instanceof L.Layer)) {
       
  7344 			latlng = layer;
       
  7345 			layer = this;
       
  7346 		}
       
  7347 
       
  7348 		if (layer instanceof L.FeatureGroup) {
       
  7349 			for (var id in this._layers) {
       
  7350 				layer = this._layers[id];
       
  7351 				break;
       
  7352 			}
       
  7353 		}
       
  7354 
       
  7355 		if (!latlng) {
       
  7356 			latlng = layer.getCenter ? layer.getCenter() : layer.getLatLng();
       
  7357 		}
       
  7358 
       
  7359 		if (this._popup && this._map) {
       
  7360 			// set popup source to this layer
       
  7361 			this._popup._source = layer;
       
  7362 
       
  7363 			// update the popup (content, layout, ect...)
       
  7364 			this._popup.update();
       
  7365 
       
  7366 			// open the popup on the map
       
  7367 			this._map.openPopup(this._popup, latlng);
       
  7368 		}
       
  7369 
       
  7370 		return this;
       
  7371 	},
       
  7372 
       
  7373 	// @method closePopup(): this
       
  7374 	// Closes the popup bound to this layer if it is open.
       
  7375 	closePopup: function () {
       
  7376 		if (this._popup) {
       
  7377 			this._popup._close();
       
  7378 		}
       
  7379 		return this;
       
  7380 	},
       
  7381 
       
  7382 	// @method togglePopup(): this
       
  7383 	// Opens or closes the popup bound to this layer depending on its current state.
       
  7384 	togglePopup: function (target) {
       
  7385 		if (this._popup) {
       
  7386 			if (this._popup._map) {
       
  7387 				this.closePopup();
       
  7388 			} else {
       
  7389 				this.openPopup(target);
       
  7390 			}
       
  7391 		}
       
  7392 		return this;
       
  7393 	},
       
  7394 
       
  7395 	// @method isPopupOpen(): boolean
       
  7396 	// Returns `true` if the popup bound to this layer is currently open.
       
  7397 	isPopupOpen: function () {
       
  7398 		return (this._popup ? this._popup.isOpen() : false);
       
  7399 	},
       
  7400 
       
  7401 	// @method setPopupContent(content: String|HTMLElement|Popup): this
       
  7402 	// Sets the content of the popup bound to this layer.
       
  7403 	setPopupContent: function (content) {
       
  7404 		if (this._popup) {
       
  7405 			this._popup.setContent(content);
       
  7406 		}
       
  7407 		return this;
       
  7408 	},
       
  7409 
       
  7410 	// @method getPopup(): Popup
       
  7411 	// Returns the popup bound to this layer.
       
  7412 	getPopup: function () {
       
  7413 		return this._popup;
       
  7414 	},
       
  7415 
       
  7416 	_openPopup: function (e) {
       
  7417 		var layer = e.layer || e.target;
       
  7418 
       
  7419 		if (!this._popup) {
       
  7420 			return;
       
  7421 		}
       
  7422 
       
  7423 		if (!this._map) {
       
  7424 			return;
       
  7425 		}
       
  7426 
       
  7427 		// prevent map click
       
  7428 		L.DomEvent.stop(e);
       
  7429 
       
  7430 		// if this inherits from Path its a vector and we can just
       
  7431 		// open the popup at the new location
       
  7432 		if (layer instanceof L.Path) {
       
  7433 			this.openPopup(e.layer || e.target, e.latlng);
       
  7434 			return;
       
  7435 		}
       
  7436 
       
  7437 		// otherwise treat it like a marker and figure out
       
  7438 		// if we should toggle it open/closed
       
  7439 		if (this._map.hasLayer(this._popup) && this._popup._source === layer) {
       
  7440 			this.closePopup();
       
  7441 		} else {
       
  7442 			this.openPopup(layer, e.latlng);
       
  7443 		}
       
  7444 	},
       
  7445 
       
  7446 	_movePopup: function (e) {
       
  7447 		this._popup.setLatLng(e.latlng);
       
  7448 	}
       
  7449 });
       
  7450 
       
  7451 
       
  7452 
       
  7453 /*
       
  7454  * @class Tooltip
       
  7455  * @inherits DivOverlay
       
  7456  * @aka L.Tooltip
       
  7457  * Used to display small texts on top of map layers.
       
  7458  *
       
  7459  * @example
       
  7460  *
       
  7461  * ```js
       
  7462  * marker.bindTooltip("my tooltip text").openTooltip();
       
  7463  * ```
       
  7464  * Note about tooltip offset. Leaflet takes two options in consideration
       
  7465  * for computing tooltip offseting:
       
  7466  * - the `offset` Tooltip option: it defaults to [0, 0], and it's specific to one tooltip.
       
  7467  *   Add a positive x offset to move the tooltip to the right, and a positive y offset to
       
  7468  *   move it to the bottom. Negatives will move to the left and top.
       
  7469  * - the `tooltipAnchor` Icon option: this will only be considered for Marker. You
       
  7470  *   should adapt this value if you use a custom icon.
       
  7471  */
       
  7472 
       
  7473 
       
  7474 // @namespace Tooltip
       
  7475 L.Tooltip = L.DivOverlay.extend({
       
  7476 
       
  7477 	// @section
       
  7478 	// @aka Tooltip options
       
  7479 	options: {
       
  7480 		// @option pane: String = 'tooltipPane'
       
  7481 		// `Map pane` where the tooltip will be added.
       
  7482 		pane: 'tooltipPane',
       
  7483 
       
  7484 		// @option offset: Point = Point(0, 0)
       
  7485 		// Optional offset of the tooltip position.
       
  7486 		offset: [0, 0],
       
  7487 
       
  7488 		// @option direction: String = 'auto'
       
  7489 		// Direction where to open the tooltip. Possible values are: `right`, `left`,
       
  7490 		// `top`, `bottom`, `center`, `auto`.
       
  7491 		// `auto` will dynamicaly switch between `right` and `left` according to the tooltip
       
  7492 		// position on the map.
       
  7493 		direction: 'auto',
       
  7494 
       
  7495 		// @option permanent: Boolean = false
       
  7496 		// Whether to open the tooltip permanently or only on mouseover.
       
  7497 		permanent: false,
       
  7498 
       
  7499 		// @option sticky: Boolean = false
       
  7500 		// If true, the tooltip will follow the mouse instead of being fixed at the feature center.
       
  7501 		sticky: false,
       
  7502 
       
  7503 		// @option interactive: Boolean = false
       
  7504 		// If true, the tooltip will listen to the feature events.
       
  7505 		interactive: false,
       
  7506 
       
  7507 		// @option opacity: Number = 0.9
       
  7508 		// Tooltip container opacity.
       
  7509 		opacity: 0.9
       
  7510 	},
       
  7511 
       
  7512 	onAdd: function (map) {
       
  7513 		L.DivOverlay.prototype.onAdd.call(this, map);
       
  7514 		this.setOpacity(this.options.opacity);
       
  7515 
       
  7516 		// @namespace Map
       
  7517 		// @section Tooltip events
       
  7518 		// @event tooltipopen: TooltipEvent
       
  7519 		// Fired when a tooltip is opened in the map.
       
  7520 		map.fire('tooltipopen', {tooltip: this});
       
  7521 
       
  7522 		if (this._source) {
       
  7523 			// @namespace Layer
       
  7524 			// @section Tooltip events
       
  7525 			// @event tooltipopen: TooltipEvent
       
  7526 			// Fired when a tooltip bound to this layer is opened.
       
  7527 			this._source.fire('tooltipopen', {tooltip: this}, true);
       
  7528 		}
       
  7529 	},
       
  7530 
       
  7531 	onRemove: function (map) {
       
  7532 		L.DivOverlay.prototype.onRemove.call(this, map);
       
  7533 
       
  7534 		// @namespace Map
       
  7535 		// @section Tooltip events
       
  7536 		// @event tooltipclose: TooltipEvent
       
  7537 		// Fired when a tooltip in the map is closed.
       
  7538 		map.fire('tooltipclose', {tooltip: this});
       
  7539 
       
  7540 		if (this._source) {
       
  7541 			// @namespace Layer
       
  7542 			// @section Tooltip events
       
  7543 			// @event tooltipclose: TooltipEvent
       
  7544 			// Fired when a tooltip bound to this layer is closed.
       
  7545 			this._source.fire('tooltipclose', {tooltip: this}, true);
       
  7546 		}
       
  7547 	},
       
  7548 
       
  7549 	getEvents: function () {
       
  7550 		var events = L.DivOverlay.prototype.getEvents.call(this);
       
  7551 
       
  7552 		if (L.Browser.touch && !this.options.permanent) {
       
  7553 			events.preclick = this._close;
       
  7554 		}
       
  7555 
       
  7556 		return events;
       
  7557 	},
       
  7558 
       
  7559 	_close: function () {
       
  7560 		if (this._map) {
       
  7561 			this._map.closeTooltip(this);
       
  7562 		}
       
  7563 	},
       
  7564 
       
  7565 	_initLayout: function () {
       
  7566 		var prefix = 'leaflet-tooltip',
       
  7567 		    className = prefix + ' ' + (this.options.className || '') + ' leaflet-zoom-' + (this._zoomAnimated ? 'animated' : 'hide');
       
  7568 
       
  7569 		this._contentNode = this._container = L.DomUtil.create('div', className);
       
  7570 	},
       
  7571 
       
  7572 	_updateLayout: function () {},
       
  7573 
       
  7574 	_adjustPan: function () {},
       
  7575 
       
  7576 	_setPosition: function (pos) {
       
  7577 		var map = this._map,
       
  7578 		    container = this._container,
       
  7579 		    centerPoint = map.latLngToContainerPoint(map.getCenter()),
       
  7580 		    tooltipPoint = map.layerPointToContainerPoint(pos),
       
  7581 		    direction = this.options.direction,
       
  7582 		    tooltipWidth = container.offsetWidth,
       
  7583 		    tooltipHeight = container.offsetHeight,
       
  7584 		    offset = L.point(this.options.offset),
       
  7585 		    anchor = this._getAnchor();
       
  7586 
       
  7587 		if (direction === 'top') {
       
  7588 			pos = pos.add(L.point(-tooltipWidth / 2 + offset.x, -tooltipHeight + offset.y + anchor.y, true));
       
  7589 		} else if (direction === 'bottom') {
       
  7590 			pos = pos.subtract(L.point(tooltipWidth / 2 - offset.x, -offset.y, true));
       
  7591 		} else if (direction === 'center') {
       
  7592 			pos = pos.subtract(L.point(tooltipWidth / 2 + offset.x, tooltipHeight / 2 - anchor.y + offset.y, true));
       
  7593 		} else if (direction === 'right' || direction === 'auto' && tooltipPoint.x < centerPoint.x) {
       
  7594 			direction = 'right';
       
  7595 			pos = pos.add(L.point(offset.x + anchor.x, anchor.y - tooltipHeight / 2 + offset.y, true));
       
  7596 		} else {
       
  7597 			direction = 'left';
       
  7598 			pos = pos.subtract(L.point(tooltipWidth + anchor.x - offset.x, tooltipHeight / 2 - anchor.y - offset.y, true));
       
  7599 		}
       
  7600 
       
  7601 		L.DomUtil.removeClass(container, 'leaflet-tooltip-right');
       
  7602 		L.DomUtil.removeClass(container, 'leaflet-tooltip-left');
       
  7603 		L.DomUtil.removeClass(container, 'leaflet-tooltip-top');
       
  7604 		L.DomUtil.removeClass(container, 'leaflet-tooltip-bottom');
       
  7605 		L.DomUtil.addClass(container, 'leaflet-tooltip-' + direction);
       
  7606 		L.DomUtil.setPosition(container, pos);
       
  7607 	},
       
  7608 
       
  7609 	_updatePosition: function () {
       
  7610 		var pos = this._map.latLngToLayerPoint(this._latlng);
       
  7611 		this._setPosition(pos);
       
  7612 	},
       
  7613 
       
  7614 	setOpacity: function (opacity) {
       
  7615 		this.options.opacity = opacity;
       
  7616 
       
  7617 		if (this._container) {
       
  7618 			L.DomUtil.setOpacity(this._container, opacity);
       
  7619 		}
       
  7620 	},
       
  7621 
       
  7622 	_animateZoom: function (e) {
       
  7623 		var pos = this._map._latLngToNewLayerPoint(this._latlng, e.zoom, e.center);
       
  7624 		this._setPosition(pos);
       
  7625 	},
       
  7626 
       
  7627 	_getAnchor: function () {
       
  7628 		// Where should we anchor the tooltip on the source layer?
       
  7629 		return L.point(this._source && this._source._getTooltipAnchor && !this.options.sticky ? this._source._getTooltipAnchor() : [0, 0]);
       
  7630 	}
       
  7631 
       
  7632 });
       
  7633 
       
  7634 // @namespace Tooltip
       
  7635 // @factory L.tooltip(options?: Tooltip options, source?: Layer)
       
  7636 // Instantiates a Tooltip object given an optional `options` object that describes its appearance and location and an optional `source` object that is used to tag the tooltip with a reference to the Layer to which it refers.
       
  7637 L.tooltip = function (options, source) {
       
  7638 	return new L.Tooltip(options, source);
       
  7639 };
       
  7640 
       
  7641 // @namespace Map
       
  7642 // @section Methods for Layers and Controls
       
  7643 L.Map.include({
       
  7644 
       
  7645 	// @method openTooltip(tooltip: Tooltip): this
       
  7646 	// Opens the specified tooltip.
       
  7647 	// @alternative
       
  7648 	// @method openTooltip(content: String|HTMLElement, latlng: LatLng, options?: Tooltip options): this
       
  7649 	// Creates a tooltip with the specified content and options and open it.
       
  7650 	openTooltip: function (tooltip, latlng, options) {
       
  7651 		if (!(tooltip instanceof L.Tooltip)) {
       
  7652 			tooltip = new L.Tooltip(options).setContent(tooltip);
       
  7653 		}
       
  7654 
       
  7655 		if (latlng) {
       
  7656 			tooltip.setLatLng(latlng);
       
  7657 		}
       
  7658 
       
  7659 		if (this.hasLayer(tooltip)) {
       
  7660 			return this;
       
  7661 		}
       
  7662 
       
  7663 		return this.addLayer(tooltip);
       
  7664 	},
       
  7665 
       
  7666 	// @method closeTooltip(tooltip?: Tooltip): this
       
  7667 	// Closes the tooltip given as parameter.
       
  7668 	closeTooltip: function (tooltip) {
       
  7669 		if (tooltip) {
       
  7670 			this.removeLayer(tooltip);
       
  7671 		}
       
  7672 		return this;
       
  7673 	}
       
  7674 
       
  7675 });
       
  7676 
       
  7677 /*
       
  7678  * @namespace Layer
       
  7679  * @section Tooltip methods example
       
  7680  *
       
  7681  * All layers share a set of methods convenient for binding tooltips to it.
       
  7682  *
       
  7683  * ```js
       
  7684  * var layer = L.Polygon(latlngs).bindTooltip('Hi There!').addTo(map);
       
  7685  * layer.openTooltip();
       
  7686  * layer.closeTooltip();
       
  7687  * ```
       
  7688  */
       
  7689 
       
  7690 // @section Tooltip methods
       
  7691 L.Layer.include({
       
  7692 
       
  7693 	// @method bindTooltip(content: String|HTMLElement|Function|Tooltip, options?: Tooltip options): this
       
  7694 	// Binds a tooltip to the layer with the passed `content` and sets up the
       
  7695 	// neccessary event listeners. If a `Function` is passed it will receive
       
  7696 	// the layer as the first argument and should return a `String` or `HTMLElement`.
       
  7697 	bindTooltip: function (content, options) {
       
  7698 
       
  7699 		if (content instanceof L.Tooltip) {
       
  7700 			L.setOptions(content, options);
       
  7701 			this._tooltip = content;
       
  7702 			content._source = this;
       
  7703 		} else {
       
  7704 			if (!this._tooltip || options) {
       
  7705 				this._tooltip = L.tooltip(options, this);
       
  7706 			}
       
  7707 			this._tooltip.setContent(content);
       
  7708 
       
  7709 		}
       
  7710 
       
  7711 		this._initTooltipInteractions();
       
  7712 
       
  7713 		if (this._tooltip.options.permanent && this._map && this._map.hasLayer(this)) {
       
  7714 			this.openTooltip();
       
  7715 		}
       
  7716 
       
  7717 		return this;
       
  7718 	},
       
  7719 
       
  7720 	// @method unbindTooltip(): this
       
  7721 	// Removes the tooltip previously bound with `bindTooltip`.
       
  7722 	unbindTooltip: function () {
       
  7723 		if (this._tooltip) {
       
  7724 			this._initTooltipInteractions(true);
       
  7725 			this.closeTooltip();
       
  7726 			this._tooltip = null;
       
  7727 		}
       
  7728 		return this;
       
  7729 	},
       
  7730 
       
  7731 	_initTooltipInteractions: function (remove) {
       
  7732 		if (!remove && this._tooltipHandlersAdded) { return; }
       
  7733 		var onOff = remove ? 'off' : 'on',
       
  7734 		    events = {
       
  7735 			remove: this.closeTooltip,
       
  7736 			move: this._moveTooltip
       
  7737 		    };
       
  7738 		if (!this._tooltip.options.permanent) {
       
  7739 			events.mouseover = this._openTooltip;
       
  7740 			events.mouseout = this.closeTooltip;
       
  7741 			if (this._tooltip.options.sticky) {
       
  7742 				events.mousemove = this._moveTooltip;
       
  7743 			}
       
  7744 			if (L.Browser.touch) {
       
  7745 				events.click = this._openTooltip;
       
  7746 			}
       
  7747 		} else {
       
  7748 			events.add = this._openTooltip;
       
  7749 		}
       
  7750 		this[onOff](events);
       
  7751 		this._tooltipHandlersAdded = !remove;
       
  7752 	},
       
  7753 
       
  7754 	// @method openTooltip(latlng?: LatLng): this
       
  7755 	// Opens the bound tooltip at the specificed `latlng` or at the default tooltip anchor if no `latlng` is passed.
       
  7756 	openTooltip: function (layer, latlng) {
       
  7757 		if (!(layer instanceof L.Layer)) {
       
  7758 			latlng = layer;
       
  7759 			layer = this;
       
  7760 		}
       
  7761 
       
  7762 		if (layer instanceof L.FeatureGroup) {
       
  7763 			for (var id in this._layers) {
       
  7764 				layer = this._layers[id];
       
  7765 				break;
       
  7766 			}
       
  7767 		}
       
  7768 
       
  7769 		if (!latlng) {
       
  7770 			latlng = layer.getCenter ? layer.getCenter() : layer.getLatLng();
       
  7771 		}
       
  7772 
       
  7773 		if (this._tooltip && this._map) {
       
  7774 
       
  7775 			// set tooltip source to this layer
       
  7776 			this._tooltip._source = layer;
       
  7777 
       
  7778 			// update the tooltip (content, layout, ect...)
       
  7779 			this._tooltip.update();
       
  7780 
       
  7781 			// open the tooltip on the map
       
  7782 			this._map.openTooltip(this._tooltip, latlng);
       
  7783 
       
  7784 			// Tooltip container may not be defined if not permanent and never
       
  7785 			// opened.
       
  7786 			if (this._tooltip.options.interactive && this._tooltip._container) {
       
  7787 				L.DomUtil.addClass(this._tooltip._container, 'leaflet-clickable');
       
  7788 				this.addInteractiveTarget(this._tooltip._container);
       
  7789 			}
       
  7790 		}
       
  7791 
       
  7792 		return this;
       
  7793 	},
       
  7794 
       
  7795 	// @method closeTooltip(): this
       
  7796 	// Closes the tooltip bound to this layer if it is open.
       
  7797 	closeTooltip: function () {
       
  7798 		if (this._tooltip) {
       
  7799 			this._tooltip._close();
       
  7800 			if (this._tooltip.options.interactive && this._tooltip._container) {
       
  7801 				L.DomUtil.removeClass(this._tooltip._container, 'leaflet-clickable');
       
  7802 				this.removeInteractiveTarget(this._tooltip._container);
       
  7803 			}
       
  7804 		}
       
  7805 		return this;
       
  7806 	},
       
  7807 
       
  7808 	// @method toggleTooltip(): this
       
  7809 	// Opens or closes the tooltip bound to this layer depending on its current state.
       
  7810 	toggleTooltip: function (target) {
       
  7811 		if (this._tooltip) {
       
  7812 			if (this._tooltip._map) {
       
  7813 				this.closeTooltip();
       
  7814 			} else {
       
  7815 				this.openTooltip(target);
       
  7816 			}
       
  7817 		}
       
  7818 		return this;
       
  7819 	},
       
  7820 
       
  7821 	// @method isTooltipOpen(): boolean
       
  7822 	// Returns `true` if the tooltip bound to this layer is currently open.
       
  7823 	isTooltipOpen: function () {
       
  7824 		return this._tooltip.isOpen();
       
  7825 	},
       
  7826 
       
  7827 	// @method setTooltipContent(content: String|HTMLElement|Tooltip): this
       
  7828 	// Sets the content of the tooltip bound to this layer.
       
  7829 	setTooltipContent: function (content) {
       
  7830 		if (this._tooltip) {
       
  7831 			this._tooltip.setContent(content);
       
  7832 		}
       
  7833 		return this;
       
  7834 	},
       
  7835 
       
  7836 	// @method getTooltip(): Tooltip
       
  7837 	// Returns the tooltip bound to this layer.
       
  7838 	getTooltip: function () {
       
  7839 		return this._tooltip;
       
  7840 	},
       
  7841 
       
  7842 	_openTooltip: function (e) {
       
  7843 		var layer = e.layer || e.target;
       
  7844 
       
  7845 		if (!this._tooltip || !this._map) {
       
  7846 			return;
       
  7847 		}
       
  7848 		this.openTooltip(layer, this._tooltip.options.sticky ? e.latlng : undefined);
       
  7849 	},
       
  7850 
       
  7851 	_moveTooltip: function (e) {
       
  7852 		var latlng = e.latlng, containerPoint, layerPoint;
       
  7853 		if (this._tooltip.options.sticky && e.originalEvent) {
       
  7854 			containerPoint = this._map.mouseEventToContainerPoint(e.originalEvent);
       
  7855 			layerPoint = this._map.containerPointToLayerPoint(containerPoint);
       
  7856 			latlng = this._map.layerPointToLatLng(layerPoint);
       
  7857 		}
       
  7858 		this._tooltip.setLatLng(latlng);
       
  7859 	}
       
  7860 });
       
  7861 
       
  7862 
       
  7863 
       
  7864 /*
       
  7865  * @class LayerGroup
       
  7866  * @aka L.LayerGroup
       
  7867  * @inherits Layer
       
  7868  *
       
  7869  * Used to group several layers and handle them as one. If you add it to the map,
       
  7870  * any layers added or removed from the group will be added/removed on the map as
       
  7871  * well. Extends `Layer`.
       
  7872  *
       
  7873  * @example
       
  7874  *
       
  7875  * ```js
       
  7876  * L.layerGroup([marker1, marker2])
       
  7877  * 	.addLayer(polyline)
       
  7878  * 	.addTo(map);
       
  7879  * ```
       
  7880  */
       
  7881 
       
  7882 L.LayerGroup = L.Layer.extend({
       
  7883 
       
  7884 	initialize: function (layers) {
       
  7885 		this._layers = {};
       
  7886 
       
  7887 		var i, len;
       
  7888 
       
  7889 		if (layers) {
       
  7890 			for (i = 0, len = layers.length; i < len; i++) {
       
  7891 				this.addLayer(layers[i]);
       
  7892 			}
       
  7893 		}
       
  7894 	},
       
  7895 
       
  7896 	// @method addLayer(layer: Layer): this
       
  7897 	// Adds the given layer to the group.
       
  7898 	addLayer: function (layer) {
       
  7899 		var id = this.getLayerId(layer);
       
  7900 
       
  7901 		this._layers[id] = layer;
       
  7902 
       
  7903 		if (this._map) {
       
  7904 			this._map.addLayer(layer);
       
  7905 		}
       
  7906 
       
  7907 		return this;
       
  7908 	},
       
  7909 
       
  7910 	// @method removeLayer(layer: Layer): this
       
  7911 	// Removes the given layer from the group.
       
  7912 	// @alternative
       
  7913 	// @method removeLayer(id: Number): this
       
  7914 	// Removes the layer with the given internal ID from the group.
       
  7915 	removeLayer: function (layer) {
       
  7916 		var id = layer in this._layers ? layer : this.getLayerId(layer);
       
  7917 
       
  7918 		if (this._map && this._layers[id]) {
       
  7919 			this._map.removeLayer(this._layers[id]);
       
  7920 		}
       
  7921 
       
  7922 		delete this._layers[id];
       
  7923 
       
  7924 		return this;
       
  7925 	},
       
  7926 
       
  7927 	// @method hasLayer(layer: Layer): Boolean
       
  7928 	// Returns `true` if the given layer is currently added to the group.
       
  7929 	hasLayer: function (layer) {
       
  7930 		return !!layer && (layer in this._layers || this.getLayerId(layer) in this._layers);
       
  7931 	},
       
  7932 
       
  7933 	// @method clearLayers(): this
       
  7934 	// Removes all the layers from the group.
       
  7935 	clearLayers: function () {
       
  7936 		for (var i in this._layers) {
       
  7937 			this.removeLayer(this._layers[i]);
       
  7938 		}
       
  7939 		return this;
       
  7940 	},
       
  7941 
       
  7942 	// @method invoke(methodName: String, …): this
       
  7943 	// Calls `methodName` on every layer contained in this group, passing any
       
  7944 	// additional parameters. Has no effect if the layers contained do not
       
  7945 	// implement `methodName`.
       
  7946 	invoke: function (methodName) {
       
  7947 		var args = Array.prototype.slice.call(arguments, 1),
       
  7948 		    i, layer;
       
  7949 
       
  7950 		for (i in this._layers) {
       
  7951 			layer = this._layers[i];
       
  7952 
       
  7953 			if (layer[methodName]) {
       
  7954 				layer[methodName].apply(layer, args);
       
  7955 			}
       
  7956 		}
       
  7957 
       
  7958 		return this;
       
  7959 	},
       
  7960 
       
  7961 	onAdd: function (map) {
       
  7962 		for (var i in this._layers) {
       
  7963 			map.addLayer(this._layers[i]);
       
  7964 		}
       
  7965 	},
       
  7966 
       
  7967 	onRemove: function (map) {
       
  7968 		for (var i in this._layers) {
       
  7969 			map.removeLayer(this._layers[i]);
       
  7970 		}
       
  7971 	},
       
  7972 
       
  7973 	// @method eachLayer(fn: Function, context?: Object): this
       
  7974 	// Iterates over the layers of the group, optionally specifying context of the iterator function.
       
  7975 	// ```js
       
  7976 	// group.eachLayer(function (layer) {
       
  7977 	// 	layer.bindPopup('Hello');
       
  7978 	// });
       
  7979 	// ```
       
  7980 	eachLayer: function (method, context) {
       
  7981 		for (var i in this._layers) {
       
  7982 			method.call(context, this._layers[i]);
       
  7983 		}
       
  7984 		return this;
       
  7985 	},
       
  7986 
       
  7987 	// @method getLayer(id: Number): Layer
       
  7988 	// Returns the layer with the given internal ID.
       
  7989 	getLayer: function (id) {
       
  7990 		return this._layers[id];
       
  7991 	},
       
  7992 
       
  7993 	// @method getLayers(): Layer[]
       
  7994 	// Returns an array of all the layers added to the group.
       
  7995 	getLayers: function () {
       
  7996 		var layers = [];
       
  7997 
       
  7998 		for (var i in this._layers) {
       
  7999 			layers.push(this._layers[i]);
       
  8000 		}
       
  8001 		return layers;
       
  8002 	},
       
  8003 
       
  8004 	// @method setZIndex(zIndex: Number): this
       
  8005 	// Calls `setZIndex` on every layer contained in this group, passing the z-index.
       
  8006 	setZIndex: function (zIndex) {
       
  8007 		return this.invoke('setZIndex', zIndex);
       
  8008 	},
       
  8009 
       
  8010 	// @method getLayerId(layer: Layer): Number
       
  8011 	// Returns the internal ID for a layer
       
  8012 	getLayerId: function (layer) {
       
  8013 		return L.stamp(layer);
       
  8014 	}
       
  8015 });
       
  8016 
       
  8017 
       
  8018 // @factory L.layerGroup(layers: Layer[])
       
  8019 // Create a layer group, optionally given an initial set of layers.
       
  8020 L.layerGroup = function (layers) {
       
  8021 	return new L.LayerGroup(layers);
       
  8022 };
       
  8023 
       
  8024 
       
  8025 
       
  8026 /*
       
  8027  * @class FeatureGroup
       
  8028  * @aka L.FeatureGroup
       
  8029  * @inherits LayerGroup
       
  8030  *
       
  8031  * Extended `LayerGroup` that makes it easier to do the same thing to all its member layers:
       
  8032  *  * [`bindPopup`](#layer-bindpopup) binds a popup to all of the layers at once (likewise with [`bindTooltip`](#layer-bindtooltip))
       
  8033  *  * Events are propagated to the `FeatureGroup`, so if the group has an event
       
  8034  * handler, it will handle events from any of the layers. This includes mouse events
       
  8035  * and custom events.
       
  8036  *  * Has `layeradd` and `layerremove` events
       
  8037  *
       
  8038  * @example
       
  8039  *
       
  8040  * ```js
       
  8041  * L.featureGroup([marker1, marker2, polyline])
       
  8042  * 	.bindPopup('Hello world!')
       
  8043  * 	.on('click', function() { alert('Clicked on a member of the group!'); })
       
  8044  * 	.addTo(map);
       
  8045  * ```
       
  8046  */
       
  8047 
       
  8048 L.FeatureGroup = L.LayerGroup.extend({
       
  8049 
       
  8050 	addLayer: function (layer) {
       
  8051 		if (this.hasLayer(layer)) {
       
  8052 			return this;
       
  8053 		}
       
  8054 
       
  8055 		layer.addEventParent(this);
       
  8056 
       
  8057 		L.LayerGroup.prototype.addLayer.call(this, layer);
       
  8058 
       
  8059 		// @event layeradd: LayerEvent
       
  8060 		// Fired when a layer is added to this `FeatureGroup`
       
  8061 		return this.fire('layeradd', {layer: layer});
       
  8062 	},
       
  8063 
       
  8064 	removeLayer: function (layer) {
       
  8065 		if (!this.hasLayer(layer)) {
       
  8066 			return this;
       
  8067 		}
       
  8068 		if (layer in this._layers) {
       
  8069 			layer = this._layers[layer];
       
  8070 		}
       
  8071 
       
  8072 		layer.removeEventParent(this);
       
  8073 
       
  8074 		L.LayerGroup.prototype.removeLayer.call(this, layer);
       
  8075 
       
  8076 		// @event layerremove: LayerEvent
       
  8077 		// Fired when a layer is removed from this `FeatureGroup`
       
  8078 		return this.fire('layerremove', {layer: layer});
       
  8079 	},
       
  8080 
       
  8081 	// @method setStyle(style: Path options): this
       
  8082 	// Sets the given path options to each layer of the group that has a `setStyle` method.
       
  8083 	setStyle: function (style) {
       
  8084 		return this.invoke('setStyle', style);
       
  8085 	},
       
  8086 
       
  8087 	// @method bringToFront(): this
       
  8088 	// Brings the layer group to the top of all other layers
       
  8089 	bringToFront: function () {
       
  8090 		return this.invoke('bringToFront');
       
  8091 	},
       
  8092 
       
  8093 	// @method bringToBack(): this
       
  8094 	// Brings the layer group to the top of all other layers
       
  8095 	bringToBack: function () {
       
  8096 		return this.invoke('bringToBack');
       
  8097 	},
       
  8098 
       
  8099 	// @method getBounds(): LatLngBounds
       
  8100 	// Returns the LatLngBounds of the Feature Group (created from bounds and coordinates of its children).
       
  8101 	getBounds: function () {
       
  8102 		var bounds = new L.LatLngBounds();
       
  8103 
       
  8104 		for (var id in this._layers) {
       
  8105 			var layer = this._layers[id];
       
  8106 			bounds.extend(layer.getBounds ? layer.getBounds() : layer.getLatLng());
       
  8107 		}
       
  8108 		return bounds;
       
  8109 	}
       
  8110 });
       
  8111 
       
  8112 // @factory L.featureGroup(layers: Layer[])
       
  8113 // Create a feature group, optionally given an initial set of layers.
       
  8114 L.featureGroup = function (layers) {
       
  8115 	return new L.FeatureGroup(layers);
       
  8116 };
       
  8117 
       
  8118 
       
  8119 
       
  8120 /*
       
  8121  * @class Renderer
       
  8122  * @inherits Layer
       
  8123  * @aka L.Renderer
       
  8124  *
       
  8125  * Base class for vector renderer implementations (`SVG`, `Canvas`). Handles the
       
  8126  * DOM container of the renderer, its bounds, and its zoom animation.
       
  8127  *
       
  8128  * A `Renderer` works as an implicit layer group for all `Path`s - the renderer
       
  8129  * itself can be added or removed to the map. All paths use a renderer, which can
       
  8130  * be implicit (the map will decide the type of renderer and use it automatically)
       
  8131  * or explicit (using the [`renderer`](#path-renderer) option of the path).
       
  8132  *
       
  8133  * Do not use this class directly, use `SVG` and `Canvas` instead.
       
  8134  *
       
  8135  * @event update: Event
       
  8136  * Fired when the renderer updates its bounds, center and zoom, for example when
       
  8137  * its map has moved
       
  8138  */
       
  8139 
       
  8140 L.Renderer = L.Layer.extend({
       
  8141 
       
  8142 	// @section
       
  8143 	// @aka Renderer options
       
  8144 	options: {
       
  8145 		// @option padding: Number = 0.1
       
  8146 		// How much to extend the clip area around the map view (relative to its size)
       
  8147 		// e.g. 0.1 would be 10% of map view in each direction
       
  8148 		padding: 0.1
       
  8149 	},
       
  8150 
       
  8151 	initialize: function (options) {
       
  8152 		L.setOptions(this, options);
       
  8153 		L.stamp(this);
       
  8154 		this._layers = this._layers || {};
       
  8155 	},
       
  8156 
       
  8157 	onAdd: function () {
       
  8158 		if (!this._container) {
       
  8159 			this._initContainer(); // defined by renderer implementations
       
  8160 
       
  8161 			if (this._zoomAnimated) {
       
  8162 				L.DomUtil.addClass(this._container, 'leaflet-zoom-animated');
       
  8163 			}
       
  8164 		}
       
  8165 
       
  8166 		this.getPane().appendChild(this._container);
       
  8167 		this._update();
       
  8168 		this.on('update', this._updatePaths, this);
       
  8169 	},
       
  8170 
       
  8171 	onRemove: function () {
       
  8172 		L.DomUtil.remove(this._container);
       
  8173 		this.off('update', this._updatePaths, this);
       
  8174 	},
       
  8175 
       
  8176 	getEvents: function () {
       
  8177 		var events = {
       
  8178 			viewreset: this._reset,
       
  8179 			zoom: this._onZoom,
       
  8180 			moveend: this._update,
       
  8181 			zoomend: this._onZoomEnd
       
  8182 		};
       
  8183 		if (this._zoomAnimated) {
       
  8184 			events.zoomanim = this._onAnimZoom;
       
  8185 		}
       
  8186 		return events;
       
  8187 	},
       
  8188 
       
  8189 	_onAnimZoom: function (ev) {
       
  8190 		this._updateTransform(ev.center, ev.zoom);
       
  8191 	},
       
  8192 
       
  8193 	_onZoom: function () {
       
  8194 		this._updateTransform(this._map.getCenter(), this._map.getZoom());
       
  8195 	},
       
  8196 
       
  8197 	_updateTransform: function (center, zoom) {
       
  8198 		var scale = this._map.getZoomScale(zoom, this._zoom),
       
  8199 		    position = L.DomUtil.getPosition(this._container),
       
  8200 		    viewHalf = this._map.getSize().multiplyBy(0.5 + this.options.padding),
       
  8201 		    currentCenterPoint = this._map.project(this._center, zoom),
       
  8202 		    destCenterPoint = this._map.project(center, zoom),
       
  8203 		    centerOffset = destCenterPoint.subtract(currentCenterPoint),
       
  8204 
       
  8205 		    topLeftOffset = viewHalf.multiplyBy(-scale).add(position).add(viewHalf).subtract(centerOffset);
       
  8206 
       
  8207 		if (L.Browser.any3d) {
       
  8208 			L.DomUtil.setTransform(this._container, topLeftOffset, scale);
       
  8209 		} else {
       
  8210 			L.DomUtil.setPosition(this._container, topLeftOffset);
       
  8211 		}
       
  8212 	},
       
  8213 
       
  8214 	_reset: function () {
       
  8215 		this._update();
       
  8216 		this._updateTransform(this._center, this._zoom);
       
  8217 
       
  8218 		for (var id in this._layers) {
       
  8219 			this._layers[id]._reset();
       
  8220 		}
       
  8221 	},
       
  8222 
       
  8223 	_onZoomEnd: function () {
       
  8224 		for (var id in this._layers) {
       
  8225 			this._layers[id]._project();
       
  8226 		}
       
  8227 	},
       
  8228 
       
  8229 	_updatePaths: function () {
       
  8230 		for (var id in this._layers) {
       
  8231 			this._layers[id]._update();
       
  8232 		}
       
  8233 	},
       
  8234 
       
  8235 	_update: function () {
       
  8236 		// Update pixel bounds of renderer container (for positioning/sizing/clipping later)
       
  8237 		// Subclasses are responsible of firing the 'update' event.
       
  8238 		var p = this.options.padding,
       
  8239 		    size = this._map.getSize(),
       
  8240 		    min = this._map.containerPointToLayerPoint(size.multiplyBy(-p)).round();
       
  8241 
       
  8242 		this._bounds = new L.Bounds(min, min.add(size.multiplyBy(1 + p * 2)).round());
       
  8243 
       
  8244 		this._center = this._map.getCenter();
       
  8245 		this._zoom = this._map.getZoom();
       
  8246 	}
       
  8247 });
       
  8248 
       
  8249 
       
  8250 L.Map.include({
       
  8251 	// @namespace Map; @method getRenderer(layer: Path): Renderer
       
  8252 	// Returns the instance of `Renderer` that should be used to render the given
       
  8253 	// `Path`. It will ensure that the `renderer` options of the map and paths
       
  8254 	// are respected, and that the renderers do exist on the map.
       
  8255 	getRenderer: function (layer) {
       
  8256 		// @namespace Path; @option renderer: Renderer
       
  8257 		// Use this specific instance of `Renderer` for this path. Takes
       
  8258 		// precedence over the map's [default renderer](#map-renderer).
       
  8259 		var renderer = layer.options.renderer || this._getPaneRenderer(layer.options.pane) || this.options.renderer || this._renderer;
       
  8260 
       
  8261 		if (!renderer) {
       
  8262 			// @namespace Map; @option preferCanvas: Boolean = false
       
  8263 			// Whether `Path`s should be rendered on a `Canvas` renderer.
       
  8264 			// By default, all `Path`s are rendered in a `SVG` renderer.
       
  8265 			renderer = this._renderer = (this.options.preferCanvas && L.canvas()) || L.svg();
       
  8266 		}
       
  8267 
       
  8268 		if (!this.hasLayer(renderer)) {
       
  8269 			this.addLayer(renderer);
       
  8270 		}
       
  8271 		return renderer;
       
  8272 	},
       
  8273 
       
  8274 	_getPaneRenderer: function (name) {
       
  8275 		if (name === 'overlayPane' || name === undefined) {
       
  8276 			return false;
       
  8277 		}
       
  8278 
       
  8279 		var renderer = this._paneRenderers[name];
       
  8280 		if (renderer === undefined) {
       
  8281 			renderer = (L.SVG && L.svg({pane: name})) || (L.Canvas && L.canvas({pane: name}));
       
  8282 			this._paneRenderers[name] = renderer;
       
  8283 		}
       
  8284 		return renderer;
       
  8285 	}
       
  8286 });
       
  8287 
       
  8288 
       
  8289 
       
  8290 /*
       
  8291  * @class Path
       
  8292  * @aka L.Path
       
  8293  * @inherits Interactive layer
       
  8294  *
       
  8295  * An abstract class that contains options and constants shared between vector
       
  8296  * overlays (Polygon, Polyline, Circle). Do not use it directly. Extends `Layer`.
       
  8297  */
       
  8298 
       
  8299 L.Path = L.Layer.extend({
       
  8300 
       
  8301 	// @section
       
  8302 	// @aka Path options
       
  8303 	options: {
       
  8304 		// @option stroke: Boolean = true
       
  8305 		// Whether to draw stroke along the path. Set it to `false` to disable borders on polygons or circles.
       
  8306 		stroke: true,
       
  8307 
       
  8308 		// @option color: String = '#3388ff'
       
  8309 		// Stroke color
       
  8310 		color: '#3388ff',
       
  8311 
       
  8312 		// @option weight: Number = 3
       
  8313 		// Stroke width in pixels
       
  8314 		weight: 3,
       
  8315 
       
  8316 		// @option opacity: Number = 1.0
       
  8317 		// Stroke opacity
       
  8318 		opacity: 1,
       
  8319 
       
  8320 		// @option lineCap: String= 'round'
       
  8321 		// A string that defines [shape to be used at the end](https://developer.mozilla.org/docs/Web/SVG/Attribute/stroke-linecap) of the stroke.
       
  8322 		lineCap: 'round',
       
  8323 
       
  8324 		// @option lineJoin: String = 'round'
       
  8325 		// A string that defines [shape to be used at the corners](https://developer.mozilla.org/docs/Web/SVG/Attribute/stroke-linejoin) of the stroke.
       
  8326 		lineJoin: 'round',
       
  8327 
       
  8328 		// @option dashArray: String = null
       
  8329 		// A string that defines the stroke [dash pattern](https://developer.mozilla.org/docs/Web/SVG/Attribute/stroke-dasharray). Doesn't work on `Canvas`-powered layers in [some old browsers](https://developer.mozilla.org/docs/Web/API/CanvasRenderingContext2D/setLineDash#Browser_compatibility).
       
  8330 		dashArray: null,
       
  8331 
       
  8332 		// @option dashOffset: String = null
       
  8333 		// A string that defines the [distance into the dash pattern to start the dash](https://developer.mozilla.org/docs/Web/SVG/Attribute/stroke-dashoffset). Doesn't work on `Canvas`-powered layers in [some old browsers](https://developer.mozilla.org/docs/Web/API/CanvasRenderingContext2D/setLineDash#Browser_compatibility).
       
  8334 		dashOffset: null,
       
  8335 
       
  8336 		// @option fill: Boolean = depends
       
  8337 		// Whether to fill the path with color. Set it to `false` to disable filling on polygons or circles.
       
  8338 		fill: false,
       
  8339 
       
  8340 		// @option fillColor: String = *
       
  8341 		// Fill color. Defaults to the value of the [`color`](#path-color) option
       
  8342 		fillColor: null,
       
  8343 
       
  8344 		// @option fillOpacity: Number = 0.2
       
  8345 		// Fill opacity.
       
  8346 		fillOpacity: 0.2,
       
  8347 
       
  8348 		// @option fillRule: String = 'evenodd'
       
  8349 		// A string that defines [how the inside of a shape](https://developer.mozilla.org/docs/Web/SVG/Attribute/fill-rule) is determined.
       
  8350 		fillRule: 'evenodd',
       
  8351 
       
  8352 		// className: '',
       
  8353 
       
  8354 		// Option inherited from "Interactive layer" abstract class
       
  8355 		interactive: true
       
  8356 	},
       
  8357 
       
  8358 	beforeAdd: function (map) {
       
  8359 		// Renderer is set here because we need to call renderer.getEvents
       
  8360 		// before this.getEvents.
       
  8361 		this._renderer = map.getRenderer(this);
       
  8362 	},
       
  8363 
       
  8364 	onAdd: function () {
       
  8365 		this._renderer._initPath(this);
       
  8366 		this._reset();
       
  8367 		this._renderer._addPath(this);
       
  8368 	},
       
  8369 
       
  8370 	onRemove: function () {
       
  8371 		this._renderer._removePath(this);
       
  8372 	},
       
  8373 
       
  8374 	// @method redraw(): this
       
  8375 	// Redraws the layer. Sometimes useful after you changed the coordinates that the path uses.
       
  8376 	redraw: function () {
       
  8377 		if (this._map) {
       
  8378 			this._renderer._updatePath(this);
       
  8379 		}
       
  8380 		return this;
       
  8381 	},
       
  8382 
       
  8383 	// @method setStyle(style: Path options): this
       
  8384 	// Changes the appearance of a Path based on the options in the `Path options` object.
       
  8385 	setStyle: function (style) {
       
  8386 		L.setOptions(this, style);
       
  8387 		if (this._renderer) {
       
  8388 			this._renderer._updateStyle(this);
       
  8389 		}
       
  8390 		return this;
       
  8391 	},
       
  8392 
       
  8393 	// @method bringToFront(): this
       
  8394 	// Brings the layer to the top of all path layers.
       
  8395 	bringToFront: function () {
       
  8396 		if (this._renderer) {
       
  8397 			this._renderer._bringToFront(this);
       
  8398 		}
       
  8399 		return this;
       
  8400 	},
       
  8401 
       
  8402 	// @method bringToBack(): this
       
  8403 	// Brings the layer to the bottom of all path layers.
       
  8404 	bringToBack: function () {
       
  8405 		if (this._renderer) {
       
  8406 			this._renderer._bringToBack(this);
       
  8407 		}
       
  8408 		return this;
       
  8409 	},
       
  8410 
       
  8411 	getElement: function () {
       
  8412 		return this._path;
       
  8413 	},
       
  8414 
       
  8415 	_reset: function () {
       
  8416 		// defined in children classes
       
  8417 		this._project();
       
  8418 		this._update();
       
  8419 	},
       
  8420 
       
  8421 	_clickTolerance: function () {
       
  8422 		// used when doing hit detection for Canvas layers
       
  8423 		return (this.options.stroke ? this.options.weight / 2 : 0) + (L.Browser.touch ? 10 : 0);
       
  8424 	}
       
  8425 });
       
  8426 
       
  8427 
       
  8428 
       
  8429 /*
       
  8430  * @namespace LineUtil
       
  8431  *
       
  8432  * Various utility functions for polyine points processing, used by Leaflet internally to make polylines lightning-fast.
       
  8433  */
       
  8434 
       
  8435 L.LineUtil = {
       
  8436 
       
  8437 	// Simplify polyline with vertex reduction and Douglas-Peucker simplification.
       
  8438 	// Improves rendering performance dramatically by lessening the number of points to draw.
       
  8439 
       
  8440 	// @function simplify(points: Point[], tolerance: Number): Point[]
       
  8441 	// Dramatically reduces the number of points in a polyline while retaining
       
  8442 	// its shape and returns a new array of simplified points, using the
       
  8443 	// [Douglas-Peucker algorithm](http://en.wikipedia.org/wiki/Douglas-Peucker_algorithm).
       
  8444 	// Used for a huge performance boost when processing/displaying Leaflet polylines for
       
  8445 	// each zoom level and also reducing visual noise. tolerance affects the amount of
       
  8446 	// simplification (lesser value means higher quality but slower and with more points).
       
  8447 	// Also released as a separated micro-library [Simplify.js](http://mourner.github.com/simplify-js/).
       
  8448 	simplify: function (points, tolerance) {
       
  8449 		if (!tolerance || !points.length) {
       
  8450 			return points.slice();
       
  8451 		}
       
  8452 
       
  8453 		var sqTolerance = tolerance * tolerance;
       
  8454 
       
  8455 		// stage 1: vertex reduction
       
  8456 		points = this._reducePoints(points, sqTolerance);
       
  8457 
       
  8458 		// stage 2: Douglas-Peucker simplification
       
  8459 		points = this._simplifyDP(points, sqTolerance);
       
  8460 
       
  8461 		return points;
       
  8462 	},
       
  8463 
       
  8464 	// @function pointToSegmentDistance(p: Point, p1: Point, p2: Point): Number
       
  8465 	// Returns the distance between point `p` and segment `p1` to `p2`.
       
  8466 	pointToSegmentDistance:  function (p, p1, p2) {
       
  8467 		return Math.sqrt(this._sqClosestPointOnSegment(p, p1, p2, true));
       
  8468 	},
       
  8469 
       
  8470 	// @function closestPointOnSegment(p: Point, p1: Point, p2: Point): Number
       
  8471 	// Returns the closest point from a point `p` on a segment `p1` to `p2`.
       
  8472 	closestPointOnSegment: function (p, p1, p2) {
       
  8473 		return this._sqClosestPointOnSegment(p, p1, p2);
       
  8474 	},
       
  8475 
       
  8476 	// Douglas-Peucker simplification, see http://en.wikipedia.org/wiki/Douglas-Peucker_algorithm
       
  8477 	_simplifyDP: function (points, sqTolerance) {
       
  8478 
       
  8479 		var len = points.length,
       
  8480 		    ArrayConstructor = typeof Uint8Array !== undefined + '' ? Uint8Array : Array,
       
  8481 		    markers = new ArrayConstructor(len);
       
  8482 
       
  8483 		markers[0] = markers[len - 1] = 1;
       
  8484 
       
  8485 		this._simplifyDPStep(points, markers, sqTolerance, 0, len - 1);
       
  8486 
       
  8487 		var i,
       
  8488 		    newPoints = [];
       
  8489 
       
  8490 		for (i = 0; i < len; i++) {
       
  8491 			if (markers[i]) {
       
  8492 				newPoints.push(points[i]);
       
  8493 			}
       
  8494 		}
       
  8495 
       
  8496 		return newPoints;
       
  8497 	},
       
  8498 
       
  8499 	_simplifyDPStep: function (points, markers, sqTolerance, first, last) {
       
  8500 
       
  8501 		var maxSqDist = 0,
       
  8502 		    index, i, sqDist;
       
  8503 
       
  8504 		for (i = first + 1; i <= last - 1; i++) {
       
  8505 			sqDist = this._sqClosestPointOnSegment(points[i], points[first], points[last], true);
       
  8506 
       
  8507 			if (sqDist > maxSqDist) {
       
  8508 				index = i;
       
  8509 				maxSqDist = sqDist;
       
  8510 			}
       
  8511 		}
       
  8512 
       
  8513 		if (maxSqDist > sqTolerance) {
       
  8514 			markers[index] = 1;
       
  8515 
       
  8516 			this._simplifyDPStep(points, markers, sqTolerance, first, index);
       
  8517 			this._simplifyDPStep(points, markers, sqTolerance, index, last);
       
  8518 		}
       
  8519 	},
       
  8520 
       
  8521 	// reduce points that are too close to each other to a single point
       
  8522 	_reducePoints: function (points, sqTolerance) {
       
  8523 		var reducedPoints = [points[0]];
       
  8524 
       
  8525 		for (var i = 1, prev = 0, len = points.length; i < len; i++) {
       
  8526 			if (this._sqDist(points[i], points[prev]) > sqTolerance) {
       
  8527 				reducedPoints.push(points[i]);
       
  8528 				prev = i;
       
  8529 			}
       
  8530 		}
       
  8531 		if (prev < len - 1) {
       
  8532 			reducedPoints.push(points[len - 1]);
       
  8533 		}
       
  8534 		return reducedPoints;
       
  8535 	},
       
  8536 
       
  8537 
       
  8538 	// @function clipSegment(a: Point, b: Point, bounds: Bounds, useLastCode?: Boolean, round?: Boolean): Point[]|Boolean
       
  8539 	// Clips the segment a to b by rectangular bounds with the
       
  8540 	// [Cohen-Sutherland algorithm](https://en.wikipedia.org/wiki/Cohen%E2%80%93Sutherland_algorithm)
       
  8541 	// (modifying the segment points directly!). Used by Leaflet to only show polyline
       
  8542 	// points that are on the screen or near, increasing performance.
       
  8543 	clipSegment: function (a, b, bounds, useLastCode, round) {
       
  8544 		var codeA = useLastCode ? this._lastCode : this._getBitCode(a, bounds),
       
  8545 		    codeB = this._getBitCode(b, bounds),
       
  8546 
       
  8547 		    codeOut, p, newCode;
       
  8548 
       
  8549 		// save 2nd code to avoid calculating it on the next segment
       
  8550 		this._lastCode = codeB;
       
  8551 
       
  8552 		while (true) {
       
  8553 			// if a,b is inside the clip window (trivial accept)
       
  8554 			if (!(codeA | codeB)) {
       
  8555 				return [a, b];
       
  8556 			}
       
  8557 
       
  8558 			// if a,b is outside the clip window (trivial reject)
       
  8559 			if (codeA & codeB) {
       
  8560 				return false;
       
  8561 			}
       
  8562 
       
  8563 			// other cases
       
  8564 			codeOut = codeA || codeB;
       
  8565 			p = this._getEdgeIntersection(a, b, codeOut, bounds, round);
       
  8566 			newCode = this._getBitCode(p, bounds);
       
  8567 
       
  8568 			if (codeOut === codeA) {
       
  8569 				a = p;
       
  8570 				codeA = newCode;
       
  8571 			} else {
       
  8572 				b = p;
       
  8573 				codeB = newCode;
       
  8574 			}
       
  8575 		}
       
  8576 	},
       
  8577 
       
  8578 	_getEdgeIntersection: function (a, b, code, bounds, round) {
       
  8579 		var dx = b.x - a.x,
       
  8580 		    dy = b.y - a.y,
       
  8581 		    min = bounds.min,
       
  8582 		    max = bounds.max,
       
  8583 		    x, y;
       
  8584 
       
  8585 		if (code & 8) { // top
       
  8586 			x = a.x + dx * (max.y - a.y) / dy;
       
  8587 			y = max.y;
       
  8588 
       
  8589 		} else if (code & 4) { // bottom
       
  8590 			x = a.x + dx * (min.y - a.y) / dy;
       
  8591 			y = min.y;
       
  8592 
       
  8593 		} else if (code & 2) { // right
       
  8594 			x = max.x;
       
  8595 			y = a.y + dy * (max.x - a.x) / dx;
       
  8596 
       
  8597 		} else if (code & 1) { // left
       
  8598 			x = min.x;
       
  8599 			y = a.y + dy * (min.x - a.x) / dx;
       
  8600 		}
       
  8601 
       
  8602 		return new L.Point(x, y, round);
       
  8603 	},
       
  8604 
       
  8605 	_getBitCode: function (p, bounds) {
       
  8606 		var code = 0;
       
  8607 
       
  8608 		if (p.x < bounds.min.x) { // left
       
  8609 			code |= 1;
       
  8610 		} else if (p.x > bounds.max.x) { // right
       
  8611 			code |= 2;
       
  8612 		}
       
  8613 
       
  8614 		if (p.y < bounds.min.y) { // bottom
       
  8615 			code |= 4;
       
  8616 		} else if (p.y > bounds.max.y) { // top
       
  8617 			code |= 8;
       
  8618 		}
       
  8619 
       
  8620 		return code;
       
  8621 	},
       
  8622 
       
  8623 	// square distance (to avoid unnecessary Math.sqrt calls)
       
  8624 	_sqDist: function (p1, p2) {
       
  8625 		var dx = p2.x - p1.x,
       
  8626 		    dy = p2.y - p1.y;
       
  8627 		return dx * dx + dy * dy;
       
  8628 	},
       
  8629 
       
  8630 	// return closest point on segment or distance to that point
       
  8631 	_sqClosestPointOnSegment: function (p, p1, p2, sqDist) {
       
  8632 		var x = p1.x,
       
  8633 		    y = p1.y,
       
  8634 		    dx = p2.x - x,
       
  8635 		    dy = p2.y - y,
       
  8636 		    dot = dx * dx + dy * dy,
       
  8637 		    t;
       
  8638 
       
  8639 		if (dot > 0) {
       
  8640 			t = ((p.x - x) * dx + (p.y - y) * dy) / dot;
       
  8641 
       
  8642 			if (t > 1) {
       
  8643 				x = p2.x;
       
  8644 				y = p2.y;
       
  8645 			} else if (t > 0) {
       
  8646 				x += dx * t;
       
  8647 				y += dy * t;
       
  8648 			}
       
  8649 		}
       
  8650 
       
  8651 		dx = p.x - x;
       
  8652 		dy = p.y - y;
       
  8653 
       
  8654 		return sqDist ? dx * dx + dy * dy : new L.Point(x, y);
       
  8655 	}
       
  8656 };
       
  8657 
       
  8658 
       
  8659 
       
  8660 /*
       
  8661  * @class Polyline
       
  8662  * @aka L.Polyline
       
  8663  * @inherits Path
       
  8664  *
       
  8665  * A class for drawing polyline overlays on a map. Extends `Path`.
       
  8666  *
       
  8667  * @example
       
  8668  *
       
  8669  * ```js
       
  8670  * // create a red polyline from an array of LatLng points
       
  8671  * var latlngs = [
       
  8672  * 	[45.51, -122.68],
       
  8673  * 	[37.77, -122.43],
       
  8674  * 	[34.04, -118.2]
       
  8675  * ];
       
  8676  *
       
  8677  * var polyline = L.polyline(latlngs, {color: 'red'}).addTo(map);
       
  8678  *
       
  8679  * // zoom the map to the polyline
       
  8680  * map.fitBounds(polyline.getBounds());
       
  8681  * ```
       
  8682  *
       
  8683  * You can also pass a multi-dimensional array to represent a `MultiPolyline` shape:
       
  8684  *
       
  8685  * ```js
       
  8686  * // create a red polyline from an array of arrays of LatLng points
       
  8687  * var latlngs = [
       
  8688  * 	[[45.51, -122.68],
       
  8689  * 	 [37.77, -122.43],
       
  8690  * 	 [34.04, -118.2]],
       
  8691  * 	[[40.78, -73.91],
       
  8692  * 	 [41.83, -87.62],
       
  8693  * 	 [32.76, -96.72]]
       
  8694  * ];
       
  8695  * ```
       
  8696  */
       
  8697 
       
  8698 L.Polyline = L.Path.extend({
       
  8699 
       
  8700 	// @section
       
  8701 	// @aka Polyline options
       
  8702 	options: {
       
  8703 		// @option smoothFactor: Number = 1.0
       
  8704 		// How much to simplify the polyline on each zoom level. More means
       
  8705 		// better performance and smoother look, and less means more accurate representation.
       
  8706 		smoothFactor: 1.0,
       
  8707 
       
  8708 		// @option noClip: Boolean = false
       
  8709 		// Disable polyline clipping.
       
  8710 		noClip: false
       
  8711 	},
       
  8712 
       
  8713 	initialize: function (latlngs, options) {
       
  8714 		L.setOptions(this, options);
       
  8715 		this._setLatLngs(latlngs);
       
  8716 	},
       
  8717 
       
  8718 	// @method getLatLngs(): LatLng[]
       
  8719 	// Returns an array of the points in the path, or nested arrays of points in case of multi-polyline.
       
  8720 	getLatLngs: function () {
       
  8721 		return this._latlngs;
       
  8722 	},
       
  8723 
       
  8724 	// @method setLatLngs(latlngs: LatLng[]): this
       
  8725 	// Replaces all the points in the polyline with the given array of geographical points.
       
  8726 	setLatLngs: function (latlngs) {
       
  8727 		this._setLatLngs(latlngs);
       
  8728 		return this.redraw();
       
  8729 	},
       
  8730 
       
  8731 	// @method isEmpty(): Boolean
       
  8732 	// Returns `true` if the Polyline has no LatLngs.
       
  8733 	isEmpty: function () {
       
  8734 		return !this._latlngs.length;
       
  8735 	},
       
  8736 
       
  8737 	closestLayerPoint: function (p) {
       
  8738 		var minDistance = Infinity,
       
  8739 		    minPoint = null,
       
  8740 		    closest = L.LineUtil._sqClosestPointOnSegment,
       
  8741 		    p1, p2;
       
  8742 
       
  8743 		for (var j = 0, jLen = this._parts.length; j < jLen; j++) {
       
  8744 			var points = this._parts[j];
       
  8745 
       
  8746 			for (var i = 1, len = points.length; i < len; i++) {
       
  8747 				p1 = points[i - 1];
       
  8748 				p2 = points[i];
       
  8749 
       
  8750 				var sqDist = closest(p, p1, p2, true);
       
  8751 
       
  8752 				if (sqDist < minDistance) {
       
  8753 					minDistance = sqDist;
       
  8754 					minPoint = closest(p, p1, p2);
       
  8755 				}
       
  8756 			}
       
  8757 		}
       
  8758 		if (minPoint) {
       
  8759 			minPoint.distance = Math.sqrt(minDistance);
       
  8760 		}
       
  8761 		return minPoint;
       
  8762 	},
       
  8763 
       
  8764 	// @method getCenter(): LatLng
       
  8765 	// Returns the center ([centroid](http://en.wikipedia.org/wiki/Centroid)) of the polyline.
       
  8766 	getCenter: function () {
       
  8767 		// throws error when not yet added to map as this center calculation requires projected coordinates
       
  8768 		if (!this._map) {
       
  8769 			throw new Error('Must add layer to map before using getCenter()');
       
  8770 		}
       
  8771 
       
  8772 		var i, halfDist, segDist, dist, p1, p2, ratio,
       
  8773 		    points = this._rings[0],
       
  8774 		    len = points.length;
       
  8775 
       
  8776 		if (!len) { return null; }
       
  8777 
       
  8778 		// polyline centroid algorithm; only uses the first ring if there are multiple
       
  8779 
       
  8780 		for (i = 0, halfDist = 0; i < len - 1; i++) {
       
  8781 			halfDist += points[i].distanceTo(points[i + 1]) / 2;
       
  8782 		}
       
  8783 
       
  8784 		// The line is so small in the current view that all points are on the same pixel.
       
  8785 		if (halfDist === 0) {
       
  8786 			return this._map.layerPointToLatLng(points[0]);
       
  8787 		}
       
  8788 
       
  8789 		for (i = 0, dist = 0; i < len - 1; i++) {
       
  8790 			p1 = points[i];
       
  8791 			p2 = points[i + 1];
       
  8792 			segDist = p1.distanceTo(p2);
       
  8793 			dist += segDist;
       
  8794 
       
  8795 			if (dist > halfDist) {
       
  8796 				ratio = (dist - halfDist) / segDist;
       
  8797 				return this._map.layerPointToLatLng([
       
  8798 					p2.x - ratio * (p2.x - p1.x),
       
  8799 					p2.y - ratio * (p2.y - p1.y)
       
  8800 				]);
       
  8801 			}
       
  8802 		}
       
  8803 	},
       
  8804 
       
  8805 	// @method getBounds(): LatLngBounds
       
  8806 	// Returns the `LatLngBounds` of the path.
       
  8807 	getBounds: function () {
       
  8808 		return this._bounds;
       
  8809 	},
       
  8810 
       
  8811 	// @method addLatLng(latlng: LatLng, latlngs? LatLng[]): this
       
  8812 	// Adds a given point to the polyline. By default, adds to the first ring of
       
  8813 	// the polyline in case of a multi-polyline, but can be overridden by passing
       
  8814 	// a specific ring as a LatLng array (that you can earlier access with [`getLatLngs`](#polyline-getlatlngs)).
       
  8815 	addLatLng: function (latlng, latlngs) {
       
  8816 		latlngs = latlngs || this._defaultShape();
       
  8817 		latlng = L.latLng(latlng);
       
  8818 		latlngs.push(latlng);
       
  8819 		this._bounds.extend(latlng);
       
  8820 		return this.redraw();
       
  8821 	},
       
  8822 
       
  8823 	_setLatLngs: function (latlngs) {
       
  8824 		this._bounds = new L.LatLngBounds();
       
  8825 		this._latlngs = this._convertLatLngs(latlngs);
       
  8826 	},
       
  8827 
       
  8828 	_defaultShape: function () {
       
  8829 		return L.Polyline._flat(this._latlngs) ? this._latlngs : this._latlngs[0];
       
  8830 	},
       
  8831 
       
  8832 	// recursively convert latlngs input into actual LatLng instances; calculate bounds along the way
       
  8833 	_convertLatLngs: function (latlngs) {
       
  8834 		var result = [],
       
  8835 		    flat = L.Polyline._flat(latlngs);
       
  8836 
       
  8837 		for (var i = 0, len = latlngs.length; i < len; i++) {
       
  8838 			if (flat) {
       
  8839 				result[i] = L.latLng(latlngs[i]);
       
  8840 				this._bounds.extend(result[i]);
       
  8841 			} else {
       
  8842 				result[i] = this._convertLatLngs(latlngs[i]);
       
  8843 			}
       
  8844 		}
       
  8845 
       
  8846 		return result;
       
  8847 	},
       
  8848 
       
  8849 	_project: function () {
       
  8850 		var pxBounds = new L.Bounds();
       
  8851 		this._rings = [];
       
  8852 		this._projectLatlngs(this._latlngs, this._rings, pxBounds);
       
  8853 
       
  8854 		var w = this._clickTolerance(),
       
  8855 		    p = new L.Point(w, w);
       
  8856 
       
  8857 		if (this._bounds.isValid() && pxBounds.isValid()) {
       
  8858 			pxBounds.min._subtract(p);
       
  8859 			pxBounds.max._add(p);
       
  8860 			this._pxBounds = pxBounds;
       
  8861 		}
       
  8862 	},
       
  8863 
       
  8864 	// recursively turns latlngs into a set of rings with projected coordinates
       
  8865 	_projectLatlngs: function (latlngs, result, projectedBounds) {
       
  8866 		var flat = latlngs[0] instanceof L.LatLng,
       
  8867 		    len = latlngs.length,
       
  8868 		    i, ring;
       
  8869 
       
  8870 		if (flat) {
       
  8871 			ring = [];
       
  8872 			for (i = 0; i < len; i++) {
       
  8873 				ring[i] = this._map.latLngToLayerPoint(latlngs[i]);
       
  8874 				projectedBounds.extend(ring[i]);
       
  8875 			}
       
  8876 			result.push(ring);
       
  8877 		} else {
       
  8878 			for (i = 0; i < len; i++) {
       
  8879 				this._projectLatlngs(latlngs[i], result, projectedBounds);
       
  8880 			}
       
  8881 		}
       
  8882 	},
       
  8883 
       
  8884 	// clip polyline by renderer bounds so that we have less to render for performance
       
  8885 	_clipPoints: function () {
       
  8886 		var bounds = this._renderer._bounds;
       
  8887 
       
  8888 		this._parts = [];
       
  8889 		if (!this._pxBounds || !this._pxBounds.intersects(bounds)) {
       
  8890 			return;
       
  8891 		}
       
  8892 
       
  8893 		if (this.options.noClip) {
       
  8894 			this._parts = this._rings;
       
  8895 			return;
       
  8896 		}
       
  8897 
       
  8898 		var parts = this._parts,
       
  8899 		    i, j, k, len, len2, segment, points;
       
  8900 
       
  8901 		for (i = 0, k = 0, len = this._rings.length; i < len; i++) {
       
  8902 			points = this._rings[i];
       
  8903 
       
  8904 			for (j = 0, len2 = points.length; j < len2 - 1; j++) {
       
  8905 				segment = L.LineUtil.clipSegment(points[j], points[j + 1], bounds, j, true);
       
  8906 
       
  8907 				if (!segment) { continue; }
       
  8908 
       
  8909 				parts[k] = parts[k] || [];
       
  8910 				parts[k].push(segment[0]);
       
  8911 
       
  8912 				// if segment goes out of screen, or it's the last one, it's the end of the line part
       
  8913 				if ((segment[1] !== points[j + 1]) || (j === len2 - 2)) {
       
  8914 					parts[k].push(segment[1]);
       
  8915 					k++;
       
  8916 				}
       
  8917 			}
       
  8918 		}
       
  8919 	},
       
  8920 
       
  8921 	// simplify each clipped part of the polyline for performance
       
  8922 	_simplifyPoints: function () {
       
  8923 		var parts = this._parts,
       
  8924 		    tolerance = this.options.smoothFactor;
       
  8925 
       
  8926 		for (var i = 0, len = parts.length; i < len; i++) {
       
  8927 			parts[i] = L.LineUtil.simplify(parts[i], tolerance);
       
  8928 		}
       
  8929 	},
       
  8930 
       
  8931 	_update: function () {
       
  8932 		if (!this._map) { return; }
       
  8933 
       
  8934 		this._clipPoints();
       
  8935 		this._simplifyPoints();
       
  8936 		this._updatePath();
       
  8937 	},
       
  8938 
       
  8939 	_updatePath: function () {
       
  8940 		this._renderer._updatePoly(this);
       
  8941 	}
       
  8942 });
       
  8943 
       
  8944 // @factory L.polyline(latlngs: LatLng[], options?: Polyline options)
       
  8945 // Instantiates a polyline object given an array of geographical points and
       
  8946 // optionally an options object. You can create a `Polyline` object with
       
  8947 // multiple separate lines (`MultiPolyline`) by passing an array of arrays
       
  8948 // of geographic points.
       
  8949 L.polyline = function (latlngs, options) {
       
  8950 	return new L.Polyline(latlngs, options);
       
  8951 };
       
  8952 
       
  8953 L.Polyline._flat = function (latlngs) {
       
  8954 	// true if it's a flat array of latlngs; false if nested
       
  8955 	return !L.Util.isArray(latlngs[0]) || (typeof latlngs[0][0] !== 'object' && typeof latlngs[0][0] !== 'undefined');
       
  8956 };
       
  8957 
       
  8958 
       
  8959 
       
  8960 /*
       
  8961  * @namespace PolyUtil
       
  8962  * Various utility functions for polygon geometries.
       
  8963  */
       
  8964 
       
  8965 L.PolyUtil = {};
       
  8966 
       
  8967 /* @function clipPolygon(points: Point[], bounds: Bounds, round?: Boolean): Point[]
       
  8968  * Clips the polygon geometry defined by the given `points` by the given bounds (using the [Sutherland-Hodgeman algorithm](https://en.wikipedia.org/wiki/Sutherland%E2%80%93Hodgman_algorithm)).
       
  8969  * Used by Leaflet to only show polygon points that are on the screen or near, increasing
       
  8970  * performance. Note that polygon points needs different algorithm for clipping
       
  8971  * than polyline, so there's a seperate method for it.
       
  8972  */
       
  8973 L.PolyUtil.clipPolygon = function (points, bounds, round) {
       
  8974 	var clippedPoints,
       
  8975 	    edges = [1, 4, 2, 8],
       
  8976 	    i, j, k,
       
  8977 	    a, b,
       
  8978 	    len, edge, p,
       
  8979 	    lu = L.LineUtil;
       
  8980 
       
  8981 	for (i = 0, len = points.length; i < len; i++) {
       
  8982 		points[i]._code = lu._getBitCode(points[i], bounds);
       
  8983 	}
       
  8984 
       
  8985 	// for each edge (left, bottom, right, top)
       
  8986 	for (k = 0; k < 4; k++) {
       
  8987 		edge = edges[k];
       
  8988 		clippedPoints = [];
       
  8989 
       
  8990 		for (i = 0, len = points.length, j = len - 1; i < len; j = i++) {
       
  8991 			a = points[i];
       
  8992 			b = points[j];
       
  8993 
       
  8994 			// if a is inside the clip window
       
  8995 			if (!(a._code & edge)) {
       
  8996 				// if b is outside the clip window (a->b goes out of screen)
       
  8997 				if (b._code & edge) {
       
  8998 					p = lu._getEdgeIntersection(b, a, edge, bounds, round);
       
  8999 					p._code = lu._getBitCode(p, bounds);
       
  9000 					clippedPoints.push(p);
       
  9001 				}
       
  9002 				clippedPoints.push(a);
       
  9003 
       
  9004 			// else if b is inside the clip window (a->b enters the screen)
       
  9005 			} else if (!(b._code & edge)) {
       
  9006 				p = lu._getEdgeIntersection(b, a, edge, bounds, round);
       
  9007 				p._code = lu._getBitCode(p, bounds);
       
  9008 				clippedPoints.push(p);
       
  9009 			}
       
  9010 		}
       
  9011 		points = clippedPoints;
       
  9012 	}
       
  9013 
       
  9014 	return points;
       
  9015 };
       
  9016 
       
  9017 
       
  9018 
       
  9019 /*
       
  9020  * @class Polygon
       
  9021  * @aka L.Polygon
       
  9022  * @inherits Polyline
       
  9023  *
       
  9024  * A class for drawing polygon overlays on a map. Extends `Polyline`.
       
  9025  *
       
  9026  * Note that points you pass when creating a polygon shouldn't have an additional last point equal to the first one — it's better to filter out such points.
       
  9027  *
       
  9028  *
       
  9029  * @example
       
  9030  *
       
  9031  * ```js
       
  9032  * // create a red polygon from an array of LatLng points
       
  9033  * var latlngs = [[37, -109.05],[41, -109.03],[41, -102.05],[37, -102.04]];
       
  9034  *
       
  9035  * var polygon = L.polygon(latlngs, {color: 'red'}).addTo(map);
       
  9036  *
       
  9037  * // zoom the map to the polygon
       
  9038  * map.fitBounds(polygon.getBounds());
       
  9039  * ```
       
  9040  *
       
  9041  * You can also pass an array of arrays of latlngs, with the first array representing the outer shape and the other arrays representing holes in the outer shape:
       
  9042  *
       
  9043  * ```js
       
  9044  * var latlngs = [
       
  9045  *   [[37, -109.05],[41, -109.03],[41, -102.05],[37, -102.04]], // outer ring
       
  9046  *   [[37.29, -108.58],[40.71, -108.58],[40.71, -102.50],[37.29, -102.50]] // hole
       
  9047  * ];
       
  9048  * ```
       
  9049  *
       
  9050  * Additionally, you can pass a multi-dimensional array to represent a MultiPolygon shape.
       
  9051  *
       
  9052  * ```js
       
  9053  * var latlngs = [
       
  9054  *   [ // first polygon
       
  9055  *     [[37, -109.05],[41, -109.03],[41, -102.05],[37, -102.04]], // outer ring
       
  9056  *     [[37.29, -108.58],[40.71, -108.58],[40.71, -102.50],[37.29, -102.50]] // hole
       
  9057  *   ],
       
  9058  *   [ // second polygon
       
  9059  *     [[41, -111.03],[45, -111.04],[45, -104.05],[41, -104.05]]
       
  9060  *   ]
       
  9061  * ];
       
  9062  * ```
       
  9063  */
       
  9064 
       
  9065 L.Polygon = L.Polyline.extend({
       
  9066 
       
  9067 	options: {
       
  9068 		fill: true
       
  9069 	},
       
  9070 
       
  9071 	isEmpty: function () {
       
  9072 		return !this._latlngs.length || !this._latlngs[0].length;
       
  9073 	},
       
  9074 
       
  9075 	getCenter: function () {
       
  9076 		// throws error when not yet added to map as this center calculation requires projected coordinates
       
  9077 		if (!this._map) {
       
  9078 			throw new Error('Must add layer to map before using getCenter()');
       
  9079 		}
       
  9080 
       
  9081 		var i, j, p1, p2, f, area, x, y, center,
       
  9082 		    points = this._rings[0],
       
  9083 		    len = points.length;
       
  9084 
       
  9085 		if (!len) { return null; }
       
  9086 
       
  9087 		// polygon centroid algorithm; only uses the first ring if there are multiple
       
  9088 
       
  9089 		area = x = y = 0;
       
  9090 
       
  9091 		for (i = 0, j = len - 1; i < len; j = i++) {
       
  9092 			p1 = points[i];
       
  9093 			p2 = points[j];
       
  9094 
       
  9095 			f = p1.y * p2.x - p2.y * p1.x;
       
  9096 			x += (p1.x + p2.x) * f;
       
  9097 			y += (p1.y + p2.y) * f;
       
  9098 			area += f * 3;
       
  9099 		}
       
  9100 
       
  9101 		if (area === 0) {
       
  9102 			// Polygon is so small that all points are on same pixel.
       
  9103 			center = points[0];
       
  9104 		} else {
       
  9105 			center = [x / area, y / area];
       
  9106 		}
       
  9107 		return this._map.layerPointToLatLng(center);
       
  9108 	},
       
  9109 
       
  9110 	_convertLatLngs: function (latlngs) {
       
  9111 		var result = L.Polyline.prototype._convertLatLngs.call(this, latlngs),
       
  9112 		    len = result.length;
       
  9113 
       
  9114 		// remove last point if it equals first one
       
  9115 		if (len >= 2 && result[0] instanceof L.LatLng && result[0].equals(result[len - 1])) {
       
  9116 			result.pop();
       
  9117 		}
       
  9118 		return result;
       
  9119 	},
       
  9120 
       
  9121 	_setLatLngs: function (latlngs) {
       
  9122 		L.Polyline.prototype._setLatLngs.call(this, latlngs);
       
  9123 		if (L.Polyline._flat(this._latlngs)) {
       
  9124 			this._latlngs = [this._latlngs];
       
  9125 		}
       
  9126 	},
       
  9127 
       
  9128 	_defaultShape: function () {
       
  9129 		return L.Polyline._flat(this._latlngs[0]) ? this._latlngs[0] : this._latlngs[0][0];
       
  9130 	},
       
  9131 
       
  9132 	_clipPoints: function () {
       
  9133 		// polygons need a different clipping algorithm so we redefine that
       
  9134 
       
  9135 		var bounds = this._renderer._bounds,
       
  9136 		    w = this.options.weight,
       
  9137 		    p = new L.Point(w, w);
       
  9138 
       
  9139 		// increase clip padding by stroke width to avoid stroke on clip edges
       
  9140 		bounds = new L.Bounds(bounds.min.subtract(p), bounds.max.add(p));
       
  9141 
       
  9142 		this._parts = [];
       
  9143 		if (!this._pxBounds || !this._pxBounds.intersects(bounds)) {
       
  9144 			return;
       
  9145 		}
       
  9146 
       
  9147 		if (this.options.noClip) {
       
  9148 			this._parts = this._rings;
       
  9149 			return;
       
  9150 		}
       
  9151 
       
  9152 		for (var i = 0, len = this._rings.length, clipped; i < len; i++) {
       
  9153 			clipped = L.PolyUtil.clipPolygon(this._rings[i], bounds, true);
       
  9154 			if (clipped.length) {
       
  9155 				this._parts.push(clipped);
       
  9156 			}
       
  9157 		}
       
  9158 	},
       
  9159 
       
  9160 	_updatePath: function () {
       
  9161 		this._renderer._updatePoly(this, true);
       
  9162 	}
       
  9163 });
       
  9164 
       
  9165 
       
  9166 // @factory L.polygon(latlngs: LatLng[], options?: Polyline options)
       
  9167 L.polygon = function (latlngs, options) {
       
  9168 	return new L.Polygon(latlngs, options);
       
  9169 };
       
  9170 
       
  9171 
       
  9172 
       
  9173 /*
       
  9174  * L.Rectangle extends Polygon and creates a rectangle when passed a LatLngBounds object.
       
  9175  */
       
  9176 
       
  9177 /*
       
  9178  * @class Rectangle
       
  9179  * @aka L.Retangle
       
  9180  * @inherits Polygon
       
  9181  *
       
  9182  * A class for drawing rectangle overlays on a map. Extends `Polygon`.
       
  9183  *
       
  9184  * @example
       
  9185  *
       
  9186  * ```js
       
  9187  * // define rectangle geographical bounds
       
  9188  * var bounds = [[54.559322, -5.767822], [56.1210604, -3.021240]];
       
  9189  *
       
  9190  * // create an orange rectangle
       
  9191  * L.rectangle(bounds, {color: "#ff7800", weight: 1}).addTo(map);
       
  9192  *
       
  9193  * // zoom the map to the rectangle bounds
       
  9194  * map.fitBounds(bounds);
       
  9195  * ```
       
  9196  *
       
  9197  */
       
  9198 
       
  9199 
       
  9200 L.Rectangle = L.Polygon.extend({
       
  9201 	initialize: function (latLngBounds, options) {
       
  9202 		L.Polygon.prototype.initialize.call(this, this._boundsToLatLngs(latLngBounds), options);
       
  9203 	},
       
  9204 
       
  9205 	// @method setBounds(latLngBounds: LatLngBounds): this
       
  9206 	// Redraws the rectangle with the passed bounds.
       
  9207 	setBounds: function (latLngBounds) {
       
  9208 		return this.setLatLngs(this._boundsToLatLngs(latLngBounds));
       
  9209 	},
       
  9210 
       
  9211 	_boundsToLatLngs: function (latLngBounds) {
       
  9212 		latLngBounds = L.latLngBounds(latLngBounds);
       
  9213 		return [
       
  9214 			latLngBounds.getSouthWest(),
       
  9215 			latLngBounds.getNorthWest(),
       
  9216 			latLngBounds.getNorthEast(),
       
  9217 			latLngBounds.getSouthEast()
       
  9218 		];
       
  9219 	}
       
  9220 });
       
  9221 
       
  9222 
       
  9223 // @factory L.rectangle(latLngBounds: LatLngBounds, options?: Polyline options)
       
  9224 L.rectangle = function (latLngBounds, options) {
       
  9225 	return new L.Rectangle(latLngBounds, options);
       
  9226 };
       
  9227 
       
  9228 
       
  9229 
       
  9230 /*
       
  9231  * @class CircleMarker
       
  9232  * @aka L.CircleMarker
       
  9233  * @inherits Path
       
  9234  *
       
  9235  * A circle of a fixed size with radius specified in pixels. Extends `Path`.
       
  9236  */
       
  9237 
       
  9238 L.CircleMarker = L.Path.extend({
       
  9239 
       
  9240 	// @section
       
  9241 	// @aka CircleMarker options
       
  9242 	options: {
       
  9243 		fill: true,
       
  9244 
       
  9245 		// @option radius: Number = 10
       
  9246 		// Radius of the circle marker, in pixels
       
  9247 		radius: 10
       
  9248 	},
       
  9249 
       
  9250 	initialize: function (latlng, options) {
       
  9251 		L.setOptions(this, options);
       
  9252 		this._latlng = L.latLng(latlng);
       
  9253 		this._radius = this.options.radius;
       
  9254 	},
       
  9255 
       
  9256 	// @method setLatLng(latLng: LatLng): this
       
  9257 	// Sets the position of a circle marker to a new location.
       
  9258 	setLatLng: function (latlng) {
       
  9259 		this._latlng = L.latLng(latlng);
       
  9260 		this.redraw();
       
  9261 		return this.fire('move', {latlng: this._latlng});
       
  9262 	},
       
  9263 
       
  9264 	// @method getLatLng(): LatLng
       
  9265 	// Returns the current geographical position of the circle marker
       
  9266 	getLatLng: function () {
       
  9267 		return this._latlng;
       
  9268 	},
       
  9269 
       
  9270 	// @method setRadius(radius: Number): this
       
  9271 	// Sets the radius of a circle marker. Units are in pixels.
       
  9272 	setRadius: function (radius) {
       
  9273 		this.options.radius = this._radius = radius;
       
  9274 		return this.redraw();
       
  9275 	},
       
  9276 
       
  9277 	// @method getRadius(): Number
       
  9278 	// Returns the current radius of the circle
       
  9279 	getRadius: function () {
       
  9280 		return this._radius;
       
  9281 	},
       
  9282 
       
  9283 	setStyle : function (options) {
       
  9284 		var radius = options && options.radius || this._radius;
       
  9285 		L.Path.prototype.setStyle.call(this, options);
       
  9286 		this.setRadius(radius);
       
  9287 		return this;
       
  9288 	},
       
  9289 
       
  9290 	_project: function () {
       
  9291 		this._point = this._map.latLngToLayerPoint(this._latlng);
       
  9292 		this._updateBounds();
       
  9293 	},
       
  9294 
       
  9295 	_updateBounds: function () {
       
  9296 		var r = this._radius,
       
  9297 		    r2 = this._radiusY || r,
       
  9298 		    w = this._clickTolerance(),
       
  9299 		    p = [r + w, r2 + w];
       
  9300 		this._pxBounds = new L.Bounds(this._point.subtract(p), this._point.add(p));
       
  9301 	},
       
  9302 
       
  9303 	_update: function () {
       
  9304 		if (this._map) {
       
  9305 			this._updatePath();
       
  9306 		}
       
  9307 	},
       
  9308 
       
  9309 	_updatePath: function () {
       
  9310 		this._renderer._updateCircle(this);
       
  9311 	},
       
  9312 
       
  9313 	_empty: function () {
       
  9314 		return this._radius && !this._renderer._bounds.intersects(this._pxBounds);
       
  9315 	}
       
  9316 });
       
  9317 
       
  9318 
       
  9319 // @factory L.circleMarker(latlng: LatLng, options?: CircleMarker options)
       
  9320 // Instantiates a circle marker object given a geographical point, and an optional options object.
       
  9321 L.circleMarker = function (latlng, options) {
       
  9322 	return new L.CircleMarker(latlng, options);
       
  9323 };
       
  9324 
       
  9325 
       
  9326 
       
  9327 /*
       
  9328  * @class Circle
       
  9329  * @aka L.Circle
       
  9330  * @inherits CircleMarker
       
  9331  *
       
  9332  * A class for drawing circle overlays on a map. Extends `CircleMarker`.
       
  9333  *
       
  9334  * It's an approximation and starts to diverge from a real circle closer to poles (due to projection distortion).
       
  9335  *
       
  9336  * @example
       
  9337  *
       
  9338  * ```js
       
  9339  * L.circle([50.5, 30.5], {radius: 200}).addTo(map);
       
  9340  * ```
       
  9341  */
       
  9342 
       
  9343 L.Circle = L.CircleMarker.extend({
       
  9344 
       
  9345 	initialize: function (latlng, options, legacyOptions) {
       
  9346 		if (typeof options === 'number') {
       
  9347 			// Backwards compatibility with 0.7.x factory (latlng, radius, options?)
       
  9348 			options = L.extend({}, legacyOptions, {radius: options});
       
  9349 		}
       
  9350 		L.setOptions(this, options);
       
  9351 		this._latlng = L.latLng(latlng);
       
  9352 
       
  9353 		if (isNaN(this.options.radius)) { throw new Error('Circle radius cannot be NaN'); }
       
  9354 
       
  9355 		// @section
       
  9356 		// @aka Circle options
       
  9357 		// @option radius: Number; Radius of the circle, in meters.
       
  9358 		this._mRadius = this.options.radius;
       
  9359 	},
       
  9360 
       
  9361 	// @method setRadius(radius: Number): this
       
  9362 	// Sets the radius of a circle. Units are in meters.
       
  9363 	setRadius: function (radius) {
       
  9364 		this._mRadius = radius;
       
  9365 		return this.redraw();
       
  9366 	},
       
  9367 
       
  9368 	// @method getRadius(): Number
       
  9369 	// Returns the current radius of a circle. Units are in meters.
       
  9370 	getRadius: function () {
       
  9371 		return this._mRadius;
       
  9372 	},
       
  9373 
       
  9374 	// @method getBounds(): LatLngBounds
       
  9375 	// Returns the `LatLngBounds` of the path.
       
  9376 	getBounds: function () {
       
  9377 		var half = [this._radius, this._radiusY || this._radius];
       
  9378 
       
  9379 		return new L.LatLngBounds(
       
  9380 			this._map.layerPointToLatLng(this._point.subtract(half)),
       
  9381 			this._map.layerPointToLatLng(this._point.add(half)));
       
  9382 	},
       
  9383 
       
  9384 	setStyle: L.Path.prototype.setStyle,
       
  9385 
       
  9386 	_project: function () {
       
  9387 
       
  9388 		var lng = this._latlng.lng,
       
  9389 		    lat = this._latlng.lat,
       
  9390 		    map = this._map,
       
  9391 		    crs = map.options.crs;
       
  9392 
       
  9393 		if (crs.distance === L.CRS.Earth.distance) {
       
  9394 			var d = Math.PI / 180,
       
  9395 			    latR = (this._mRadius / L.CRS.Earth.R) / d,
       
  9396 			    top = map.project([lat + latR, lng]),
       
  9397 			    bottom = map.project([lat - latR, lng]),
       
  9398 			    p = top.add(bottom).divideBy(2),
       
  9399 			    lat2 = map.unproject(p).lat,
       
  9400 			    lngR = Math.acos((Math.cos(latR * d) - Math.sin(lat * d) * Math.sin(lat2 * d)) /
       
  9401 			            (Math.cos(lat * d) * Math.cos(lat2 * d))) / d;
       
  9402 
       
  9403 			if (isNaN(lngR) || lngR === 0) {
       
  9404 				lngR = latR / Math.cos(Math.PI / 180 * lat); // Fallback for edge case, #2425
       
  9405 			}
       
  9406 
       
  9407 			this._point = p.subtract(map.getPixelOrigin());
       
  9408 			this._radius = isNaN(lngR) ? 0 : Math.max(Math.round(p.x - map.project([lat2, lng - lngR]).x), 1);
       
  9409 			this._radiusY = Math.max(Math.round(p.y - top.y), 1);
       
  9410 
       
  9411 		} else {
       
  9412 			var latlng2 = crs.unproject(crs.project(this._latlng).subtract([this._mRadius, 0]));
       
  9413 
       
  9414 			this._point = map.latLngToLayerPoint(this._latlng);
       
  9415 			this._radius = this._point.x - map.latLngToLayerPoint(latlng2).x;
       
  9416 		}
       
  9417 
       
  9418 		this._updateBounds();
       
  9419 	}
       
  9420 });
       
  9421 
       
  9422 // @factory L.circle(latlng: LatLng, options?: Circle options)
       
  9423 // Instantiates a circle object given a geographical point, and an options object
       
  9424 // which contains the circle radius.
       
  9425 // @alternative
       
  9426 // @factory L.circle(latlng: LatLng, radius: Number, options?: Circle options)
       
  9427 // Obsolete way of instantiating a circle, for compatibility with 0.7.x code.
       
  9428 // Do not use in new applications or plugins.
       
  9429 L.circle = function (latlng, options, legacyOptions) {
       
  9430 	return new L.Circle(latlng, options, legacyOptions);
       
  9431 };
       
  9432 
       
  9433 
       
  9434 
       
  9435 /*
       
  9436  * @class SVG
       
  9437  * @inherits Renderer
       
  9438  * @aka L.SVG
       
  9439  *
       
  9440  * Allows vector layers to be displayed with [SVG](https://developer.mozilla.org/docs/Web/SVG).
       
  9441  * Inherits `Renderer`.
       
  9442  *
       
  9443  * Due to [technical limitations](http://caniuse.com/#search=svg), SVG is not
       
  9444  * available in all web browsers, notably Android 2.x and 3.x.
       
  9445  *
       
  9446  * Although SVG is not available on IE7 and IE8, these browsers support
       
  9447  * [VML](https://en.wikipedia.org/wiki/Vector_Markup_Language)
       
  9448  * (a now deprecated technology), and the SVG renderer will fall back to VML in
       
  9449  * this case.
       
  9450  *
       
  9451  * @example
       
  9452  *
       
  9453  * Use SVG by default for all paths in the map:
       
  9454  *
       
  9455  * ```js
       
  9456  * var map = L.map('map', {
       
  9457  * 	renderer: L.svg()
       
  9458  * });
       
  9459  * ```
       
  9460  *
       
  9461  * Use a SVG renderer with extra padding for specific vector geometries:
       
  9462  *
       
  9463  * ```js
       
  9464  * var map = L.map('map');
       
  9465  * var myRenderer = L.svg({ padding: 0.5 });
       
  9466  * var line = L.polyline( coordinates, { renderer: myRenderer } );
       
  9467  * var circle = L.circle( center, { renderer: myRenderer } );
       
  9468  * ```
       
  9469  */
       
  9470 
       
  9471 L.SVG = L.Renderer.extend({
       
  9472 
       
  9473 	getEvents: function () {
       
  9474 		var events = L.Renderer.prototype.getEvents.call(this);
       
  9475 		events.zoomstart = this._onZoomStart;
       
  9476 		return events;
       
  9477 	},
       
  9478 
       
  9479 	_initContainer: function () {
       
  9480 		this._container = L.SVG.create('svg');
       
  9481 
       
  9482 		// makes it possible to click through svg root; we'll reset it back in individual paths
       
  9483 		this._container.setAttribute('pointer-events', 'none');
       
  9484 
       
  9485 		this._rootGroup = L.SVG.create('g');
       
  9486 		this._container.appendChild(this._rootGroup);
       
  9487 	},
       
  9488 
       
  9489 	_onZoomStart: function () {
       
  9490 		// Drag-then-pinch interactions might mess up the center and zoom.
       
  9491 		// In this case, the easiest way to prevent this is re-do the renderer
       
  9492 		//   bounds and padding when the zooming starts.
       
  9493 		this._update();
       
  9494 	},
       
  9495 
       
  9496 	_update: function () {
       
  9497 		if (this._map._animatingZoom && this._bounds) { return; }
       
  9498 
       
  9499 		L.Renderer.prototype._update.call(this);
       
  9500 
       
  9501 		var b = this._bounds,
       
  9502 		    size = b.getSize(),
       
  9503 		    container = this._container;
       
  9504 
       
  9505 		// set size of svg-container if changed
       
  9506 		if (!this._svgSize || !this._svgSize.equals(size)) {
       
  9507 			this._svgSize = size;
       
  9508 			container.setAttribute('width', size.x);
       
  9509 			container.setAttribute('height', size.y);
       
  9510 		}
       
  9511 
       
  9512 		// movement: update container viewBox so that we don't have to change coordinates of individual layers
       
  9513 		L.DomUtil.setPosition(container, b.min);
       
  9514 		container.setAttribute('viewBox', [b.min.x, b.min.y, size.x, size.y].join(' '));
       
  9515 
       
  9516 		this.fire('update');
       
  9517 	},
       
  9518 
       
  9519 	// methods below are called by vector layers implementations
       
  9520 
       
  9521 	_initPath: function (layer) {
       
  9522 		var path = layer._path = L.SVG.create('path');
       
  9523 
       
  9524 		// @namespace Path
       
  9525 		// @option className: String = null
       
  9526 		// Custom class name set on an element. Only for SVG renderer.
       
  9527 		if (layer.options.className) {
       
  9528 			L.DomUtil.addClass(path, layer.options.className);
       
  9529 		}
       
  9530 
       
  9531 		if (layer.options.interactive) {
       
  9532 			L.DomUtil.addClass(path, 'leaflet-interactive');
       
  9533 		}
       
  9534 
       
  9535 		this._updateStyle(layer);
       
  9536 		this._layers[L.stamp(layer)] = layer;
       
  9537 	},
       
  9538 
       
  9539 	_addPath: function (layer) {
       
  9540 		this._rootGroup.appendChild(layer._path);
       
  9541 		layer.addInteractiveTarget(layer._path);
       
  9542 	},
       
  9543 
       
  9544 	_removePath: function (layer) {
       
  9545 		L.DomUtil.remove(layer._path);
       
  9546 		layer.removeInteractiveTarget(layer._path);
       
  9547 		delete this._layers[L.stamp(layer)];
       
  9548 	},
       
  9549 
       
  9550 	_updatePath: function (layer) {
       
  9551 		layer._project();
       
  9552 		layer._update();
       
  9553 	},
       
  9554 
       
  9555 	_updateStyle: function (layer) {
       
  9556 		var path = layer._path,
       
  9557 		    options = layer.options;
       
  9558 
       
  9559 		if (!path) { return; }
       
  9560 
       
  9561 		if (options.stroke) {
       
  9562 			path.setAttribute('stroke', options.color);
       
  9563 			path.setAttribute('stroke-opacity', options.opacity);
       
  9564 			path.setAttribute('stroke-width', options.weight);
       
  9565 			path.setAttribute('stroke-linecap', options.lineCap);
       
  9566 			path.setAttribute('stroke-linejoin', options.lineJoin);
       
  9567 
       
  9568 			if (options.dashArray) {
       
  9569 				path.setAttribute('stroke-dasharray', options.dashArray);
       
  9570 			} else {
       
  9571 				path.removeAttribute('stroke-dasharray');
       
  9572 			}
       
  9573 
       
  9574 			if (options.dashOffset) {
       
  9575 				path.setAttribute('stroke-dashoffset', options.dashOffset);
       
  9576 			} else {
       
  9577 				path.removeAttribute('stroke-dashoffset');
       
  9578 			}
       
  9579 		} else {
       
  9580 			path.setAttribute('stroke', 'none');
       
  9581 		}
       
  9582 
       
  9583 		if (options.fill) {
       
  9584 			path.setAttribute('fill', options.fillColor || options.color);
       
  9585 			path.setAttribute('fill-opacity', options.fillOpacity);
       
  9586 			path.setAttribute('fill-rule', options.fillRule || 'evenodd');
       
  9587 		} else {
       
  9588 			path.setAttribute('fill', 'none');
       
  9589 		}
       
  9590 	},
       
  9591 
       
  9592 	_updatePoly: function (layer, closed) {
       
  9593 		this._setPath(layer, L.SVG.pointsToPath(layer._parts, closed));
       
  9594 	},
       
  9595 
       
  9596 	_updateCircle: function (layer) {
       
  9597 		var p = layer._point,
       
  9598 		    r = layer._radius,
       
  9599 		    r2 = layer._radiusY || r,
       
  9600 		    arc = 'a' + r + ',' + r2 + ' 0 1,0 ';
       
  9601 
       
  9602 		// drawing a circle with two half-arcs
       
  9603 		var d = layer._empty() ? 'M0 0' :
       
  9604 				'M' + (p.x - r) + ',' + p.y +
       
  9605 				arc + (r * 2) + ',0 ' +
       
  9606 				arc + (-r * 2) + ',0 ';
       
  9607 
       
  9608 		this._setPath(layer, d);
       
  9609 	},
       
  9610 
       
  9611 	_setPath: function (layer, path) {
       
  9612 		layer._path.setAttribute('d', path);
       
  9613 	},
       
  9614 
       
  9615 	// SVG does not have the concept of zIndex so we resort to changing the DOM order of elements
       
  9616 	_bringToFront: function (layer) {
       
  9617 		L.DomUtil.toFront(layer._path);
       
  9618 	},
       
  9619 
       
  9620 	_bringToBack: function (layer) {
       
  9621 		L.DomUtil.toBack(layer._path);
       
  9622 	}
       
  9623 });
       
  9624 
       
  9625 
       
  9626 // @namespace SVG; @section
       
  9627 // There are several static functions which can be called without instantiating L.SVG:
       
  9628 L.extend(L.SVG, {
       
  9629 	// @function create(name: String): SVGElement
       
  9630 	// Returns a instance of [SVGElement](https://developer.mozilla.org/docs/Web/API/SVGElement),
       
  9631 	// corresponding to the class name passed. For example, using 'line' will return
       
  9632 	// an instance of [SVGLineElement](https://developer.mozilla.org/docs/Web/API/SVGLineElement).
       
  9633 	create: function (name) {
       
  9634 		return document.createElementNS('http://www.w3.org/2000/svg', name);
       
  9635 	},
       
  9636 
       
  9637 	// @function pointsToPath(rings: Point[], closed: Boolean): String
       
  9638 	// Generates a SVG path string for multiple rings, with each ring turning
       
  9639 	// into "M..L..L.." instructions
       
  9640 	pointsToPath: function (rings, closed) {
       
  9641 		var str = '',
       
  9642 		    i, j, len, len2, points, p;
       
  9643 
       
  9644 		for (i = 0, len = rings.length; i < len; i++) {
       
  9645 			points = rings[i];
       
  9646 
       
  9647 			for (j = 0, len2 = points.length; j < len2; j++) {
       
  9648 				p = points[j];
       
  9649 				str += (j ? 'L' : 'M') + p.x + ' ' + p.y;
       
  9650 			}
       
  9651 
       
  9652 			// closes the ring for polygons; "x" is VML syntax
       
  9653 			str += closed ? (L.Browser.svg ? 'z' : 'x') : '';
       
  9654 		}
       
  9655 
       
  9656 		// SVG complains about empty path strings
       
  9657 		return str || 'M0 0';
       
  9658 	}
       
  9659 });
       
  9660 
       
  9661 // @namespace Browser; @property svg: Boolean
       
  9662 // `true` when the browser supports [SVG](https://developer.mozilla.org/docs/Web/SVG).
       
  9663 L.Browser.svg = !!(document.createElementNS && L.SVG.create('svg').createSVGRect);
       
  9664 
       
  9665 
       
  9666 // @namespace SVG
       
  9667 // @factory L.svg(options?: Renderer options)
       
  9668 // Creates a SVG renderer with the given options.
       
  9669 L.svg = function (options) {
       
  9670 	return L.Browser.svg || L.Browser.vml ? new L.SVG(options) : null;
       
  9671 };
       
  9672 
       
  9673 
       
  9674 
       
  9675 /*
       
  9676  * Thanks to Dmitry Baranovsky and his Raphael library for inspiration!
       
  9677  */
       
  9678 
       
  9679 /*
       
  9680  * @class SVG
       
  9681  *
       
  9682  * Although SVG is not available on IE7 and IE8, these browsers support [VML](https://en.wikipedia.org/wiki/Vector_Markup_Language), and the SVG renderer will fall back to VML in this case.
       
  9683  *
       
  9684  * VML was deprecated in 2012, which means VML functionality exists only for backwards compatibility
       
  9685  * with old versions of Internet Explorer.
       
  9686  */
       
  9687 
       
  9688 // @namespace Browser; @property vml: Boolean
       
  9689 // `true` if the browser supports [VML](https://en.wikipedia.org/wiki/Vector_Markup_Language).
       
  9690 L.Browser.vml = !L.Browser.svg && (function () {
       
  9691 	try {
       
  9692 		var div = document.createElement('div');
       
  9693 		div.innerHTML = '<v:shape adj="1"/>';
       
  9694 
       
  9695 		var shape = div.firstChild;
       
  9696 		shape.style.behavior = 'url(#default#VML)';
       
  9697 
       
  9698 		return shape && (typeof shape.adj === 'object');
       
  9699 
       
  9700 	} catch (e) {
       
  9701 		return false;
       
  9702 	}
       
  9703 }());
       
  9704 
       
  9705 // redefine some SVG methods to handle VML syntax which is similar but with some differences
       
  9706 L.SVG.include(!L.Browser.vml ? {} : {
       
  9707 
       
  9708 	_initContainer: function () {
       
  9709 		this._container = L.DomUtil.create('div', 'leaflet-vml-container');
       
  9710 	},
       
  9711 
       
  9712 	_update: function () {
       
  9713 		if (this._map._animatingZoom) { return; }
       
  9714 		L.Renderer.prototype._update.call(this);
       
  9715 		this.fire('update');
       
  9716 	},
       
  9717 
       
  9718 	_initPath: function (layer) {
       
  9719 		var container = layer._container = L.SVG.create('shape');
       
  9720 
       
  9721 		L.DomUtil.addClass(container, 'leaflet-vml-shape ' + (this.options.className || ''));
       
  9722 
       
  9723 		container.coordsize = '1 1';
       
  9724 
       
  9725 		layer._path = L.SVG.create('path');
       
  9726 		container.appendChild(layer._path);
       
  9727 
       
  9728 		this._updateStyle(layer);
       
  9729 		this._layers[L.stamp(layer)] = layer;
       
  9730 	},
       
  9731 
       
  9732 	_addPath: function (layer) {
       
  9733 		var container = layer._container;
       
  9734 		this._container.appendChild(container);
       
  9735 
       
  9736 		if (layer.options.interactive) {
       
  9737 			layer.addInteractiveTarget(container);
       
  9738 		}
       
  9739 	},
       
  9740 
       
  9741 	_removePath: function (layer) {
       
  9742 		var container = layer._container;
       
  9743 		L.DomUtil.remove(container);
       
  9744 		layer.removeInteractiveTarget(container);
       
  9745 		delete this._layers[L.stamp(layer)];
       
  9746 	},
       
  9747 
       
  9748 	_updateStyle: function (layer) {
       
  9749 		var stroke = layer._stroke,
       
  9750 		    fill = layer._fill,
       
  9751 		    options = layer.options,
       
  9752 		    container = layer._container;
       
  9753 
       
  9754 		container.stroked = !!options.stroke;
       
  9755 		container.filled = !!options.fill;
       
  9756 
       
  9757 		if (options.stroke) {
       
  9758 			if (!stroke) {
       
  9759 				stroke = layer._stroke = L.SVG.create('stroke');
       
  9760 			}
       
  9761 			container.appendChild(stroke);
       
  9762 			stroke.weight = options.weight + 'px';
       
  9763 			stroke.color = options.color;
       
  9764 			stroke.opacity = options.opacity;
       
  9765 
       
  9766 			if (options.dashArray) {
       
  9767 				stroke.dashStyle = L.Util.isArray(options.dashArray) ?
       
  9768 				    options.dashArray.join(' ') :
       
  9769 				    options.dashArray.replace(/( *, *)/g, ' ');
       
  9770 			} else {
       
  9771 				stroke.dashStyle = '';
       
  9772 			}
       
  9773 			stroke.endcap = options.lineCap.replace('butt', 'flat');
       
  9774 			stroke.joinstyle = options.lineJoin;
       
  9775 
       
  9776 		} else if (stroke) {
       
  9777 			container.removeChild(stroke);
       
  9778 			layer._stroke = null;
       
  9779 		}
       
  9780 
       
  9781 		if (options.fill) {
       
  9782 			if (!fill) {
       
  9783 				fill = layer._fill = L.SVG.create('fill');
       
  9784 			}
       
  9785 			container.appendChild(fill);
       
  9786 			fill.color = options.fillColor || options.color;
       
  9787 			fill.opacity = options.fillOpacity;
       
  9788 
       
  9789 		} else if (fill) {
       
  9790 			container.removeChild(fill);
       
  9791 			layer._fill = null;
       
  9792 		}
       
  9793 	},
       
  9794 
       
  9795 	_updateCircle: function (layer) {
       
  9796 		var p = layer._point.round(),
       
  9797 		    r = Math.round(layer._radius),
       
  9798 		    r2 = Math.round(layer._radiusY || r);
       
  9799 
       
  9800 		this._setPath(layer, layer._empty() ? 'M0 0' :
       
  9801 				'AL ' + p.x + ',' + p.y + ' ' + r + ',' + r2 + ' 0,' + (65535 * 360));
       
  9802 	},
       
  9803 
       
  9804 	_setPath: function (layer, path) {
       
  9805 		layer._path.v = path;
       
  9806 	},
       
  9807 
       
  9808 	_bringToFront: function (layer) {
       
  9809 		L.DomUtil.toFront(layer._container);
       
  9810 	},
       
  9811 
       
  9812 	_bringToBack: function (layer) {
       
  9813 		L.DomUtil.toBack(layer._container);
       
  9814 	}
       
  9815 });
       
  9816 
       
  9817 if (L.Browser.vml) {
       
  9818 	L.SVG.create = (function () {
       
  9819 		try {
       
  9820 			document.namespaces.add('lvml', 'urn:schemas-microsoft-com:vml');
       
  9821 			return function (name) {
       
  9822 				return document.createElement('<lvml:' + name + ' class="lvml">');
       
  9823 			};
       
  9824 		} catch (e) {
       
  9825 			return function (name) {
       
  9826 				return document.createElement('<' + name + ' xmlns="urn:schemas-microsoft.com:vml" class="lvml">');
       
  9827 			};
       
  9828 		}
       
  9829 	})();
       
  9830 }
       
  9831 
       
  9832 
       
  9833 
       
  9834 /*
       
  9835  * @class Canvas
       
  9836  * @inherits Renderer
       
  9837  * @aka L.Canvas
       
  9838  *
       
  9839  * Allows vector layers to be displayed with [`<canvas>`](https://developer.mozilla.org/docs/Web/API/Canvas_API).
       
  9840  * Inherits `Renderer`.
       
  9841  *
       
  9842  * Due to [technical limitations](http://caniuse.com/#search=canvas), Canvas is not
       
  9843  * available in all web browsers, notably IE8, and overlapping geometries might
       
  9844  * not display properly in some edge cases.
       
  9845  *
       
  9846  * @example
       
  9847  *
       
  9848  * Use Canvas by default for all paths in the map:
       
  9849  *
       
  9850  * ```js
       
  9851  * var map = L.map('map', {
       
  9852  * 	renderer: L.canvas()
       
  9853  * });
       
  9854  * ```
       
  9855  *
       
  9856  * Use a Canvas renderer with extra padding for specific vector geometries:
       
  9857  *
       
  9858  * ```js
       
  9859  * var map = L.map('map');
       
  9860  * var myRenderer = L.canvas({ padding: 0.5 });
       
  9861  * var line = L.polyline( coordinates, { renderer: myRenderer } );
       
  9862  * var circle = L.circle( center, { renderer: myRenderer } );
       
  9863  * ```
       
  9864  */
       
  9865 
       
  9866 L.Canvas = L.Renderer.extend({
       
  9867 	getEvents: function () {
       
  9868 		var events = L.Renderer.prototype.getEvents.call(this);
       
  9869 		events.viewprereset = this._onViewPreReset;
       
  9870 		return events;
       
  9871 	},
       
  9872 
       
  9873 	_onViewPreReset: function () {
       
  9874 		// Set a flag so that a viewprereset+moveend+viewreset only updates&redraws once
       
  9875 		this._postponeUpdatePaths = true;
       
  9876 	},
       
  9877 
       
  9878 	onAdd: function () {
       
  9879 		L.Renderer.prototype.onAdd.call(this);
       
  9880 
       
  9881 		// Redraw vectors since canvas is cleared upon removal,
       
  9882 		// in case of removing the renderer itself from the map.
       
  9883 		this._draw();
       
  9884 	},
       
  9885 
       
  9886 	_initContainer: function () {
       
  9887 		var container = this._container = document.createElement('canvas');
       
  9888 
       
  9889 		L.DomEvent
       
  9890 			.on(container, 'mousemove', L.Util.throttle(this._onMouseMove, 32, this), this)
       
  9891 			.on(container, 'click dblclick mousedown mouseup contextmenu', this._onClick, this)
       
  9892 			.on(container, 'mouseout', this._handleMouseOut, this);
       
  9893 
       
  9894 		this._ctx = container.getContext('2d');
       
  9895 	},
       
  9896 
       
  9897 	_updatePaths: function () {
       
  9898 		if (this._postponeUpdatePaths) { return; }
       
  9899 
       
  9900 		var layer;
       
  9901 		this._redrawBounds = null;
       
  9902 		for (var id in this._layers) {
       
  9903 			layer = this._layers[id];
       
  9904 			layer._update();
       
  9905 		}
       
  9906 		this._redraw();
       
  9907 	},
       
  9908 
       
  9909 	_update: function () {
       
  9910 		if (this._map._animatingZoom && this._bounds) { return; }
       
  9911 
       
  9912 		this._drawnLayers = {};
       
  9913 
       
  9914 		L.Renderer.prototype._update.call(this);
       
  9915 
       
  9916 		var b = this._bounds,
       
  9917 		    container = this._container,
       
  9918 		    size = b.getSize(),
       
  9919 		    m = L.Browser.retina ? 2 : 1;
       
  9920 
       
  9921 		L.DomUtil.setPosition(container, b.min);
       
  9922 
       
  9923 		// set canvas size (also clearing it); use double size on retina
       
  9924 		container.width = m * size.x;
       
  9925 		container.height = m * size.y;
       
  9926 		container.style.width = size.x + 'px';
       
  9927 		container.style.height = size.y + 'px';
       
  9928 
       
  9929 		if (L.Browser.retina) {
       
  9930 			this._ctx.scale(2, 2);
       
  9931 		}
       
  9932 
       
  9933 		// translate so we use the same path coordinates after canvas element moves
       
  9934 		this._ctx.translate(-b.min.x, -b.min.y);
       
  9935 
       
  9936 		// Tell paths to redraw themselves
       
  9937 		this.fire('update');
       
  9938 	},
       
  9939 
       
  9940 	_reset: function () {
       
  9941 		L.Renderer.prototype._reset.call(this);
       
  9942 
       
  9943 		if (this._postponeUpdatePaths) {
       
  9944 			this._postponeUpdatePaths = false;
       
  9945 			this._updatePaths();
       
  9946 		}
       
  9947 	},
       
  9948 
       
  9949 	_initPath: function (layer) {
       
  9950 		this._updateDashArray(layer);
       
  9951 		this._layers[L.stamp(layer)] = layer;
       
  9952 
       
  9953 		var order = layer._order = {
       
  9954 			layer: layer,
       
  9955 			prev: this._drawLast,
       
  9956 			next: null
       
  9957 		};
       
  9958 		if (this._drawLast) { this._drawLast.next = order; }
       
  9959 		this._drawLast = order;
       
  9960 		this._drawFirst = this._drawFirst || this._drawLast;
       
  9961 	},
       
  9962 
       
  9963 	_addPath: function (layer) {
       
  9964 		this._requestRedraw(layer);
       
  9965 	},
       
  9966 
       
  9967 	_removePath: function (layer) {
       
  9968 		var order = layer._order;
       
  9969 		var next = order.next;
       
  9970 		var prev = order.prev;
       
  9971 
       
  9972 		if (next) {
       
  9973 			next.prev = prev;
       
  9974 		} else {
       
  9975 			this._drawLast = prev;
       
  9976 		}
       
  9977 		if (prev) {
       
  9978 			prev.next = next;
       
  9979 		} else {
       
  9980 			this._drawFirst = next;
       
  9981 		}
       
  9982 
       
  9983 		delete layer._order;
       
  9984 
       
  9985 		delete this._layers[L.stamp(layer)];
       
  9986 
       
  9987 		this._requestRedraw(layer);
       
  9988 	},
       
  9989 
       
  9990 	_updatePath: function (layer) {
       
  9991 		// Redraw the union of the layer's old pixel
       
  9992 		// bounds and the new pixel bounds.
       
  9993 		this._extendRedrawBounds(layer);
       
  9994 		layer._project();
       
  9995 		layer._update();
       
  9996 		// The redraw will extend the redraw bounds
       
  9997 		// with the new pixel bounds.
       
  9998 		this._requestRedraw(layer);
       
  9999 	},
       
 10000 
       
 10001 	_updateStyle: function (layer) {
       
 10002 		this._updateDashArray(layer);
       
 10003 		this._requestRedraw(layer);
       
 10004 	},
       
 10005 
       
 10006 	_updateDashArray: function (layer) {
       
 10007 		if (layer.options.dashArray) {
       
 10008 			var parts = layer.options.dashArray.split(','),
       
 10009 			    dashArray = [],
       
 10010 			    i;
       
 10011 			for (i = 0; i < parts.length; i++) {
       
 10012 				dashArray.push(Number(parts[i]));
       
 10013 			}
       
 10014 			layer.options._dashArray = dashArray;
       
 10015 		}
       
 10016 	},
       
 10017 
       
 10018 	_requestRedraw: function (layer) {
       
 10019 		if (!this._map) { return; }
       
 10020 
       
 10021 		this._extendRedrawBounds(layer);
       
 10022 		this._redrawRequest = this._redrawRequest || L.Util.requestAnimFrame(this._redraw, this);
       
 10023 	},
       
 10024 
       
 10025 	_extendRedrawBounds: function (layer) {
       
 10026 		var padding = (layer.options.weight || 0) + 1;
       
 10027 		this._redrawBounds = this._redrawBounds || new L.Bounds();
       
 10028 		this._redrawBounds.extend(layer._pxBounds.min.subtract([padding, padding]));
       
 10029 		this._redrawBounds.extend(layer._pxBounds.max.add([padding, padding]));
       
 10030 	},
       
 10031 
       
 10032 	_redraw: function () {
       
 10033 		this._redrawRequest = null;
       
 10034 
       
 10035 		if (this._redrawBounds) {
       
 10036 			this._redrawBounds.min._floor();
       
 10037 			this._redrawBounds.max._ceil();
       
 10038 		}
       
 10039 
       
 10040 		this._clear(); // clear layers in redraw bounds
       
 10041 		this._draw(); // draw layers
       
 10042 
       
 10043 		this._redrawBounds = null;
       
 10044 	},
       
 10045 
       
 10046 	_clear: function () {
       
 10047 		var bounds = this._redrawBounds;
       
 10048 		if (bounds) {
       
 10049 			var size = bounds.getSize();
       
 10050 			this._ctx.clearRect(bounds.min.x, bounds.min.y, size.x, size.y);
       
 10051 		} else {
       
 10052 			this._ctx.clearRect(0, 0, this._container.width, this._container.height);
       
 10053 		}
       
 10054 	},
       
 10055 
       
 10056 	_draw: function () {
       
 10057 		var layer, bounds = this._redrawBounds;
       
 10058 		this._ctx.save();
       
 10059 		if (bounds) {
       
 10060 			var size = bounds.getSize();
       
 10061 			this._ctx.beginPath();
       
 10062 			this._ctx.rect(bounds.min.x, bounds.min.y, size.x, size.y);
       
 10063 			this._ctx.clip();
       
 10064 		}
       
 10065 
       
 10066 		this._drawing = true;
       
 10067 
       
 10068 		for (var order = this._drawFirst; order; order = order.next) {
       
 10069 			layer = order.layer;
       
 10070 			if (!bounds || (layer._pxBounds && layer._pxBounds.intersects(bounds))) {
       
 10071 				layer._updatePath();
       
 10072 			}
       
 10073 		}
       
 10074 
       
 10075 		this._drawing = false;
       
 10076 
       
 10077 		this._ctx.restore();  // Restore state before clipping.
       
 10078 	},
       
 10079 
       
 10080 	_updatePoly: function (layer, closed) {
       
 10081 		if (!this._drawing) { return; }
       
 10082 
       
 10083 		var i, j, len2, p,
       
 10084 		    parts = layer._parts,
       
 10085 		    len = parts.length,
       
 10086 		    ctx = this._ctx;
       
 10087 
       
 10088 		if (!len) { return; }
       
 10089 
       
 10090 		this._drawnLayers[layer._leaflet_id] = layer;
       
 10091 
       
 10092 		ctx.beginPath();
       
 10093 
       
 10094 		if (ctx.setLineDash) {
       
 10095 			ctx.setLineDash(layer.options && layer.options._dashArray || []);
       
 10096 		}
       
 10097 
       
 10098 		for (i = 0; i < len; i++) {
       
 10099 			for (j = 0, len2 = parts[i].length; j < len2; j++) {
       
 10100 				p = parts[i][j];
       
 10101 				ctx[j ? 'lineTo' : 'moveTo'](p.x, p.y);
       
 10102 			}
       
 10103 			if (closed) {
       
 10104 				ctx.closePath();
       
 10105 			}
       
 10106 		}
       
 10107 
       
 10108 		this._fillStroke(ctx, layer);
       
 10109 
       
 10110 		// TODO optimization: 1 fill/stroke for all features with equal style instead of 1 for each feature
       
 10111 	},
       
 10112 
       
 10113 	_updateCircle: function (layer) {
       
 10114 
       
 10115 		if (!this._drawing || layer._empty()) { return; }
       
 10116 
       
 10117 		var p = layer._point,
       
 10118 		    ctx = this._ctx,
       
 10119 		    r = layer._radius,
       
 10120 		    s = (layer._radiusY || r) / r;
       
 10121 
       
 10122 		this._drawnLayers[layer._leaflet_id] = layer;
       
 10123 
       
 10124 		if (s !== 1) {
       
 10125 			ctx.save();
       
 10126 			ctx.scale(1, s);
       
 10127 		}
       
 10128 
       
 10129 		ctx.beginPath();
       
 10130 		ctx.arc(p.x, p.y / s, r, 0, Math.PI * 2, false);
       
 10131 
       
 10132 		if (s !== 1) {
       
 10133 			ctx.restore();
       
 10134 		}
       
 10135 
       
 10136 		this._fillStroke(ctx, layer);
       
 10137 	},
       
 10138 
       
 10139 	_fillStroke: function (ctx, layer) {
       
 10140 		var options = layer.options;
       
 10141 
       
 10142 		if (options.fill) {
       
 10143 			ctx.globalAlpha = options.fillOpacity;
       
 10144 			ctx.fillStyle = options.fillColor || options.color;
       
 10145 			ctx.fill(options.fillRule || 'evenodd');
       
 10146 		}
       
 10147 
       
 10148 		if (options.stroke && options.weight !== 0) {
       
 10149 			ctx.globalAlpha = options.opacity;
       
 10150 			ctx.lineWidth = options.weight;
       
 10151 			ctx.strokeStyle = options.color;
       
 10152 			ctx.lineCap = options.lineCap;
       
 10153 			ctx.lineJoin = options.lineJoin;
       
 10154 			ctx.stroke();
       
 10155 		}
       
 10156 	},
       
 10157 
       
 10158 	// Canvas obviously doesn't have mouse events for individual drawn objects,
       
 10159 	// so we emulate that by calculating what's under the mouse on mousemove/click manually
       
 10160 
       
 10161 	_onClick: function (e) {
       
 10162 		var point = this._map.mouseEventToLayerPoint(e), layer, clickedLayer;
       
 10163 
       
 10164 		for (var order = this._drawFirst; order; order = order.next) {
       
 10165 			layer = order.layer;
       
 10166 			if (layer.options.interactive && layer._containsPoint(point) && !this._map._draggableMoved(layer)) {
       
 10167 				clickedLayer = layer;
       
 10168 			}
       
 10169 		}
       
 10170 		if (clickedLayer)  {
       
 10171 			L.DomEvent._fakeStop(e);
       
 10172 			this._fireEvent([clickedLayer], e);
       
 10173 		}
       
 10174 	},
       
 10175 
       
 10176 	_onMouseMove: function (e) {
       
 10177 		if (!this._map || this._map.dragging.moving() || this._map._animatingZoom) { return; }
       
 10178 
       
 10179 		var point = this._map.mouseEventToLayerPoint(e);
       
 10180 		this._handleMouseHover(e, point);
       
 10181 	},
       
 10182 
       
 10183 
       
 10184 	_handleMouseOut: function (e) {
       
 10185 		var layer = this._hoveredLayer;
       
 10186 		if (layer) {
       
 10187 			// if we're leaving the layer, fire mouseout
       
 10188 			L.DomUtil.removeClass(this._container, 'leaflet-interactive');
       
 10189 			this._fireEvent([layer], e, 'mouseout');
       
 10190 			this._hoveredLayer = null;
       
 10191 		}
       
 10192 	},
       
 10193 
       
 10194 	_handleMouseHover: function (e, point) {
       
 10195 		var layer, candidateHoveredLayer;
       
 10196 
       
 10197 		for (var order = this._drawFirst; order; order = order.next) {
       
 10198 			layer = order.layer;
       
 10199 			if (layer.options.interactive && layer._containsPoint(point)) {
       
 10200 				candidateHoveredLayer = layer;
       
 10201 			}
       
 10202 		}
       
 10203 
       
 10204 		if (candidateHoveredLayer !== this._hoveredLayer) {
       
 10205 			this._handleMouseOut(e);
       
 10206 
       
 10207 			if (candidateHoveredLayer) {
       
 10208 				L.DomUtil.addClass(this._container, 'leaflet-interactive'); // change cursor
       
 10209 				this._fireEvent([candidateHoveredLayer], e, 'mouseover');
       
 10210 				this._hoveredLayer = candidateHoveredLayer;
       
 10211 			}
       
 10212 		}
       
 10213 
       
 10214 		if (this._hoveredLayer) {
       
 10215 			this._fireEvent([this._hoveredLayer], e);
       
 10216 		}
       
 10217 	},
       
 10218 
       
 10219 	_fireEvent: function (layers, e, type) {
       
 10220 		this._map._fireDOMEvent(e, type || e.type, layers);
       
 10221 	},
       
 10222 
       
 10223 	_bringToFront: function (layer) {
       
 10224 		var order = layer._order;
       
 10225 		var next = order.next;
       
 10226 		var prev = order.prev;
       
 10227 
       
 10228 		if (next) {
       
 10229 			next.prev = prev;
       
 10230 		} else {
       
 10231 			// Already last
       
 10232 			return;
       
 10233 		}
       
 10234 		if (prev) {
       
 10235 			prev.next = next;
       
 10236 		} else if (next) {
       
 10237 			// Update first entry unless this is the
       
 10238 			// signle entry
       
 10239 			this._drawFirst = next;
       
 10240 		}
       
 10241 
       
 10242 		order.prev = this._drawLast;
       
 10243 		this._drawLast.next = order;
       
 10244 
       
 10245 		order.next = null;
       
 10246 		this._drawLast = order;
       
 10247 
       
 10248 		this._requestRedraw(layer);
       
 10249 	},
       
 10250 
       
 10251 	_bringToBack: function (layer) {
       
 10252 		var order = layer._order;
       
 10253 		var next = order.next;
       
 10254 		var prev = order.prev;
       
 10255 
       
 10256 		if (prev) {
       
 10257 			prev.next = next;
       
 10258 		} else {
       
 10259 			// Already first
       
 10260 			return;
       
 10261 		}
       
 10262 		if (next) {
       
 10263 			next.prev = prev;
       
 10264 		} else if (prev) {
       
 10265 			// Update last entry unless this is the
       
 10266 			// signle entry
       
 10267 			this._drawLast = prev;
       
 10268 		}
       
 10269 
       
 10270 		order.prev = null;
       
 10271 
       
 10272 		order.next = this._drawFirst;
       
 10273 		this._drawFirst.prev = order;
       
 10274 		this._drawFirst = order;
       
 10275 
       
 10276 		this._requestRedraw(layer);
       
 10277 	}
       
 10278 });
       
 10279 
       
 10280 // @namespace Browser; @property canvas: Boolean
       
 10281 // `true` when the browser supports [`<canvas>`](https://developer.mozilla.org/docs/Web/API/Canvas_API).
       
 10282 L.Browser.canvas = (function () {
       
 10283 	return !!document.createElement('canvas').getContext;
       
 10284 }());
       
 10285 
       
 10286 // @namespace Canvas
       
 10287 // @factory L.canvas(options?: Renderer options)
       
 10288 // Creates a Canvas renderer with the given options.
       
 10289 L.canvas = function (options) {
       
 10290 	return L.Browser.canvas ? new L.Canvas(options) : null;
       
 10291 };
       
 10292 
       
 10293 L.Polyline.prototype._containsPoint = function (p, closed) {
       
 10294 	var i, j, k, len, len2, part,
       
 10295 	    w = this._clickTolerance();
       
 10296 
       
 10297 	if (!this._pxBounds.contains(p)) { return false; }
       
 10298 
       
 10299 	// hit detection for polylines
       
 10300 	for (i = 0, len = this._parts.length; i < len; i++) {
       
 10301 		part = this._parts[i];
       
 10302 
       
 10303 		for (j = 0, len2 = part.length, k = len2 - 1; j < len2; k = j++) {
       
 10304 			if (!closed && (j === 0)) { continue; }
       
 10305 
       
 10306 			if (L.LineUtil.pointToSegmentDistance(p, part[k], part[j]) <= w) {
       
 10307 				return true;
       
 10308 			}
       
 10309 		}
       
 10310 	}
       
 10311 	return false;
       
 10312 };
       
 10313 
       
 10314 L.Polygon.prototype._containsPoint = function (p) {
       
 10315 	var inside = false,
       
 10316 	    part, p1, p2, i, j, k, len, len2;
       
 10317 
       
 10318 	if (!this._pxBounds.contains(p)) { return false; }
       
 10319 
       
 10320 	// ray casting algorithm for detecting if point is in polygon
       
 10321 	for (i = 0, len = this._parts.length; i < len; i++) {
       
 10322 		part = this._parts[i];
       
 10323 
       
 10324 		for (j = 0, len2 = part.length, k = len2 - 1; j < len2; k = j++) {
       
 10325 			p1 = part[j];
       
 10326 			p2 = part[k];
       
 10327 
       
 10328 			if (((p1.y > p.y) !== (p2.y > p.y)) && (p.x < (p2.x - p1.x) * (p.y - p1.y) / (p2.y - p1.y) + p1.x)) {
       
 10329 				inside = !inside;
       
 10330 			}
       
 10331 		}
       
 10332 	}
       
 10333 
       
 10334 	// also check if it's on polygon stroke
       
 10335 	return inside || L.Polyline.prototype._containsPoint.call(this, p, true);
       
 10336 };
       
 10337 
       
 10338 L.CircleMarker.prototype._containsPoint = function (p) {
       
 10339 	return p.distanceTo(this._point) <= this._radius + this._clickTolerance();
       
 10340 };
       
 10341 
       
 10342 
       
 10343 
       
 10344 /*
       
 10345  * @class GeoJSON
       
 10346  * @aka L.GeoJSON
       
 10347  * @inherits FeatureGroup
       
 10348  *
       
 10349  * Represents a GeoJSON object or an array of GeoJSON objects. Allows you to parse
       
 10350  * GeoJSON data and display it on the map. Extends `FeatureGroup`.
       
 10351  *
       
 10352  * @example
       
 10353  *
       
 10354  * ```js
       
 10355  * L.geoJSON(data, {
       
 10356  * 	style: function (feature) {
       
 10357  * 		return {color: feature.properties.color};
       
 10358  * 	}
       
 10359  * }).bindPopup(function (layer) {
       
 10360  * 	return layer.feature.properties.description;
       
 10361  * }).addTo(map);
       
 10362  * ```
       
 10363  */
       
 10364 
       
 10365 L.GeoJSON = L.FeatureGroup.extend({
       
 10366 
       
 10367 	/* @section
       
 10368 	 * @aka GeoJSON options
       
 10369 	 *
       
 10370 	 * @option pointToLayer: Function = *
       
 10371 	 * A `Function` defining how GeoJSON points spawn Leaflet layers. It is internally
       
 10372 	 * called when data is added, passing the GeoJSON point feature and its `LatLng`.
       
 10373 	 * The default is to spawn a default `Marker`:
       
 10374 	 * ```js
       
 10375 	 * function(geoJsonPoint, latlng) {
       
 10376 	 * 	return L.marker(latlng);
       
 10377 	 * }
       
 10378 	 * ```
       
 10379 	 *
       
 10380 	 * @option style: Function = *
       
 10381 	 * A `Function` defining the `Path options` for styling GeoJSON lines and polygons,
       
 10382 	 * called internally when data is added.
       
 10383 	 * The default value is to not override any defaults:
       
 10384 	 * ```js
       
 10385 	 * function (geoJsonFeature) {
       
 10386 	 * 	return {}
       
 10387 	 * }
       
 10388 	 * ```
       
 10389 	 *
       
 10390 	 * @option onEachFeature: Function = *
       
 10391 	 * A `Function` that will be called once for each created `Feature`, after it has
       
 10392 	 * been created and styled. Useful for attaching events and popups to features.
       
 10393 	 * The default is to do nothing with the newly created layers:
       
 10394 	 * ```js
       
 10395 	 * function (feature, layer) {}
       
 10396 	 * ```
       
 10397 	 *
       
 10398 	 * @option filter: Function = *
       
 10399 	 * A `Function` that will be used to decide whether to include a feature or not.
       
 10400 	 * The default is to include all features:
       
 10401 	 * ```js
       
 10402 	 * function (geoJsonFeature) {
       
 10403 	 * 	return true;
       
 10404 	 * }
       
 10405 	 * ```
       
 10406 	 * Note: dynamically changing the `filter` option will have effect only on newly
       
 10407 	 * added data. It will _not_ re-evaluate already included features.
       
 10408 	 *
       
 10409 	 * @option coordsToLatLng: Function = *
       
 10410 	 * A `Function` that will be used for converting GeoJSON coordinates to `LatLng`s.
       
 10411 	 * The default is the `coordsToLatLng` static method.
       
 10412 	 */
       
 10413 
       
 10414 	initialize: function (geojson, options) {
       
 10415 		L.setOptions(this, options);
       
 10416 
       
 10417 		this._layers = {};
       
 10418 
       
 10419 		if (geojson) {
       
 10420 			this.addData(geojson);
       
 10421 		}
       
 10422 	},
       
 10423 
       
 10424 	// @method addData( <GeoJSON> data ): this
       
 10425 	// Adds a GeoJSON object to the layer.
       
 10426 	addData: function (geojson) {
       
 10427 		var features = L.Util.isArray(geojson) ? geojson : geojson.features,
       
 10428 		    i, len, feature;
       
 10429 
       
 10430 		if (features) {
       
 10431 			for (i = 0, len = features.length; i < len; i++) {
       
 10432 				// only add this if geometry or geometries are set and not null
       
 10433 				feature = features[i];
       
 10434 				if (feature.geometries || feature.geometry || feature.features || feature.coordinates) {
       
 10435 					this.addData(feature);
       
 10436 				}
       
 10437 			}
       
 10438 			return this;
       
 10439 		}
       
 10440 
       
 10441 		var options = this.options;
       
 10442 
       
 10443 		if (options.filter && !options.filter(geojson)) { return this; }
       
 10444 
       
 10445 		var layer = L.GeoJSON.geometryToLayer(geojson, options);
       
 10446 		if (!layer) {
       
 10447 			return this;
       
 10448 		}
       
 10449 		layer.feature = L.GeoJSON.asFeature(geojson);
       
 10450 
       
 10451 		layer.defaultOptions = layer.options;
       
 10452 		this.resetStyle(layer);
       
 10453 
       
 10454 		if (options.onEachFeature) {
       
 10455 			options.onEachFeature(geojson, layer);
       
 10456 		}
       
 10457 
       
 10458 		return this.addLayer(layer);
       
 10459 	},
       
 10460 
       
 10461 	// @method resetStyle( <Path> layer ): this
       
 10462 	// Resets the given vector layer's style to the original GeoJSON style, useful for resetting style after hover events.
       
 10463 	resetStyle: function (layer) {
       
 10464 		// reset any custom styles
       
 10465 		layer.options = L.Util.extend({}, layer.defaultOptions);
       
 10466 		this._setLayerStyle(layer, this.options.style);
       
 10467 		return this;
       
 10468 	},
       
 10469 
       
 10470 	// @method setStyle( <Function> style ): this
       
 10471 	// Changes styles of GeoJSON vector layers with the given style function.
       
 10472 	setStyle: function (style) {
       
 10473 		return this.eachLayer(function (layer) {
       
 10474 			this._setLayerStyle(layer, style);
       
 10475 		}, this);
       
 10476 	},
       
 10477 
       
 10478 	_setLayerStyle: function (layer, style) {
       
 10479 		if (typeof style === 'function') {
       
 10480 			style = style(layer.feature);
       
 10481 		}
       
 10482 		if (layer.setStyle) {
       
 10483 			layer.setStyle(style);
       
 10484 		}
       
 10485 	}
       
 10486 });
       
 10487 
       
 10488 // @section
       
 10489 // There are several static functions which can be called without instantiating L.GeoJSON:
       
 10490 L.extend(L.GeoJSON, {
       
 10491 	// @function geometryToLayer(featureData: Object, options?: GeoJSON options): Layer
       
 10492 	// Creates a `Layer` from a given GeoJSON feature. Can use a custom
       
 10493 	// [`pointToLayer`](#geojson-pointtolayer) and/or [`coordsToLatLng`](#geojson-coordstolatlng)
       
 10494 	// functions if provided as options.
       
 10495 	geometryToLayer: function (geojson, options) {
       
 10496 
       
 10497 		var geometry = geojson.type === 'Feature' ? geojson.geometry : geojson,
       
 10498 		    coords = geometry ? geometry.coordinates : null,
       
 10499 		    layers = [],
       
 10500 		    pointToLayer = options && options.pointToLayer,
       
 10501 		    coordsToLatLng = options && options.coordsToLatLng || this.coordsToLatLng,
       
 10502 		    latlng, latlngs, i, len;
       
 10503 
       
 10504 		if (!coords && !geometry) {
       
 10505 			return null;
       
 10506 		}
       
 10507 
       
 10508 		switch (geometry.type) {
       
 10509 		case 'Point':
       
 10510 			latlng = coordsToLatLng(coords);
       
 10511 			return pointToLayer ? pointToLayer(geojson, latlng) : new L.Marker(latlng);
       
 10512 
       
 10513 		case 'MultiPoint':
       
 10514 			for (i = 0, len = coords.length; i < len; i++) {
       
 10515 				latlng = coordsToLatLng(coords[i]);
       
 10516 				layers.push(pointToLayer ? pointToLayer(geojson, latlng) : new L.Marker(latlng));
       
 10517 			}
       
 10518 			return new L.FeatureGroup(layers);
       
 10519 
       
 10520 		case 'LineString':
       
 10521 		case 'MultiLineString':
       
 10522 			latlngs = this.coordsToLatLngs(coords, geometry.type === 'LineString' ? 0 : 1, coordsToLatLng);
       
 10523 			return new L.Polyline(latlngs, options);
       
 10524 
       
 10525 		case 'Polygon':
       
 10526 		case 'MultiPolygon':
       
 10527 			latlngs = this.coordsToLatLngs(coords, geometry.type === 'Polygon' ? 1 : 2, coordsToLatLng);
       
 10528 			return new L.Polygon(latlngs, options);
       
 10529 
       
 10530 		case 'GeometryCollection':
       
 10531 			for (i = 0, len = geometry.geometries.length; i < len; i++) {
       
 10532 				var layer = this.geometryToLayer({
       
 10533 					geometry: geometry.geometries[i],
       
 10534 					type: 'Feature',
       
 10535 					properties: geojson.properties
       
 10536 				}, options);
       
 10537 
       
 10538 				if (layer) {
       
 10539 					layers.push(layer);
       
 10540 				}
       
 10541 			}
       
 10542 			return new L.FeatureGroup(layers);
       
 10543 
       
 10544 		default:
       
 10545 			throw new Error('Invalid GeoJSON object.');
       
 10546 		}
       
 10547 	},
       
 10548 
       
 10549 	// @function coordsToLatLng(coords: Array): LatLng
       
 10550 	// Creates a `LatLng` object from an array of 2 numbers (longitude, latitude)
       
 10551 	// or 3 numbers (longitude, latitude, altitude) used in GeoJSON for points.
       
 10552 	coordsToLatLng: function (coords) {
       
 10553 		return new L.LatLng(coords[1], coords[0], coords[2]);
       
 10554 	},
       
 10555 
       
 10556 	// @function coordsToLatLngs(coords: Array, levelsDeep?: Number, coordsToLatLng?: Function): Array
       
 10557 	// Creates a multidimensional array of `LatLng`s from a GeoJSON coordinates array.
       
 10558 	// `levelsDeep` specifies the nesting level (0 is for an array of points, 1 for an array of arrays of points, etc., 0 by default).
       
 10559 	// Can use a custom [`coordsToLatLng`](#geojson-coordstolatlng) function.
       
 10560 	coordsToLatLngs: function (coords, levelsDeep, coordsToLatLng) {
       
 10561 		var latlngs = [];
       
 10562 
       
 10563 		for (var i = 0, len = coords.length, latlng; i < len; i++) {
       
 10564 			latlng = levelsDeep ?
       
 10565 			        this.coordsToLatLngs(coords[i], levelsDeep - 1, coordsToLatLng) :
       
 10566 			        (coordsToLatLng || this.coordsToLatLng)(coords[i]);
       
 10567 
       
 10568 			latlngs.push(latlng);
       
 10569 		}
       
 10570 
       
 10571 		return latlngs;
       
 10572 	},
       
 10573 
       
 10574 	// @function latLngToCoords(latlng: LatLng): Array
       
 10575 	// Reverse of [`coordsToLatLng`](#geojson-coordstolatlng)
       
 10576 	latLngToCoords: function (latlng) {
       
 10577 		return latlng.alt !== undefined ?
       
 10578 				[latlng.lng, latlng.lat, latlng.alt] :
       
 10579 				[latlng.lng, latlng.lat];
       
 10580 	},
       
 10581 
       
 10582 	// @function latLngsToCoords(latlngs: Array, levelsDeep?: Number, closed?: Boolean): Array
       
 10583 	// Reverse of [`coordsToLatLngs`](#geojson-coordstolatlngs)
       
 10584 	// `closed` determines whether the first point should be appended to the end of the array to close the feature, only used when `levelsDeep` is 0. False by default.
       
 10585 	latLngsToCoords: function (latlngs, levelsDeep, closed) {
       
 10586 		var coords = [];
       
 10587 
       
 10588 		for (var i = 0, len = latlngs.length; i < len; i++) {
       
 10589 			coords.push(levelsDeep ?
       
 10590 				L.GeoJSON.latLngsToCoords(latlngs[i], levelsDeep - 1, closed) :
       
 10591 				L.GeoJSON.latLngToCoords(latlngs[i]));
       
 10592 		}
       
 10593 
       
 10594 		if (!levelsDeep && closed) {
       
 10595 			coords.push(coords[0]);
       
 10596 		}
       
 10597 
       
 10598 		return coords;
       
 10599 	},
       
 10600 
       
 10601 	getFeature: function (layer, newGeometry) {
       
 10602 		return layer.feature ?
       
 10603 				L.extend({}, layer.feature, {geometry: newGeometry}) :
       
 10604 				L.GeoJSON.asFeature(newGeometry);
       
 10605 	},
       
 10606 
       
 10607 	// @function asFeature(geojson: Object): Object
       
 10608 	// Normalize GeoJSON geometries/features into GeoJSON features.
       
 10609 	asFeature: function (geojson) {
       
 10610 		if (geojson.type === 'Feature' || geojson.type === 'FeatureCollection') {
       
 10611 			return geojson;
       
 10612 		}
       
 10613 
       
 10614 		return {
       
 10615 			type: 'Feature',
       
 10616 			properties: {},
       
 10617 			geometry: geojson
       
 10618 		};
       
 10619 	}
       
 10620 });
       
 10621 
       
 10622 var PointToGeoJSON = {
       
 10623 	toGeoJSON: function () {
       
 10624 		return L.GeoJSON.getFeature(this, {
       
 10625 			type: 'Point',
       
 10626 			coordinates: L.GeoJSON.latLngToCoords(this.getLatLng())
       
 10627 		});
       
 10628 	}
       
 10629 };
       
 10630 
       
 10631 // @namespace Marker
       
 10632 // @method toGeoJSON(): Object
       
 10633 // Returns a [`GeoJSON`](http://en.wikipedia.org/wiki/GeoJSON) representation of the marker (as a GeoJSON `Point` Feature).
       
 10634 L.Marker.include(PointToGeoJSON);
       
 10635 
       
 10636 // @namespace CircleMarker
       
 10637 // @method toGeoJSON(): Object
       
 10638 // Returns a [`GeoJSON`](http://en.wikipedia.org/wiki/GeoJSON) representation of the circle marker (as a GeoJSON `Point` Feature).
       
 10639 L.Circle.include(PointToGeoJSON);
       
 10640 L.CircleMarker.include(PointToGeoJSON);
       
 10641 
       
 10642 
       
 10643 // @namespace Polyline
       
 10644 // @method toGeoJSON(): Object
       
 10645 // Returns a [`GeoJSON`](http://en.wikipedia.org/wiki/GeoJSON) representation of the polyline (as a GeoJSON `LineString` or `MultiLineString` Feature).
       
 10646 L.Polyline.prototype.toGeoJSON = function () {
       
 10647 	var multi = !L.Polyline._flat(this._latlngs);
       
 10648 
       
 10649 	var coords = L.GeoJSON.latLngsToCoords(this._latlngs, multi ? 1 : 0);
       
 10650 
       
 10651 	return L.GeoJSON.getFeature(this, {
       
 10652 		type: (multi ? 'Multi' : '') + 'LineString',
       
 10653 		coordinates: coords
       
 10654 	});
       
 10655 };
       
 10656 
       
 10657 // @namespace Polygon
       
 10658 // @method toGeoJSON(): Object
       
 10659 // Returns a [`GeoJSON`](http://en.wikipedia.org/wiki/GeoJSON) representation of the polygon (as a GeoJSON `Polygon` or `MultiPolygon` Feature).
       
 10660 L.Polygon.prototype.toGeoJSON = function () {
       
 10661 	var holes = !L.Polyline._flat(this._latlngs),
       
 10662 	    multi = holes && !L.Polyline._flat(this._latlngs[0]);
       
 10663 
       
 10664 	var coords = L.GeoJSON.latLngsToCoords(this._latlngs, multi ? 2 : holes ? 1 : 0, true);
       
 10665 
       
 10666 	if (!holes) {
       
 10667 		coords = [coords];
       
 10668 	}
       
 10669 
       
 10670 	return L.GeoJSON.getFeature(this, {
       
 10671 		type: (multi ? 'Multi' : '') + 'Polygon',
       
 10672 		coordinates: coords
       
 10673 	});
       
 10674 };
       
 10675 
       
 10676 
       
 10677 // @namespace LayerGroup
       
 10678 L.LayerGroup.include({
       
 10679 	toMultiPoint: function () {
       
 10680 		var coords = [];
       
 10681 
       
 10682 		this.eachLayer(function (layer) {
       
 10683 			coords.push(layer.toGeoJSON().geometry.coordinates);
       
 10684 		});
       
 10685 
       
 10686 		return L.GeoJSON.getFeature(this, {
       
 10687 			type: 'MultiPoint',
       
 10688 			coordinates: coords
       
 10689 		});
       
 10690 	},
       
 10691 
       
 10692 	// @method toGeoJSON(): Object
       
 10693 	// Returns a [`GeoJSON`](http://en.wikipedia.org/wiki/GeoJSON) representation of the layer group (as a GeoJSON `GeometryCollection`).
       
 10694 	toGeoJSON: function () {
       
 10695 
       
 10696 		var type = this.feature && this.feature.geometry && this.feature.geometry.type;
       
 10697 
       
 10698 		if (type === 'MultiPoint') {
       
 10699 			return this.toMultiPoint();
       
 10700 		}
       
 10701 
       
 10702 		var isGeometryCollection = type === 'GeometryCollection',
       
 10703 		    jsons = [];
       
 10704 
       
 10705 		this.eachLayer(function (layer) {
       
 10706 			if (layer.toGeoJSON) {
       
 10707 				var json = layer.toGeoJSON();
       
 10708 				jsons.push(isGeometryCollection ? json.geometry : L.GeoJSON.asFeature(json));
       
 10709 			}
       
 10710 		});
       
 10711 
       
 10712 		if (isGeometryCollection) {
       
 10713 			return L.GeoJSON.getFeature(this, {
       
 10714 				geometries: jsons,
       
 10715 				type: 'GeometryCollection'
       
 10716 			});
       
 10717 		}
       
 10718 
       
 10719 		return {
       
 10720 			type: 'FeatureCollection',
       
 10721 			features: jsons
       
 10722 		};
       
 10723 	}
       
 10724 });
       
 10725 
       
 10726 // @namespace GeoJSON
       
 10727 // @factory L.geoJSON(geojson?: Object, options?: GeoJSON options)
       
 10728 // Creates a GeoJSON layer. Optionally accepts an object in
       
 10729 // [GeoJSON format](http://geojson.org/geojson-spec.html) to display on the map
       
 10730 // (you can alternatively add it later with `addData` method) and an `options` object.
       
 10731 L.geoJSON = function (geojson, options) {
       
 10732 	return new L.GeoJSON(geojson, options);
       
 10733 };
       
 10734 // Backward compatibility.
       
 10735 L.geoJson = L.geoJSON;
       
 10736 
       
 10737 
       
 10738 
       
 10739 /*
       
 10740  * @class Draggable
       
 10741  * @aka L.Draggable
       
 10742  * @inherits Evented
       
 10743  *
       
 10744  * A class for making DOM elements draggable (including touch support).
       
 10745  * Used internally for map and marker dragging. Only works for elements
       
 10746  * that were positioned with [`L.DomUtil.setPosition`](#domutil-setposition).
       
 10747  *
       
 10748  * @example
       
 10749  * ```js
       
 10750  * var draggable = new L.Draggable(elementToDrag);
       
 10751  * draggable.enable();
       
 10752  * ```
       
 10753  */
       
 10754 
       
 10755 L.Draggable = L.Evented.extend({
       
 10756 
       
 10757 	options: {
       
 10758 		// @option clickTolerance: Number = 3
       
 10759 		// The max number of pixels a user can shift the mouse pointer during a click
       
 10760 		// for it to be considered a valid click (as opposed to a mouse drag).
       
 10761 		clickTolerance: 3
       
 10762 	},
       
 10763 
       
 10764 	statics: {
       
 10765 		START: L.Browser.touch ? ['touchstart', 'mousedown'] : ['mousedown'],
       
 10766 		END: {
       
 10767 			mousedown: 'mouseup',
       
 10768 			touchstart: 'touchend',
       
 10769 			pointerdown: 'touchend',
       
 10770 			MSPointerDown: 'touchend'
       
 10771 		},
       
 10772 		MOVE: {
       
 10773 			mousedown: 'mousemove',
       
 10774 			touchstart: 'touchmove',
       
 10775 			pointerdown: 'touchmove',
       
 10776 			MSPointerDown: 'touchmove'
       
 10777 		}
       
 10778 	},
       
 10779 
       
 10780 	// @constructor L.Draggable(el: HTMLElement, dragHandle?: HTMLElement, preventOutline: Boolean)
       
 10781 	// Creates a `Draggable` object for moving `el` when you start dragging the `dragHandle` element (equals `el` itself by default).
       
 10782 	initialize: function (element, dragStartTarget, preventOutline) {
       
 10783 		this._element = element;
       
 10784 		this._dragStartTarget = dragStartTarget || element;
       
 10785 		this._preventOutline = preventOutline;
       
 10786 	},
       
 10787 
       
 10788 	// @method enable()
       
 10789 	// Enables the dragging ability
       
 10790 	enable: function () {
       
 10791 		if (this._enabled) { return; }
       
 10792 
       
 10793 		L.DomEvent.on(this._dragStartTarget, L.Draggable.START.join(' '), this._onDown, this);
       
 10794 
       
 10795 		this._enabled = true;
       
 10796 	},
       
 10797 
       
 10798 	// @method disable()
       
 10799 	// Disables the dragging ability
       
 10800 	disable: function () {
       
 10801 		if (!this._enabled) { return; }
       
 10802 
       
 10803 		// If we're currently dragging this draggable,
       
 10804 		// disabling it counts as first ending the drag.
       
 10805 		if (L.Draggable._dragging === this) {
       
 10806 			this.finishDrag();
       
 10807 		}
       
 10808 
       
 10809 		L.DomEvent.off(this._dragStartTarget, L.Draggable.START.join(' '), this._onDown, this);
       
 10810 
       
 10811 		this._enabled = false;
       
 10812 		this._moved = false;
       
 10813 	},
       
 10814 
       
 10815 	_onDown: function (e) {
       
 10816 		// Ignore simulated events, since we handle both touch and
       
 10817 		// mouse explicitly; otherwise we risk getting duplicates of
       
 10818 		// touch events, see #4315.
       
 10819 		// Also ignore the event if disabled; this happens in IE11
       
 10820 		// under some circumstances, see #3666.
       
 10821 		if (e._simulated || !this._enabled) { return; }
       
 10822 
       
 10823 		this._moved = false;
       
 10824 
       
 10825 		if (L.DomUtil.hasClass(this._element, 'leaflet-zoom-anim')) { return; }
       
 10826 
       
 10827 		if (L.Draggable._dragging || e.shiftKey || ((e.which !== 1) && (e.button !== 1) && !e.touches)) { return; }
       
 10828 		L.Draggable._dragging = this;  // Prevent dragging multiple objects at once.
       
 10829 
       
 10830 		if (this._preventOutline) {
       
 10831 			L.DomUtil.preventOutline(this._element);
       
 10832 		}
       
 10833 
       
 10834 		L.DomUtil.disableImageDrag();
       
 10835 		L.DomUtil.disableTextSelection();
       
 10836 
       
 10837 		if (this._moving) { return; }
       
 10838 
       
 10839 		// @event down: Event
       
 10840 		// Fired when a drag is about to start.
       
 10841 		this.fire('down');
       
 10842 
       
 10843 		var first = e.touches ? e.touches[0] : e;
       
 10844 
       
 10845 		this._startPoint = new L.Point(first.clientX, first.clientY);
       
 10846 
       
 10847 		L.DomEvent
       
 10848 			.on(document, L.Draggable.MOVE[e.type], this._onMove, this)
       
 10849 			.on(document, L.Draggable.END[e.type], this._onUp, this);
       
 10850 	},
       
 10851 
       
 10852 	_onMove: function (e) {
       
 10853 		// Ignore simulated events, since we handle both touch and
       
 10854 		// mouse explicitly; otherwise we risk getting duplicates of
       
 10855 		// touch events, see #4315.
       
 10856 		// Also ignore the event if disabled; this happens in IE11
       
 10857 		// under some circumstances, see #3666.
       
 10858 		if (e._simulated || !this._enabled) { return; }
       
 10859 
       
 10860 		if (e.touches && e.touches.length > 1) {
       
 10861 			this._moved = true;
       
 10862 			return;
       
 10863 		}
       
 10864 
       
 10865 		var first = (e.touches && e.touches.length === 1 ? e.touches[0] : e),
       
 10866 		    newPoint = new L.Point(first.clientX, first.clientY),
       
 10867 		    offset = newPoint.subtract(this._startPoint);
       
 10868 
       
 10869 		if (!offset.x && !offset.y) { return; }
       
 10870 		if (Math.abs(offset.x) + Math.abs(offset.y) < this.options.clickTolerance) { return; }
       
 10871 
       
 10872 		L.DomEvent.preventDefault(e);
       
 10873 
       
 10874 		if (!this._moved) {
       
 10875 			// @event dragstart: Event
       
 10876 			// Fired when a drag starts
       
 10877 			this.fire('dragstart');
       
 10878 
       
 10879 			this._moved = true;
       
 10880 			this._startPos = L.DomUtil.getPosition(this._element).subtract(offset);
       
 10881 
       
 10882 			L.DomUtil.addClass(document.body, 'leaflet-dragging');
       
 10883 
       
 10884 			this._lastTarget = e.target || e.srcElement;
       
 10885 			// IE and Edge do not give the <use> element, so fetch it
       
 10886 			// if necessary
       
 10887 			if ((window.SVGElementInstance) && (this._lastTarget instanceof SVGElementInstance)) {
       
 10888 				this._lastTarget = this._lastTarget.correspondingUseElement;
       
 10889 			}
       
 10890 			L.DomUtil.addClass(this._lastTarget, 'leaflet-drag-target');
       
 10891 		}
       
 10892 
       
 10893 		this._newPos = this._startPos.add(offset);
       
 10894 		this._moving = true;
       
 10895 
       
 10896 		L.Util.cancelAnimFrame(this._animRequest);
       
 10897 		this._lastEvent = e;
       
 10898 		this._animRequest = L.Util.requestAnimFrame(this._updatePosition, this, true);
       
 10899 	},
       
 10900 
       
 10901 	_updatePosition: function () {
       
 10902 		var e = {originalEvent: this._lastEvent};
       
 10903 
       
 10904 		// @event predrag: Event
       
 10905 		// Fired continuously during dragging *before* each corresponding
       
 10906 		// update of the element's position.
       
 10907 		this.fire('predrag', e);
       
 10908 		L.DomUtil.setPosition(this._element, this._newPos);
       
 10909 
       
 10910 		// @event drag: Event
       
 10911 		// Fired continuously during dragging.
       
 10912 		this.fire('drag', e);
       
 10913 	},
       
 10914 
       
 10915 	_onUp: function (e) {
       
 10916 		// Ignore simulated events, since we handle both touch and
       
 10917 		// mouse explicitly; otherwise we risk getting duplicates of
       
 10918 		// touch events, see #4315.
       
 10919 		// Also ignore the event if disabled; this happens in IE11
       
 10920 		// under some circumstances, see #3666.
       
 10921 		if (e._simulated || !this._enabled) { return; }
       
 10922 		this.finishDrag();
       
 10923 	},
       
 10924 
       
 10925 	finishDrag: function () {
       
 10926 		L.DomUtil.removeClass(document.body, 'leaflet-dragging');
       
 10927 
       
 10928 		if (this._lastTarget) {
       
 10929 			L.DomUtil.removeClass(this._lastTarget, 'leaflet-drag-target');
       
 10930 			this._lastTarget = null;
       
 10931 		}
       
 10932 
       
 10933 		for (var i in L.Draggable.MOVE) {
       
 10934 			L.DomEvent
       
 10935 				.off(document, L.Draggable.MOVE[i], this._onMove, this)
       
 10936 				.off(document, L.Draggable.END[i], this._onUp, this);
       
 10937 		}
       
 10938 
       
 10939 		L.DomUtil.enableImageDrag();
       
 10940 		L.DomUtil.enableTextSelection();
       
 10941 
       
 10942 		if (this._moved && this._moving) {
       
 10943 			// ensure drag is not fired after dragend
       
 10944 			L.Util.cancelAnimFrame(this._animRequest);
       
 10945 
       
 10946 			// @event dragend: DragEndEvent
       
 10947 			// Fired when the drag ends.
       
 10948 			this.fire('dragend', {
       
 10949 				distance: this._newPos.distanceTo(this._startPos)
       
 10950 			});
       
 10951 		}
       
 10952 
       
 10953 		this._moving = false;
       
 10954 		L.Draggable._dragging = false;
       
 10955 	}
       
 10956 
       
 10957 });
       
 10958 
       
 10959 
       
 10960 
       
 10961 /*
       
 10962 	L.Handler is a base class for handler classes that are used internally to inject
       
 10963 	interaction features like dragging to classes like Map and Marker.
       
 10964 */
       
 10965 
       
 10966 // @class Handler
       
 10967 // @aka L.Handler
       
 10968 // Abstract class for map interaction handlers
       
 10969 
       
 10970 L.Handler = L.Class.extend({
       
 10971 	initialize: function (map) {
       
 10972 		this._map = map;
       
 10973 	},
       
 10974 
       
 10975 	// @method enable(): this
       
 10976 	// Enables the handler
       
 10977 	enable: function () {
       
 10978 		if (this._enabled) { return this; }
       
 10979 
       
 10980 		this._enabled = true;
       
 10981 		this.addHooks();
       
 10982 		return this;
       
 10983 	},
       
 10984 
       
 10985 	// @method disable(): this
       
 10986 	// Disables the handler
       
 10987 	disable: function () {
       
 10988 		if (!this._enabled) { return this; }
       
 10989 
       
 10990 		this._enabled = false;
       
 10991 		this.removeHooks();
       
 10992 		return this;
       
 10993 	},
       
 10994 
       
 10995 	// @method enabled(): Boolean
       
 10996 	// Returns `true` if the handler is enabled
       
 10997 	enabled: function () {
       
 10998 		return !!this._enabled;
       
 10999 	}
       
 11000 
       
 11001 	// @section Extension methods
       
 11002 	// Classes inheriting from `Handler` must implement the two following methods:
       
 11003 	// @method addHooks()
       
 11004 	// Called when the handler is enabled, should add event hooks.
       
 11005 	// @method removeHooks()
       
 11006 	// Called when the handler is disabled, should remove the event hooks added previously.
       
 11007 });
       
 11008 
       
 11009 
       
 11010 
       
 11011 /*
       
 11012  * L.Handler.MapDrag is used to make the map draggable (with panning inertia), enabled by default.
       
 11013  */
       
 11014 
       
 11015 // @namespace Map
       
 11016 // @section Interaction Options
       
 11017 L.Map.mergeOptions({
       
 11018 	// @option dragging: Boolean = true
       
 11019 	// Whether the map be draggable with mouse/touch or not.
       
 11020 	dragging: true,
       
 11021 
       
 11022 	// @section Panning Inertia Options
       
 11023 	// @option inertia: Boolean = *
       
 11024 	// If enabled, panning of the map will have an inertia effect where
       
 11025 	// the map builds momentum while dragging and continues moving in
       
 11026 	// the same direction for some time. Feels especially nice on touch
       
 11027 	// devices. Enabled by default unless running on old Android devices.
       
 11028 	inertia: !L.Browser.android23,
       
 11029 
       
 11030 	// @option inertiaDeceleration: Number = 3000
       
 11031 	// The rate with which the inertial movement slows down, in pixels/second².
       
 11032 	inertiaDeceleration: 3400, // px/s^2
       
 11033 
       
 11034 	// @option inertiaMaxSpeed: Number = Infinity
       
 11035 	// Max speed of the inertial movement, in pixels/second.
       
 11036 	inertiaMaxSpeed: Infinity, // px/s
       
 11037 
       
 11038 	// @option easeLinearity: Number = 0.2
       
 11039 	easeLinearity: 0.2,
       
 11040 
       
 11041 	// TODO refactor, move to CRS
       
 11042 	// @option worldCopyJump: Boolean = false
       
 11043 	// With this option enabled, the map tracks when you pan to another "copy"
       
 11044 	// of the world and seamlessly jumps to the original one so that all overlays
       
 11045 	// like markers and vector layers are still visible.
       
 11046 	worldCopyJump: false,
       
 11047 
       
 11048 	// @option maxBoundsViscosity: Number = 0.0
       
 11049 	// If `maxBounds` is set, this option will control how solid the bounds
       
 11050 	// are when dragging the map around. The default value of `0.0` allows the
       
 11051 	// user to drag outside the bounds at normal speed, higher values will
       
 11052 	// slow down map dragging outside bounds, and `1.0` makes the bounds fully
       
 11053 	// solid, preventing the user from dragging outside the bounds.
       
 11054 	maxBoundsViscosity: 0.0
       
 11055 });
       
 11056 
       
 11057 L.Map.Drag = L.Handler.extend({
       
 11058 	addHooks: function () {
       
 11059 		if (!this._draggable) {
       
 11060 			var map = this._map;
       
 11061 
       
 11062 			this._draggable = new L.Draggable(map._mapPane, map._container);
       
 11063 
       
 11064 			this._draggable.on({
       
 11065 				down: this._onDown,
       
 11066 				dragstart: this._onDragStart,
       
 11067 				drag: this._onDrag,
       
 11068 				dragend: this._onDragEnd
       
 11069 			}, this);
       
 11070 
       
 11071 			this._draggable.on('predrag', this._onPreDragLimit, this);
       
 11072 			if (map.options.worldCopyJump) {
       
 11073 				this._draggable.on('predrag', this._onPreDragWrap, this);
       
 11074 				map.on('zoomend', this._onZoomEnd, this);
       
 11075 
       
 11076 				map.whenReady(this._onZoomEnd, this);
       
 11077 			}
       
 11078 		}
       
 11079 		L.DomUtil.addClass(this._map._container, 'leaflet-grab leaflet-touch-drag');
       
 11080 		this._draggable.enable();
       
 11081 		this._positions = [];
       
 11082 		this._times = [];
       
 11083 	},
       
 11084 
       
 11085 	removeHooks: function () {
       
 11086 		L.DomUtil.removeClass(this._map._container, 'leaflet-grab');
       
 11087 		L.DomUtil.removeClass(this._map._container, 'leaflet-touch-drag');
       
 11088 		this._draggable.disable();
       
 11089 	},
       
 11090 
       
 11091 	moved: function () {
       
 11092 		return this._draggable && this._draggable._moved;
       
 11093 	},
       
 11094 
       
 11095 	moving: function () {
       
 11096 		return this._draggable && this._draggable._moving;
       
 11097 	},
       
 11098 
       
 11099 	_onDown: function () {
       
 11100 		this._map._stop();
       
 11101 	},
       
 11102 
       
 11103 	_onDragStart: function () {
       
 11104 		var map = this._map;
       
 11105 
       
 11106 		if (this._map.options.maxBounds && this._map.options.maxBoundsViscosity) {
       
 11107 			var bounds = L.latLngBounds(this._map.options.maxBounds);
       
 11108 
       
 11109 			this._offsetLimit = L.bounds(
       
 11110 				this._map.latLngToContainerPoint(bounds.getNorthWest()).multiplyBy(-1),
       
 11111 				this._map.latLngToContainerPoint(bounds.getSouthEast()).multiplyBy(-1)
       
 11112 					.add(this._map.getSize()));
       
 11113 
       
 11114 			this._viscosity = Math.min(1.0, Math.max(0.0, this._map.options.maxBoundsViscosity));
       
 11115 		} else {
       
 11116 			this._offsetLimit = null;
       
 11117 		}
       
 11118 
       
 11119 		map
       
 11120 		    .fire('movestart')
       
 11121 		    .fire('dragstart');
       
 11122 
       
 11123 		if (map.options.inertia) {
       
 11124 			this._positions = [];
       
 11125 			this._times = [];
       
 11126 		}
       
 11127 	},
       
 11128 
       
 11129 	_onDrag: function (e) {
       
 11130 		if (this._map.options.inertia) {
       
 11131 			var time = this._lastTime = +new Date(),
       
 11132 			    pos = this._lastPos = this._draggable._absPos || this._draggable._newPos;
       
 11133 
       
 11134 			this._positions.push(pos);
       
 11135 			this._times.push(time);
       
 11136 
       
 11137 			if (time - this._times[0] > 50) {
       
 11138 				this._positions.shift();
       
 11139 				this._times.shift();
       
 11140 			}
       
 11141 		}
       
 11142 
       
 11143 		this._map
       
 11144 		    .fire('move', e)
       
 11145 		    .fire('drag', e);
       
 11146 	},
       
 11147 
       
 11148 	_onZoomEnd: function () {
       
 11149 		var pxCenter = this._map.getSize().divideBy(2),
       
 11150 		    pxWorldCenter = this._map.latLngToLayerPoint([0, 0]);
       
 11151 
       
 11152 		this._initialWorldOffset = pxWorldCenter.subtract(pxCenter).x;
       
 11153 		this._worldWidth = this._map.getPixelWorldBounds().getSize().x;
       
 11154 	},
       
 11155 
       
 11156 	_viscousLimit: function (value, threshold) {
       
 11157 		return value - (value - threshold) * this._viscosity;
       
 11158 	},
       
 11159 
       
 11160 	_onPreDragLimit: function () {
       
 11161 		if (!this._viscosity || !this._offsetLimit) { return; }
       
 11162 
       
 11163 		var offset = this._draggable._newPos.subtract(this._draggable._startPos);
       
 11164 
       
 11165 		var limit = this._offsetLimit;
       
 11166 		if (offset.x < limit.min.x) { offset.x = this._viscousLimit(offset.x, limit.min.x); }
       
 11167 		if (offset.y < limit.min.y) { offset.y = this._viscousLimit(offset.y, limit.min.y); }
       
 11168 		if (offset.x > limit.max.x) { offset.x = this._viscousLimit(offset.x, limit.max.x); }
       
 11169 		if (offset.y > limit.max.y) { offset.y = this._viscousLimit(offset.y, limit.max.y); }
       
 11170 
       
 11171 		this._draggable._newPos = this._draggable._startPos.add(offset);
       
 11172 	},
       
 11173 
       
 11174 	_onPreDragWrap: function () {
       
 11175 		// TODO refactor to be able to adjust map pane position after zoom
       
 11176 		var worldWidth = this._worldWidth,
       
 11177 		    halfWidth = Math.round(worldWidth / 2),
       
 11178 		    dx = this._initialWorldOffset,
       
 11179 		    x = this._draggable._newPos.x,
       
 11180 		    newX1 = (x - halfWidth + dx) % worldWidth + halfWidth - dx,
       
 11181 		    newX2 = (x + halfWidth + dx) % worldWidth - halfWidth - dx,
       
 11182 		    newX = Math.abs(newX1 + dx) < Math.abs(newX2 + dx) ? newX1 : newX2;
       
 11183 
       
 11184 		this._draggable._absPos = this._draggable._newPos.clone();
       
 11185 		this._draggable._newPos.x = newX;
       
 11186 	},
       
 11187 
       
 11188 	_onDragEnd: function (e) {
       
 11189 		var map = this._map,
       
 11190 		    options = map.options,
       
 11191 
       
 11192 		    noInertia = !options.inertia || this._times.length < 2;
       
 11193 
       
 11194 		map.fire('dragend', e);
       
 11195 
       
 11196 		if (noInertia) {
       
 11197 			map.fire('moveend');
       
 11198 
       
 11199 		} else {
       
 11200 
       
 11201 			var direction = this._lastPos.subtract(this._positions[0]),
       
 11202 			    duration = (this._lastTime - this._times[0]) / 1000,
       
 11203 			    ease = options.easeLinearity,
       
 11204 
       
 11205 			    speedVector = direction.multiplyBy(ease / duration),
       
 11206 			    speed = speedVector.distanceTo([0, 0]),
       
 11207 
       
 11208 			    limitedSpeed = Math.min(options.inertiaMaxSpeed, speed),
       
 11209 			    limitedSpeedVector = speedVector.multiplyBy(limitedSpeed / speed),
       
 11210 
       
 11211 			    decelerationDuration = limitedSpeed / (options.inertiaDeceleration * ease),
       
 11212 			    offset = limitedSpeedVector.multiplyBy(-decelerationDuration / 2).round();
       
 11213 
       
 11214 			if (!offset.x && !offset.y) {
       
 11215 				map.fire('moveend');
       
 11216 
       
 11217 			} else {
       
 11218 				offset = map._limitOffset(offset, map.options.maxBounds);
       
 11219 
       
 11220 				L.Util.requestAnimFrame(function () {
       
 11221 					map.panBy(offset, {
       
 11222 						duration: decelerationDuration,
       
 11223 						easeLinearity: ease,
       
 11224 						noMoveStart: true,
       
 11225 						animate: true
       
 11226 					});
       
 11227 				});
       
 11228 			}
       
 11229 		}
       
 11230 	}
       
 11231 });
       
 11232 
       
 11233 // @section Handlers
       
 11234 // @property dragging: Handler
       
 11235 // Map dragging handler (by both mouse and touch).
       
 11236 L.Map.addInitHook('addHandler', 'dragging', L.Map.Drag);
       
 11237 
       
 11238 
       
 11239 
       
 11240 /*
       
 11241  * L.Handler.DoubleClickZoom is used to handle double-click zoom on the map, enabled by default.
       
 11242  */
       
 11243 
       
 11244 // @namespace Map
       
 11245 // @section Interaction Options
       
 11246 
       
 11247 L.Map.mergeOptions({
       
 11248 	// @option doubleClickZoom: Boolean|String = true
       
 11249 	// Whether the map can be zoomed in by double clicking on it and
       
 11250 	// zoomed out by double clicking while holding shift. If passed
       
 11251 	// `'center'`, double-click zoom will zoom to the center of the
       
 11252 	//  view regardless of where the mouse was.
       
 11253 	doubleClickZoom: true
       
 11254 });
       
 11255 
       
 11256 L.Map.DoubleClickZoom = L.Handler.extend({
       
 11257 	addHooks: function () {
       
 11258 		this._map.on('dblclick', this._onDoubleClick, this);
       
 11259 	},
       
 11260 
       
 11261 	removeHooks: function () {
       
 11262 		this._map.off('dblclick', this._onDoubleClick, this);
       
 11263 	},
       
 11264 
       
 11265 	_onDoubleClick: function (e) {
       
 11266 		var map = this._map,
       
 11267 		    oldZoom = map.getZoom(),
       
 11268 		    delta = map.options.zoomDelta,
       
 11269 		    zoom = e.originalEvent.shiftKey ? oldZoom - delta : oldZoom + delta;
       
 11270 
       
 11271 		if (map.options.doubleClickZoom === 'center') {
       
 11272 			map.setZoom(zoom);
       
 11273 		} else {
       
 11274 			map.setZoomAround(e.containerPoint, zoom);
       
 11275 		}
       
 11276 	}
       
 11277 });
       
 11278 
       
 11279 // @section Handlers
       
 11280 //
       
 11281 // Map properties include interaction handlers that allow you to control
       
 11282 // interaction behavior in runtime, enabling or disabling certain features such
       
 11283 // as dragging or touch zoom (see `Handler` methods). For example:
       
 11284 //
       
 11285 // ```js
       
 11286 // map.doubleClickZoom.disable();
       
 11287 // ```
       
 11288 //
       
 11289 // @property doubleClickZoom: Handler
       
 11290 // Double click zoom handler.
       
 11291 L.Map.addInitHook('addHandler', 'doubleClickZoom', L.Map.DoubleClickZoom);
       
 11292 
       
 11293 
       
 11294 
       
 11295 /*
       
 11296  * L.Handler.ScrollWheelZoom is used by L.Map to enable mouse scroll wheel zoom on the map.
       
 11297  */
       
 11298 
       
 11299 // @namespace Map
       
 11300 // @section Interaction Options
       
 11301 L.Map.mergeOptions({
       
 11302 	// @section Mousewheel options
       
 11303 	// @option scrollWheelZoom: Boolean|String = true
       
 11304 	// Whether the map can be zoomed by using the mouse wheel. If passed `'center'`,
       
 11305 	// it will zoom to the center of the view regardless of where the mouse was.
       
 11306 	scrollWheelZoom: true,
       
 11307 
       
 11308 	// @option wheelDebounceTime: Number = 40
       
 11309 	// Limits the rate at which a wheel can fire (in milliseconds). By default
       
 11310 	// user can't zoom via wheel more often than once per 40 ms.
       
 11311 	wheelDebounceTime: 40,
       
 11312 
       
 11313 	// @option wheelPxPerZoomLevel: Number = 60
       
 11314 	// How many scroll pixels (as reported by [L.DomEvent.getWheelDelta](#domevent-getwheeldelta))
       
 11315 	// mean a change of one full zoom level. Smaller values will make wheel-zooming
       
 11316 	// faster (and vice versa).
       
 11317 	wheelPxPerZoomLevel: 60
       
 11318 });
       
 11319 
       
 11320 L.Map.ScrollWheelZoom = L.Handler.extend({
       
 11321 	addHooks: function () {
       
 11322 		L.DomEvent.on(this._map._container, 'mousewheel', this._onWheelScroll, this);
       
 11323 
       
 11324 		this._delta = 0;
       
 11325 	},
       
 11326 
       
 11327 	removeHooks: function () {
       
 11328 		L.DomEvent.off(this._map._container, 'mousewheel', this._onWheelScroll, this);
       
 11329 	},
       
 11330 
       
 11331 	_onWheelScroll: function (e) {
       
 11332 		var delta = L.DomEvent.getWheelDelta(e);
       
 11333 
       
 11334 		var debounce = this._map.options.wheelDebounceTime;
       
 11335 
       
 11336 		this._delta += delta;
       
 11337 		this._lastMousePos = this._map.mouseEventToContainerPoint(e);
       
 11338 
       
 11339 		if (!this._startTime) {
       
 11340 			this._startTime = +new Date();
       
 11341 		}
       
 11342 
       
 11343 		var left = Math.max(debounce - (+new Date() - this._startTime), 0);
       
 11344 
       
 11345 		clearTimeout(this._timer);
       
 11346 		this._timer = setTimeout(L.bind(this._performZoom, this), left);
       
 11347 
       
 11348 		L.DomEvent.stop(e);
       
 11349 	},
       
 11350 
       
 11351 	_performZoom: function () {
       
 11352 		var map = this._map,
       
 11353 		    zoom = map.getZoom(),
       
 11354 		    snap = this._map.options.zoomSnap || 0;
       
 11355 
       
 11356 		map._stop(); // stop panning and fly animations if any
       
 11357 
       
 11358 		// map the delta with a sigmoid function to -4..4 range leaning on -1..1
       
 11359 		var d2 = this._delta / (this._map.options.wheelPxPerZoomLevel * 4),
       
 11360 		    d3 = 4 * Math.log(2 / (1 + Math.exp(-Math.abs(d2)))) / Math.LN2,
       
 11361 		    d4 = snap ? Math.ceil(d3 / snap) * snap : d3,
       
 11362 		    delta = map._limitZoom(zoom + (this._delta > 0 ? d4 : -d4)) - zoom;
       
 11363 
       
 11364 		this._delta = 0;
       
 11365 		this._startTime = null;
       
 11366 
       
 11367 		if (!delta) { return; }
       
 11368 
       
 11369 		if (map.options.scrollWheelZoom === 'center') {
       
 11370 			map.setZoom(zoom + delta);
       
 11371 		} else {
       
 11372 			map.setZoomAround(this._lastMousePos, zoom + delta);
       
 11373 		}
       
 11374 	}
       
 11375 });
       
 11376 
       
 11377 // @section Handlers
       
 11378 // @property scrollWheelZoom: Handler
       
 11379 // Scroll wheel zoom handler.
       
 11380 L.Map.addInitHook('addHandler', 'scrollWheelZoom', L.Map.ScrollWheelZoom);
       
 11381 
       
 11382 
       
 11383 
       
 11384 /*
       
 11385  * Extends the event handling code with double tap support for mobile browsers.
       
 11386  */
       
 11387 
       
 11388 L.extend(L.DomEvent, {
       
 11389 
       
 11390 	_touchstart: L.Browser.msPointer ? 'MSPointerDown' : L.Browser.pointer ? 'pointerdown' : 'touchstart',
       
 11391 	_touchend: L.Browser.msPointer ? 'MSPointerUp' : L.Browser.pointer ? 'pointerup' : 'touchend',
       
 11392 
       
 11393 	// inspired by Zepto touch code by Thomas Fuchs
       
 11394 	addDoubleTapListener: function (obj, handler, id) {
       
 11395 		var last, touch,
       
 11396 		    doubleTap = false,
       
 11397 		    delay = 250;
       
 11398 
       
 11399 		function onTouchStart(e) {
       
 11400 			var count;
       
 11401 
       
 11402 			if (L.Browser.pointer) {
       
 11403 				if ((!L.Browser.edge) || e.pointerType === 'mouse') { return; }
       
 11404 				count = L.DomEvent._pointersCount;
       
 11405 			} else {
       
 11406 				count = e.touches.length;
       
 11407 			}
       
 11408 
       
 11409 			if (count > 1) { return; }
       
 11410 
       
 11411 			var now = Date.now(),
       
 11412 			    delta = now - (last || now);
       
 11413 
       
 11414 			touch = e.touches ? e.touches[0] : e;
       
 11415 			doubleTap = (delta > 0 && delta <= delay);
       
 11416 			last = now;
       
 11417 		}
       
 11418 
       
 11419 		function onTouchEnd(e) {
       
 11420 			if (doubleTap && !touch.cancelBubble) {
       
 11421 				if (L.Browser.pointer) {
       
 11422 					if ((!L.Browser.edge) || e.pointerType === 'mouse') { return; }
       
 11423 
       
 11424 					// work around .type being readonly with MSPointer* events
       
 11425 					var newTouch = {},
       
 11426 					    prop, i;
       
 11427 
       
 11428 					for (i in touch) {
       
 11429 						prop = touch[i];
       
 11430 						newTouch[i] = prop && prop.bind ? prop.bind(touch) : prop;
       
 11431 					}
       
 11432 					touch = newTouch;
       
 11433 				}
       
 11434 				touch.type = 'dblclick';
       
 11435 				handler(touch);
       
 11436 				last = null;
       
 11437 			}
       
 11438 		}
       
 11439 
       
 11440 		var pre = '_leaflet_',
       
 11441 		    touchstart = this._touchstart,
       
 11442 		    touchend = this._touchend;
       
 11443 
       
 11444 		obj[pre + touchstart + id] = onTouchStart;
       
 11445 		obj[pre + touchend + id] = onTouchEnd;
       
 11446 		obj[pre + 'dblclick' + id] = handler;
       
 11447 
       
 11448 		obj.addEventListener(touchstart, onTouchStart, false);
       
 11449 		obj.addEventListener(touchend, onTouchEnd, false);
       
 11450 
       
 11451 		// On some platforms (notably, chrome<55 on win10 + touchscreen + mouse),
       
 11452 		// the browser doesn't fire touchend/pointerup events but does fire
       
 11453 		// native dblclicks. See #4127.
       
 11454 		// Edge 14 also fires native dblclicks, but only for pointerType mouse, see #5180.
       
 11455 		obj.addEventListener('dblclick', handler, false);
       
 11456 
       
 11457 		return this;
       
 11458 	},
       
 11459 
       
 11460 	removeDoubleTapListener: function (obj, id) {
       
 11461 		var pre = '_leaflet_',
       
 11462 		    touchstart = obj[pre + this._touchstart + id],
       
 11463 		    touchend = obj[pre + this._touchend + id],
       
 11464 		    dblclick = obj[pre + 'dblclick' + id];
       
 11465 
       
 11466 		obj.removeEventListener(this._touchstart, touchstart, false);
       
 11467 		obj.removeEventListener(this._touchend, touchend, false);
       
 11468 		if (!L.Browser.edge) {
       
 11469 			obj.removeEventListener('dblclick', dblclick, false);
       
 11470 		}
       
 11471 
       
 11472 		return this;
       
 11473 	}
       
 11474 });
       
 11475 
       
 11476 
       
 11477 
       
 11478 /*
       
 11479  * Extends L.DomEvent to provide touch support for Internet Explorer and Windows-based devices.
       
 11480  */
       
 11481 
       
 11482 L.extend(L.DomEvent, {
       
 11483 
       
 11484 	POINTER_DOWN:   L.Browser.msPointer ? 'MSPointerDown'   : 'pointerdown',
       
 11485 	POINTER_MOVE:   L.Browser.msPointer ? 'MSPointerMove'   : 'pointermove',
       
 11486 	POINTER_UP:     L.Browser.msPointer ? 'MSPointerUp'     : 'pointerup',
       
 11487 	POINTER_CANCEL: L.Browser.msPointer ? 'MSPointerCancel' : 'pointercancel',
       
 11488 	TAG_WHITE_LIST: ['INPUT', 'SELECT', 'OPTION'],
       
 11489 
       
 11490 	_pointers: {},
       
 11491 	_pointersCount: 0,
       
 11492 
       
 11493 	// Provides a touch events wrapper for (ms)pointer events.
       
 11494 	// ref http://www.w3.org/TR/pointerevents/ https://www.w3.org/Bugs/Public/show_bug.cgi?id=22890
       
 11495 
       
 11496 	addPointerListener: function (obj, type, handler, id) {
       
 11497 
       
 11498 		if (type === 'touchstart') {
       
 11499 			this._addPointerStart(obj, handler, id);
       
 11500 
       
 11501 		} else if (type === 'touchmove') {
       
 11502 			this._addPointerMove(obj, handler, id);
       
 11503 
       
 11504 		} else if (type === 'touchend') {
       
 11505 			this._addPointerEnd(obj, handler, id);
       
 11506 		}
       
 11507 
       
 11508 		return this;
       
 11509 	},
       
 11510 
       
 11511 	removePointerListener: function (obj, type, id) {
       
 11512 		var handler = obj['_leaflet_' + type + id];
       
 11513 
       
 11514 		if (type === 'touchstart') {
       
 11515 			obj.removeEventListener(this.POINTER_DOWN, handler, false);
       
 11516 
       
 11517 		} else if (type === 'touchmove') {
       
 11518 			obj.removeEventListener(this.POINTER_MOVE, handler, false);
       
 11519 
       
 11520 		} else if (type === 'touchend') {
       
 11521 			obj.removeEventListener(this.POINTER_UP, handler, false);
       
 11522 			obj.removeEventListener(this.POINTER_CANCEL, handler, false);
       
 11523 		}
       
 11524 
       
 11525 		return this;
       
 11526 	},
       
 11527 
       
 11528 	_addPointerStart: function (obj, handler, id) {
       
 11529 		var onDown = L.bind(function (e) {
       
 11530 			if (e.pointerType !== 'mouse' && e.MSPOINTER_TYPE_MOUSE && e.pointerType !== e.MSPOINTER_TYPE_MOUSE) {
       
 11531 				// In IE11, some touch events needs to fire for form controls, or
       
 11532 				// the controls will stop working. We keep a whitelist of tag names that
       
 11533 				// need these events. For other target tags, we prevent default on the event.
       
 11534 				if (this.TAG_WHITE_LIST.indexOf(e.target.tagName) < 0) {
       
 11535 					L.DomEvent.preventDefault(e);
       
 11536 				} else {
       
 11537 					return;
       
 11538 				}
       
 11539 			}
       
 11540 
       
 11541 			this._handlePointer(e, handler);
       
 11542 		}, this);
       
 11543 
       
 11544 		obj['_leaflet_touchstart' + id] = onDown;
       
 11545 		obj.addEventListener(this.POINTER_DOWN, onDown, false);
       
 11546 
       
 11547 		// need to keep track of what pointers and how many are active to provide e.touches emulation
       
 11548 		if (!this._pointerDocListener) {
       
 11549 			var pointerUp = L.bind(this._globalPointerUp, this);
       
 11550 
       
 11551 			// we listen documentElement as any drags that end by moving the touch off the screen get fired there
       
 11552 			document.documentElement.addEventListener(this.POINTER_DOWN, L.bind(this._globalPointerDown, this), true);
       
 11553 			document.documentElement.addEventListener(this.POINTER_MOVE, L.bind(this._globalPointerMove, this), true);
       
 11554 			document.documentElement.addEventListener(this.POINTER_UP, pointerUp, true);
       
 11555 			document.documentElement.addEventListener(this.POINTER_CANCEL, pointerUp, true);
       
 11556 
       
 11557 			this._pointerDocListener = true;
       
 11558 		}
       
 11559 	},
       
 11560 
       
 11561 	_globalPointerDown: function (e) {
       
 11562 		this._pointers[e.pointerId] = e;
       
 11563 		this._pointersCount++;
       
 11564 	},
       
 11565 
       
 11566 	_globalPointerMove: function (e) {
       
 11567 		if (this._pointers[e.pointerId]) {
       
 11568 			this._pointers[e.pointerId] = e;
       
 11569 		}
       
 11570 	},
       
 11571 
       
 11572 	_globalPointerUp: function (e) {
       
 11573 		delete this._pointers[e.pointerId];
       
 11574 		this._pointersCount--;
       
 11575 	},
       
 11576 
       
 11577 	_handlePointer: function (e, handler) {
       
 11578 		e.touches = [];
       
 11579 		for (var i in this._pointers) {
       
 11580 			e.touches.push(this._pointers[i]);
       
 11581 		}
       
 11582 		e.changedTouches = [e];
       
 11583 
       
 11584 		handler(e);
       
 11585 	},
       
 11586 
       
 11587 	_addPointerMove: function (obj, handler, id) {
       
 11588 		var onMove = L.bind(function (e) {
       
 11589 			// don't fire touch moves when mouse isn't down
       
 11590 			if ((e.pointerType === e.MSPOINTER_TYPE_MOUSE || e.pointerType === 'mouse') && e.buttons === 0) { return; }
       
 11591 
       
 11592 			this._handlePointer(e, handler);
       
 11593 		}, this);
       
 11594 
       
 11595 		obj['_leaflet_touchmove' + id] = onMove;
       
 11596 		obj.addEventListener(this.POINTER_MOVE, onMove, false);
       
 11597 	},
       
 11598 
       
 11599 	_addPointerEnd: function (obj, handler, id) {
       
 11600 		var onUp = L.bind(function (e) {
       
 11601 			this._handlePointer(e, handler);
       
 11602 		}, this);
       
 11603 
       
 11604 		obj['_leaflet_touchend' + id] = onUp;
       
 11605 		obj.addEventListener(this.POINTER_UP, onUp, false);
       
 11606 		obj.addEventListener(this.POINTER_CANCEL, onUp, false);
       
 11607 	}
       
 11608 });
       
 11609 
       
 11610 
       
 11611 
       
 11612 /*
       
 11613  * L.Handler.TouchZoom is used by L.Map to add pinch zoom on supported mobile browsers.
       
 11614  */
       
 11615 
       
 11616 // @namespace Map
       
 11617 // @section Interaction Options
       
 11618 L.Map.mergeOptions({
       
 11619 	// @section Touch interaction options
       
 11620 	// @option touchZoom: Boolean|String = *
       
 11621 	// Whether the map can be zoomed by touch-dragging with two fingers. If
       
 11622 	// passed `'center'`, it will zoom to the center of the view regardless of
       
 11623 	// where the touch events (fingers) were. Enabled for touch-capable web
       
 11624 	// browsers except for old Androids.
       
 11625 	touchZoom: L.Browser.touch && !L.Browser.android23,
       
 11626 
       
 11627 	// @option bounceAtZoomLimits: Boolean = true
       
 11628 	// Set it to false if you don't want the map to zoom beyond min/max zoom
       
 11629 	// and then bounce back when pinch-zooming.
       
 11630 	bounceAtZoomLimits: true
       
 11631 });
       
 11632 
       
 11633 L.Map.TouchZoom = L.Handler.extend({
       
 11634 	addHooks: function () {
       
 11635 		L.DomUtil.addClass(this._map._container, 'leaflet-touch-zoom');
       
 11636 		L.DomEvent.on(this._map._container, 'touchstart', this._onTouchStart, this);
       
 11637 	},
       
 11638 
       
 11639 	removeHooks: function () {
       
 11640 		L.DomUtil.removeClass(this._map._container, 'leaflet-touch-zoom');
       
 11641 		L.DomEvent.off(this._map._container, 'touchstart', this._onTouchStart, this);
       
 11642 	},
       
 11643 
       
 11644 	_onTouchStart: function (e) {
       
 11645 		var map = this._map;
       
 11646 		if (!e.touches || e.touches.length !== 2 || map._animatingZoom || this._zooming) { return; }
       
 11647 
       
 11648 		var p1 = map.mouseEventToContainerPoint(e.touches[0]),
       
 11649 		    p2 = map.mouseEventToContainerPoint(e.touches[1]);
       
 11650 
       
 11651 		this._centerPoint = map.getSize()._divideBy(2);
       
 11652 		this._startLatLng = map.containerPointToLatLng(this._centerPoint);
       
 11653 		if (map.options.touchZoom !== 'center') {
       
 11654 			this._pinchStartLatLng = map.containerPointToLatLng(p1.add(p2)._divideBy(2));
       
 11655 		}
       
 11656 
       
 11657 		this._startDist = p1.distanceTo(p2);
       
 11658 		this._startZoom = map.getZoom();
       
 11659 
       
 11660 		this._moved = false;
       
 11661 		this._zooming = true;
       
 11662 
       
 11663 		map._stop();
       
 11664 
       
 11665 		L.DomEvent
       
 11666 		    .on(document, 'touchmove', this._onTouchMove, this)
       
 11667 		    .on(document, 'touchend', this._onTouchEnd, this);
       
 11668 
       
 11669 		L.DomEvent.preventDefault(e);
       
 11670 	},
       
 11671 
       
 11672 	_onTouchMove: function (e) {
       
 11673 		if (!e.touches || e.touches.length !== 2 || !this._zooming) { return; }
       
 11674 
       
 11675 		var map = this._map,
       
 11676 		    p1 = map.mouseEventToContainerPoint(e.touches[0]),
       
 11677 		    p2 = map.mouseEventToContainerPoint(e.touches[1]),
       
 11678 		    scale = p1.distanceTo(p2) / this._startDist;
       
 11679 
       
 11680 
       
 11681 		this._zoom = map.getScaleZoom(scale, this._startZoom);
       
 11682 
       
 11683 		if (!map.options.bounceAtZoomLimits && (
       
 11684 			(this._zoom < map.getMinZoom() && scale < 1) ||
       
 11685 			(this._zoom > map.getMaxZoom() && scale > 1))) {
       
 11686 			this._zoom = map._limitZoom(this._zoom);
       
 11687 		}
       
 11688 
       
 11689 		if (map.options.touchZoom === 'center') {
       
 11690 			this._center = this._startLatLng;
       
 11691 			if (scale === 1) { return; }
       
 11692 		} else {
       
 11693 			// Get delta from pinch to center, so centerLatLng is delta applied to initial pinchLatLng
       
 11694 			var delta = p1._add(p2)._divideBy(2)._subtract(this._centerPoint);
       
 11695 			if (scale === 1 && delta.x === 0 && delta.y === 0) { return; }
       
 11696 			this._center = map.unproject(map.project(this._pinchStartLatLng, this._zoom).subtract(delta), this._zoom);
       
 11697 		}
       
 11698 
       
 11699 		if (!this._moved) {
       
 11700 			map._moveStart(true);
       
 11701 			this._moved = true;
       
 11702 		}
       
 11703 
       
 11704 		L.Util.cancelAnimFrame(this._animRequest);
       
 11705 
       
 11706 		var moveFn = L.bind(map._move, map, this._center, this._zoom, {pinch: true, round: false});
       
 11707 		this._animRequest = L.Util.requestAnimFrame(moveFn, this, true);
       
 11708 
       
 11709 		L.DomEvent.preventDefault(e);
       
 11710 	},
       
 11711 
       
 11712 	_onTouchEnd: function () {
       
 11713 		if (!this._moved || !this._zooming) {
       
 11714 			this._zooming = false;
       
 11715 			return;
       
 11716 		}
       
 11717 
       
 11718 		this._zooming = false;
       
 11719 		L.Util.cancelAnimFrame(this._animRequest);
       
 11720 
       
 11721 		L.DomEvent
       
 11722 		    .off(document, 'touchmove', this._onTouchMove)
       
 11723 		    .off(document, 'touchend', this._onTouchEnd);
       
 11724 
       
 11725 		// Pinch updates GridLayers' levels only when zoomSnap is off, so zoomSnap becomes noUpdate.
       
 11726 		if (this._map.options.zoomAnimation) {
       
 11727 			this._map._animateZoom(this._center, this._map._limitZoom(this._zoom), true, this._map.options.zoomSnap);
       
 11728 		} else {
       
 11729 			this._map._resetView(this._center, this._map._limitZoom(this._zoom));
       
 11730 		}
       
 11731 	}
       
 11732 });
       
 11733 
       
 11734 // @section Handlers
       
 11735 // @property touchZoom: Handler
       
 11736 // Touch zoom handler.
       
 11737 L.Map.addInitHook('addHandler', 'touchZoom', L.Map.TouchZoom);
       
 11738 
       
 11739 
       
 11740 
       
 11741 /*
       
 11742  * L.Map.Tap is used to enable mobile hacks like quick taps and long hold.
       
 11743  */
       
 11744 
       
 11745 // @namespace Map
       
 11746 // @section Interaction Options
       
 11747 L.Map.mergeOptions({
       
 11748 	// @section Touch interaction options
       
 11749 	// @option tap: Boolean = true
       
 11750 	// Enables mobile hacks for supporting instant taps (fixing 200ms click
       
 11751 	// delay on iOS/Android) and touch holds (fired as `contextmenu` events).
       
 11752 	tap: true,
       
 11753 
       
 11754 	// @option tapTolerance: Number = 15
       
 11755 	// The max number of pixels a user can shift his finger during touch
       
 11756 	// for it to be considered a valid tap.
       
 11757 	tapTolerance: 15
       
 11758 });
       
 11759 
       
 11760 L.Map.Tap = L.Handler.extend({
       
 11761 	addHooks: function () {
       
 11762 		L.DomEvent.on(this._map._container, 'touchstart', this._onDown, this);
       
 11763 	},
       
 11764 
       
 11765 	removeHooks: function () {
       
 11766 		L.DomEvent.off(this._map._container, 'touchstart', this._onDown, this);
       
 11767 	},
       
 11768 
       
 11769 	_onDown: function (e) {
       
 11770 		if (!e.touches) { return; }
       
 11771 
       
 11772 		L.DomEvent.preventDefault(e);
       
 11773 
       
 11774 		this._fireClick = true;
       
 11775 
       
 11776 		// don't simulate click or track longpress if more than 1 touch
       
 11777 		if (e.touches.length > 1) {
       
 11778 			this._fireClick = false;
       
 11779 			clearTimeout(this._holdTimeout);
       
 11780 			return;
       
 11781 		}
       
 11782 
       
 11783 		var first = e.touches[0],
       
 11784 		    el = first.target;
       
 11785 
       
 11786 		this._startPos = this._newPos = new L.Point(first.clientX, first.clientY);
       
 11787 
       
 11788 		// if touching a link, highlight it
       
 11789 		if (el.tagName && el.tagName.toLowerCase() === 'a') {
       
 11790 			L.DomUtil.addClass(el, 'leaflet-active');
       
 11791 		}
       
 11792 
       
 11793 		// simulate long hold but setting a timeout
       
 11794 		this._holdTimeout = setTimeout(L.bind(function () {
       
 11795 			if (this._isTapValid()) {
       
 11796 				this._fireClick = false;
       
 11797 				this._onUp();
       
 11798 				this._simulateEvent('contextmenu', first);
       
 11799 			}
       
 11800 		}, this), 1000);
       
 11801 
       
 11802 		this._simulateEvent('mousedown', first);
       
 11803 
       
 11804 		L.DomEvent.on(document, {
       
 11805 			touchmove: this._onMove,
       
 11806 			touchend: this._onUp
       
 11807 		}, this);
       
 11808 	},
       
 11809 
       
 11810 	_onUp: function (e) {
       
 11811 		clearTimeout(this._holdTimeout);
       
 11812 
       
 11813 		L.DomEvent.off(document, {
       
 11814 			touchmove: this._onMove,
       
 11815 			touchend: this._onUp
       
 11816 		}, this);
       
 11817 
       
 11818 		if (this._fireClick && e && e.changedTouches) {
       
 11819 
       
 11820 			var first = e.changedTouches[0],
       
 11821 			    el = first.target;
       
 11822 
       
 11823 			if (el && el.tagName && el.tagName.toLowerCase() === 'a') {
       
 11824 				L.DomUtil.removeClass(el, 'leaflet-active');
       
 11825 			}
       
 11826 
       
 11827 			this._simulateEvent('mouseup', first);
       
 11828 
       
 11829 			// simulate click if the touch didn't move too much
       
 11830 			if (this._isTapValid()) {
       
 11831 				this._simulateEvent('click', first);
       
 11832 			}
       
 11833 		}
       
 11834 	},
       
 11835 
       
 11836 	_isTapValid: function () {
       
 11837 		return this._newPos.distanceTo(this._startPos) <= this._map.options.tapTolerance;
       
 11838 	},
       
 11839 
       
 11840 	_onMove: function (e) {
       
 11841 		var first = e.touches[0];
       
 11842 		this._newPos = new L.Point(first.clientX, first.clientY);
       
 11843 		this._simulateEvent('mousemove', first);
       
 11844 	},
       
 11845 
       
 11846 	_simulateEvent: function (type, e) {
       
 11847 		var simulatedEvent = document.createEvent('MouseEvents');
       
 11848 
       
 11849 		simulatedEvent._simulated = true;
       
 11850 		e.target._simulatedClick = true;
       
 11851 
       
 11852 		simulatedEvent.initMouseEvent(
       
 11853 		        type, true, true, window, 1,
       
 11854 		        e.screenX, e.screenY,
       
 11855 		        e.clientX, e.clientY,
       
 11856 		        false, false, false, false, 0, null);
       
 11857 
       
 11858 		e.target.dispatchEvent(simulatedEvent);
       
 11859 	}
       
 11860 });
       
 11861 
       
 11862 // @section Handlers
       
 11863 // @property tap: Handler
       
 11864 // Mobile touch hacks (quick tap and touch hold) handler.
       
 11865 if (L.Browser.touch && !L.Browser.pointer) {
       
 11866 	L.Map.addInitHook('addHandler', 'tap', L.Map.Tap);
       
 11867 }
       
 11868 
       
 11869 
       
 11870 
       
 11871 /*
       
 11872  * L.Handler.BoxZoom is used to add shift-drag zoom interaction to the map
       
 11873  * (zoom to a selected bounding box), enabled by default.
       
 11874  */
       
 11875 
       
 11876 // @namespace Map
       
 11877 // @section Interaction Options
       
 11878 L.Map.mergeOptions({
       
 11879 	// @option boxZoom: Boolean = true
       
 11880 	// Whether the map can be zoomed to a rectangular area specified by
       
 11881 	// dragging the mouse while pressing the shift key.
       
 11882 	boxZoom: true
       
 11883 });
       
 11884 
       
 11885 L.Map.BoxZoom = L.Handler.extend({
       
 11886 	initialize: function (map) {
       
 11887 		this._map = map;
       
 11888 		this._container = map._container;
       
 11889 		this._pane = map._panes.overlayPane;
       
 11890 	},
       
 11891 
       
 11892 	addHooks: function () {
       
 11893 		L.DomEvent.on(this._container, 'mousedown', this._onMouseDown, this);
       
 11894 	},
       
 11895 
       
 11896 	removeHooks: function () {
       
 11897 		L.DomEvent.off(this._container, 'mousedown', this._onMouseDown, this);
       
 11898 	},
       
 11899 
       
 11900 	moved: function () {
       
 11901 		return this._moved;
       
 11902 	},
       
 11903 
       
 11904 	_resetState: function () {
       
 11905 		this._moved = false;
       
 11906 	},
       
 11907 
       
 11908 	_onMouseDown: function (e) {
       
 11909 		if (!e.shiftKey || ((e.which !== 1) && (e.button !== 1))) { return false; }
       
 11910 
       
 11911 		this._resetState();
       
 11912 
       
 11913 		L.DomUtil.disableTextSelection();
       
 11914 		L.DomUtil.disableImageDrag();
       
 11915 
       
 11916 		this._startPoint = this._map.mouseEventToContainerPoint(e);
       
 11917 
       
 11918 		L.DomEvent.on(document, {
       
 11919 			contextmenu: L.DomEvent.stop,
       
 11920 			mousemove: this._onMouseMove,
       
 11921 			mouseup: this._onMouseUp,
       
 11922 			keydown: this._onKeyDown
       
 11923 		}, this);
       
 11924 	},
       
 11925 
       
 11926 	_onMouseMove: function (e) {
       
 11927 		if (!this._moved) {
       
 11928 			this._moved = true;
       
 11929 
       
 11930 			this._box = L.DomUtil.create('div', 'leaflet-zoom-box', this._container);
       
 11931 			L.DomUtil.addClass(this._container, 'leaflet-crosshair');
       
 11932 
       
 11933 			this._map.fire('boxzoomstart');
       
 11934 		}
       
 11935 
       
 11936 		this._point = this._map.mouseEventToContainerPoint(e);
       
 11937 
       
 11938 		var bounds = new L.Bounds(this._point, this._startPoint),
       
 11939 		    size = bounds.getSize();
       
 11940 
       
 11941 		L.DomUtil.setPosition(this._box, bounds.min);
       
 11942 
       
 11943 		this._box.style.width  = size.x + 'px';
       
 11944 		this._box.style.height = size.y + 'px';
       
 11945 	},
       
 11946 
       
 11947 	_finish: function () {
       
 11948 		if (this._moved) {
       
 11949 			L.DomUtil.remove(this._box);
       
 11950 			L.DomUtil.removeClass(this._container, 'leaflet-crosshair');
       
 11951 		}
       
 11952 
       
 11953 		L.DomUtil.enableTextSelection();
       
 11954 		L.DomUtil.enableImageDrag();
       
 11955 
       
 11956 		L.DomEvent.off(document, {
       
 11957 			contextmenu: L.DomEvent.stop,
       
 11958 			mousemove: this._onMouseMove,
       
 11959 			mouseup: this._onMouseUp,
       
 11960 			keydown: this._onKeyDown
       
 11961 		}, this);
       
 11962 	},
       
 11963 
       
 11964 	_onMouseUp: function (e) {
       
 11965 		if ((e.which !== 1) && (e.button !== 1)) { return; }
       
 11966 
       
 11967 		this._finish();
       
 11968 
       
 11969 		if (!this._moved) { return; }
       
 11970 		// Postpone to next JS tick so internal click event handling
       
 11971 		// still see it as "moved".
       
 11972 		setTimeout(L.bind(this._resetState, this), 0);
       
 11973 
       
 11974 		var bounds = new L.LatLngBounds(
       
 11975 		        this._map.containerPointToLatLng(this._startPoint),
       
 11976 		        this._map.containerPointToLatLng(this._point));
       
 11977 
       
 11978 		this._map
       
 11979 			.fitBounds(bounds)
       
 11980 			.fire('boxzoomend', {boxZoomBounds: bounds});
       
 11981 	},
       
 11982 
       
 11983 	_onKeyDown: function (e) {
       
 11984 		if (e.keyCode === 27) {
       
 11985 			this._finish();
       
 11986 		}
       
 11987 	}
       
 11988 });
       
 11989 
       
 11990 // @section Handlers
       
 11991 // @property boxZoom: Handler
       
 11992 // Box (shift-drag with mouse) zoom handler.
       
 11993 L.Map.addInitHook('addHandler', 'boxZoom', L.Map.BoxZoom);
       
 11994 
       
 11995 
       
 11996 
       
 11997 /*
       
 11998  * L.Map.Keyboard is handling keyboard interaction with the map, enabled by default.
       
 11999  */
       
 12000 
       
 12001 // @namespace Map
       
 12002 // @section Keyboard Navigation Options
       
 12003 L.Map.mergeOptions({
       
 12004 	// @option keyboard: Boolean = true
       
 12005 	// Makes the map focusable and allows users to navigate the map with keyboard
       
 12006 	// arrows and `+`/`-` keys.
       
 12007 	keyboard: true,
       
 12008 
       
 12009 	// @option keyboardPanDelta: Number = 80
       
 12010 	// Amount of pixels to pan when pressing an arrow key.
       
 12011 	keyboardPanDelta: 80
       
 12012 });
       
 12013 
       
 12014 L.Map.Keyboard = L.Handler.extend({
       
 12015 
       
 12016 	keyCodes: {
       
 12017 		left:    [37],
       
 12018 		right:   [39],
       
 12019 		down:    [40],
       
 12020 		up:      [38],
       
 12021 		zoomIn:  [187, 107, 61, 171],
       
 12022 		zoomOut: [189, 109, 54, 173]
       
 12023 	},
       
 12024 
       
 12025 	initialize: function (map) {
       
 12026 		this._map = map;
       
 12027 
       
 12028 		this._setPanDelta(map.options.keyboardPanDelta);
       
 12029 		this._setZoomDelta(map.options.zoomDelta);
       
 12030 	},
       
 12031 
       
 12032 	addHooks: function () {
       
 12033 		var container = this._map._container;
       
 12034 
       
 12035 		// make the container focusable by tabbing
       
 12036 		if (container.tabIndex <= 0) {
       
 12037 			container.tabIndex = '0';
       
 12038 		}
       
 12039 
       
 12040 		L.DomEvent.on(container, {
       
 12041 			focus: this._onFocus,
       
 12042 			blur: this._onBlur,
       
 12043 			mousedown: this._onMouseDown
       
 12044 		}, this);
       
 12045 
       
 12046 		this._map.on({
       
 12047 			focus: this._addHooks,
       
 12048 			blur: this._removeHooks
       
 12049 		}, this);
       
 12050 	},
       
 12051 
       
 12052 	removeHooks: function () {
       
 12053 		this._removeHooks();
       
 12054 
       
 12055 		L.DomEvent.off(this._map._container, {
       
 12056 			focus: this._onFocus,
       
 12057 			blur: this._onBlur,
       
 12058 			mousedown: this._onMouseDown
       
 12059 		}, this);
       
 12060 
       
 12061 		this._map.off({
       
 12062 			focus: this._addHooks,
       
 12063 			blur: this._removeHooks
       
 12064 		}, this);
       
 12065 	},
       
 12066 
       
 12067 	_onMouseDown: function () {
       
 12068 		if (this._focused) { return; }
       
 12069 
       
 12070 		var body = document.body,
       
 12071 		    docEl = document.documentElement,
       
 12072 		    top = body.scrollTop || docEl.scrollTop,
       
 12073 		    left = body.scrollLeft || docEl.scrollLeft;
       
 12074 
       
 12075 		this._map._container.focus();
       
 12076 
       
 12077 		window.scrollTo(left, top);
       
 12078 	},
       
 12079 
       
 12080 	_onFocus: function () {
       
 12081 		this._focused = true;
       
 12082 		this._map.fire('focus');
       
 12083 	},
       
 12084 
       
 12085 	_onBlur: function () {
       
 12086 		this._focused = false;
       
 12087 		this._map.fire('blur');
       
 12088 	},
       
 12089 
       
 12090 	_setPanDelta: function (panDelta) {
       
 12091 		var keys = this._panKeys = {},
       
 12092 		    codes = this.keyCodes,
       
 12093 		    i, len;
       
 12094 
       
 12095 		for (i = 0, len = codes.left.length; i < len; i++) {
       
 12096 			keys[codes.left[i]] = [-1 * panDelta, 0];
       
 12097 		}
       
 12098 		for (i = 0, len = codes.right.length; i < len; i++) {
       
 12099 			keys[codes.right[i]] = [panDelta, 0];
       
 12100 		}
       
 12101 		for (i = 0, len = codes.down.length; i < len; i++) {
       
 12102 			keys[codes.down[i]] = [0, panDelta];
       
 12103 		}
       
 12104 		for (i = 0, len = codes.up.length; i < len; i++) {
       
 12105 			keys[codes.up[i]] = [0, -1 * panDelta];
       
 12106 		}
       
 12107 	},
       
 12108 
       
 12109 	_setZoomDelta: function (zoomDelta) {
       
 12110 		var keys = this._zoomKeys = {},
       
 12111 		    codes = this.keyCodes,
       
 12112 		    i, len;
       
 12113 
       
 12114 		for (i = 0, len = codes.zoomIn.length; i < len; i++) {
       
 12115 			keys[codes.zoomIn[i]] = zoomDelta;
       
 12116 		}
       
 12117 		for (i = 0, len = codes.zoomOut.length; i < len; i++) {
       
 12118 			keys[codes.zoomOut[i]] = -zoomDelta;
       
 12119 		}
       
 12120 	},
       
 12121 
       
 12122 	_addHooks: function () {
       
 12123 		L.DomEvent.on(document, 'keydown', this._onKeyDown, this);
       
 12124 	},
       
 12125 
       
 12126 	_removeHooks: function () {
       
 12127 		L.DomEvent.off(document, 'keydown', this._onKeyDown, this);
       
 12128 	},
       
 12129 
       
 12130 	_onKeyDown: function (e) {
       
 12131 		if (e.altKey || e.ctrlKey || e.metaKey) { return; }
       
 12132 
       
 12133 		var key = e.keyCode,
       
 12134 		    map = this._map,
       
 12135 		    offset;
       
 12136 
       
 12137 		if (key in this._panKeys) {
       
 12138 
       
 12139 			if (map._panAnim && map._panAnim._inProgress) { return; }
       
 12140 
       
 12141 			offset = this._panKeys[key];
       
 12142 			if (e.shiftKey) {
       
 12143 				offset = L.point(offset).multiplyBy(3);
       
 12144 			}
       
 12145 
       
 12146 			map.panBy(offset);
       
 12147 
       
 12148 			if (map.options.maxBounds) {
       
 12149 				map.panInsideBounds(map.options.maxBounds);
       
 12150 			}
       
 12151 
       
 12152 		} else if (key in this._zoomKeys) {
       
 12153 			map.setZoom(map.getZoom() + (e.shiftKey ? 3 : 1) * this._zoomKeys[key]);
       
 12154 
       
 12155 		} else if (key === 27) {
       
 12156 			map.closePopup();
       
 12157 
       
 12158 		} else {
       
 12159 			return;
       
 12160 		}
       
 12161 
       
 12162 		L.DomEvent.stop(e);
       
 12163 	}
       
 12164 });
       
 12165 
       
 12166 // @section Handlers
       
 12167 // @section Handlers
       
 12168 // @property keyboard: Handler
       
 12169 // Keyboard navigation handler.
       
 12170 L.Map.addInitHook('addHandler', 'keyboard', L.Map.Keyboard);
       
 12171 
       
 12172 
       
 12173 
       
 12174 /*
       
 12175  * L.Handler.MarkerDrag is used internally by L.Marker to make the markers draggable.
       
 12176  */
       
 12177 
       
 12178 
       
 12179 /* @namespace Marker
       
 12180  * @section Interaction handlers
       
 12181  *
       
 12182  * Interaction handlers are properties of a marker instance that allow you to control interaction behavior in runtime, enabling or disabling certain features such as dragging (see `Handler` methods). Example:
       
 12183  *
       
 12184  * ```js
       
 12185  * marker.dragging.disable();
       
 12186  * ```
       
 12187  *
       
 12188  * @property dragging: Handler
       
 12189  * Marker dragging handler (by both mouse and touch).
       
 12190  */
       
 12191 
       
 12192 L.Handler.MarkerDrag = L.Handler.extend({
       
 12193 	initialize: function (marker) {
       
 12194 		this._marker = marker;
       
 12195 	},
       
 12196 
       
 12197 	addHooks: function () {
       
 12198 		var icon = this._marker._icon;
       
 12199 
       
 12200 		if (!this._draggable) {
       
 12201 			this._draggable = new L.Draggable(icon, icon, true);
       
 12202 		}
       
 12203 
       
 12204 		this._draggable.on({
       
 12205 			dragstart: this._onDragStart,
       
 12206 			drag: this._onDrag,
       
 12207 			dragend: this._onDragEnd
       
 12208 		}, this).enable();
       
 12209 
       
 12210 		L.DomUtil.addClass(icon, 'leaflet-marker-draggable');
       
 12211 	},
       
 12212 
       
 12213 	removeHooks: function () {
       
 12214 		this._draggable.off({
       
 12215 			dragstart: this._onDragStart,
       
 12216 			drag: this._onDrag,
       
 12217 			dragend: this._onDragEnd
       
 12218 		}, this).disable();
       
 12219 
       
 12220 		if (this._marker._icon) {
       
 12221 			L.DomUtil.removeClass(this._marker._icon, 'leaflet-marker-draggable');
       
 12222 		}
       
 12223 	},
       
 12224 
       
 12225 	moved: function () {
       
 12226 		return this._draggable && this._draggable._moved;
       
 12227 	},
       
 12228 
       
 12229 	_onDragStart: function () {
       
 12230 		// @section Dragging events
       
 12231 		// @event dragstart: Event
       
 12232 		// Fired when the user starts dragging the marker.
       
 12233 
       
 12234 		// @event movestart: Event
       
 12235 		// Fired when the marker starts moving (because of dragging).
       
 12236 
       
 12237 		this._oldLatLng = this._marker.getLatLng();
       
 12238 		this._marker
       
 12239 		    .closePopup()
       
 12240 		    .fire('movestart')
       
 12241 		    .fire('dragstart');
       
 12242 	},
       
 12243 
       
 12244 	_onDrag: function (e) {
       
 12245 		var marker = this._marker,
       
 12246 		    shadow = marker._shadow,
       
 12247 		    iconPos = L.DomUtil.getPosition(marker._icon),
       
 12248 		    latlng = marker._map.layerPointToLatLng(iconPos);
       
 12249 
       
 12250 		// update shadow position
       
 12251 		if (shadow) {
       
 12252 			L.DomUtil.setPosition(shadow, iconPos);
       
 12253 		}
       
 12254 
       
 12255 		marker._latlng = latlng;
       
 12256 		e.latlng = latlng;
       
 12257 		e.oldLatLng = this._oldLatLng;
       
 12258 
       
 12259 		// @event drag: Event
       
 12260 		// Fired repeatedly while the user drags the marker.
       
 12261 		marker
       
 12262 		    .fire('move', e)
       
 12263 		    .fire('drag', e);
       
 12264 	},
       
 12265 
       
 12266 	_onDragEnd: function (e) {
       
 12267 		// @event dragend: DragEndEvent
       
 12268 		// Fired when the user stops dragging the marker.
       
 12269 
       
 12270 		// @event moveend: Event
       
 12271 		// Fired when the marker stops moving (because of dragging).
       
 12272 		delete this._oldLatLng;
       
 12273 		this._marker
       
 12274 		    .fire('moveend')
       
 12275 		    .fire('dragend', e);
       
 12276 	}
       
 12277 });
       
 12278 
       
 12279 
       
 12280 
       
 12281 /*
       
 12282  * @class Control
       
 12283  * @aka L.Control
       
 12284  * @inherits Class
       
 12285  *
       
 12286  * L.Control is a base class for implementing map controls. Handles positioning.
       
 12287  * All other controls extend from this class.
       
 12288  */
       
 12289 
       
 12290 L.Control = L.Class.extend({
       
 12291 	// @section
       
 12292 	// @aka Control options
       
 12293 	options: {
       
 12294 		// @option position: String = 'topright'
       
 12295 		// The position of the control (one of the map corners). Possible values are `'topleft'`,
       
 12296 		// `'topright'`, `'bottomleft'` or `'bottomright'`
       
 12297 		position: 'topright'
       
 12298 	},
       
 12299 
       
 12300 	initialize: function (options) {
       
 12301 		L.setOptions(this, options);
       
 12302 	},
       
 12303 
       
 12304 	/* @section
       
 12305 	 * Classes extending L.Control will inherit the following methods:
       
 12306 	 *
       
 12307 	 * @method getPosition: string
       
 12308 	 * Returns the position of the control.
       
 12309 	 */
       
 12310 	getPosition: function () {
       
 12311 		return this.options.position;
       
 12312 	},
       
 12313 
       
 12314 	// @method setPosition(position: string): this
       
 12315 	// Sets the position of the control.
       
 12316 	setPosition: function (position) {
       
 12317 		var map = this._map;
       
 12318 
       
 12319 		if (map) {
       
 12320 			map.removeControl(this);
       
 12321 		}
       
 12322 
       
 12323 		this.options.position = position;
       
 12324 
       
 12325 		if (map) {
       
 12326 			map.addControl(this);
       
 12327 		}
       
 12328 
       
 12329 		return this;
       
 12330 	},
       
 12331 
       
 12332 	// @method getContainer: HTMLElement
       
 12333 	// Returns the HTMLElement that contains the control.
       
 12334 	getContainer: function () {
       
 12335 		return this._container;
       
 12336 	},
       
 12337 
       
 12338 	// @method addTo(map: Map): this
       
 12339 	// Adds the control to the given map.
       
 12340 	addTo: function (map) {
       
 12341 		this.remove();
       
 12342 		this._map = map;
       
 12343 
       
 12344 		var container = this._container = this.onAdd(map),
       
 12345 		    pos = this.getPosition(),
       
 12346 		    corner = map._controlCorners[pos];
       
 12347 
       
 12348 		L.DomUtil.addClass(container, 'leaflet-control');
       
 12349 
       
 12350 		if (pos.indexOf('bottom') !== -1) {
       
 12351 			corner.insertBefore(container, corner.firstChild);
       
 12352 		} else {
       
 12353 			corner.appendChild(container);
       
 12354 		}
       
 12355 
       
 12356 		return this;
       
 12357 	},
       
 12358 
       
 12359 	// @method remove: this
       
 12360 	// Removes the control from the map it is currently active on.
       
 12361 	remove: function () {
       
 12362 		if (!this._map) {
       
 12363 			return this;
       
 12364 		}
       
 12365 
       
 12366 		L.DomUtil.remove(this._container);
       
 12367 
       
 12368 		if (this.onRemove) {
       
 12369 			this.onRemove(this._map);
       
 12370 		}
       
 12371 
       
 12372 		this._map = null;
       
 12373 
       
 12374 		return this;
       
 12375 	},
       
 12376 
       
 12377 	_refocusOnMap: function (e) {
       
 12378 		// if map exists and event is not a keyboard event
       
 12379 		if (this._map && e && e.screenX > 0 && e.screenY > 0) {
       
 12380 			this._map.getContainer().focus();
       
 12381 		}
       
 12382 	}
       
 12383 });
       
 12384 
       
 12385 L.control = function (options) {
       
 12386 	return new L.Control(options);
       
 12387 };
       
 12388 
       
 12389 /* @section Extension methods
       
 12390  * @uninheritable
       
 12391  *
       
 12392  * Every control should extend from `L.Control` and (re-)implement the following methods.
       
 12393  *
       
 12394  * @method onAdd(map: Map): HTMLElement
       
 12395  * Should return the container DOM element for the control and add listeners on relevant map events. Called on [`control.addTo(map)`](#control-addTo).
       
 12396  *
       
 12397  * @method onRemove(map: Map)
       
 12398  * Optional method. Should contain all clean up code that removes the listeners previously added in [`onAdd`](#control-onadd). Called on [`control.remove()`](#control-remove).
       
 12399  */
       
 12400 
       
 12401 /* @namespace Map
       
 12402  * @section Methods for Layers and Controls
       
 12403  */
       
 12404 L.Map.include({
       
 12405 	// @method addControl(control: Control): this
       
 12406 	// Adds the given control to the map
       
 12407 	addControl: function (control) {
       
 12408 		control.addTo(this);
       
 12409 		return this;
       
 12410 	},
       
 12411 
       
 12412 	// @method removeControl(control: Control): this
       
 12413 	// Removes the given control from the map
       
 12414 	removeControl: function (control) {
       
 12415 		control.remove();
       
 12416 		return this;
       
 12417 	},
       
 12418 
       
 12419 	_initControlPos: function () {
       
 12420 		var corners = this._controlCorners = {},
       
 12421 		    l = 'leaflet-',
       
 12422 		    container = this._controlContainer =
       
 12423 		            L.DomUtil.create('div', l + 'control-container', this._container);
       
 12424 
       
 12425 		function createCorner(vSide, hSide) {
       
 12426 			var className = l + vSide + ' ' + l + hSide;
       
 12427 
       
 12428 			corners[vSide + hSide] = L.DomUtil.create('div', className, container);
       
 12429 		}
       
 12430 
       
 12431 		createCorner('top', 'left');
       
 12432 		createCorner('top', 'right');
       
 12433 		createCorner('bottom', 'left');
       
 12434 		createCorner('bottom', 'right');
       
 12435 	},
       
 12436 
       
 12437 	_clearControlPos: function () {
       
 12438 		L.DomUtil.remove(this._controlContainer);
       
 12439 	}
       
 12440 });
       
 12441 
       
 12442 
       
 12443 
       
 12444 /*
       
 12445  * @class Control.Zoom
       
 12446  * @aka L.Control.Zoom
       
 12447  * @inherits Control
       
 12448  *
       
 12449  * A basic zoom control with two buttons (zoom in and zoom out). It is put on the map by default unless you set its [`zoomControl` option](#map-zoomcontrol) to `false`. Extends `Control`.
       
 12450  */
       
 12451 
       
 12452 L.Control.Zoom = L.Control.extend({
       
 12453 	// @section
       
 12454 	// @aka Control.Zoom options
       
 12455 	options: {
       
 12456 		position: 'topleft',
       
 12457 
       
 12458 		// @option zoomInText: String = '+'
       
 12459 		// The text set on the 'zoom in' button.
       
 12460 		zoomInText: '+',
       
 12461 
       
 12462 		// @option zoomInTitle: String = 'Zoom in'
       
 12463 		// The title set on the 'zoom in' button.
       
 12464 		zoomInTitle: 'Zoom in',
       
 12465 
       
 12466 		// @option zoomOutText: String = '-'
       
 12467 		// The text set on the 'zoom out' button.
       
 12468 		zoomOutText: '-',
       
 12469 
       
 12470 		// @option zoomOutTitle: String = 'Zoom out'
       
 12471 		// The title set on the 'zoom out' button.
       
 12472 		zoomOutTitle: 'Zoom out'
       
 12473 	},
       
 12474 
       
 12475 	onAdd: function (map) {
       
 12476 		var zoomName = 'leaflet-control-zoom',
       
 12477 		    container = L.DomUtil.create('div', zoomName + ' leaflet-bar'),
       
 12478 		    options = this.options;
       
 12479 
       
 12480 		this._zoomInButton  = this._createButton(options.zoomInText, options.zoomInTitle,
       
 12481 		        zoomName + '-in',  container, this._zoomIn);
       
 12482 		this._zoomOutButton = this._createButton(options.zoomOutText, options.zoomOutTitle,
       
 12483 		        zoomName + '-out', container, this._zoomOut);
       
 12484 
       
 12485 		this._updateDisabled();
       
 12486 		map.on('zoomend zoomlevelschange', this._updateDisabled, this);
       
 12487 
       
 12488 		return container;
       
 12489 	},
       
 12490 
       
 12491 	onRemove: function (map) {
       
 12492 		map.off('zoomend zoomlevelschange', this._updateDisabled, this);
       
 12493 	},
       
 12494 
       
 12495 	disable: function () {
       
 12496 		this._disabled = true;
       
 12497 		this._updateDisabled();
       
 12498 		return this;
       
 12499 	},
       
 12500 
       
 12501 	enable: function () {
       
 12502 		this._disabled = false;
       
 12503 		this._updateDisabled();
       
 12504 		return this;
       
 12505 	},
       
 12506 
       
 12507 	_zoomIn: function (e) {
       
 12508 		if (!this._disabled && this._map._zoom < this._map.getMaxZoom()) {
       
 12509 			this._map.zoomIn(this._map.options.zoomDelta * (e.shiftKey ? 3 : 1));
       
 12510 		}
       
 12511 	},
       
 12512 
       
 12513 	_zoomOut: function (e) {
       
 12514 		if (!this._disabled && this._map._zoom > this._map.getMinZoom()) {
       
 12515 			this._map.zoomOut(this._map.options.zoomDelta * (e.shiftKey ? 3 : 1));
       
 12516 		}
       
 12517 	},
       
 12518 
       
 12519 	_createButton: function (html, title, className, container, fn) {
       
 12520 		var link = L.DomUtil.create('a', className, container);
       
 12521 		link.innerHTML = html;
       
 12522 		link.href = '#';
       
 12523 		link.title = title;
       
 12524 
       
 12525 		/*
       
 12526 		 * Will force screen readers like VoiceOver to read this as "Zoom in - button"
       
 12527 		 */
       
 12528 		link.setAttribute('role', 'button');
       
 12529 		link.setAttribute('aria-label', title);
       
 12530 
       
 12531 		L.DomEvent
       
 12532 		    .on(link, 'mousedown dblclick', L.DomEvent.stopPropagation)
       
 12533 		    .on(link, 'click', L.DomEvent.stop)
       
 12534 		    .on(link, 'click', fn, this)
       
 12535 		    .on(link, 'click', this._refocusOnMap, this);
       
 12536 
       
 12537 		return link;
       
 12538 	},
       
 12539 
       
 12540 	_updateDisabled: function () {
       
 12541 		var map = this._map,
       
 12542 		    className = 'leaflet-disabled';
       
 12543 
       
 12544 		L.DomUtil.removeClass(this._zoomInButton, className);
       
 12545 		L.DomUtil.removeClass(this._zoomOutButton, className);
       
 12546 
       
 12547 		if (this._disabled || map._zoom === map.getMinZoom()) {
       
 12548 			L.DomUtil.addClass(this._zoomOutButton, className);
       
 12549 		}
       
 12550 		if (this._disabled || map._zoom === map.getMaxZoom()) {
       
 12551 			L.DomUtil.addClass(this._zoomInButton, className);
       
 12552 		}
       
 12553 	}
       
 12554 });
       
 12555 
       
 12556 // @namespace Map
       
 12557 // @section Control options
       
 12558 // @option zoomControl: Boolean = true
       
 12559 // Whether a [zoom control](#control-zoom) is added to the map by default.
       
 12560 L.Map.mergeOptions({
       
 12561 	zoomControl: true
       
 12562 });
       
 12563 
       
 12564 L.Map.addInitHook(function () {
       
 12565 	if (this.options.zoomControl) {
       
 12566 		this.zoomControl = new L.Control.Zoom();
       
 12567 		this.addControl(this.zoomControl);
       
 12568 	}
       
 12569 });
       
 12570 
       
 12571 // @namespace Control.Zoom
       
 12572 // @factory L.control.zoom(options: Control.Zoom options)
       
 12573 // Creates a zoom control
       
 12574 L.control.zoom = function (options) {
       
 12575 	return new L.Control.Zoom(options);
       
 12576 };
       
 12577 
       
 12578 
       
 12579 
       
 12580 /*
       
 12581  * @class Control.Attribution
       
 12582  * @aka L.Control.Attribution
       
 12583  * @inherits Control
       
 12584  *
       
 12585  * The attribution control allows you to display attribution data in a small text box on a map. It is put on the map by default unless you set its [`attributionControl` option](#map-attributioncontrol) to `false`, and it fetches attribution texts from layers with the [`getAttribution` method](#layer-getattribution) automatically. Extends Control.
       
 12586  */
       
 12587 
       
 12588 L.Control.Attribution = L.Control.extend({
       
 12589 	// @section
       
 12590 	// @aka Control.Attribution options
       
 12591 	options: {
       
 12592 		position: 'bottomright',
       
 12593 
       
 12594 		// @option prefix: String = 'Leaflet'
       
 12595 		// The HTML text shown before the attributions. Pass `false` to disable.
       
 12596 		prefix: '<a href="http://leafletjs.com" title="A JS library for interactive maps">Leaflet</a>'
       
 12597 	},
       
 12598 
       
 12599 	initialize: function (options) {
       
 12600 		L.setOptions(this, options);
       
 12601 
       
 12602 		this._attributions = {};
       
 12603 	},
       
 12604 
       
 12605 	onAdd: function (map) {
       
 12606 		map.attributionControl = this;
       
 12607 		this._container = L.DomUtil.create('div', 'leaflet-control-attribution');
       
 12608 		if (L.DomEvent) {
       
 12609 			L.DomEvent.disableClickPropagation(this._container);
       
 12610 		}
       
 12611 
       
 12612 		// TODO ugly, refactor
       
 12613 		for (var i in map._layers) {
       
 12614 			if (map._layers[i].getAttribution) {
       
 12615 				this.addAttribution(map._layers[i].getAttribution());
       
 12616 			}
       
 12617 		}
       
 12618 
       
 12619 		this._update();
       
 12620 
       
 12621 		return this._container;
       
 12622 	},
       
 12623 
       
 12624 	// @method setPrefix(prefix: String): this
       
 12625 	// Sets the text before the attributions.
       
 12626 	setPrefix: function (prefix) {
       
 12627 		this.options.prefix = prefix;
       
 12628 		this._update();
       
 12629 		return this;
       
 12630 	},
       
 12631 
       
 12632 	// @method addAttribution(text: String): this
       
 12633 	// Adds an attribution text (e.g. `'Vector data &copy; Mapbox'`).
       
 12634 	addAttribution: function (text) {
       
 12635 		if (!text) { return this; }
       
 12636 
       
 12637 		if (!this._attributions[text]) {
       
 12638 			this._attributions[text] = 0;
       
 12639 		}
       
 12640 		this._attributions[text]++;
       
 12641 
       
 12642 		this._update();
       
 12643 
       
 12644 		return this;
       
 12645 	},
       
 12646 
       
 12647 	// @method removeAttribution(text: String): this
       
 12648 	// Removes an attribution text.
       
 12649 	removeAttribution: function (text) {
       
 12650 		if (!text) { return this; }
       
 12651 
       
 12652 		if (this._attributions[text]) {
       
 12653 			this._attributions[text]--;
       
 12654 			this._update();
       
 12655 		}
       
 12656 
       
 12657 		return this;
       
 12658 	},
       
 12659 
       
 12660 	_update: function () {
       
 12661 		if (!this._map) { return; }
       
 12662 
       
 12663 		var attribs = [];
       
 12664 
       
 12665 		for (var i in this._attributions) {
       
 12666 			if (this._attributions[i]) {
       
 12667 				attribs.push(i);
       
 12668 			}
       
 12669 		}
       
 12670 
       
 12671 		var prefixAndAttribs = [];
       
 12672 
       
 12673 		if (this.options.prefix) {
       
 12674 			prefixAndAttribs.push(this.options.prefix);
       
 12675 		}
       
 12676 		if (attribs.length) {
       
 12677 			prefixAndAttribs.push(attribs.join(', '));
       
 12678 		}
       
 12679 
       
 12680 		this._container.innerHTML = prefixAndAttribs.join(' | ');
       
 12681 	}
       
 12682 });
       
 12683 
       
 12684 // @namespace Map
       
 12685 // @section Control options
       
 12686 // @option attributionControl: Boolean = true
       
 12687 // Whether a [attribution control](#control-attribution) is added to the map by default.
       
 12688 L.Map.mergeOptions({
       
 12689 	attributionControl: true
       
 12690 });
       
 12691 
       
 12692 L.Map.addInitHook(function () {
       
 12693 	if (this.options.attributionControl) {
       
 12694 		new L.Control.Attribution().addTo(this);
       
 12695 	}
       
 12696 });
       
 12697 
       
 12698 // @namespace Control.Attribution
       
 12699 // @factory L.control.attribution(options: Control.Attribution options)
       
 12700 // Creates an attribution control.
       
 12701 L.control.attribution = function (options) {
       
 12702 	return new L.Control.Attribution(options);
       
 12703 };
       
 12704 
       
 12705 
       
 12706 
       
 12707 /*
       
 12708  * @class Control.Scale
       
 12709  * @aka L.Control.Scale
       
 12710  * @inherits Control
       
 12711  *
       
 12712  * A simple scale control that shows the scale of the current center of screen in metric (m/km) and imperial (mi/ft) systems. Extends `Control`.
       
 12713  *
       
 12714  * @example
       
 12715  *
       
 12716  * ```js
       
 12717  * L.control.scale().addTo(map);
       
 12718  * ```
       
 12719  */
       
 12720 
       
 12721 L.Control.Scale = L.Control.extend({
       
 12722 	// @section
       
 12723 	// @aka Control.Scale options
       
 12724 	options: {
       
 12725 		position: 'bottomleft',
       
 12726 
       
 12727 		// @option maxWidth: Number = 100
       
 12728 		// Maximum width of the control in pixels. The width is set dynamically to show round values (e.g. 100, 200, 500).
       
 12729 		maxWidth: 100,
       
 12730 
       
 12731 		// @option metric: Boolean = True
       
 12732 		// Whether to show the metric scale line (m/km).
       
 12733 		metric: true,
       
 12734 
       
 12735 		// @option imperial: Boolean = True
       
 12736 		// Whether to show the imperial scale line (mi/ft).
       
 12737 		imperial: true
       
 12738 
       
 12739 		// @option updateWhenIdle: Boolean = false
       
 12740 		// If `true`, the control is updated on [`moveend`](#map-moveend), otherwise it's always up-to-date (updated on [`move`](#map-move)).
       
 12741 	},
       
 12742 
       
 12743 	onAdd: function (map) {
       
 12744 		var className = 'leaflet-control-scale',
       
 12745 		    container = L.DomUtil.create('div', className),
       
 12746 		    options = this.options;
       
 12747 
       
 12748 		this._addScales(options, className + '-line', container);
       
 12749 
       
 12750 		map.on(options.updateWhenIdle ? 'moveend' : 'move', this._update, this);
       
 12751 		map.whenReady(this._update, this);
       
 12752 
       
 12753 		return container;
       
 12754 	},
       
 12755 
       
 12756 	onRemove: function (map) {
       
 12757 		map.off(this.options.updateWhenIdle ? 'moveend' : 'move', this._update, this);
       
 12758 	},
       
 12759 
       
 12760 	_addScales: function (options, className, container) {
       
 12761 		if (options.metric) {
       
 12762 			this._mScale = L.DomUtil.create('div', className, container);
       
 12763 		}
       
 12764 		if (options.imperial) {
       
 12765 			this._iScale = L.DomUtil.create('div', className, container);
       
 12766 		}
       
 12767 	},
       
 12768 
       
 12769 	_update: function () {
       
 12770 		var map = this._map,
       
 12771 		    y = map.getSize().y / 2;
       
 12772 
       
 12773 		var maxMeters = map.distance(
       
 12774 				map.containerPointToLatLng([0, y]),
       
 12775 				map.containerPointToLatLng([this.options.maxWidth, y]));
       
 12776 
       
 12777 		this._updateScales(maxMeters);
       
 12778 	},
       
 12779 
       
 12780 	_updateScales: function (maxMeters) {
       
 12781 		if (this.options.metric && maxMeters) {
       
 12782 			this._updateMetric(maxMeters);
       
 12783 		}
       
 12784 		if (this.options.imperial && maxMeters) {
       
 12785 			this._updateImperial(maxMeters);
       
 12786 		}
       
 12787 	},
       
 12788 
       
 12789 	_updateMetric: function (maxMeters) {
       
 12790 		var meters = this._getRoundNum(maxMeters),
       
 12791 		    label = meters < 1000 ? meters + ' m' : (meters / 1000) + ' km';
       
 12792 
       
 12793 		this._updateScale(this._mScale, label, meters / maxMeters);
       
 12794 	},
       
 12795 
       
 12796 	_updateImperial: function (maxMeters) {
       
 12797 		var maxFeet = maxMeters * 3.2808399,
       
 12798 		    maxMiles, miles, feet;
       
 12799 
       
 12800 		if (maxFeet > 5280) {
       
 12801 			maxMiles = maxFeet / 5280;
       
 12802 			miles = this._getRoundNum(maxMiles);
       
 12803 			this._updateScale(this._iScale, miles + ' mi', miles / maxMiles);
       
 12804 
       
 12805 		} else {
       
 12806 			feet = this._getRoundNum(maxFeet);
       
 12807 			this._updateScale(this._iScale, feet + ' ft', feet / maxFeet);
       
 12808 		}
       
 12809 	},
       
 12810 
       
 12811 	_updateScale: function (scale, text, ratio) {
       
 12812 		scale.style.width = Math.round(this.options.maxWidth * ratio) + 'px';
       
 12813 		scale.innerHTML = text;
       
 12814 	},
       
 12815 
       
 12816 	_getRoundNum: function (num) {
       
 12817 		var pow10 = Math.pow(10, (Math.floor(num) + '').length - 1),
       
 12818 		    d = num / pow10;
       
 12819 
       
 12820 		d = d >= 10 ? 10 :
       
 12821 		    d >= 5 ? 5 :
       
 12822 		    d >= 3 ? 3 :
       
 12823 		    d >= 2 ? 2 : 1;
       
 12824 
       
 12825 		return pow10 * d;
       
 12826 	}
       
 12827 });
       
 12828 
       
 12829 
       
 12830 // @factory L.control.scale(options?: Control.Scale options)
       
 12831 // Creates an scale control with the given options.
       
 12832 L.control.scale = function (options) {
       
 12833 	return new L.Control.Scale(options);
       
 12834 };
       
 12835 
       
 12836 
       
 12837 
       
 12838 /*
       
 12839  * @class Control.Layers
       
 12840  * @aka L.Control.Layers
       
 12841  * @inherits Control
       
 12842  *
       
 12843  * The layers control gives users the ability to switch between different base layers and switch overlays on/off (check out the [detailed example](http://leafletjs.com/examples/layers-control.html)). Extends `Control`.
       
 12844  *
       
 12845  * @example
       
 12846  *
       
 12847  * ```js
       
 12848  * var baseLayers = {
       
 12849  * 	"Mapbox": mapbox,
       
 12850  * 	"OpenStreetMap": osm
       
 12851  * };
       
 12852  *
       
 12853  * var overlays = {
       
 12854  * 	"Marker": marker,
       
 12855  * 	"Roads": roadsLayer
       
 12856  * };
       
 12857  *
       
 12858  * L.control.layers(baseLayers, overlays).addTo(map);
       
 12859  * ```
       
 12860  *
       
 12861  * The `baseLayers` and `overlays` parameters are object literals with layer names as keys and `Layer` objects as values:
       
 12862  *
       
 12863  * ```js
       
 12864  * {
       
 12865  *     "<someName1>": layer1,
       
 12866  *     "<someName2>": layer2
       
 12867  * }
       
 12868  * ```
       
 12869  *
       
 12870  * The layer names can contain HTML, which allows you to add additional styling to the items:
       
 12871  *
       
 12872  * ```js
       
 12873  * {"<img src='my-layer-icon' /> <span class='my-layer-item'>My Layer</span>": myLayer}
       
 12874  * ```
       
 12875  */
       
 12876 
       
 12877 
       
 12878 L.Control.Layers = L.Control.extend({
       
 12879 	// @section
       
 12880 	// @aka Control.Layers options
       
 12881 	options: {
       
 12882 		// @option collapsed: Boolean = true
       
 12883 		// If `true`, the control will be collapsed into an icon and expanded on mouse hover or touch.
       
 12884 		collapsed: true,
       
 12885 		position: 'topright',
       
 12886 
       
 12887 		// @option autoZIndex: Boolean = true
       
 12888 		// 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.
       
 12889 		autoZIndex: true,
       
 12890 
       
 12891 		// @option hideSingleBase: Boolean = false
       
 12892 		// If `true`, the base layers in the control will be hidden when there is only one.
       
 12893 		hideSingleBase: false,
       
 12894 
       
 12895 		// @option sortLayers: Boolean = false
       
 12896 		// Whether to sort the layers. When `false`, layers will keep the order
       
 12897 		// in which they were added to the control.
       
 12898 		sortLayers: false,
       
 12899 
       
 12900 		// @option sortFunction: Function = *
       
 12901 		// A [compare function](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Array/sort)
       
 12902 		// that will be used for sorting the layers, when `sortLayers` is `true`.
       
 12903 		// The function receives both the `L.Layer` instances and their names, as in
       
 12904 		// `sortFunction(layerA, layerB, nameA, nameB)`.
       
 12905 		// By default, it sorts layers alphabetically by their name.
       
 12906 		sortFunction: function (layerA, layerB, nameA, nameB) {
       
 12907 			return nameA < nameB ? -1 : (nameB < nameA ? 1 : 0);
       
 12908 		}
       
 12909 	},
       
 12910 
       
 12911 	initialize: function (baseLayers, overlays, options) {
       
 12912 		L.setOptions(this, options);
       
 12913 
       
 12914 		this._layers = [];
       
 12915 		this._lastZIndex = 0;
       
 12916 		this._handlingClick = false;
       
 12917 
       
 12918 		for (var i in baseLayers) {
       
 12919 			this._addLayer(baseLayers[i], i);
       
 12920 		}
       
 12921 
       
 12922 		for (i in overlays) {
       
 12923 			this._addLayer(overlays[i], i, true);
       
 12924 		}
       
 12925 	},
       
 12926 
       
 12927 	onAdd: function (map) {
       
 12928 		this._initLayout();
       
 12929 		this._update();
       
 12930 
       
 12931 		this._map = map;
       
 12932 		map.on('zoomend', this._checkDisabledLayers, this);
       
 12933 
       
 12934 		return this._container;
       
 12935 	},
       
 12936 
       
 12937 	onRemove: function () {
       
 12938 		this._map.off('zoomend', this._checkDisabledLayers, this);
       
 12939 
       
 12940 		for (var i = 0; i < this._layers.length; i++) {
       
 12941 			this._layers[i].layer.off('add remove', this._onLayerChange, this);
       
 12942 		}
       
 12943 	},
       
 12944 
       
 12945 	// @method addBaseLayer(layer: Layer, name: String): this
       
 12946 	// Adds a base layer (radio button entry) with the given name to the control.
       
 12947 	addBaseLayer: function (layer, name) {
       
 12948 		this._addLayer(layer, name);
       
 12949 		return (this._map) ? this._update() : this;
       
 12950 	},
       
 12951 
       
 12952 	// @method addOverlay(layer: Layer, name: String): this
       
 12953 	// Adds an overlay (checkbox entry) with the given name to the control.
       
 12954 	addOverlay: function (layer, name) {
       
 12955 		this._addLayer(layer, name, true);
       
 12956 		return (this._map) ? this._update() : this;
       
 12957 	},
       
 12958 
       
 12959 	// @method removeLayer(layer: Layer): this
       
 12960 	// Remove the given layer from the control.
       
 12961 	removeLayer: function (layer) {
       
 12962 		layer.off('add remove', this._onLayerChange, this);
       
 12963 
       
 12964 		var obj = this._getLayer(L.stamp(layer));
       
 12965 		if (obj) {
       
 12966 			this._layers.splice(this._layers.indexOf(obj), 1);
       
 12967 		}
       
 12968 		return (this._map) ? this._update() : this;
       
 12969 	},
       
 12970 
       
 12971 	// @method expand(): this
       
 12972 	// Expand the control container if collapsed.
       
 12973 	expand: function () {
       
 12974 		L.DomUtil.addClass(this._container, 'leaflet-control-layers-expanded');
       
 12975 		this._form.style.height = null;
       
 12976 		var acceptableHeight = this._map.getSize().y - (this._container.offsetTop + 50);
       
 12977 		if (acceptableHeight < this._form.clientHeight) {
       
 12978 			L.DomUtil.addClass(this._form, 'leaflet-control-layers-scrollbar');
       
 12979 			this._form.style.height = acceptableHeight + 'px';
       
 12980 		} else {
       
 12981 			L.DomUtil.removeClass(this._form, 'leaflet-control-layers-scrollbar');
       
 12982 		}
       
 12983 		this._checkDisabledLayers();
       
 12984 		return this;
       
 12985 	},
       
 12986 
       
 12987 	// @method collapse(): this
       
 12988 	// Collapse the control container if expanded.
       
 12989 	collapse: function () {
       
 12990 		L.DomUtil.removeClass(this._container, 'leaflet-control-layers-expanded');
       
 12991 		return this;
       
 12992 	},
       
 12993 
       
 12994 	_initLayout: function () {
       
 12995 		var className = 'leaflet-control-layers',
       
 12996 		    container = this._container = L.DomUtil.create('div', className),
       
 12997 		    collapsed = this.options.collapsed;
       
 12998 
       
 12999 		// makes this work on IE touch devices by stopping it from firing a mouseout event when the touch is released
       
 13000 		container.setAttribute('aria-haspopup', true);
       
 13001 
       
 13002 		L.DomEvent.disableClickPropagation(container);
       
 13003 		if (!L.Browser.touch) {
       
 13004 			L.DomEvent.disableScrollPropagation(container);
       
 13005 		}
       
 13006 
       
 13007 		var form = this._form = L.DomUtil.create('form', className + '-list');
       
 13008 
       
 13009 		if (collapsed) {
       
 13010 			this._map.on('click', this.collapse, this);
       
 13011 
       
 13012 			if (!L.Browser.android) {
       
 13013 				L.DomEvent.on(container, {
       
 13014 					mouseenter: this.expand,
       
 13015 					mouseleave: this.collapse
       
 13016 				}, this);
       
 13017 			}
       
 13018 		}
       
 13019 
       
 13020 		var link = this._layersLink = L.DomUtil.create('a', className + '-toggle', container);
       
 13021 		link.href = '#';
       
 13022 		link.title = 'Layers';
       
 13023 
       
 13024 		if (L.Browser.touch) {
       
 13025 			L.DomEvent
       
 13026 			    .on(link, 'click', L.DomEvent.stop)
       
 13027 			    .on(link, 'click', this.expand, this);
       
 13028 		} else {
       
 13029 			L.DomEvent.on(link, 'focus', this.expand, this);
       
 13030 		}
       
 13031 
       
 13032 		// work around for Firefox Android issue https://github.com/Leaflet/Leaflet/issues/2033
       
 13033 		L.DomEvent.on(form, 'click', function () {
       
 13034 			setTimeout(L.bind(this._onInputClick, this), 0);
       
 13035 		}, this);
       
 13036 
       
 13037 		// TODO keyboard accessibility
       
 13038 
       
 13039 		if (!collapsed) {
       
 13040 			this.expand();
       
 13041 		}
       
 13042 
       
 13043 		this._baseLayersList = L.DomUtil.create('div', className + '-base', form);
       
 13044 		this._separator = L.DomUtil.create('div', className + '-separator', form);
       
 13045 		this._overlaysList = L.DomUtil.create('div', className + '-overlays', form);
       
 13046 
       
 13047 		container.appendChild(form);
       
 13048 	},
       
 13049 
       
 13050 	_getLayer: function (id) {
       
 13051 		for (var i = 0; i < this._layers.length; i++) {
       
 13052 
       
 13053 			if (this._layers[i] && L.stamp(this._layers[i].layer) === id) {
       
 13054 				return this._layers[i];
       
 13055 			}
       
 13056 		}
       
 13057 	},
       
 13058 
       
 13059 	_addLayer: function (layer, name, overlay) {
       
 13060 		layer.on('add remove', this._onLayerChange, this);
       
 13061 
       
 13062 		this._layers.push({
       
 13063 			layer: layer,
       
 13064 			name: name,
       
 13065 			overlay: overlay
       
 13066 		});
       
 13067 
       
 13068 		if (this.options.sortLayers) {
       
 13069 			this._layers.sort(L.bind(function (a, b) {
       
 13070 				return this.options.sortFunction(a.layer, b.layer, a.name, b.name);
       
 13071 			}, this));
       
 13072 		}
       
 13073 
       
 13074 		if (this.options.autoZIndex && layer.setZIndex) {
       
 13075 			this._lastZIndex++;
       
 13076 			layer.setZIndex(this._lastZIndex);
       
 13077 		}
       
 13078 	},
       
 13079 
       
 13080 	_update: function () {
       
 13081 		if (!this._container) { return this; }
       
 13082 
       
 13083 		L.DomUtil.empty(this._baseLayersList);
       
 13084 		L.DomUtil.empty(this._overlaysList);
       
 13085 
       
 13086 		var baseLayersPresent, overlaysPresent, i, obj, baseLayersCount = 0;
       
 13087 
       
 13088 		for (i = 0; i < this._layers.length; i++) {
       
 13089 			obj = this._layers[i];
       
 13090 			this._addItem(obj);
       
 13091 			overlaysPresent = overlaysPresent || obj.overlay;
       
 13092 			baseLayersPresent = baseLayersPresent || !obj.overlay;
       
 13093 			baseLayersCount += !obj.overlay ? 1 : 0;
       
 13094 		}
       
 13095 
       
 13096 		// Hide base layers section if there's only one layer.
       
 13097 		if (this.options.hideSingleBase) {
       
 13098 			baseLayersPresent = baseLayersPresent && baseLayersCount > 1;
       
 13099 			this._baseLayersList.style.display = baseLayersPresent ? '' : 'none';
       
 13100 		}
       
 13101 
       
 13102 		this._separator.style.display = overlaysPresent && baseLayersPresent ? '' : 'none';
       
 13103 
       
 13104 		return this;
       
 13105 	},
       
 13106 
       
 13107 	_onLayerChange: function (e) {
       
 13108 		if (!this._handlingClick) {
       
 13109 			this._update();
       
 13110 		}
       
 13111 
       
 13112 		var obj = this._getLayer(L.stamp(e.target));
       
 13113 
       
 13114 		// @namespace Map
       
 13115 		// @section Layer events
       
 13116 		// @event baselayerchange: LayersControlEvent
       
 13117 		// Fired when the base layer is changed through the [layer control](#control-layers).
       
 13118 		// @event overlayadd: LayersControlEvent
       
 13119 		// Fired when an overlay is selected through the [layer control](#control-layers).
       
 13120 		// @event overlayremove: LayersControlEvent
       
 13121 		// Fired when an overlay is deselected through the [layer control](#control-layers).
       
 13122 		// @namespace Control.Layers
       
 13123 		var type = obj.overlay ?
       
 13124 			(e.type === 'add' ? 'overlayadd' : 'overlayremove') :
       
 13125 			(e.type === 'add' ? 'baselayerchange' : null);
       
 13126 
       
 13127 		if (type) {
       
 13128 			this._map.fire(type, obj);
       
 13129 		}
       
 13130 	},
       
 13131 
       
 13132 	// IE7 bugs out if you create a radio dynamically, so you have to do it this hacky way (see http://bit.ly/PqYLBe)
       
 13133 	_createRadioElement: function (name, checked) {
       
 13134 
       
 13135 		var radioHtml = '<input type="radio" class="leaflet-control-layers-selector" name="' +
       
 13136 				name + '"' + (checked ? ' checked="checked"' : '') + '/>';
       
 13137 
       
 13138 		var radioFragment = document.createElement('div');
       
 13139 		radioFragment.innerHTML = radioHtml;
       
 13140 
       
 13141 		return radioFragment.firstChild;
       
 13142 	},
       
 13143 
       
 13144 	_addItem: function (obj) {
       
 13145 		var label = document.createElement('label'),
       
 13146 		    checked = this._map.hasLayer(obj.layer),
       
 13147 		    input;
       
 13148 
       
 13149 		if (obj.overlay) {
       
 13150 			input = document.createElement('input');
       
 13151 			input.type = 'checkbox';
       
 13152 			input.className = 'leaflet-control-layers-selector';
       
 13153 			input.defaultChecked = checked;
       
 13154 		} else {
       
 13155 			input = this._createRadioElement('leaflet-base-layers', checked);
       
 13156 		}
       
 13157 
       
 13158 		input.layerId = L.stamp(obj.layer);
       
 13159 
       
 13160 		L.DomEvent.on(input, 'click', this._onInputClick, this);
       
 13161 
       
 13162 		var name = document.createElement('span');
       
 13163 		name.innerHTML = ' ' + obj.name;
       
 13164 
       
 13165 		// Helps from preventing layer control flicker when checkboxes are disabled
       
 13166 		// https://github.com/Leaflet/Leaflet/issues/2771
       
 13167 		var holder = document.createElement('div');
       
 13168 
       
 13169 		label.appendChild(holder);
       
 13170 		holder.appendChild(input);
       
 13171 		holder.appendChild(name);
       
 13172 
       
 13173 		var container = obj.overlay ? this._overlaysList : this._baseLayersList;
       
 13174 		container.appendChild(label);
       
 13175 
       
 13176 		this._checkDisabledLayers();
       
 13177 		return label;
       
 13178 	},
       
 13179 
       
 13180 	_onInputClick: function () {
       
 13181 		var inputs = this._form.getElementsByTagName('input'),
       
 13182 		    input, layer, hasLayer;
       
 13183 		var addedLayers = [],
       
 13184 		    removedLayers = [];
       
 13185 
       
 13186 		this._handlingClick = true;
       
 13187 
       
 13188 		for (var i = inputs.length - 1; i >= 0; i--) {
       
 13189 			input = inputs[i];
       
 13190 			layer = this._getLayer(input.layerId).layer;
       
 13191 			hasLayer = this._map.hasLayer(layer);
       
 13192 
       
 13193 			if (input.checked && !hasLayer) {
       
 13194 				addedLayers.push(layer);
       
 13195 
       
 13196 			} else if (!input.checked && hasLayer) {
       
 13197 				removedLayers.push(layer);
       
 13198 			}
       
 13199 		}
       
 13200 
       
 13201 		// Bugfix issue 2318: Should remove all old layers before readding new ones
       
 13202 		for (i = 0; i < removedLayers.length; i++) {
       
 13203 			this._map.removeLayer(removedLayers[i]);
       
 13204 		}
       
 13205 		for (i = 0; i < addedLayers.length; i++) {
       
 13206 			this._map.addLayer(addedLayers[i]);
       
 13207 		}
       
 13208 
       
 13209 		this._handlingClick = false;
       
 13210 
       
 13211 		this._refocusOnMap();
       
 13212 	},
       
 13213 
       
 13214 	_checkDisabledLayers: function () {
       
 13215 		var inputs = this._form.getElementsByTagName('input'),
       
 13216 		    input,
       
 13217 		    layer,
       
 13218 		    zoom = this._map.getZoom();
       
 13219 
       
 13220 		for (var i = inputs.length - 1; i >= 0; i--) {
       
 13221 			input = inputs[i];
       
 13222 			layer = this._getLayer(input.layerId).layer;
       
 13223 			input.disabled = (layer.options.minZoom !== undefined && zoom < layer.options.minZoom) ||
       
 13224 			                 (layer.options.maxZoom !== undefined && zoom > layer.options.maxZoom);
       
 13225 
       
 13226 		}
       
 13227 	},
       
 13228 
       
 13229 	_expand: function () {
       
 13230 		// Backward compatibility, remove me in 1.1.
       
 13231 		return this.expand();
       
 13232 	},
       
 13233 
       
 13234 	_collapse: function () {
       
 13235 		// Backward compatibility, remove me in 1.1.
       
 13236 		return this.collapse();
       
 13237 	}
       
 13238 
       
 13239 });
       
 13240 
       
 13241 
       
 13242 // @factory L.control.layers(baselayers?: Object, overlays?: Object, options?: Control.Layers options)
       
 13243 // Creates an attribution 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.
       
 13244 L.control.layers = function (baseLayers, overlays, options) {
       
 13245 	return new L.Control.Layers(baseLayers, overlays, options);
       
 13246 };
       
 13247 
       
 13248 
       
 13249 
       
 13250 }(window, document));
       
 13251 //# sourceMappingURL=leaflet-src.map