PageSourceSearch

https://hotelfriend.com/assets/leaflet/leaflet-src.js

js hotelfriend.com collected 2026-10-01 14:01:25 UTC 450,229 bytes, 14,512 lines download raw bytes

vendor: 450,229 bytes, lines 1-14512
1/* @preserve
2 * Leaflet 1.9.4, a JS library for interactive maps. https://leafletjs.com
3 * (c) 2010-2023 Vladimir Agafonkin, (c) 2010-2011 CloudMade
4 */
5
6(function (global, factory) {
7  typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) :
8  typeof define === 'function' && define.amd ? define(['exports'], factory) :
9  (global = typeof globalThis !== 'undefined' ? globalThis : global || self, factory(global.leaflet = {}));
10})(this, (function (exports) { 'use strict';
11
12  var version = "1.9.4";
13
14  /*
15   * @namespace Util
16   *
17   * Various utility functions, used by Leaflet internally.
18   */
19
20  // @function extend(dest: Object, src?: Object): Object
21  // Merges the properties of the `src` object (or multiple objects) into `dest` object and returns the latter. Has an `L.extend` shortcut.
22  function extend(dest) {
23  	var i, j, len, src;
24
25  	for (j = 1, len = arguments.length; j < len; j++) {
26  		src = arguments[j];
27  		for (i in src) {
28  			dest[i] = src[i];
29  		}
30  	}
31  	return dest;
32  }
33
34  // @function create(proto: Object, properties?: Object): Object
35  // Compatibility polyfill for [Object.create](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Object/create)
36  var create$2 = Object.create || (function () {
37  	function F() {}
38  	return function (proto) {
39  		F.prototype = proto;
40  		return new F();
41  	};
42  })();
43
44  // @function bind(fn: Function, …): Function
45  // 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).
46  // Has a `L.bind()` shortcut.
47  function bind(fn, obj) {
48  	var slice = Array.prototype.slice;
49
50  	if (fn.bind) {
51  		return fn.bind.apply(fn, slice.call(arguments, 1));
52  	}
53
54  	var args = slice.call(arguments, 2);
55
56  	return function () {
57  		return fn.apply(obj, args.length ? args.concat(slice.call(arguments)) : arguments);
58  	};
59  }
60
61  // @property lastId: Number
62  // Last unique ID used by [`stamp()`](#util-stamp)
63  var lastId = 0;
64
65  // @function stamp(obj: Object): Number
66  // Returns the unique ID of an object, assigning it one if it doesn't have it.
67  function stamp(obj) {
68  	if (!('_leaflet_id' in obj)) {
69  		obj['_leaflet_id'] = ++lastId;
70  	}
71  	return obj._leaflet_id;
72  }
73
74  // @function throttle(fn: Function, time: Number, context: Object): Function
75  // Returns a function which executes function `fn` with the given scope `context`
76  // (so that the `this` keyword refers to `context` inside `fn`'s code). The function
77  // `fn` will be called no more than one time per given amount of `time`. The arguments
78  // received by the bound function will be any arguments passed when binding the
79  // function, followed by any arguments passed when invoking the bound function.
80  // Has an `L.throttle` shortcut.
81  function throttle(fn, time, context) {
82  	var lock, args, wrapperFn, later;
83
84  	later = function () {
85  		// reset lock and call if queued
86  		lock = false;
87  		if (args) {
88  			wrapperFn.apply(context, args);
89  			args = false;
90  		}
91  	};
92
93  	wrapperFn = function () {
94  		if (lock) {
95  			// called too soon, queue to call later
96  			args = arguments;
97
98  		} else {
99  			// call and lock until later
100  			fn.apply(context, arguments);
101  			setTimeout(later, time);
102  			lock = true;
103  		}
104  	};
105
106  	return wrapperFn;
107  }
108
109  // @function wrapNum(num: Number, range: Number[], includeMax?: Boolean): Number
110  // Returns the number `num` modulo `range` in such a way so it lies within
111  // `range[0]` and `range[1]`. The returned value will be always smaller than
112  // `range[1]` unless `includeMax` is set to `true`.
113  function wrapNum(x, range, includeMax) {
114  	var max = range[1],
115  	    min = range[0],
116  	    d = max - min;
117  	return x === max && includeMax ? x : ((x - min) % d + d) % d + min;
118  }
119
120  // @function falseFn(): Function
121  // Returns a function which always returns `false`.
122  function falseFn() { return false; }
123
124  // @function formatNum(num: Number, precision?: Number|false): Number
125  // Returns the number `num` rounded with specified `precision`.
126  // The default `precision` value is 6 decimal places.
127  // `false` can be passed to skip any processing (can be useful to avoid round-off errors).
128  function formatNum(num, precision) {
129  	if (precision === false) { return num; }
130  	var pow = Math.pow(10, precision === undefined ? 6 : precision);
131  	return Math.round(num * pow) / pow;
132  }
133
134  // @function trim(str: String): String
135  // Compatibility polyfill for [String.prototype.trim](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String/Trim)
136  function trim(str) {
137  	return str.trim ? str.trim() : str.replace(/^\s+|\s+$/g, '');
138  }
139
140  // @function splitWords(str: String): String[]
141  // Trims and splits the string on whitespace and returns the array of parts.
142  function splitWords(str) {
143  	return trim(str).split(/\s+/);
144  }
145
146  // @function setOptions(obj: Object, options: Object): Object
147  // Merges the given properties to the `options` of the `obj` object, returning the resulting options. See `Class options`. Has an `L.setOptions` shortcut.
148  function setOptions(obj, options) {
149  	if (!Object.prototype.hasOwnProperty.call(obj, 'options')) {
150  		obj.options = obj.options ? create$2(obj.options) : {};
151  	}
152  	for (var i in options) {
153  		obj.options[i] = options[i];
154  	}
155  	return obj.options;
156  }
157
158  // @function getParamString(obj: Object, existingUrl?: String, uppercase?: Boolean): String
159  // Converts an object into a parameter URL string, e.g. `{a: "foo", b: "bar"}`
160  // translates to `'?a=foo&b=bar'`. If `existingUrl` is set, the parameters will
161  // be appended at the end. If `uppercase` is `true`, the parameter names will
162  // be uppercased (e.g. `'?A=foo&B=bar'`)
163  function getParamString(obj, existingUrl, uppercase) {
164  	var params = [];
165  	for (var i in obj) {
166  		params.push(encodeURIComponent(uppercase ? i.toUpperCase() : i) + '=' + encodeURIComponent(obj[i]));
167  	}
168  	return ((!existingUrl || existingUrl.indexOf('?') === -1) ? '?' : '&') + params.join('&');
169  }
170
171  var templateRe = /\{ *([\w_ -]+) *\}/g;
172
173  // @function template(str: String, data: Object): String
174  // Simple templating facility, accepts a template string of the form `'Hello {a}, {b}'`
175  // and a data object like `{a: 'foo', b: 'bar'}`, returns evaluated string
176  // `('Hello foo, bar')`. You can also specify functions instead of strings for
177  // data values — they will be evaluated passing `data` as an argument.
178  function template(str, data) {
179  	return str.replace(templateRe, function (str, key) {
180  		var value = data[key];
181
182  		if (value === undefined) {
183  			throw new Error('No value provided for variable ' + str);
184
185  		} else if (typeof value === 'function') {
186  			value = value(data);
187  		}
188  		return value;
189  	});
190  }
191
192  // @function isArray(obj): Boolean
193  // Compatibility polyfill for [Array.isArray](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Array/isArray)
194  var isArray = Array.isArray || function (obj) {
195  	return (Object.prototype.toString.call(obj) === '[object Array]');
196  };
197
198  // @function indexOf(array: Array, el: Object): Number
199  // Compatibility polyfill for [Array.prototype.indexOf](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Array/indexOf)
200  function indexOf(array, el) {
201  	for (var i = 0; i < array.length; i++) {
202  		if (array[i] === el) { return i; }
203  	}
204  	return -1;
205  }
206
207  // @property emptyImageUrl: String
208  // Data URI string containing a base64-encoded empty GIF image.
209  // Used as a hack to free memory from unused images on WebKit-powered
210  // mobile devices (by setting image `src` to this string).
211  var emptyImageUrl = 'data:image/gif;base64,R0lGODlhAQABAAD/ACwAAAAAAQABAAACADs=';
212
213  // inspired by https://paulirish.com/2011/requestanimationframe-for-smart-animating/
214
215  function getPrefixed(name) {
216  	return window['webkit' + name] || window['moz' + name] || window['ms' + name];
217  }
218
219  var lastTime = 0;
220
221  // fallback for IE 7-8
222  function timeoutDefer(fn) {
223  	var time = +new Date(),
224  	    timeToCall = Math.max(0, 16 - (time - lastTime));
225
226  	lastTime = time + timeToCall;
227  	return window.setTimeout(fn, timeToCall);
228  }
229
230  var requestFn = window.requestAnimationFrame || getPrefixed('RequestAnimationFrame') || timeoutDefer;
231  var cancelFn = window.cancelAnimationFrame || getPrefixed('CancelAnimationFrame') ||
232  		getPrefixed('CancelRequestAnimationFrame') || function (id) { window.clearTimeout(id); };
233
234  // @function requestAnimFrame(fn: Function, context?: Object, immediate?: Boolean): Number
235  // Schedules `fn` to be executed when the browser repaints. `fn` is bound to
236  // `context` if given. When `immediate` is set, `fn` is called immediately if
237  // the browser doesn't have native support for
238  // [`window.requestAnimationFrame`](https://developer.mozilla.org/docs/Web/API/window/requestAnimationFrame),
239  // otherwise it's delayed. Returns a request ID that can be used to cancel the request.
240  function requestAnimFrame(fn, context, immediate) {
241  	if (immediate && requestFn === timeoutDefer) {
242  		fn.call(context);
243  	} else {
244  		return requestFn.call(window, bind(fn, context));
245  	}
246  }
247
248  // @function cancelAnimFrame(id: Number): undefined
249  // Cancels a previous `requestAnimFrame`. See also [window.cancelAnimationFrame](https://developer.mozilla.org/docs/Web/API/window/cancelAnimationFrame).
250  function cancelAnimFrame(id) {
251  	if (id) {
252  		cancelFn.call(window, id);
253  	}
254  }
255
256  var Util = {
257    __proto__: null,
258    extend: extend,
259    create: create$2,
260    bind: bind,
261    get lastId () { return lastId; },
262    stamp: stamp,
263    throttle: throttle,
264    wrapNum: wrapNum,
265    falseFn: falseFn,
266    formatNum: formatNum,
267    trim: trim,
268    splitWords: splitWords,
269    setOptions: setOptions,
270    getParamString: getParamString,
271    template: template,
272    isArray: isArray,
273    indexOf: indexOf,
274    emptyImageUrl: emptyImageUrl,
275    requestFn: requestFn,
276    cancelFn: cancelFn,
277    requestAnimFrame: requestAnimFrame,
278    cancelAnimFrame: cancelAnimFrame
279  };
280
281  // @class Class
282  // @aka L.Class
283
284  // @section
285  // @uninheritable
286
287  // Thanks to John Resig and Dean Edwards for inspiration!
288
289  function Class() {}
290
291  Class.extend = function (props) {
292
293  	// @function extend(props: Object): Function
294  	// [Extends the current class](#class-inheritance) given the properties to be included.
295  	// Returns a Javascript function that is a class constructor (to be called with `new`).
296  	var NewClass = function () {
297
298  		setOptions(this);
299
300  		// call the constructor
301  		if (this.initialize) {
302  			this.initialize.apply(this, arguments);
303  		}
304
305  		// call all constructor hooks
306  		this.callInitHooks();
307  	};
308
309  	var parentProto = NewClass.__super__ = this.prototype;
310
311  	var proto = create$2(parentProto);
312  	proto.constructor = NewClass;
313
314  	NewClass.prototype = proto;
315
316  	// inherit parent's statics
317  	for (var i in this) {
318  		if (Object.prototype.hasOwnProperty.call(this, i) && i !== 'prototype' && i !== '__super__') {
319  			NewClass[i] = this[i];
320  		}
321  	}
322
323  	// mix static properties into the class
324  	if (props.statics) {
325  		extend(NewClass, props.statics);
326  	}
327
328  	// mix includes into the prototype
329  	if (props.includes) {
330  		checkDeprecatedMixinEvents(props.includes);
331  		extend.apply(null, [proto].concat(props.includes));
332  	}
333
334  	// mix given properties into the prototype
335  	extend(proto, props);
336  	delete proto.statics;
337  	delete proto.includes;
338
339  	// merge options
340  	if (proto.options) {
341  		proto.options = parentProto.options ? create$2(parentProto.options) : {};
342  		extend(proto.options, props.options);
343  	}
344
345  	proto._initHooks = [];
346
347  	// add method for calling all hooks
348  	proto.callInitHooks = function () {
349
350  		if (this._initHooksCalled) { return; }
351
352  		if (parentProto.callInitHooks) {
353  			parentProto.callInitHooks.call(this);
354  		}
355
356  		this._initHooksCalled = true;
357
358  		for (var i = 0, len = proto._initHooks.length; i < len; i++) {
359  			proto._initHooks[i].call(this);
360  		}
361  	};
362
363  	return NewClass;
364  };
365
366
367  // @function include(properties: Object): this
368  // [Includes a mixin](#class-includes) into the current class.
369  Class.include = function (props) {
370  	var parentOptions = this.prototype.options;
371  	extend(this.prototype, props);
372  	if (props.options) {
373  		this.prototype.options = parentOptions;
374  		this.mergeOptions(props.options);
375  	}
376  	return this;
377  };
378
379  // @function mergeOptions(options: Object): this
380  // [Merges `options`](#class-options) into the defaults of the class.
381  Class.mergeOptions = function (options) {
382  	extend(this.prototype.options, options);
383  	return this;
384  };
385
386  // @function addInitHook(fn: Function): this
387  // Adds a [constructor hook](#class-constructor-hooks) to the class.
388  Class.addInitHook = function (fn) { // (Function) || (String, args...)
389  	var args = Array.prototype.slice.call(arguments, 1);
390
391  	var init = typeof fn === 'function' ? fn : function () {
392  		this[fn].apply(this, args);
393  	};
394
395  	this.prototype._initHooks = this.prototype._initHooks || [];
396  	this.prototype._initHooks.push(init);
397  	return this;
398  };
399
400  function checkDeprecatedMixinEvents(includes) {
401  	/* global L: true */
402  	if (typeof L === 'undefined' || !L || !L.Mixin) { return; }
403
404  	includes = isArray(includes) ? includes : [includes];
405
406  	for (var i = 0; i < includes.length; i++) {
407  		if (includes[i] === L.Mixin.Events) {
408  			console.warn('Deprecated include of L.Mixin.Events: ' +
409  				'this property will be removed in future releases, ' +
410  				'please inherit from L.Evented instead.', new Error().stack);
411  		}
412  	}
413  }
414
415  /*
416   * @class Evented
417   * @aka L.Evented
418   * @inherits Class
419   *
420   * 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).
421   *
422   * @example
423   *
424   * ```js
425   * map.on('click', function(e) {
426   * 	alert(e.latlng);
427   * } );
428   * ```
429   *
430   * Leaflet deals with event listeners by reference, so if you want to add a listener and then remove it, define it as a function:
431   *
432   * ```js
433   * function onClick(e) { ... }
434   *
435   * map.on('click', onClick);
436   * map.off('click', onClick);
437   * ```
438   */
439
440  var Events = {
441  	/* @method on(type: String, fn: Function, context?: Object): this
442  	 * 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'`).
443  	 *
444  	 * @alternative
445  	 * @method on(eventMap: Object): this
446  	 * Adds a set of type/listener pairs, e.g. `{click: onClick, mousemove: onMouseMove}`
447  	 */
448  	on: function (types, fn, context) {
449
450  		// types can be a map of types/handlers
451  		if (typeof types === 'object') {
452  			for (var type in types) {
453  				// we don't process space-separated events here for performance;
454  				// it's a hot path since Layer uses the on(obj) syntax
455  				this._on(type, types[type], fn);
456  			}
457
458  		} else {
459  			// types can be a string of space-separated words
460  			types = splitWords(types);
461
462  			for (var i = 0, len = types.length; i < len; i++) {
463  				this._on(types[i], fn, context);
464  			}
465  		}
466
467  		return this;
468  	},
469
470  	/* @method off(type: String, fn?: Function, context?: Object): this
471  	 * 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.
472  	 *
473  	 * @alternative
474  	 * @method off(eventMap: Object): this
475  	 * Removes a set of type/listener pairs.
476  	 *
477  	 * @alternative
478  	 * @method off: this
479  	 * Removes all listeners to all events on the object. This includes implicitly attached events.
480  	 */
481  	off: function (types, fn, context) {
482
483  		if (!arguments.length) {
484  			// clear all listeners if called without arguments
485  			delete this._events;
486
487  		} else if (typeof types === 'object') {
488  			for (var type in types) {
489  				this._off(type, types[type], fn);
490  			}
491
492  		} else {
493  			types = splitWords(types);
494
495  			var removeAll = arguments.length === 1;
496  			for (var i = 0, len = types.length; i < len; i++) {
497  				if (removeAll) {
498  					this._off(types[i]);
499  				} else {
500  					this._off(types[i], fn, context);
501  				}
502  			}
503  		}
504
505  		return this;
506  	},
507
508  	// attach listener (without syntactic sugar now)
509  	_on: function (type, fn, context, _once) {
510  		if (typeof fn !== 'function') {
511  			console.warn('wrong listener type: ' + typeof fn);
512  			return;
513  		}
514
515  		// check if fn already there
516  		if (this._listens(type, fn, context) !== false) {
517  			return;
518  		}
519
520  		if (context === this) {
521  			// Less memory footprint.
522  			context = undefined;
523  		}
524
525  		var newListener = {fn: fn, ctx: context};
526  		if (_once) {
527  			newListener.once = true;
528  		}
529
530  		this._events = this._events || {};
531  		this._events[type] = this._events[type] || [];
532  		this._events[type].push(newListener);
533  	},
534
535  	_off: function (type, fn, context) {
536  		var listeners,
537  		    i,
538  		    len;
539
540  		if (!this._events) {
541  			return;
542  		}
543
544  		listeners = this._events[type];
545  		if (!listeners) {
546  			return;
547  		}
548
549  		if (arguments.length === 1) { // remove all
550  			if (this._firingCount) {
551  				// Set all removed listeners to noop
552  				// so they are not called if remove happens in fire
553  				for (i = 0, len = listeners.length; i < len; i++) {
554  					listeners[i].fn = falseFn;
555  				}
556  			}
557  			// clear all listeners for a type if function isn't specified
558  			delete this._events[type];
559  			return;
560  		}
561
562  		if (typeof fn !== 'function') {
563  			console.warn('wrong listener type: ' + typeof fn);
564  			return;
565  		}
566
567  		// find fn and remove it
568  		var index = this._listens(type, fn, context);
569  		if (index !== false) {
570  			var listener = listeners[index];
571  			if (this._firingCount) {
572  				// set the removed listener to noop so that's not called if remove happens in fire
573  				listener.fn = falseFn;
574
575  				/* copy array in case events are being fired */
576  				this._events[type] = listeners = listeners.slice();
577  			}
578  			listeners.splice(index, 1);
579  		}
580  	},
581
582  	// @method fire(type: String, data?: Object, propagate?: Boolean): this
583  	// Fires an event of the specified type. You can optionally provide a data
584  	// object — the first argument of the listener function will contain its
585  	// properties. The event can optionally be propagated to event parents.
586  	fire: function (type, data, propagate) {
587  		if (!this.listens(type, propagate)) { return this; }
588
589  		var event = extend({}, data, {
590  			type: type,
591  			target: this,
592  			sourceTarget: data && data.sourceTarget || this
593  		});
594
595  		if (this._events) {
596  			var listeners = this._events[type];
597  			if (listeners) {
598  				this._firingCount = (this._firingCount + 1) || 1;
599  				for (var i = 0, len = listeners.length; i < len; i++) {
600  					var l = listeners[i];
601  					// off overwrites l.fn, so we need to copy fn to a var
602  					var fn = l.fn;
603  					if (l.once) {
604  						this.off(type, fn, l.ctx);
605  					}
606  					fn.call(l.ctx || this, event);
607  				}
608
609  				this._firingCount--;
610  			}
611  		}
612
613  		if (propagate) {
614  			// propagate the event to parents (set with addEventParent)
615  			this._propagateEvent(event);
616  		}
617
618  		return this;
619  	},
620
621  	// @method listens(type: String, propagate?: Boolean): Boolean
622  	// @method listens(type: String, fn: Function, context?: Object, propagate?: Boolean): Boolean
623  	// Returns `true` if a particular event type has any listeners attached to it.
624  	// The verification can optionally be propagated, it will return `true` if parents have the listener attached to it.
625  	listens: function (type, fn, context, propagate) {
626  		if (typeof type !== 'string') {
627  			console.warn('"string" type argument expected');
628  		}
629
630  		// we don't overwrite the input `fn` value, because we need to use it for propagation
631  		var _fn = fn;
632  		if (typeof fn !== 'function') {
633  			propagate = !!fn;
634  			_fn = undefined;
635  			context = undefined;
636  		}
637
638  		var listeners = this._events && this._events[type];
639  		if (listeners && listeners.length) {
640  			if (this._listens(type, _fn, context) !== false) {
641  				return true;
642  			}
643  		}
644
645  		if (propagate) {
646  			// also check parents for listeners if event propagates
647  			for (var id in this._eventParents) {
648  				if (this._eventParents[id].listens(type, fn, context, propagate)) { return true; }
649  			}
650  		}
651  		return false;
652  	},
653
654  	// returns the index (number) or false
655  	_listens: function (type, fn, context) {
656  		if (!this._events) {
657  			return false;
658  		}
659
660  		var listeners = this._events[type] || [];
661  		if (!fn) {
662  			return !!listeners.length;
663  		}
664
665  		if (context === this) {
666  			// Less memory footprint.
667  			context = undefined;
668  		}
669
670  		for (var i = 0, len = listeners.length; i < len; i++) {
671  			if (listeners[i].fn === fn && listeners[i].ctx === context) {
672  				return i;
673  			}
674  		}
675  		return false;
676
677  	},
678
679  	// @method once(…): this
680  	// Behaves as [`on(…)`](#evented-on), except the listener will only get fired once and then removed.
681  	once: function (types, fn, context) {
682
683  		// types can be a map of types/handlers
684  		if (typeof types === 'object') {
685  			for (var type in types) {
686  				// we don't process space-separated events here for performance;
687  				// it's a hot path since Layer uses the on(obj) syntax
688  				this._on(type, types[type], fn, true);
689  			}
690
691  		} else {
692  			// types can be a string of space-separated words
693  			types = splitWords(types);
694
695  			for (var i = 0, len = types.length; i < len; i++) {
696  				this._on(types[i], fn, context, true);
697  			}
698  		}
699
700  		return this;
701  	},
702
703  	// @method addEventParent(obj: Evented): this
704  	// Adds an event parent - an `Evented` that will receive propagated events
705  	addEventParent: function (obj) {
706  		this._eventParents = this._eventParents || {};
707  		this._eventParents[stamp(obj)] = obj;
708  		return this;
709  	},
710
711  	// @method removeEventParent(obj: Evented): this
712  	// Removes an event parent, so it will stop receiving propagated events
713  	removeEventParent: function (obj) {
714  		if (this._eventParents) {
715  			delete this._eventParents[stamp(obj)];
716  		}
717  		return this;
718  	},
719
720  	_propagateEvent: function (e) {
721  		for (var id in this._eventParents) {
722  			this._eventParents[id].fire(e.type, extend({
723  				layer: e.target,
724  				propagatedFrom: e.target
725  			}, e), true);
726  		}
727  	}
728  };
729
730  // aliases; we should ditch those eventually
731
732  // @method addEventListener(…): this
733  // Alias to [`on(…)`](#evented-on)
734  Events.addEventListener = Events.on;
735
736  // @method removeEventListener(…): this
737  // Alias to [`off(…)`](#evented-off)
738
739  // @method clearAllEventListeners(…): this
740  // Alias to [`off()`](#evented-off)
741  Events.removeEventListener = Events.clearAllEventListeners = Events.off;
742
743  // @method addOneTimeEventListener(…): this
744  // Alias to [`once(…)`](#evented-once)
745  Events.addOneTimeEventListener = Events.once;
746
747  // @method fireEvent(…): this
748  // Alias to [`fire(…)`](#evented-fire)
749  Events.fireEvent = Events.fire;
750
751  // @method hasEventListeners(…): Boolean
752  // Alias to [`listens(…)`](#evented-listens)
753  Events.hasEventListeners = Events.listens;
754
755  var Evented = Class.extend(Events);
756
757  /*
758   * @class Point
759   * @aka L.Point
760   *
761   * Represents a point with `x` and `y` coordinates in pixels.
762   *
763   * @example
764   *
765   * ```js
766   * var point = L.point(200, 300);
767   * ```
768   *
769   * 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:
770   *
771   * ```js
772   * map.panBy([200, 300]);
773   * map.panBy(L.point(200, 300));
774   * ```
775   *
776   * Note that `Point` does not inherit from Leaflet's `Class` object,
777   * which means new classes can't inherit from it, and new methods
778   * can't be added to it with the `include` function.
779   */
780
781  function Point(x, y, round) {
782  	// @property x: Number; The `x` coordinate of the point
783  	this.x = (round ? Math.round(x) : x);
784  	// @property y: Number; The `y` coordinate of the point
785  	this.y = (round ? Math.round(y) : y);
786  }
787
788  var trunc = Math.trunc || function (v) {
789  	return v > 0 ? Math.floor(v) : Math.ceil(v);
790  };
791
792  Point.prototype = {
793
794  	// @method clone(): Point
795  	// Returns a copy of the current point.
796  	clone: function () {
797  		return new Point(this.x, this.y);
798  	},
799
800  	// @method add(otherPoint: Point): Point
801  	// Returns the result of addition of the current and the given points.
802  	add: function (point) {
803  		// non-destructive, returns a new point
804  		return this.clone()._add(toPoint(point));
805  	},
806
807  	_add: function (point) {
808  		// destructive, used directly for performance in situations where it's safe to modify existing point
809  		this.x += point.x;
810  		this.y += point.y;
811  		return this;
812  	},
813
814  	// @method subtract(otherPoint: Point): Point
815  	// Returns the result of subtraction of the given point from the current.
816  	subtract: function (point) {
817  		return this.clone()._subtract(toPoint(point));
818  	},
819
820  	_subtract: function (point) {
821  		this.x -= point.x;
822  		this.y -= point.y;
823  		return this;
824  	},
825
826  	// @method divideBy(num: Number): Point
827  	// Returns the result of division of the current point by the given number.
828  	divideBy: function (num) {
829  		return this.clone()._divideBy(num);
830  	},
831
832  	_divideBy: function (num) {
833  		this.x /= num;
834  		this.y /= num;
835  		return this;
836  	},
837
838  	// @method multiplyBy(num: Number): Point
839  	// Returns the result of multiplication of the current point by the given number.
840  	multiplyBy: function (num) {
841  		return this.clone()._multiplyBy(num);
842  	},
843
844  	_multiplyBy: function (num) {
845  		this.x *= num;
846  		this.y *= num;
847  		return this;
848  	},
849
850  	// @method scaleBy(scale: Point): Point
851  	// Multiply each coordinate of the current point by each coordinate of
852  	// `scale`. In linear algebra terms, multiply the point by the
853  	// [scaling matrix](https://en.wikipedia.org/wiki/Scaling_%28geometry%29#Matrix_representation)
854  	// defined by `scale`.
855  	scaleBy: function (point) {
856  		return new Point(this.x * point.x, this.y * point.y);
857  	},
858
859  	// @method unscaleBy(scale: Point): Point
860  	// Inverse of `scaleBy`. Divide each coordinate of the current point by
861  	// each coordinate of `scale`.
862  	unscaleBy: function (point) {
863  		return new Point(this.x / point.x, this.y / point.y);
864  	},
865
866  	// @method round(): Point
867  	// Returns a copy of the current point with rounded coordinates.
868  	round: function () {
869  		return this.clone()._round();
870  	},
871
872  	_round: function () {
873  		this.x = Math.round(this.x);
874  		this.y = Math.round(this.y);
875  		return this;
876  	},
877
878  	// @method floor(): Point
879  	// Returns a copy of the current point with floored coordinates (rounded down).
880  	floor: function () {
881  		return this.clone()._floor();
882  	},
883
884  	_floor: function () {
885  		this.x = Math.floor(this.x);
886  		this.y = Math.floor(this.y);
887  		return this;
888  	},
889
890  	// @method ceil(): Point
891  	// Returns a copy of the current point with ceiled coordinates (rounded up).
892  	ceil: function () {
893  		return this.clone()._ceil();
894  	},
895
896  	_ceil: function () {
897  		this.x = Math.ceil(this.x);
898  		this.y = Math.ceil(this.y);
899  		return this;
900  	},
901
902  	// @method trunc(): Point
903  	// Returns a copy of the current point with truncated coordinates (rounded towards zero).
904  	trunc: function () {
905  		return this.clone()._trunc();
906  	},
907
908  	_trunc: function () {
909  		this.x = trunc(this.x);
910  		this.y = trunc(this.y);
911  		return this;
912  	},
913
914  	// @method distanceTo(otherPoint: Point): Number
915  	// Returns the cartesian distance between the current and the given points.
916  	distanceTo: function (point) {
917  		point = toPoint(point);
918
919  		var x = point.x - this.x,
920  		    y = point.y - this.y;
921
922  		return Math.sqrt(x * x + y * y);
923  	},
924
925  	// @method equals(otherPoint: Point): Boolean
926  	// Returns `true` if the given point has the same coordinates.
927  	equals: function (point) {
928  		point = toPoint(point);
929
930  		return point.x === this.x &&
931  		       point.y === this.y;
932  	},
933
934  	// @method contains(otherPoint: Point): Boolean
935  	// Returns `true` if both coordinates of the given point are less than the corresponding current point coordinates (in absolute values).
936  	contains: function (point) {
937  		point = toPoint(point);
938
939  		return Math.abs(point.x) <= Math.abs(this.x) &&
940  		       Math.abs(point.y) <= Math.abs(this.y);
941  	},
942
943  	// @method toString(): String
944  	// Returns a string representation of the point for debugging purposes.
945  	toString: function () {
946  		return 'Point(' +
947  		        formatNum(this.x) + ', ' +
948  		        formatNum(this.y) + ')';
949  	}
950  };
951
952  // @factory L.point(x: Number, y: Number, round?: Boolean)
953  // Creates a Point object with the given `x` and `y` coordinates. If optional `round` is set to true, rounds the `x` and `y` values.
954
955  // @alternative
956  // @factory L.point(coords: Number[])
957  // Expects an array of the form `[x, y]` instead.
958
959  // @alternative
960  // @factory L.point(coords: Object)
961  // Expects a plain object of the form `{x: Number, y: Number}` instead.
962  function toPoint(x, y, round) {
963  	if (x instanceof Point) {
964  		return x;
965  	}
966  	if (isArray(x)) {
967  		return new Point(x[0], x[1]);
968  	}
969  	if (x === undefined || x === null) {
970  		return x;
971  	}
972  	if (typeof x === 'object' && 'x' in x && 'y' in x) {
973  		return new Point(x.x, x.y);
974  	}
975  	return new Point(x, y, round);
976  }
977
978  /*
979   * @class Bounds
980   * @aka L.Bounds
981   *
982   * Represents a rectangular area in pixel coordinates.
983   *
984   * @example
985   *
986   * ```js
987   * var p1 = L.point(10, 10),
988   * p2 = L.point(40, 60),
989   * bounds = L.bounds(p1, p2);
990   * ```
991   *
992   * 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:
993   *
994   * ```js
995   * otherBounds.intersects([[10, 10], [40, 60]]);
996   * ```
997   *
998   * Note that `Bounds` does not inherit from Leaflet's `Class` object,
999   * which means new classes can't inherit from it, and new methods
1000   * can't be added to it with the `include` function.
1001   */
1002
1003  function Bounds(a, b) {
1004  	if (!a) { return; }
1005
1006  	var points = b ? [a, b] : a;
1007
1008  	for (var i = 0, len = points.length; i < len; i++) {
1009  		this.extend(points[i]);
1010  	}
1011  }
1012
1013  Bounds.prototype = {
1014  	// @method extend(point: Point): this
1015  	// Extends the bounds to contain the given point.
1016
1017  	// @alternative
1018  	// @method extend(otherBounds: Bounds): this
1019  	// Extend the bounds to contain the given bounds
1020  	extend: function (obj) {
1021  		var min2, max2;
1022  		if (!obj) { return this; }
1023
1024  		if (obj instanceof Point || typeof obj[0] === 'number' || 'x' in obj) {
1025  			min2 = max2 = toPoint(obj);
1026  		} else {
1027  			obj = toBounds(obj);
1028  			min2 = obj.min;
1029  			max2 = obj.max;
1030
1031  			if (!min2 || !max2) { return this; }
1032  		}
1033
1034  		// @property min: Point
1035  		// The top left corner of the rectangle.
1036  		// @property max: Point
1037  		// The bottom right corner of the rectangle.
1038  		if (!this.min && !this.max) {
1039  			this.min = min2.clone();
1040  			this.max = max2.clone();
1041  		} else {
1042  			this.min.x = Math.min(min2.x, this.min.x);
1043  			this.max.x = Math.max(max2.x, this.max.x);
1044  			this.min.y = Math.min(min2.y, this.min.y);
1045  			this.max.y = Math.max(max2.y, this.max.y);
1046  		}
1047  		return this;
1048  	},
1049
1050  	// @method getCenter(round?: Boolean): Point
1051  	// Returns the center point of the bounds.
1052  	getCenter: function (round) {
1053  		return toPoint(
1054  		        (this.min.x + this.max.x) / 2,
1055  		        (this.min.y + this.max.y) / 2, round);
1056  	},
1057
1058  	// @method getBottomLeft(): Point
1059  	// Returns the bottom-left point of the bounds.
1060  	getBottomLeft: function () {
1061  		return toPoint(this.min.x, this.max.y);
1062  	},
1063
1064  	// @method getTopRight(): Point
1065  	// Returns the top-right point of the bounds.
1066  	getTopRight: function () { // -> Point
1067  		return toPoint(this.max.x, this.min.y);
1068  	},
1069
1070  	// @method getTopLeft(): Point
1071  	// Returns the top-left point of the bounds (i.e. [`this.min`](#bounds-min)).
1072  	getTopLeft: function () {
1073  		return this.min; // left, top
1074  	},
1075
1076  	// @method getBottomRight(): Point
1077  	// Returns the bottom-right point of the bounds (i.e. [`this.max`](#bounds-max)).
1078  	getBottomRight: function () {
1079  		return this.max; // right, bottom
1080  	},
1081
1082  	// @method getSize(): Point
1083  	// Returns the size of the given bounds
1084  	getSize: function () {
1085  		return this.max.subtract(this.min);
1086  	},
1087
1088  	// @method contains(otherBounds: Bounds): Boolean
1089  	// Returns `true` if the rectangle contains the given one.
1090  	// @alternative
1091  	// @method contains(point: Point): Boolean
1092  	// Returns `true` if the rectangle contains the given point.
1093  	contains: function (obj) {
1094  		var min, max;
1095
1096  		if (typeof obj[0] === 'number' || obj instanceof Point) {
1097  			obj = toPoint(obj);
1098  		} else {
1099  			obj = toBounds(obj);
1100  		}
1101
1102  		if (obj instanceof Bounds) {
1103  			min = obj.min;
1104  			max = obj.max;
1105  		} else {
1106  			min = max = obj;
1107  		}
1108
1109  		return (min.x >= this.min.x) &&
1110  		       (max.x <= this.max.x) &&
1111  		       (min.y >= this.min.y) &&
1112  		       (max.y <= this.max.y);
1113  	},
1114
1115  	// @method intersects(otherBounds: Bounds): Boolean
1116  	// Returns `true` if the rectangle intersects the given bounds. Two bounds
1117  	// intersect if they have at least one point in common.
1118  	intersects: function (bounds) { // (Bounds) -> Boolean
1119  		bounds = toBounds(bounds);
1120
1121  		var min = this.min,
1122  		    max = this.max,
1123  		    min2 = bounds.min,
1124  		    max2 = bounds.max,
1125  		    xIntersects = (max2.x >= min.x) && (min2.x <= max.x),
1126  		    yIntersects = (max2.y >= min.y) && (min2.y <= max.y);
1127
1128  		return xIntersects && yIntersects;
1129  	},
1130
1131  	// @method overlaps(otherBounds: Bounds): Boolean
1132  	// Returns `true` if the rectangle overlaps the given bounds. Two bounds
1133  	// overlap if their intersection is an area.
1134  	overlaps: function (bounds) { // (Bounds) -> Boolean
1135  		bounds = toBounds(bounds);
1136
1137  		var min = this.min,
1138  		    max = this.max,
1139  		    min2 = bounds.min,
1140  		    max2 = bounds.max,
1141  		    xOverlaps = (max2.x > min.x) && (min2.x < max.x),
1142  		    yOverlaps = (max2.y > min.y) && (min2.y < max.y);
1143
1144  		return xOverlaps && yOverlaps;
1145  	},
1146
1147  	// @method isValid(): Boolean
1148  	// Returns `true` if the bounds are properly initialized.
1149  	isValid: function () {
1150  		return !!(this.min && this.max);
1151  	},
1152
1153
1154  	// @method pad(bufferRatio: Number): Bounds
1155  	// Returns bounds created by extending or retracting the current bounds by a given ratio in each direction.
1156  	// For example, a ratio of 0.5 extends the bounds by 50% in each direction.
1157  	// Negative values will retract the bounds.
1158  	pad: function (bufferRatio) {
1159  		var min = this.min,
1160  		max = this.max,
1161  		heightBuffer = Math.abs(min.x - max.x) * bufferRatio,
1162  		widthBuffer = Math.abs(min.y - max.y) * bufferRatio;
1163
1164
1165  		return toBounds(
1166  			toPoint(min.x - heightBuffer, min.y - widthBuffer),
1167  			toPoint(max.x + heightBuffer, max.y + widthBuffer));
1168  	},
1169
1170
1171  	// @method equals(otherBounds: Bounds): Boolean
1172  	// Returns `true` if the rectangle is equivalent to the given bounds.
1173  	equals: function (bounds) {
1174  		if (!bounds) { return false; }
1175
1176  		bounds = toBounds(bounds);
1177
1178  		return this.min.equals(bounds.getTopLeft()) &&
1179  			this.max.equals(bounds.getBottomRight());
1180  	},
1181  };
1182
1183
1184  // @factory L.bounds(corner1: Point, corner2: Point)
1185  // Creates a Bounds object from two corners coordinate pairs.
1186  // @alternative
1187  // @factory L.bounds(points: Point[])
1188  // Creates a Bounds object from the given array of points.
1189  function toBounds(a, b) {
1190  	if (!a || a instanceof Bounds) {
1191  		return a;
1192  	}
1193  	return new Bounds(a, b);
1194  }
1195
1196  /*
1197   * @class LatLngBounds
1198   * @aka L.LatLngBounds
1199   *
1200   * Represents a rectangular geographical area on a map.
1201   *
1202   * @example
1203   *
1204   * ```js
1205   * var corner1 = L.latLng(40.712, -74.227),
1206   * corner2 = L.latLng(40.774, -74.125),
1207   * bounds = L.latLngBounds(corner1, corner2);
1208   * ```
1209   *
1210   * 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:
1211   *
1212   * ```js
1213   * map.fitBounds([
1214   * 	[40.712, -74.227],
1215   * 	[40.774, -74.125]
1216   * ]);
1217   * ```
1218   *
1219   * 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.
1220   *
1221   * Note that `LatLngBounds` does not inherit from Leaflet's `Class` object,
1222   * which means new classes can't inherit from it, and new methods
1223   * can't be added to it with the `include` function.
1224   */
1225
1226  function LatLngBounds(corner1, corner2) { // (LatLng, LatLng) or (LatLng[])
1227  	if (!corner1) { return; }
1228
1229  	var latlngs = corner2 ? [corner1, corner2] : corner1;
1230
1231  	for (var i = 0, len = latlngs.length; i < len; i++) {
1232  		this.extend(latlngs[i]);
1233  	}
1234  }
1235
1236  LatLngBounds.prototype = {
1237
1238  	// @method extend(latlng: LatLng): this
1239  	// Extend the bounds to contain the given point
1240
1241  	// @alternative
1242  	// @method extend(otherBounds: LatLngBounds): this
1243  	// Extend the bounds to contain the given bounds
1244  	extend: function (obj) {
1245  		var sw = this._southWest,
1246  		    ne = this._northEast,
1247  		    sw2, ne2;
1248
1249  		if (obj instanceof LatLng) {
1250  			sw2 = obj;
1251  			ne2 = obj;
1252
1253  		} else if (obj instanceof LatLngBounds) {
1254  			sw2 = obj._southWest;
1255  			ne2 = obj._northEast;
1256
1257  			if (!sw2 || !ne2) { return this; }
1258
1259  		} else {
1260  			return obj ? this.extend(toLatLng(obj) || toLatLngBounds(obj)) : this;
1261  		}
1262
1263  		if (!sw && !ne) {
1264  			this._southWest = new LatLng(sw2.lat, sw2.lng);
1265  			this._northEast = new LatLng(ne2.lat, ne2.lng);
1266  		} else {
1267  			sw.lat = Math.min(sw2.lat, sw.lat);
1268  			sw.lng = Math.min(sw2.lng, sw.lng);
1269  			ne.lat = Math.max(ne2.lat, ne.lat);
1270  			ne.lng = Math.max(ne2.lng, ne.lng);
1271  		}
1272
1273  		return this;
1274  	},
1275
1276  	// @method pad(bufferRatio: Number): LatLngBounds
1277  	// Returns bounds created by extending or retracting the current bounds by a given ratio in each direction.
1278  	// For example, a ratio of 0.5 extends the bounds by 50% in each direction.
1279  	// Negative values will retract the bounds.
1280  	pad: function (bufferRatio) {
1281  		var sw = this._southWest,
1282  		    ne = this._northEast,
1283  		    heightBuffer = Math.abs(sw.lat - ne.lat) * bufferRatio,
1284  		    widthBuffer = Math.abs(sw.lng - ne.lng) * bufferRatio;
1285
1286  		return new LatLngBounds(
1287  		        new LatLng(sw.lat - heightBuffer, sw.lng - widthBuffer),
1288  		        new LatLng(ne.lat + heightBuffer, ne.lng + widthBuffer));
1289  	},
1290
1291  	// @method getCenter(): LatLng
1292  	// Returns the center point of the bounds.
1293  	getCenter: function () {
1294  		return new LatLng(
1295  		        (this._southWest.lat + this._northEast.lat) / 2,
1296  		        (this._southWest.lng + this._northEast.lng) / 2);
1297  	},
1298
1299  	// @method getSouthWest(): LatLng
1300  	// Returns the south-west point of the bounds.
1301  	getSouthWest: function () {
1302  		return this._southWest;
1303  	},
1304
1305  	// @method getNorthEast(): LatLng
1306  	// Returns the north-east point of the bounds.
1307  	getNorthEast: function () {
1308  		return this._northEast;
1309  	},
1310
1311  	// @method getNorthWest(): LatLng
1312  	// Returns the north-west point of the bounds.
1313  	getNorthWest: function () {
1314  		return new LatLng(this.getNorth(), this.getWest());
1315  	},
1316
1317  	// @method getSouthEast(): LatLng
1318  	// Returns the south-east point of the bounds.
1319  	getSouthEast: function () {
1320  		return new LatLng(this.getSouth(), this.getEast());
1321  	},
1322
1323  	// @method getWest(): Number
1324  	// Returns the west longitude of the bounds
1325  	getWest: function () {
1326  		return this._southWest.lng;
1327  	},
1328
1329  	// @method getSouth(): Number
1330  	// Returns the south latitude of the bounds
1331  	getSouth: function () {
1332  		return this._southWest.lat;
1333  	},
1334
1335  	// @method getEast(): Number
1336  	// Returns the east longitude of the bounds
1337  	getEast: function () {
1338  		return this._northEast.lng;
1339  	},
1340
1341  	// @method getNorth(): Number
1342  	// Returns the north latitude of the bounds
1343  	getNorth: function () {
1344  		return this._northEast.lat;
1345  	},
1346
1347  	// @method contains(otherBounds: LatLngBounds): Boolean
1348  	// Returns `true` if the rectangle contains the given one.
1349
1350  	// @alternative
1351  	// @method contains (latlng: LatLng): Boolean
1352  	// Returns `true` if the rectangle contains the given point.
1353  	contains: function (obj) { // (LatLngBounds) or (LatLng) -> Boolean
1354  		if (typeof obj[0] === 'number' || obj instanceof LatLng || 'lat' in obj) {
1355  			obj = toLatLng(obj);
1356  		} else {
1357  			obj = toLatLngBounds(obj);
1358  		}
1359
1360  		var sw = this._southWest,
1361  		    ne = this._northEast,
1362  		    sw2, ne2;
1363
1364  		if (obj instanceof LatLngBounds) {
1365  			sw2 = obj.getSouthWest();
1366  			ne2 = obj.getNorthEast();
1367  		} else {
1368  			sw2 = ne2 = obj;
1369  		}
1370
1371  		return (sw2.lat >= sw.lat) && (ne2.lat <= ne.lat) &&
1372  		       (sw2.lng >= sw.lng) && (ne2.lng <= ne.lng);
1373  	},
1374
1375  	// @method intersects(otherBounds: LatLngBounds): Boolean
1376  	// Returns `true` if the rectangle intersects the given bounds. Two bounds intersect if they have at least one point in common.
1377  	intersects: function (bounds) {
1378  		bounds = toLatLngBounds(bounds);
1379
1380  		var sw = this._southWest,
1381  		    ne = this._northEast,
1382  		    sw2 = bounds.getSouthWest(),
1383  		    ne2 = bounds.getNorthEast(),
1384
1385  		    latIntersects = (ne2.lat >= sw.lat) && (sw2.lat <= ne.lat),
1386  		    lngIntersects = (ne2.lng >= sw.lng) && (sw2.lng <= ne.lng);
1387
1388  		return latIntersects && lngIntersects;
1389  	},
1390
1391  	// @method overlaps(otherBounds: LatLngBounds): Boolean
1392  	// Returns `true` if the rectangle overlaps the given bounds. Two bounds overlap if their intersection is an area.
1393  	overlaps: function (bounds) {
1394  		bounds = toLatLngBounds(bounds);
1395
1396  		var sw = this._southWest,
1397  		    ne = this._northEast,
1398  		    sw2 = bounds.getSouthWest(),
1399  		    ne2 = bounds.getNorthEast(),
1400
1401  		    latOverlaps = (ne2.lat > sw.lat) && (sw2.lat < ne.lat),
1402  		    lngOverlaps = (ne2.lng > sw.lng) && (sw2.lng < ne.lng);
1403
1404  		return latOverlaps && lngOverlaps;
1405  	},
1406
1407  	// @method toBBoxString(): String
1408  	// 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.
1409  	toBBoxString: function () {
1410  		return [this.getWest(), this.getSouth(), this.getEast(), this.getNorth()].join(',');
1411  	},
1412
1413  	// @method equals(otherBounds: LatLngBounds, maxMargin?: Number): Boolean
1414  	// Returns `true` if the rectangle is equivalent (within a small margin of error) to the given bounds. The margin of error can be overridden by setting `maxMargin` to a small number.
1415  	equals: function (bounds, maxMargin) {
1416  		if (!bounds) { return false; }
1417
1418  		bounds = toLatLngBounds(bounds);
1419
1420  		return this._southWest.equals(bounds.getSouthWest(), maxMargin) &&
1421  		       this._northEast.equals(bounds.getNorthEast(), maxMargin);
1422  	},
1423
1424  	// @method isValid(): Boolean
1425  	// Returns `true` if the bounds are properly initialized.
1426  	isValid: function () {
1427  		return !!(this._southWest && this._northEast);
1428  	}
1429  };
1430
1431  // TODO International date line?
1432
1433  // @factory L.latLngBounds(corner1: LatLng, corner2: LatLng)
1434  // Creates a `LatLngBounds` object by defining two diagonally opposite corners of the rectangle.
1435
1436  // @alternative
1437  // @factory L.latLngBounds(latlngs: LatLng[])
1438  // 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).
1439  function toLatLngBounds(a, b) {
1440  	if (a instanceof LatLngBounds) {
1441  		return a;
1442  	}
1443  	return new LatLngBounds(a, b);
1444  }
1445
1446  /* @class LatLng
1447   * @aka L.LatLng
1448   *
1449   * Represents a geographical point with a certain latitude and longitude.
1450   *
1451   * @example
1452   *
1453   * ```
1454   * var latlng = L.latLng(50.5, 30.5);
1455   * ```
1456   *
1457   * 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:
1458   *
1459   * ```
1460   * map.panTo([50, 30]);
1461   * map.panTo({lon: 30, lat: 50});
1462   * map.panTo({lat: 50, lng: 30});
1463   * map.panTo(L.latLng(50, 30));
1464   * ```
1465   *
1466   * Note that `LatLng` does not inherit from Leaflet's `Class` object,
1467   * which means new classes can't inherit from it, and new methods
1468   * can't be added to it with the `include` function.
1469   */
1470
1471  function LatLng(lat, lng, alt) {
1472  	if (isNaN(lat) || isNaN(lng)) {
1473  		throw new Error('Invalid LatLng object: (' + lat + ', ' + lng + ')');
1474  	}
1475
1476  	// @property lat: Number
1477  	// Latitude in degrees
1478  	this.lat = +lat;
1479
1480  	// @property lng: Number
1481  	// Longitude in degrees
1482  	this.lng = +lng;
1483
1484  	// @property alt: Number
1485  	// Altitude in meters (optional)
1486  	if (alt !== undefined) {
1487  		this.alt = +alt;
1488  	}
1489  }
1490
1491  LatLng.prototype = {
1492  	// @method equals(otherLatLng: LatLng, maxMargin?: Number): Boolean
1493  	// Returns `true` if the given `LatLng` point is at the same position (within a small margin of error). The margin of error can be overridden by setting `maxMargin` to a small number.
1494  	equals: function (obj, maxMargin) {
1495  		if (!obj) { return false; }
1496
1497  		obj = toLatLng(obj);
1498
1499  		var margin = Math.max(
1500  		        Math.abs(this.lat - obj.lat),
1501  		        Math.abs(this.lng - obj.lng));
1502
1503  		return margin <= (maxMargin === undefined ? 1.0E-9 : maxMargin);
1504  	},
1505
1506  	// @method toString(): String
1507  	// Returns a string representation of the point (for debugging purposes).
1508  	toString: function (precision) {
1509  		return 'LatLng(' +
1510  		        formatNum(this.lat, precision) + ', ' +
1511  		        formatNum(this.lng, precision) + ')';
1512  	},
1513
1514  	// @method distanceTo(otherLatLng: LatLng): Number
1515  	// Returns the distance (in meters) to the given `LatLng` calculated using the [Spherical Law of Cosines](https://en.wikipedia.org/wiki/Spherical_law_of_cosines).
1516  	distanceTo: function (other) {
1517  		return Earth.distance(this, toLatLng(other));
1518  	},
1519
1520  	// @method wrap(): LatLng
1521  	// Returns a new `LatLng` object with the longitude wrapped so it's always between -180 and +180 degrees.
1522  	wrap: function () {
1523  		return Earth.wrapLatLng(this);
1524  	},
1525
1526  	// @method toBounds(sizeInMeters: Number): LatLngBounds
1527  	// Returns a new `LatLngBounds` object in which each boundary is `sizeInMeters/2` meters apart from the `LatLng`.
1528  	toBounds: function (sizeInMeters) {
1529  		var latAccuracy = 180 * sizeInMeters / 40075017,
1530  		    lngAccuracy = latAccuracy / Math.cos((Math.PI / 180) * this.lat);
1531
1532  		return toLatLngBounds(
1533  		        [this.lat - latAccuracy, this.lng - lngAccuracy],
1534  		        [this.lat + latAccuracy, this.lng + lngAccuracy]);
1535  	},
1536
1537  	clone: function () {
1538  		return new LatLng(this.lat, this.lng, this.alt);
1539  	}
1540  };
1541
1542
1543
1544  // @factory L.latLng(latitude: Number, longitude: Number, altitude?: Number): LatLng
1545  // Creates an object representing a geographical point with the given latitude and longitude (and optionally altitude).
1546
1547  // @alternative
1548  // @factory L.latLng(coords: Array): LatLng
1549  // Expects an array of the form `[Number, Number]` or `[Number, Number, Number]` instead.
1550
1551  // @alternative
1552  // @factory L.latLng(coords: Object): LatLng
1553  // Expects an plain object of the form `{lat: Number, lng: Number}` or `{lat: Number, lng: Number, alt: Number}` instead.
1554
1555  function toLatLng(a, b, c) {
1556  	if (a instanceof LatLng) {
1557  		return a;
1558  	}
1559  	if (isArray(a) && typeof a[0] !== 'object') {
1560  		if (a.length === 3) {
1561  			return new LatLng(a[0], a[1], a[2]);
1562  		}
1563  		if (a.length === 2) {
1564  			return new LatLng(a[0], a[1]);
1565  		}
1566  		return null;
1567  	}
1568  	if (a === undefined || a === null) {
1569  		return a;
1570  	}
1571  	if (typeof a === 'object' && 'lat' in a) {
1572  		return new LatLng(a.lat, 'lng' in a ? a.lng : a.lon, a.alt);
1573  	}
1574  	if (b === undefined) {
1575  		return null;
1576  	}
1577  	return new LatLng(a, b, c);
1578  }
1579
1580  /*
1581   * @namespace CRS
1582   * @crs L.CRS.Base
1583   * Object that defines coordinate reference systems for projecting
1584   * geographical points into pixel (screen) coordinates and back (and to
1585   * coordinates in other units for [WMS](https://en.wikipedia.org/wiki/Web_Map_Service) services). See
1586   * [spatial reference system](https://en.wikipedia.org/wiki/Spatial_reference_system).
1587   *
1588   * Leaflet defines the most usual CRSs by default. If you want to use a
1589   * CRS not defined by default, take a look at the
1590   * [Proj4Leaflet](https://github.com/kartena/Proj4Leaflet) plugin.
1591   *
1592   * Note that the CRS instances do not inherit from Leaflet's `Class` object,
1593   * and can't be instantiated. Also, new classes can't inherit from them,
1594   * and methods can't be added to them with the `include` function.
1595   */
1596
1597  var CRS = {
1598  	// @method latLngToPoint(latlng: LatLng, zoom: Number): Point
1599  	// Projects geographical coordinates into pixel coordinates for a given zoom.
1600  	latLngToPoint: function (latlng, zoom) {
1601  		var projectedPoint = this.projection.project(latlng),
1602  		    scale = this.scale(zoom);
1603
1604  		return this.transformation._transform(projectedPoint, scale);
1605  	},
1606
1607  	// @method pointToLatLng(point: Point, zoom: Number): LatLng
1608  	// The inverse of `latLngToPoint`. Projects pixel coordinates on a given
1609  	// zoom into geographical coordinates.
1610  	pointToLatLng: function (point, zoom) {
1611  		var scale = this.scale(zoom),
1612  		    untransformedPoint = this.transformation.untransform(point, scale);
1613
1614  		return this.projection.unproject(untransformedPoint);
1615  	},
1616
1617  	// @method project(latlng: LatLng): Point
1618  	// Projects geographical coordinates into coordinates in units accepted for
1619  	// this CRS (e.g. meters for EPSG:3857, for passing it to WMS services).
1620  	project: function (latlng) {
1621  		return this.projection.project(latlng);
1622  	},
1623
1624  	// @method unproject(point: Point): LatLng
1625  	// Given a projected coordinate returns the corresponding LatLng.
1626  	// The inverse of `project`.
1627  	unproject: function (point) {
1628  		return this.projection.unproject(point);
1629  	},
1630
1631  	// @method scale(zoom: Number): Number
1632  	// Returns the scale used when transforming projected coordinates into
1633  	// pixel coordinates for a particular zoom. For example, it returns
1634  	// `256 * 2^zoom` for Mercator-based CRS.
1635  	scale: function (zoom) {
1636  		return 256 * Math.pow(2, zoom);
1637  	},
1638
1639  	// @method zoom(scale: Number): Number
1640  	// Inverse of `scale()`, returns the zoom level corresponding to a scale
1641  	// factor of `scale`.
1642  	zoom: function (scale) {
1643  		return Math.log(scale / 256) / Math.LN2;
1644  	},
1645
1646  	// @method getProjectedBounds(zoom: Number): Bounds
1647  	// Returns the projection's bounds scaled and transformed for the provided `zoom`.
1648  	getProjectedBounds: function (zoom) {
1649  		if (this.infinite) { return null; }
1650
1651  		var b = this.projection.bounds,
1652  		    s = this.scale(zoom),
1653  		    min = this.transformation.transform(b.min, s),
1654  		    max = this.transformation.transform(b.max, s);
1655
1656  		return new Bounds(min, max);
1657  	},
1658
1659  	// @method distance(latlng1: LatLng, latlng2: LatLng): Number
1660  	// Returns the distance between two geographical coordinates.
1661
1662  	// @property code: String
1663  	// Standard code name of the CRS passed into WMS services (e.g. `'EPSG:3857'`)
1664  	//
1665  	// @property wrapLng: Number[]
1666  	// An array of two numbers defining whether the longitude (horizontal) coordinate
1667  	// axis wraps around a given range and how. Defaults to `[-180, 180]` in most
1668  	// geographical CRSs. If `undefined`, the longitude axis does not wrap around.
1669  	//
1670  	// @property wrapLat: Number[]
1671  	// Like `wrapLng`, but for the latitude (vertical) axis.
1672
1673  	// wrapLng: [min, max],
1674  	// wrapLat: [min, max],
1675
1676  	// @property infinite: Boolean
1677  	// If true, the coordinate space will be unbounded (infinite in both axes)
1678  	infinite: false,
1679
1680  	// @method wrapLatLng(latlng: LatLng): LatLng
1681  	// Returns a `LatLng` where lat and lng has been wrapped according to the
1682  	// CRS's `wrapLat` and `wrapLng` properties, if they are outside the CRS's bounds.
1683  	wrapLatLng: function (latlng) {
1684  		var lng = this.wrapLng ? wrapNum(latlng.lng, this.wrapLng, true) : latlng.lng,
1685  		    lat = this.wrapLat ? wrapNum(latlng.lat, this.wrapLat, true) : latlng.lat,
1686  		    alt = latlng.alt;
1687
1688  		return new LatLng(lat, lng, alt);
1689  	},
1690
1691  	// @method wrapLatLngBounds(bounds: LatLngBounds): LatLngBounds
1692  	// Returns a `LatLngBounds` with the same size as the given one, ensuring
1693  	// that its center is within the CRS's bounds.
1694  	// Only accepts actual `L.LatLngBounds` instances, not arrays.
1695  	wrapLatLngBounds: function (bounds) {
1696  		var center = bounds.getCenter(),
1697  		    newCenter = this.wrapLatLng(center),
1698  		    latShift = center.lat - newCenter.lat,
1699  		    lngShift = center.lng - newCenter.lng;
1700
1701  		if (latShift === 0 && lngShift === 0) {
1702  			return bounds;
1703  		}
1704
1705  		var sw = bounds.getSouthWest(),
1706  		    ne = bounds.getNorthEast(),
1707  		    newSw = new LatLng(sw.lat - latShift, sw.lng - lngShift),
1708  		    newNe = new LatLng(ne.lat - latShift, ne.lng - lngShift);
1709
1710  		return new LatLngBounds(newSw, newNe);
1711  	}
1712  };
1713
1714  /*
1715   * @namespace CRS
1716   * @crs L.CRS.Earth
1717   *
1718   * Serves as the base for CRS that are global such that they cover the earth.
1719   * Can only be used as the base for other CRS and cannot be used directly,
1720   * since it does not have a `code`, `projection` or `transformation`. `distance()` returns
1721   * meters.
1722   */
1723
1724  var Earth = extend({}, CRS, {
1725  	wrapLng: [-180, 180],
1726
1727  	// Mean Earth Radius, as recommended for use by
1728  	// the International Union of Geodesy and Geophysics,
1729  	// see https://rosettacode.org/wiki/Haversine_formula
1730  	R: 6371000,
1731
1732  	// distance between two geographical points using spherical law of cosines approximation
1733  	distance: function (latlng1, latlng2) {
1734  		var rad = Math.PI / 180,
1735  		    lat1 = latlng1.lat * rad,
1736  		    lat2 = latlng2.lat * rad,
1737  		    sinDLat = Math.sin((latlng2.lat - latlng1.lat) * rad / 2),
1738  		    sinDLon = Math.sin((latlng2.lng - latlng1.lng) * rad / 2),
1739  		    a = sinDLat * sinDLat + Math.cos(lat1) * Math.cos(lat2) * sinDLon * sinDLon,
1740  		    c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
1741  		return this.R * c;
1742  	}
1743  });
1744
1745  /*
1746   * @namespace Projection
1747   * @projection L.Projection.SphericalMercator
1748   *
1749   * Spherical Mercator projection — the most common projection for online maps,
1750   * used by almost all free and commercial tile providers. Assumes that Earth is
1751   * a sphere. Used by the `EPSG:3857` CRS.
1752   */
1753
1754  var earthRadius = 6378137;
1755
1756  var SphericalMercator = {
1757
1758  	R: earthRadius,
1759  	MAX_LATITUDE: 85.0511287798,
1760
1761  	project: function (latlng) {
1762  		var d = Math.PI / 180,
1763  		    max = this.MAX_LATITUDE,
1764  		    lat = Math.max(Math.min(max, latlng.lat), -max),
1765  		    sin = Math.sin(lat * d);
1766
1767  		return new Point(
1768  			this.R * latlng.lng * d,
1769  			this.R * Math.log((1 + sin) / (1 - sin)) / 2);
1770  	},
1771
1772  	unproject: function (point) {
1773  		var d = 180 / Math.PI;
1774
1775  		return new LatLng(
1776  			(2 * Math.atan(Math.exp(point.y / this.R)) - (Math.PI / 2)) * d,
1777  			point.x * d / this.R);
1778  	},
1779
1780  	bounds: (function () {
1781  		var d = earthRadius * Math.PI;
1782  		return new Bounds([-d, -d], [d, d]);
1783  	})()
1784  };
1785
1786  /*
1787   * @class Transformation
1788   * @aka L.Transformation
1789   *
1790   * Represents an affine transformation: a set of coefficients `a`, `b`, `c`, `d`
1791   * for transforming a point of a form `(x, y)` into `(a*x + b, c*y + d)` and doing
1792   * the reverse. Used by Leaflet in its projections code.
1793   *
1794   * @example
1795   *
1796   * ```js
1797   * var transformation = L.transformation(2, 5, -1, 10),
1798   * 	p = L.point(1, 2),
1799   * 	p2 = transformation.transform(p), //  L.point(7, 8)
1800   * 	p3 = transformation.untransform(p2); //  L.point(1, 2)
1801   * ```
1802   */
1803
1804
1805  // factory new L.Transformation(a: Number, b: Number, c: Number, d: Number)
1806  // Creates a `Transformation` object with the given coefficients.
1807  function Transformation(a, b, c, d) {
1808  	if (isArray(a)) {
1809  		// use array properties
1810  		this._a = a[0];
1811  		this._b = a[1];
1812  		this._c = a[2];
1813  		this._d = a[3];
1814  		return;
1815  	}
1816  	this._a = a;
1817  	this._b = b;
1818  	this._c = c;
1819  	this._d = d;
1820  }
1821
1822  Transformation.prototype = {
1823  	// @method transform(point: Point, scale?: Number): Point
1824  	// Returns a transformed point, optionally multiplied by the given scale.
1825  	// Only accepts actual `L.Point` instances, not arrays.
1826  	transform: function (point, scale) { // (Point, Number) -> Point
1827  		return this._transform(point.clone(), scale);
1828  	},
1829
1830  	// destructive transform (faster)
1831  	_transform: function (point, scale) {
1832  		scale = scale || 1;
1833  		point.x = scale * (this._a * point.x + this._b);
1834  		point.y = scale * (this._c * point.y + this._d);
1835  		return point;
1836  	},
1837
1838  	// @method untransform(point: Point, scale?: Number): Point
1839  	// Returns the reverse transformation of the given point, optionally divided
1840  	// by the given scale. Only accepts actual `L.Point` instances, not arrays.
1841  	untransform: function (point, scale) {
1842  		scale = scale || 1;
1843  		return new Point(
1844  		        (point.x / scale - this._b) / this._a,
1845  		        (point.y / scale - this._d) / this._c);
1846  	}
1847  };
1848
1849  // factory L.transformation(a: Number, b: Number, c: Number, d: Number)
1850
1851  // @factory L.transformation(a: Number, b: Number, c: Number, d: Number)
1852  // Instantiates a Transformation object with the given coefficients.
1853
1854  // @alternative
1855  // @factory L.transformation(coefficients: Array): Transformation
1856  // Expects an coefficients array of the form
1857  // `[a: Number, b: Number, c: Number, d: Number]`.
1858
1859  function toTransformation(a, b, c, d) {
1860  	return new Transformation(a, b, c, d);
1861  }
1862
1863  /*
1864   * @namespace CRS
1865   * @crs L.CRS.EPSG3857
1866   *
1867   * The most common CRS for online maps, used by almost all free and commercial
1868   * tile providers. Uses Spherical Mercator projection. Set in by default in
1869   * Map's `crs` option.
1870   */
1871
1872  var EPSG3857 = extend({}, Earth, {
1873  	code: 'EPSG:3857',
1874  	projection: SphericalMercator,
1875
1876  	transformation: (function () {
1877  		var scale = 0.5 / (Math.PI * SphericalMercator.R);
1878  		return toTransformation(scale, 0.5, -scale, 0.5);
1879  	}())
1880  });
1881
1882  var EPSG900913 = extend({}, EPSG3857, {
1883  	code: 'EPSG:900913'
1884  });
1885
1886  // @namespace SVG; @section
1887  // There are several static functions which can be called without instantiating L.SVG:
1888
1889  // @function create(name: String): SVGElement
1890  // Returns a instance of [SVGElement](https://developer.mozilla.org/docs/Web/API/SVGElement),
1891  // corresponding to the class name passed. For example, using 'line' will return
1892  // an instance of [SVGLineElement](https://developer.mozilla.org/docs/Web/API/SVGLineElement).
1893  function svgCreate(name) {
1894  	return document.createElementNS('http://www.w3.org/2000/svg', name);
1895  }
1896
1897  // @function pointsToPath(rings: Point[], closed: Boolean): String
1898  // Generates a SVG path string for multiple rings, with each ring turning
1899  // into "M..L..L.." instructions
1900  function pointsToPath(rings, closed) {
1901  	var str = '',
1902  	i, j, len, len2, points, p;
1903
1904  	for (i = 0, len = rings.length; i < len; i++) {
1905  		points = rings[i];
1906
1907  		for (j = 0, len2 = points.length; j < len2; j++) {
1908  			p = points[j];
1909  			str += (j ? 'L' : 'M') + p.x + ' ' + p.y;
1910  		}
1911
1912  		// closes the ring for polygons; "x" is VML syntax
1913  		str += closed ? (Browser.svg ? 'z' : 'x') : '';
1914  	}
1915
1916  	// SVG complains about empty path strings
1917  	return str || 'M0 0';
1918  }
1919
1920  /*
1921   * @namespace Browser
1922   * @aka L.Browser
1923   *
1924   * A namespace with static properties for browser/feature detection used by Leaflet internally.
1925   *
1926   * @example
1927   *
1928   * ```js
1929   * if (L.Browser.ielt9) {
1930   *   alert('Upgrade your browser, dude!');
1931   * }
1932   * ```
1933   */
1934
1935  var style = document.documentElement.style;
1936
1937  // @property ie: Boolean; `true` for all Internet Explorer versions (not Edge).
1938  var ie = 'ActiveXObject' in window;
1939
1940  // @property ielt9: Boolean; `true` for Internet Explorer versions less than 9.
1941  var ielt9 = ie && !document.addEventListener;
1942
1943  // @property edge: Boolean; `true` for the Edge web browser.
1944  var edge = 'msLaunchUri' in navigator && !('documentMode' in document);
1945
1946  // @property webkit: Boolean;
1947  // `true` for webkit-based browsers like Chrome and Safari (including mobile versions).
1948  var webkit = userAgentContains('webkit');
1949
1950  // @property android: Boolean
1951  // **Deprecated.** `true` for any browser running on an Android platform.
1952  var android = userAgentContains('android');
1953
1954  // @property android23: Boolean; **Deprecated.** `true` for browsers running on Android 2 or Android 3.
1955  var android23 = userAgentContains('android 2') || userAgentContains('android 3');
1956
1957  /* See https://stackoverflow.com/a/17961266 for details on detecting stock Android */
1958  var webkitVer = parseInt(/WebKit\/([0-9]+)|$/.exec(navigator.userAgent)[1], 10); // also matches AppleWebKit
1959  // @property androidStock: Boolean; **Deprecated.** `true` for the Android stock browser (i.e. not Chrome)
1960  var androidStock = android && userAgentContains('Google') && webkitVer < 537 && !('AudioNode' in window);
1961
1962  // @property opera: Boolean; `true` for the Opera browser
1963  var opera = !!window.opera;
1964
1965  // @property chrome: Boolean; `true` for the Chrome browser.
1966  var chrome = !edge && userAgentContains('chrome');
1967
1968  // @property gecko: Boolean; `true` for gecko-based browsers like Firefox.
1969  var gecko = userAgentContains('gecko') && !webkit && !opera && !ie;
1970
1971  // @property safari: Boolean; `true` for the Safari browser.
1972  var safari = !chrome && userAgentContains('safari');
1973
1974  var phantom = userAgentContains('phantom');
1975
1976  // @property opera12: Boolean
1977  // `true` for the Opera browser supporting CSS transforms (version 12 or later).
1978  var opera12 = 'OTransition' in style;
1979
1980  // @property win: Boolean; `true` when the browser is running in a Windows platform
1981  var win = navigator.platform.indexOf('Win') === 0;
1982
1983  // @property ie3d: Boolean; `true` for all Internet Explorer versions supporting CSS transforms.
1984  var ie3d = ie && ('transition' in style);
1985
1986  // @property webkit3d: Boolean; `true` for webkit-based browsers supporting CSS transforms.
1987  var webkit3d = ('WebKitCSSMatrix' in window) && ('m11' in new window.WebKitCSSMatrix()) && !android23;
1988
1989  // @property gecko3d: Boolean; `true` for gecko-based browsers supporting CSS transforms.
1990  var gecko3d = 'MozPerspective' in style;
1991
1992  // @property any3d: Boolean
1993  // `true` for all browsers supporting CSS transforms.
1994  var any3d = !window.L_DISABLE_3D && (ie3d || webkit3d || gecko3d) && !opera12 && !phantom;
1995
1996  // @property mobile: Boolean; `true` for all browsers running in a mobile device.
1997  var mobile = typeof orientation !== 'undefined' || userAgentContains('mobile');
1998
1999  // @property mobileWebkit: Boolean; `true` for all webkit-based browsers in a mobile device.
2000  var mobileWebkit = mobile && webkit;
2001
2002  // @property mobileWebkit3d: Boolean
2003  // `true` for all webkit-based browsers in a mobile device supporting CSS transforms.
2004  var mobileWebkit3d = mobile && webkit3d;
2005
2006  // @property msPointer: Boolean
2007  // `true` for browsers implementing the Microsoft touch events model (notably IE10).
2008  var msPointer = !window.PointerEvent && window.MSPointerEvent;
2009
2010  // @property pointer: Boolean
2011  // `true` for all browsers supporting [pointer events](https://msdn.microsoft.com/en-us/library/dn433244%28v=vs.85%29.aspx).
2012  var pointer = !!(window.PointerEvent || msPointer);
2013
2014  // @property touchNative: Boolean
2015  // `true` for all browsers supporting [touch events](https://developer.mozilla.org/docs/Web/API/Touch_events).
2016  // **This does not necessarily mean** that the browser is running in a computer with
2017  // a touchscreen, it only means that the browser is capable of understanding
2018  // touch events.
2019  var touchNative = 'ontouchstart' in window || !!window.TouchEvent;
2020
2021  // @property touch: Boolean
2022  // `true` for all browsers supporting either [touch](#browser-touch) or [pointer](#browser-pointer) events.
2023  // Note: pointer events will be preferred (if available), and processed for all `touch*` listeners.
2024  var touch = !window.L_NO_TOUCH && (touchNative || pointer);
2025
2026  // @property mobileOpera: Boolean; `true` for the Opera browser in a mobile device.
2027  var mobileOpera = mobile && opera;
2028
2029  // @property mobileGecko: Boolean
2030  // `true` for gecko-based browsers running in a mobile device.
2031  var mobileGecko = mobile && gecko;
2032
2033  // @property retina: Boolean
2034  // `true` for browsers on a high-resolution "retina" screen or on any screen when browser's display zoom is more than 100%.
2035  var retina = (window.devicePixelRatio || (window.screen.deviceXDPI / window.screen.logicalXDPI)) > 1;
2036
2037  // @property passiveEvents: Boolean
2038  // `true` for browsers that support passive events.
2039  var passiveEvents = (function () {
2040  	var supportsPassiveOption = false;
2041  	try {
2042  		var opts = Object.defineProperty({}, 'passive', {
2043  			get: function () { // eslint-disable-line getter-return
2044  				supportsPassiveOption = true;
2045  			}
2046  		});
2047  		window.addEventListener('testPassiveEventSupport', falseFn, opts);
2048  		window.removeEventListener('testPassiveEventSupport', falseFn, opts);
2049  	} catch (e) {
2050  		// Errors can safely be ignored since this is only a browser support test.
2051  	}
2052  	return supportsPassiveOption;
2053  }());
2054
2055  // @property canvas: Boolean
2056  // `true` when the browser supports [`<canvas>`](https://developer.mozilla.org/docs/Web/API/Canvas_API).
2057  var canvas$1 = (function () {
2058  	return !!document.createElement('canvas').getContext;
2059  }());
2060
2061  // @property svg: Boolean
2062  // `true` when the browser supports [SVG](https://developer.mozilla.org/docs/Web/SVG).
2063  var svg$1 = !!(document.createElementNS && svgCreate('svg').createSVGRect);
2064
2065  var inlineSvg = !!svg$1 && (function () {
2066  	var div = document.createElement('div');
2067  	div.innerHTML = '<svg/>';
2068  	return (div.firstChild && div.firstChild.namespaceURI) === 'http://www.w3.org/2000/svg';
2069  })();
2070
2071  // @property vml: Boolean
2072  // `true` if the browser supports [VML](https://en.wikipedia.org/wiki/Vector_Markup_Language).
2073  var vml = !svg$1 && (function () {
2074  	try {
2075  		var div = document.createElement('div');
2076  		div.innerHTML = '<v:shape adj="1"/>';
2077
2078  		var shape = div.firstChild;
2079  		shape.style.behavior = 'url(#default#VML)';
2080
2081  		return shape && (typeof shape.adj === 'object');
2082
2083  	} catch (e) {
2084  		return false;
2085  	}
2086  }());
2087
2088
2089  // @property mac: Boolean; `true` when the browser is running in a Mac platform
2090  var mac = navigator.platform.indexOf('Mac') === 0;
2091
2092  // @property mac: Boolean; `true` when the browser is running in a Linux platform
2093  var linux = navigator.platform.indexOf('Linux') === 0;
2094
2095  function userAgentContains(str) {
2096  	return navigator.userAgent.toLowerCase().indexOf(str) >= 0;
2097  }
2098
2099
2100  var Browser = {
2101  	ie: ie,
2102  	ielt9: ielt9,
2103  	edge: edge,
2104  	webkit: webkit,
2105  	android: android,
2106  	android23: android23,
2107  	androidStock: androidStock,
2108  	opera: opera,
2109  	chrome: chrome,
2110  	gecko: gecko,
2111  	safari: safari,
2112  	phantom: phantom,
2113  	opera12: opera12,
2114  	win: win,
2115  	ie3d: ie3d,
2116  	webkit3d: webkit3d,
2117  	gecko3d: gecko3d,
2118  	any3d: any3d,
2119  	mobile: mobile,
2120  	mobileWebkit: mobileWebkit,
2121  	mobileWebkit3d: mobileWebkit3d,
2122  	msPointer: msPointer,
2123  	pointer: pointer,
2124  	touch: touch,
2125  	touchNative: touchNative,
2126  	mobileOpera: mobileOpera,
2127  	mobileGecko: mobileGecko,
2128  	retina: retina,
2129  	passiveEvents: passiveEvents,
2130  	canvas: canvas$1,
2131  	svg: svg$1,
2132  	vml: vml,
2133  	inlineSvg: inlineSvg,
2134  	mac: mac,
2135  	linux: linux
2136  };
2137
2138  /*
2139   * Extends L.DomEvent to provide touch support for Internet Explorer and Windows-based devices.
2140   */
2141
2142  var POINTER_DOWN =   Browser.msPointer ? 'MSPointerDown'   : 'pointerdown';
2143  var POINTER_MOVE =   Browser.msPointer ? 'MSPointerMove'   : 'pointermove';
2144  var POINTER_UP =     Browser.msPointer ? 'MSPointerUp'     : 'pointerup';
2145  var POINTER_CANCEL = Browser.msPointer ? 'MSPointerCancel' : 'pointercancel';
2146  var pEvent = {
2147  	touchstart  : POINTER_DOWN,
2148  	touchmove   : POINTER_MOVE,
2149  	touchend    : POINTER_UP,
2150  	touchcancel : POINTER_CANCEL
2151  };
2152  var handle = {
2153  	touchstart  : _onPointerStart,
2154  	touchmove   : _handlePointer,
2155  	touchend    : _handlePointer,
2156  	touchcancel : _handlePointer
2157  };
2158  var _pointers = {};
2159  var _pointerDocListener = false;
2160
2161  // Provides a touch events wrapper for (ms)pointer events.
2162  // ref https://www.w3.org/TR/pointerevents/ https://www.w3.org/Bugs/Public/show_bug.cgi?id=22890
2163
2164  function addPointerListener(obj, type, handler) {
2165  	if (type === 'touchstart') {
2166  		_addPointerDocListener();
2167  	}
2168  	if (!handle[type]) {
2169  		console.warn('wrong event specified:', type);
2170  		return falseFn;
2171  	}
2172  	handler = handle[type].bind(this, handler);
2173  	obj.addEventListener(pEvent[type], handler, false);
2174  	return handler;
2175  }
2176
2177  function removePointerListener(obj, type, handler) {
2178  	if (!pEvent[type]) {
2179  		console.warn('wrong event specified:', type);
2180  		return;
2181  	}
2182  	obj.removeEventListener(pEvent[type], handler, false);
2183  }
2184
2185  function _globalPointerDown(e) {
2186  	_pointers[e.pointerId] = e;
2187  }
2188
2189  function _globalPointerMove(e) {
2190  	if (_pointers[e.pointerId]) {
2191  		_pointers[e.pointerId] = e;
2192  	}
2193  }
2194
2195  function _globalPointerUp(e) {
2196  	delete _pointers[e.pointerId];
2197  }
2198
2199  function _addPointerDocListener() {
2200  	// need to keep track of what pointers and how many are active to provide e.touches emulation
2201  	if (!_pointerDocListener) {
2202  		// we listen document as any drags that end by moving the touch off the screen get fired there
2203  		document.addEventListener(POINTER_DOWN, _globalPointerDown, true);
2204  		document.addEventListener(POINTER_MOVE, _globalPointerMove, true);
2205  		document.addEventListener(POINTER_UP, _globalPointerUp, true);
2206  		document.addEventListener(POINTER_CANCEL, _globalPointerUp, true);
2207
2208  		_pointerDocListener = true;
2209  	}
2210  }
2211
2212  function _handlePointer(handler, e) {
2213  	if (e.pointerType === (e.MSPOINTER_TYPE_MOUSE || 'mouse')) { return; }
2214
2215  	e.touches = [];
2216  	for (var i in _pointers) {
2217  		e.touches.push(_pointers[i]);
2218  	}
2219  	e.changedTouches = [e];
2220
2221  	handler(e);
2222  }
2223
2224  function _onPointerStart(handler, e) {
2225  	// IE10 specific: MsTouch needs preventDefault. See #2000
2226  	if (e.MSPOINTER_TYPE_TOUCH && e.pointerType === e.MSPOINTER_TYPE_TOUCH) {
2227  		preventDefault(e);
2228  	}
2229  	_handlePointer(handler, e);
2230  }
2231
2232  /*
2233   * Extends the event handling code with double tap support for mobile browsers.
2234   *
2235   * Note: currently most browsers fire native dblclick, with only a few exceptions
2236   * (see https://github.com/Leaflet/Leaflet/issues/7012#issuecomment-595087386)
2237   */
2238
2239  function makeDblclick(event) {
2240  	// in modern browsers `type` cannot be just overridden:
2241  	// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Errors/Getter_only
2242  	var newEvent = {},
2243  	    prop, i;
2244  	for (i in event) {
2245  		prop = event[i];
2246  		newEvent[i] = prop && prop.bind ? prop.bind(event) : prop;
2247  	}
2248  	event = newEvent;
2249  	newEvent.type = 'dblclick';
2250  	newEvent.detail = 2;
2251  	newEvent.isTrusted = false;
2252  	newEvent._simulated = true; // for debug purposes
2253  	return newEvent;
2254  }
2255
2256  var delay = 200;
2257  function addDoubleTapListener(obj, handler) {
2258  	// Most browsers handle double tap natively
2259  	obj.addEventListener('dblclick', handler);
2260
2261  	// On some platforms the browser doesn't fire native dblclicks for touch events.
2262  	// It seems that in all such cases `detail` property of `click` event is always `1`.
2263  	// So here we rely on that fact to avoid excessive 'dblclick' simulation when not needed.
2264  	var last = 0,
2265  	    detail;
2266  	function simDblclick(e) {
2267  		if (e.detail !== 1) {
2268  			detail = e.detail; // keep in sync to avoid false dblclick in some cases
2269  			return;
2270  		}
2271
2272  		if (e.pointerType === 'mouse' ||
2273  			(e.sourceCapabilities && !e.sourceCapabilities.firesTouchEvents)) {
2274
2275  			return;
2276  		}
2277
2278  		// When clicking on an <input>, the browser generates a click on its
2279  		// <label> (and vice versa) triggering two clicks in quick succession.
2280  		// This ignores clicks on elements which are a label with a 'for'
2281  		// attribute (or children of such a label), but not children of
2282  		// a <input>.
2283  		var path = getPropagationPath(e);
2284  		if (path.some(function (el) {
2285  			return el instanceof HTMLLabelElement && el.attributes.for;
2286  		}) &&
2287  			!path.some(function (el) {
2288  				return (
2289  					el instanceof HTMLInputElement ||
2290  					el instanceof HTMLSelectElement
2291  				);
2292  			})
2293  		) {
2294  			return;
2295  		}
2296
2297  		var now = Date.now();
2298  		if (now - last <= delay) {
2299  			detail++;
2300  			if (detail === 2) {
2301  				handler(makeDblclick(e));
2302  			}
2303  		} else {
2304  			detail = 1;
2305  		}
2306  		last = now;
2307  	}
2308
2309  	obj.addEventListener('click', simDblclick);
2310
2311  	return {
2312  		dblclick: handler,
2313  		simDblclick: simDblclick
2314  	};
2315  }
2316
2317  function removeDoubleTapListener(obj, handlers) {
2318  	obj.removeEventListener('dblclick', handlers.dblclick);
2319  	obj.removeEventListener('click', handlers.simDblclick);
2320  }
2321
2322  /*
2323   * @namespace DomUtil
2324   *
2325   * Utility functions to work with the [DOM](https://developer.mozilla.org/docs/Web/API/Document_Object_Model)
2326   * tree, used by Leaflet internally.
2327   *
2328   * Most functions expecting or returning a `HTMLElement` also work for
2329   * SVG elements. The only difference is that classes refer to CSS classes
2330   * in HTML and SVG classes in SVG.
2331   */
2332
2333
2334  // @property TRANSFORM: String
2335  // Vendor-prefixed transform style name (e.g. `'webkitTransform'` for WebKit).
2336  var TRANSFORM = testProp(
2337  	['transform', 'webkitTransform', 'OTransform', 'MozTransform', 'msTransform']);
2338
2339  // webkitTransition comes first because some browser versions that drop vendor prefix don't do
2340  // the same for the transitionend event, in particular the Android 4.1 stock browser
2341
2342  // @property TRANSITION: String
2343  // Vendor-prefixed transition style name.
2344  var TRANSITION = testProp(
2345  	['webkitTransition', 'transition', 'OTransition', 'MozTransition', 'msTransition']);
2346
2347  // @property TRANSITION_END: String
2348  // Vendor-prefixed transitionend event name.
2349  var TRANSITION_END =
2350  	TRANSITION === 'webkitTransition' || TRANSITION === 'OTransition' ? TRANSITION + 'End' : 'transitionend';
2351
2352
2353  // @function get(id: String|HTMLElement): HTMLElement
2354  // Returns an element given its DOM id, or returns the element itself
2355  // if it was passed directly.
2356  function get(id) {
2357  	return typeof id === 'string' ? document.getElementById(id) : id;
2358  }
2359
2360  // @function getStyle(el: HTMLElement, styleAttrib: String): String
2361  // Returns the value for a certain style attribute on an element,
2362  // including computed values or values set through CSS.
2363  function getStyle(el, style) {
2364  	var value = el.style[style] || (el.currentStyle && el.currentStyle[style]);
2365
2366  	if ((!value || value === 'auto') && document.defaultView) {
2367  		var css = document.defaultView.getComputedStyle(el, null);
2368  		value = css ? css[style] : null;
2369  	}
2370  	return value === 'auto' ? null : value;
2371  }
2372
2373  // @function create(tagName: String, className?: String, container?: HTMLElement): HTMLElement
2374  // Creates an HTML element with `tagName`, sets its class to `className`, and optionally appends it to `container` element.
2375  function create$1(tagName, className, container) {
2376  	var el = document.createElement(tagName);
2377  	el.className = className || '';
2378
2379  	if (container) {
2380  		container.appendChild(el);
2381  	}
2382  	return el;
2383  }
2384
2385  // @function remove(el: HTMLElement)
2386  // Removes `el` from its parent element
2387  function remove(el) {
2388  	var parent = el.parentNode;
2389  	if (parent) {
2390  		parent.removeChild(el);
2391  	}
2392  }
2393
2394  // @function empty(el: HTMLElement)
2395  // Removes all of `el`'s children elements from `el`
2396  function empty(el) {
2397  	while (el.firstChild) {
2398  		el.removeChild(el.firstChild);
2399  	}
2400  }
2401
2402  // @function toFront(el: HTMLElement)
2403  // Makes `el` the last child of its parent, so it renders in front of the other children.
2404  function toFront(el) {
2405  	var parent = el.parentNode;
2406  	if (parent && parent.lastChild !== el) {
2407  		parent.appendChild(el);
2408  	}
2409  }
2410
2411  // @function toBack(el: HTMLElement)
2412  // Makes `el` the first child of its parent, so it renders behind the other children.
2413  function toBack(el) {
2414  	var parent = el.parentNode;
2415  	if (parent && parent.firstChild !== el) {
2416  		parent.insertBefore(el, parent.firstChild);
2417  	}
2418  }
2419
2420  // @function hasClass(el: HTMLElement, name: String): Boolean
2421  // Returns `true` if the element's class attribute contains `name`.
2422  function hasClass(el, name) {
2423  	if (el.classList !== undefined) {
2424  		return el.classList.contains(name);
2425  	}
2426  	var className = getClass(el);
2427  	return className.length > 0 && new RegExp('(^|\\s)' + name + '(\\s|$)').test(className);
2428  }
2429
2430  // @function addClass(el: HTMLElement, name: String)
2431  // Adds `name` to the element's class attribute.
2432  function addClass(el, name) {
2433  	if (el.classList !== undefined) {
2434  		var classes = splitWords(name);
2435  		for (var i = 0, len = classes.length; i < len; i++) {
2436  			el.classList.add(classes[i]);
2437  		}
2438  	} else if (!hasClass(el, name)) {
2439  		var className = getClass(el);
2440  		setClass(el, (className ? className + ' ' : '') + name);
2441  	}
2442  }
2443
2444  // @function removeClass(el: HTMLElement, name: String)
2445  // Removes `name` from the element's class attribute.
2446  function removeClass(el, name) {
2447  	if (el.classList !== undefined) {
2448  		el.classList.remove(name);
2449  	} else {
2450  		setClass(el, trim((' ' + getClass(el) + ' ').replace(' ' + name + ' ', ' ')));
2451  	}
2452  }
2453
2454  // @function setClass(el: HTMLElement, name: String)
2455  // Sets the element's class.
2456  function setClass(el, name) {
2457  	if (el.className.baseVal === undefined) {
2458  		el.className = name;
2459  	} else {
2460  		// in case of SVG element
2461  		el.className.baseVal = name;
2462  	}
2463  }
2464
2465  // @function getClass(el: HTMLElement): String
2466  // Returns the element's class.
2467  function getClass(el) {
2468  	// Check if the element is an SVGElementInstance and use the correspondingElement instead
2469  	// (Required for linked SVG elements in IE11.)
2470  	if (el.correspondingElement) {
2471  		el = el.correspondingElement;
2472  	}
2473  	return el.className.baseVal === undefined ? el.className : el.className.baseVal;
2474  }
2475
2476  // @function setOpacity(el: HTMLElement, opacity: Number)
2477  // Set the opacity of an element (including old IE support).
2478  // `opacity` must be a number from `0` to `1`.
2479  function setOpacity(el, value) {
2480  	if ('opacity' in el.style) {
2481  		el.style.opacity = value;
2482  	} else if ('filter' in el.style) {
2483  		_setOpacityIE(el, value);
2484  	}
2485  }
2486
2487  function _setOpacityIE(el, value) {
2488  	var filter = false,
2489  	    filterName = 'DXImageTransform.Microsoft.Alpha';
2490
2491  	// filters collection throws an error if we try to retrieve a filter that doesn't exist
2492  	try {
2493  		filter = el.filters.item(filterName);
2494  	} catch (e) {
2495  		// don't set opacity to 1 if we haven't already set an opacity,
2496  		// it isn't needed and breaks transparent pngs.
2497  		if (value === 1) { return; }
2498  	}
2499
2500  	value = Math.round(value * 100);
2501
2502  	if (filter) {
2503  		filter.Enabled = (value !== 100);
2504  		filter.Opacity = value;
2505  	} else {
2506  		el.style.filter += ' progid:' + filterName + '(opacity=' + value + ')';
2507  	}
2508  }
2509
2510  // @function testProp(props: String[]): String|false
2511  // Goes through the array of style names and returns the first name
2512  // that is a valid style name for an element. If no such name is found,
2513  // it returns false. Useful for vendor-prefixed styles like `transform`.
2514  function testProp(props) {
2515  	var style = document.documentElement.style;
2516
2517  	for (var i = 0; i < props.length; i++) {
2518  		if (props[i] in style) {
2519  			return props[i];
2520  		}
2521  	}
2522  	return false;
2523  }
2524
2525  // @function setTransform(el: HTMLElement, offset: Point, scale?: Number)
2526  // Resets the 3D CSS transform of `el` so it is translated by `offset` pixels
2527  // and optionally scaled by `scale`. Does not have an effect if the
2528  // browser doesn't support 3D CSS transforms.
2529  function setTransform(el, offset, scale) {
2530  	var pos = offset || new Point(0, 0);
2531
2532  	el.style[TRANSFORM] =
2533  		(Browser.ie3d ?
2534  			'translate(' + pos.x + 'px,' + pos.y + 'px)' :
2535  			'translate3d(' + pos.x + 'px,' + pos.y + 'px,0)') +
2536  		(scale ? ' scale(' + scale + ')' : '');
2537  }
2538
2539  // @function setPosition(el: HTMLElement, position: Point)
2540  // Sets the position of `el` to coordinates specified by `position`,
2541  // using CSS translate or top/left positioning depending on the browser
2542  // (used by Leaflet internally to position its layers).
2543  function setPosition(el, point) {
2544
2545  	/*eslint-disable */
2546  	el._leaflet_pos = point;
2547  	/* eslint-enable */
2548
2549  	if (Browser.any3d) {
2550  		setTransform(el, point);
2551  	} else {
2552  		el.style.left = point.x + 'px';
2553  		el.style.top = point.y + 'px';
2554  	}
2555  }
2556
2557  // @function getPosition(el: HTMLElement): Point
2558  // Returns the coordinates of an element previously positioned with setPosition.
2559  function getPosition(el) {
2560  	// this method is only used for elements previously positioned using setPosition,
2561  	// so it's safe to cache the position for performance
2562
2563  	return el._leaflet_pos || new Point(0, 0);
2564  }
2565
2566  // @function disableTextSelection()
2567  // Prevents the user from generating `selectstart` DOM events, usually generated
2568  // when the user drags the mouse through a page with text. Used internally
2569  // by Leaflet to override the behaviour of any click-and-drag interaction on
2570  // the map. Affects drag interactions on the whole document.
2571
2572  // @function enableTextSelection()
2573  // Cancels the effects of a previous [`L.DomUtil.disableTextSelection`](#domutil-disabletextselection).
2574  var disableTextSelection;
2575  var enableTextSelection;
2576  var _userSelect;
2577  if ('onselectstart' in document) {
2578  	disableTextSelection = function () {
2579  		on(window, 'selectstart', preventDefault);
2580  	};
2581  	enableTextSelection = function () {
2582  		off(window, 'selectstart', preventDefault);
2583  	};
2584  } else {
2585  	var userSelectProperty = testProp(
2586  		['userSelect', 'WebkitUserSelect', 'OUserSelect', 'MozUserSelect', 'msUserSelect']);
2587
2588  	disableTextSelection = function () {
2589  		if (userSelectProperty) {
2590  			var style = document.documentElement.style;
2591  			_userSelect = style[userSelectProperty];
2592  			style[userSelectProperty] = 'none';
2593  		}
2594  	};
2595  	enableTextSelection = function () {
2596  		if (userSelectProperty) {
2597  			document.documentElement.style[userSelectProperty] = _userSelect;
2598  			_userSelect = undefined;
2599  		}
2600  	};
2601  }
2602
2603  // @function disableImageDrag()
2604  // As [`L.DomUtil.disableTextSelection`](#domutil-disabletextselection), but
2605  // for `dragstart` DOM events, usually generated when the user drags an image.
2606  function disableImageDrag() {
2607  	on(window, 'dragstart', preventDefault);
2608  }
2609
2610  // @function enableImageDrag()
2611  // Cancels the effects of a previous [`L.DomUtil.disableImageDrag`](#domutil-disabletextselection).
2612  function enableImageDrag() {
2613  	off(window, 'dragstart', preventDefault);
2614  }
2615
2616  var _outlineElement, _outlineStyle;
2617  // @function preventOutline(el: HTMLElement)
2618  // Makes the [outline](https://developer.mozilla.org/docs/Web/CSS/outline)
2619  // of the element `el` invisible. Used internally by Leaflet to prevent
2620  // focusable elements from displaying an outline when the user performs a
2621  // drag interaction on them.
2622  function preventOutline(element) {
2623  	while (element.tabIndex === -1) {
2624  		element = element.parentNode;
2625  	}
2626  	if (!element.style) { return; }
2627  	restoreOutline();
2628  	_outlineElement = element;
2629  	_outlineStyle = element.style.outlineStyle;
2630  	element.style.outlineStyle = 'none';
2631  	on(window, 'keydown', restoreOutline);
2632  }
2633
2634  // @function restoreOutline()
2635  // Cancels the effects of a previous [`L.DomUtil.preventOutline`]().
2636  function restoreOutline() {
2637  	if (!_outlineElement) { return; }
2638  	_outlineElement.style.outlineStyle = _outlineStyle;
2639  	_outlineElement = undefined;
2640  	_outlineStyle = undefined;
2641  	off(window, 'keydown', restoreOutline);
2642  }
2643
2644  // @function getSizedParentNode(el: HTMLElement): HTMLElement
2645  // Finds the closest parent node which size (width and height) is not null.
2646  function getSizedParentNode(element) {
2647  	do {
2648  		element = element.parentNode;
2649  	} while ((!element.offsetWidth || !element.offsetHeight) && element !== document.body);
2650  	return element;
2651  }
2652
2653  // @function getScale(el: HTMLElement): Object
2654  // Computes the CSS scale currently applied on the element.
2655  // Returns an object with `x` and `y` members as horizontal and vertical scales respectively,
2656  // and `boundingClientRect` as the result of [`getBoundingClientRect()`](https://developer.mozilla.org/en-US/docs/Web/API/Element/getBoundingClientRect).
2657  function getScale(element) {
2658  	var rect = element.getBoundingClientRect(); // Read-only in old browsers.
2659
2660  	return {
2661  		x: rect.width / element.offsetWidth || 1,
2662  		y: rect.height / element.offsetHeight || 1,
2663  		boundingClientRect: rect
2664  	};
2665  }
2666
2667  var DomUtil = {
2668    __proto__: null,
2669    TRANSFORM: TRANSFORM,
2670    TRANSITION: TRANSITION,
2671    TRANSITION_END: TRANSITION_END,
2672    get: get,
2673    getStyle: getStyle,
2674    create: create$1,
2675    remove: remove,
2676    empty: empty,
2677    toFront: toFront,
2678    toBack: toBack,
2679    hasClass: hasClass,
2680    addClass: addClass,
2681    removeClass: removeClass,
2682    setClass: setClass,
2683    getClass: getClass,
2684    setOpacity: setOpacity,
2685    testProp: testProp,
2686    setTransform: setTransform,
2687    setPosition: setPosition,
2688    getPosition: getPosition,
2689    get disableTextSelection () { return disableTextSelection; },
2690    get enableTextSelection () { return enableTextSelection; },
2691    disableImageDrag: disableImageDrag,
2692    enableImageDrag: enableImageDrag,
2693    preventOutline: preventOutline,
2694    restoreOutline: restoreOutline,
2695    getSizedParentNode: getSizedParentNode,
2696    getScale: getScale
2697  };
2698
2699  /*
2700   * @namespace DomEvent
2701   * Utility functions to work with the [DOM events](https://developer.mozilla.org/docs/Web/API/Event), used by Leaflet internally.
2702   */
2703
2704  // Inspired by John Resig, Dean Edwards and YUI addEvent implementations.
2705
2706  // @function on(el: HTMLElement, types: String, fn: Function, context?: Object): this
2707  // Adds a listener function (`fn`) to a particular DOM event type of the
2708  // element `el`. You can optionally specify the context of the listener
2709  // (object the `this` keyword will point to). You can also pass several
2710  // space-separated types (e.g. `'click dblclick'`).
2711
2712  // @alternative
2713  // @function on(el: HTMLElement, eventMap: Object, context?: Object): this
2714  // Adds a set of type/listener pairs, e.g. `{click: onClick, mousemove: onMouseMove}`
2715  function on(obj, types, fn, context) {
2716
2717  	if (types && typeof types === 'object') {
2718  		for (var type in types) {
2719  			addOne(obj, type, types[type], fn);
2720  		}
2721  	} else {
2722  		types = splitWords(types);
2723
2724  		for (var i = 0, len = types.length; i < len; i++) {
2725  			addOne(obj, types[i], fn, context);
2726  		}
2727  	}
2728
2729  	return this;
2730  }
2731
2732  var eventsKey = '_leaflet_events';
2733
2734  // @function off(el: HTMLElement, types: String, fn: Function, context?: Object): this
2735  // Removes a previously added listener function.
2736  // Note that if you passed a custom context to on, you must pass the same
2737  // context to `off` in order to remove the listener.
2738
2739  // @alternative
2740  // @function off(el: HTMLElement, eventMap: Object, context?: Object): this
2741  // Removes a set of type/listener pairs, e.g. `{click: onClick, mousemove: onMouseMove}`
2742
2743  // @alternative
2744  // @function off(el: HTMLElement, types: String): this
2745  // Removes all previously added listeners of given types.
2746
2747  // @alternative
2748  // @function off(el: HTMLElement): this
2749  // Removes all previously added listeners from given HTMLElement
2750  function off(obj, types, fn, context) {
2751
2752  	if (arguments.length === 1) {
2753  		batchRemove(obj);
2754  		delete obj[eventsKey];
2755
2756  	} else if (types && typeof types === 'object') {
2757  		for (var type in types) {
2758  			removeOne(obj, type, types[type], fn);
2759  		}
2760
2761  	} else {
2762  		types = splitWords(types);
2763
2764  		if (arguments.length === 2) {
2765  			batchRemove(obj, function (type) {
2766  				return indexOf(types, type) !== -1;
2767  			});
2768  		} else {
2769  			for (var i = 0, len = types.length; i < len; i++) {
2770  				removeOne(obj, types[i], fn, context);
2771  			}
2772  		}
2773  	}
2774
2775  	return this;
2776  }
2777
2778  function batchRemove(obj, filterFn) {
2779  	for (var id in obj[eventsKey]) {
2780  		var type = id.split(/\d/)[0];
2781  		if (!filterFn || filterFn(type)) {
2782  			removeOne(obj, type, null, null, id);
2783  		}
2784  	}
2785  }
2786
2787  var mouseSubst = {
2788  	mouseenter: 'mouseover',
2789  	mouseleave: 'mouseout',
2790  	wheel: !('onwheel' in window) && 'mousewheel'
2791  };
2792
2793  function addOne(obj, type, fn, context) {
2794  	var id = type + stamp(fn) + (context ? '_' + stamp(context) : '');
2795
2796  	if (obj[eventsKey] && obj[eventsKey][id]) { return this; }
2797
2798  	var handler = function (e) {
2799  		return fn.call(context || obj, e || window.event);
2800  	};
2801
2802  	var originalHandler = handler;
2803
2804  	if (!Browser.touchNative && Browser.pointer && type.indexOf('touch') === 0) {
2805  		// Needs DomEvent.Pointer.js
2806  		handler = addPointerListener(obj, type, handler);
2807
2808  	} else if (Browser.touch && (type === 'dblclick')) {
2809  		handler = addDoubleTapListener(obj, handler);
2810
2811  	} else if ('addEventListener' in obj) {
2812
2813  		if (type === 'touchstart' || type === 'touchmove' || type === 'wheel' ||  type === 'mousewheel') {
2814  			obj.addEventListener(mouseSubst[type] || type, handler, Browser.passiveEvents ? {passive: false} : false);
2815
2816  		} else if (type === 'mouseenter' || type === 'mouseleave') {
2817  			handler = function (e) {
2818  				e = e || window.event;
2819  				if (isExternalTarget(obj, e)) {
2820  					originalHandler(e);
2821  				}
2822  			};
2823  			obj.addEventListener(mouseSubst[type], handler, false);
2824
2825  		} else {
2826  			obj.addEventListener(type, originalHandler, false);
2827  		}
2828
2829  	} else {
2830  		obj.attachEvent('on' + type, handler);
2831  	}
2832
2833  	obj[eventsKey] = obj[eventsKey] || {};
2834  	obj[eventsKey][id] = handler;
2835  }
2836
2837  function removeOne(obj, type, fn, context, id) {
2838  	id = id || type + stamp(fn) + (context ? '_' + stamp(context) : '');
2839  	var handler = obj[eventsKey] && obj[eventsKey][id];
2840
2841  	if (!handler) { return this; }
2842
2843  	if (!Browser.touchNative && Browser.pointer && type.indexOf('touch') === 0) {
2844  		removePointerListener(obj, type, handler);
2845
2846  	} else if (Browser.touch && (type === 'dblclick')) {
2847  		removeDoubleTapListener(obj, handler);
2848
2849  	} else if ('removeEventListener' in obj) {
2850
2851  		obj.removeEventListener(mouseSubst[type] || type, handler, false);
2852
2853  	} else {
2854  		obj.detachEvent('on' + type, handler);
2855  	}
2856
2857  	obj[eventsKey][id] = null;
2858  }
2859
2860  // @function stopPropagation(ev: DOMEvent): this
2861  // Stop the given event from propagation to parent elements. Used inside the listener functions:
2862  // ```js
2863  // L.DomEvent.on(div, 'click', function (ev) {
2864  // 	L.DomEvent.stopPropagation(ev);
2865  // });
2866  // ```
2867  function stopPropagation(e) {
2868
2869  	if (e.stopPropagation) {
2870  		e.stopPropagation();
2871  	} else if (e.originalEvent) {  // In case of Leaflet event.
2872  		e.originalEvent._stopped = true;
2873  	} else {
2874  		e.cancelBubble = true;
2875  	}
2876
2877  	return this;
2878  }
2879
2880  // @function disableScrollPropagation(el: HTMLElement): this
2881  // Adds `stopPropagation` to the element's `'wheel'` events (plus browser variants).
2882  function disableScrollPropagation(el) {
2883  	addOne(el, 'wheel', stopPropagation);
2884  	return this;
2885  }
2886
2887  // @function disableClickPropagation(el: HTMLElement): this
2888  // Adds `stopPropagation` to the element's `'click'`, `'dblclick'`, `'contextmenu'`,
2889  // `'mousedown'` and `'touchstart'` events (plus browser variants).
2890  function disableClickPropagation(el) {
2891  	on(el, 'mousedown touchstart dblclick contextmenu', stopPropagation);
2892  	el['_leaflet_disable_click'] = true;
2893  	return this;
2894  }
2895
2896  // @function preventDefault(ev: DOMEvent): this
2897  // Prevents the default action of the DOM Event `ev` from happening (such as
2898  // following a link in the href of the a element, or doing a POST request
2899  // with page reload when a `<form>` is submitted).
2900  // Use it inside listener functions.
2901  function preventDefault(e) {
2902  	if (e.preventDefault) {
2903  		e.preventDefault();
2904  	} else {
2905  		e.returnValue = false;
2906  	}
2907  	return this;
2908  }
2909
2910  // @function stop(ev: DOMEvent): this
2911  // Does `stopPropagation` and `preventDefault` at the same time.
2912  function stop(e) {
2913  	preventDefault(e);
2914  	stopPropagation(e);
2915  	return this;
2916  }
2917
2918  // @function getPropagationPath(ev: DOMEvent): Array
2919  // Compatibility polyfill for [`Event.composedPath()`](https://developer.mozilla.org/en-US/docs/Web/API/Event/composedPath).
2920  // Returns an array containing the `HTMLElement`s that the given DOM event
2921  // should propagate to (if not stopped).
2922  function getPropagationPath(ev) {
2923  	if (ev.composedPath) {
2924  		return ev.composedPath();
2925  	}
2926
2927  	var path = [];
2928  	var el = ev.target;
2929
2930  	while (el) {
2931  		path.push(el);
2932  		el = el.parentNode;
2933  	}
2934  	return path;
2935  }
2936
2937
2938  // @function getMousePosition(ev: DOMEvent, container?: HTMLElement): Point
2939  // Gets normalized mouse position from a DOM event relative to the
2940  // `container` (border excluded) or to the whole page if not specified.
2941  function getMousePosition(e, container) {
2942  	if (!container) {
2943  		return new Point(e.clientX, e.clientY);
2944  	}
2945
2946  	var scale = getScale(container),
2947  	    offset = scale.boundingClientRect; // left and top  values are in page scale (like the event clientX/Y)
2948
2949  	return new Point(
2950  		// offset.left/top values are in page scale (like clientX/Y),
2951  		// whereas clientLeft/Top (border width) values are the original values (before CSS scale applies).
2952  		(e.clientX - offset.left) / scale.x - container.clientLeft,
2953  		(e.clientY - offset.top) / scale.y - container.clientTop
2954  	);
2955  }
2956
2957
2958  //  except , Safari and
2959  // We need double the scroll pixels (see #7403 and #4538) for all Browsers
2960  // except OSX (Mac) -> 3x, Chrome running on Linux 1x
2961
2962  var wheelPxFactor =
2963  	(Browser.linux && Browser.chrome) ? window.devicePixelRatio :
2964  	Browser.mac ? window.devicePixelRatio * 3 :
2965  	window.devicePixelRatio > 0 ? 2 * window.devicePixelRatio : 1;
2966  // @function getWheelDelta(ev: DOMEvent): Number
2967  // Gets normalized wheel delta from a wheel DOM event, in vertical
2968  // pixels scrolled (negative if scrolling down).
2969  // Events from pointing devices without precise scrolling are mapped to
2970  // a best guess of 60 pixels.
2971  function getWheelDelta(e) {
2972  	return (Browser.edge) ? e.wheelDeltaY / 2 : // Don't trust window-geometry-based delta
2973  	       (e.deltaY && e.deltaMode === 0) ? -e.deltaY / wheelPxFactor : // Pixels
2974  	       (e.deltaY && e.deltaMode === 1) ? -e.deltaY * 20 : // Lines
2975  	       (e.deltaY && e.deltaMode === 2) ? -e.deltaY * 60 : // Pages
2976  	       (e.deltaX || e.deltaZ) ? 0 :	// Skip horizontal/depth wheel events
2977  	       e.wheelDelta ? (e.wheelDeltaY || e.wheelDelta) / 2 : // Legacy IE pixels
2978  	       (e.detail && Math.abs(e.detail) < 32765) ? -e.detail * 20 : // Legacy Moz lines
2979  	       e.detail ? e.detail / -32765 * 60 : // Legacy Moz pages
2980  	       0;
2981  }
2982
2983  // check if element really left/entered the event target (for mouseenter/mouseleave)
2984  function isExternalTarget(el, e) {
2985
2986  	var related = e.relatedTarget;
2987
2988  	if (!related) { return true; }
2989
2990  	try {
2991  		while (related && (related !== el)) {
2992  			related = related.parentNode;
2993  		}
2994  	} catch (err) {
2995  		return false;
2996  	}
2997  	return (related !== el);
2998  }
2999
3000  var DomEvent = {
3001    __proto__: null,
3002    on: on,
3003    off: off,
3004    stopPropagation: stopPropagation,
3005    disableScrollPropagation: disableScrollPropagation,
3006    disableClickPropagation: disableClickPropagation,
3007    preventDefault: preventDefault,
3008    stop: stop,
3009    getPropagationPath: getPropagationPath,
3010    getMousePosition: getMousePosition,
3011    getWheelDelta: getWheelDelta,
3012    isExternalTarget: isExternalTarget,
3013    addListener: on,
3014    removeListener: off
3015  };
3016
3017  /*
3018   * @class PosAnimation
3019   * @aka L.PosAnimation
3020   * @inherits Evented
3021   * Used internally for panning animations, utilizing CSS3 Transitions for modern browsers and a timer fallback for IE6-9.
3022   *
3023   * @example
3024   * ```js
3025   * var myPositionMarker = L.marker([48.864716, 2.294694]).addTo(map);
3026   *
3027   * myPositionMarker.on("click", function() {
3028   * 	var pos = map.latLngToLayerPoint(myPositionMarker.getLatLng());
3029   * 	pos.y -= 25;
3030   * 	var fx = new L.PosAnimation();
3031   *
3032   * 	fx.once('end',function() {
3033   * 		pos.y += 25;
3034   * 		fx.run(myPositionMarker._icon, pos, 0.8);
3035   * 	});
3036   *
3037   * 	fx.run(myPositionMarker._icon, pos, 0.3);
3038   * });
3039   *
3040   * ```
3041   *
3042   * @constructor L.PosAnimation()
3043   * Creates a `PosAnimation` object.
3044   *
3045   */
3046
3047  var PosAnimation = Evented.extend({
3048
3049  	// @method run(el: HTMLElement, newPos: Point, duration?: Number, easeLinearity?: Number)
3050  	// Run an animation of a given element to a new position, optionally setting
3051  	// duration in seconds (`0.25` by default) and easing linearity factor (3rd
3052  	// argument of the [cubic bezier curve](https://cubic-bezier.com/#0,0,.5,1),
3053  	// `0.5` by default).
3054  	run: function (el, newPos, duration, easeLinearity) {
3055  		this.stop();
3056
3057  		this._el = el;
3058  		this._inProgress = true;
3059  		this._duration = duration || 0.25;
3060  		this._easeOutPower = 1 / Math.max(easeLinearity || 0.5, 0.2);
3061
3062  		this._startPos = getPosition(el);
3063  		this._offset = newPos.subtract(this._startPos);
3064  		this._startTime = +new Date();
3065
3066  		// @event start: Event
3067  		// Fired when the animation starts
3068  		this.fire('start');
3069
3070  		this._animate();
3071  	},
3072
3073  	// @method stop()
3074  	// Stops the animation (if currently running).
3075  	stop: function () {
3076  		if (!this._inProgress) { return; }
3077
3078  		this._step(true);
3079  		this._complete();
3080  	},
3081
3082  	_animate: function () {
3083  		// animation loop
3084  		this._animId = requestAnimFrame(this._animate, this);
3085  		this._step();
3086  	},
3087
3088  	_step: function (round) {
3089  		var elapsed = (+new Date()) - this._startTime,
3090  		    duration = this._duration * 1000;
3091
3092  		if (elapsed < duration) {
3093  			this._runFrame(this._easeOut(elapsed / duration), round);
3094  		} else {
3095  			this._runFrame(1);
3096  			this._complete();
3097  		}
3098  	},
3099
3100  	_runFrame: function (progress, round) {
3101  		var pos = this._startPos.add(this._offset.multiplyBy(progress));
3102  		if (round) {
3103  			pos._round();
3104  		}
3105  		setPosition(this._el, pos);
3106
3107  		// @event step: Event
3108  		// Fired continuously during the animation.
3109  		this.fire('step');
3110  	},
3111
3112  	_complete: function () {
3113  		cancelAnimFrame(this._animId);
3114
3115  		this._inProgress = false;
3116  		// @event end: Event
3117  		// Fired when the animation ends.
3118  		this.fire('end');
3119  	},
3120
3121  	_easeOut: function (t) {
3122  		return 1 - Math.pow(1 - t, this._easeOutPower);
3123  	}
3124  });
3125
3126  /*
3127   * @class Map
3128   * @aka L.Map
3129   * @inherits Evented
3130   *
3131   * The central class of the API — it is used to create a map on a page and manipulate it.
3132   *
3133   * @example
3134   *
3135   * ```js
3136   * // initialize the map on the "map" div with a given center and zoom
3137   * var map = L.map('map', {
3138   * 	center: [51.505, -0.09],
3139   * 	zoom: 13
3140   * });
3141   * ```
3142   *
3143   */
3144
3145  var Map = Evented.extend({
3146
3147  	options: {
3148  		// @section Map State Options
3149  		// @option crs: CRS = L.CRS.EPSG3857
3150  		// The [Coordinate Reference System](#crs) to use. Don't change this if you're not
3151  		// sure what it means.
3152  		crs: EPSG3857,
3153
3154  		// @option center: LatLng = undefined
3155  		// Initial geographic center of the map
3156  		center: undefined,
3157
3158  		// @option zoom: Number = undefined
3159  		// Initial map zoom level
3160  		zoom: undefined,
3161
3162  		// @option minZoom: Number = *
3163  		// Minimum zoom level of the map.
3164  		// If not specified and at least one `GridLayer` or `TileLayer` is in the map,
3165  		// the lowest of their `minZoom` options will be used instead.
3166  		minZoom: undefined,
3167
3168  		// @option maxZoom: Number = *
3169  		// Maximum zoom level of the map.
3170  		// If not specified and at least one `GridLayer` or `TileLayer` is in the map,
3171  		// the highest of their `maxZoom` options will be used instead.
3172  		maxZoom: undefined,
3173
3174  		// @option layers: Layer[] = []
3175  		// Array of layers that will be added to the map initially
3176  		layers: [],
3177
3178  		// @option maxBounds: LatLngBounds = null
3179  		// When this option is set, the map restricts the view to the given
3180  		// geographical bounds, bouncing the user back if the user tries to pan
3181  		// outside the view. To set the restriction dynamically, use
3182  		// [`setMaxBounds`](#map-setmaxbounds) method.
3183  		maxBounds: undefined,
3184
3185  		// @option renderer: Renderer = *
3186  		// The default method for drawing vector layers on the map. `L.SVG`
3187  		// or `L.Canvas` by default depending on browser support.
3188  		renderer: undefined,
3189
3190
3191  		// @section Animation Options
3192  		// @option zoomAnimation: Boolean = true
3193  		// Whether the map zoom animation is enabled. By default it's enabled
3194  		// in all browsers that support CSS3 Transitions except Android.
3195  		zoomAnimation: true,
3196
3197  		// @option zoomAnimationThreshold: Number = 4
3198  		// Won't animate zoom if the zoom difference exceeds this value.
3199  		zoomAnimationThreshold: 4,
3200
3201  		// @option fadeAnimation: Boolean = true
3202  		// Whether the tile fade animation is enabled. By default it's enabled
3203  		// in all browsers that support CSS3 Transitions except Android.
3204  		fadeAnimation: true,
3205
3206  		// @option markerZoomAnimation: Boolean = true
3207  		// Whether markers animate their zoom with the zoom animation, if disabled
3208  		// they will disappear for the length of the animation. By default it's
3209  		// enabled in all browsers that support CSS3 Transitions except Android.
3210  		markerZoomAnimation: true,
3211
3212  		// @option transform3DLimit: Number = 2^23
3213  		// Defines the maximum size of a CSS translation transform. The default
3214  		// value should not be changed unless a web browser positions layers in
3215  		// the wrong place after doing a large `panBy`.
3216  		transform3DLimit: 8388608, // Precision limit of a 32-bit float
3217
3218  		// @section Interaction Options
3219  		// @option zoomSnap: Number = 1
3220  		// Forces the map's zoom level to always be a multiple of this, particularly
3221  		// right after a [`fitBounds()`](#map-fitbounds) or a pinch-zoom.
3222  		// By default, the zoom level snaps to the nearest integer; lower values
3223  		// (e.g. `0.5` or `0.1`) allow for greater granularity. A value of `0`
3224  		// means the zoom level will not be snapped after `fitBounds` or a pinch-zoom.
3225  		zoomSnap: 1,
3226
3227  		// @option zoomDelta: Number = 1
3228  		// Controls how much the map's zoom level will change after a
3229  		// [`zoomIn()`](#map-zoomin), [`zoomOut()`](#map-zoomout), pressing `+`
3230  		// or `-` on the keyboard, or using the [zoom controls](#control-zoom).
3231  		// Values smaller than `1` (e.g. `0.5`) allow for greater granularity.
3232  		zoomDelta: 1,
3233
3234  		// @option trackResize: Boolean = true
3235  		// Whether the map automatically handles browser window resize to update itself.
3236  		trackResize: true
3237  	},
3238
3239  	initialize: function (id, options) { // (HTMLElement or String, Object)
3240  		options = setOptions(this, options);
3241
3242  		// Make sure to assign internal flags at the beginning,
3243  		// to avoid inconsistent state in some edge cases.
3244  		this._handlers = [];
3245  		this._layers = {};
3246  		this._zoomBoundLayers = {};
3247  		this._sizeChanged = true;
3248
3249  		this._initContainer(id);
3250  		this._initLayout();
3251
3252  		// hack for https://github.com/Leaflet/Leaflet/issues/1980
3253  		this._onResize = bind(this._onResize, this);
3254
3255  		this._initEvents();
3256
3257  		if (options.maxBounds) {
3258  			this.setMaxBounds(options.maxBounds);
3259  		}
3260
3261  		if (options.zoom !== undefined) {
3262  			this._zoom = this._limitZoom(options.zoom);
3263  		}
3264
3265  		if (options.center && options.zoom !== undefined) {
3266  			this.setView(toLatLng(options.center), options.zoom, {reset: true});
3267  		}
3268
3269  		this.callInitHooks();
3270
3271  		// don't animate on browsers without hardware-accelerated transitions or old Android/Opera
3272  		this._zoomAnimated = TRANSITION && Browser.any3d && !Browser.mobileOpera &&
3273  				this.options.zoomAnimation;
3274
3275  		// zoom transitions run with the same duration for all layers, so if one of transitionend events
3276  		// happens after starting zoom animation (propagating to the map pane), we know that it ended globally
3277  		if (this._zoomAnimated) {
3278  			this._createAnimProxy();
3279  			on(this._proxy, TRANSITION_END, this._catchTransitionEnd, this);
3280  		}
3281
3282  		this._addLayers(this.options.layers);
3283  	},
3284
3285
3286  	// @section Methods for modifying map state
3287
3288  	// @method setView(center: LatLng, zoom: Number, options?: Zoom/pan options): this
3289  	// Sets the view of the map (geographical center and zoom) with the given
3290  	// animation options.
3291  	setView: function (center, zoom, options) {
3292
3293  		zoom = zoom === undefined ? this._zoom : this._limitZoom(zoom);
3294  		center = this._limitCenter(toLatLng(center), zoom, this.options.maxBounds);
3295  		options = options || {};
3296
3297  		this._stop();
3298
3299  		if (this._loaded && !options.reset && options !== true) {
3300
3301  			if (options.animate !== undefined) {
3302  				options.zoom = extend({animate: options.animate}, options.zoom);
3303  				options.pan = extend({animate: options.animate, duration: options.duration}, options.pan);
3304  			}
3305
3306  			// try animating pan or zoom
3307  			var moved = (this._zoom !== zoom) ?
3308  				this._tryAnimatedZoom && this._tryAnimatedZoom(center, zoom, options.zoom) :
3309  				this._tryAnimatedPan(center, options.pan);
3310
3311  			if (moved) {
3312  				// prevent resize handler call, the view will refresh after animation anyway
3313  				clearTimeout(this._sizeTimer);
3314  				return this;
3315  			}
3316  		}
3317
3318  		// animation didn't start, just reset the map view
3319  		this._resetView(center, zoom, options.pan && options.pan.noMoveStart);
3320
3321  		return this;
3322  	},
3323
3324  	// @method setZoom(zoom: Number, options?: Zoom/pan options): this
3325  	// Sets the zoom of the map.
3326  	setZoom: function (zoom, options) {
3327  		if (!this._loaded) {
3328  			this._zoom = zoom;
3329  			return this;
3330  		}
3331  		return this.setView(this.getCenter(), zoom, {zoom: options});
3332  	},
3333
3334  	// @method zoomIn(delta?: Number, options?: Zoom options): this
3335  	// Increases the zoom of the map by `delta` ([`zoomDelta`](#map-zoomdelta) by default).
3336  	zoomIn: function (delta, options) {
3337  		delta = delta || (Browser.any3d ? this.options.zoomDelta : 1);
3338  		return this.setZoom(this._zoom + delta, options);
3339  	},
3340
3341  	// @method zoomOut(delta?: Number, options?: Zoom options): this
3342  	// Decreases the zoom of the map by `delta` ([`zoomDelta`](#map-zoomdelta) by default).
3343  	zoomOut: function (delta, options) {
3344  		delta = delta || (Browser.any3d ? this.options.zoomDelta : 1);
3345  		return this.setZoom(this._zoom - delta, options);
3346  	},
3347
3348  	// @method setZoomAround(latlng: LatLng, zoom: Number, options: Zoom options): this
3349  	// Zooms the map while keeping a specified geographical point on the map
3350  	// stationary (e.g. used internally for scroll zoom and double-click zoom).
3351  	// @alternative
3352  	// @method setZoomAround(offset: Point, zoom: Number, options: Zoom options): this
3353  	// Zooms the map while keeping a specified pixel on the map (relative to the top-left corner) stationary.
3354  	setZoomAround: function (latlng, zoom, options) {
3355  		var scale = this.getZoomScale(zoom),
3356  		    viewHalf = this.getSize().divideBy(2),
3357  		    containerPoint = latlng instanceof Point ? latlng : this.latLngToContainerPoint(latlng),
3358
3359  		    centerOffset = containerPoint.subtract(viewHalf).multiplyBy(1 - 1 / scale),
3360  		    newCenter = this.containerPointToLatLng(viewHalf.add(centerOffset));
3361
3362  		return this.setView(newCenter, zoom, {zoom: options});
3363  	},
3364
3365  	_getBoundsCenterZoom: function (bounds, options) {
3366
3367  		options = options || {};
3368  		bounds = bounds.getBounds ? bounds.getBounds() : toLatLngBounds(bounds);
3369
3370  		var paddingTL = toPoint(options.paddingTopLeft || options.padding || [0, 0]),
3371  		    paddingBR = toPoint(options.paddingBottomRight || options.padding || [0, 0]),
3372
3373  		    zoom = this.getBoundsZoom(bounds, false, paddingTL.add(paddingBR));
3374
3375  		zoom = (typeof options.maxZoom === 'number') ? Math.min(options.maxZoom, zoom) : zoom;
3376
3377  		if (zoom === Infinity) {
3378  			return {
3379  				center: bounds.getCenter(),
3380  				zoom: zoom
3381  			};
3382  		}
3383
3384  		var paddingOffset = paddingBR.subtract(paddingTL).divideBy(2),
3385
3386  		    swPoint = this.project(bounds.getSouthWest(), zoom),
3387  		    nePoint = this.project(bounds.getNorthEast(), zoom),
3388  		    center = this.unproject(swPoint.add(nePoint).divideBy(2).add(paddingOffset), zoom);
3389
3390  		return {
3391  			center: center,
3392  			zoom: zoom
3393  		};
3394  	},
3395
3396  	// @method fitBounds(bounds: LatLngBounds, options?: fitBounds options): this
3397  	// Sets a map view that contains the given geographical bounds with the
3398  	// maximum zoom level possible.
3399  	fitBounds: function (bounds, options) {
3400
3401  		bounds = toLatLngBounds(bounds);
3402
3403  		if (!bounds.isValid()) {
3404  			throw new Error('Bounds are not valid.');
3405  		}
3406
3407  		var target = this._getBoundsCenterZoom(bounds, options);
3408  		return this.setView(target.center, target.zoom, options);
3409  	},
3410
3411  	// @method fitWorld(options?: fitBounds options): this
3412  	// Sets a map view that mostly contains the whole world with the maximum
3413  	// zoom level possible.
3414  	fitWorld: function (options) {
3415  		return this.fitBounds([[-90, -180], [90, 180]], options);
3416  	},
3417
3418  	// @method panTo(latlng: LatLng, options?: Pan options): this
3419  	// Pans the map to a given center.
3420  	panTo: function (center, options) { // (LatLng)
3421  		return this.setView(center, this._zoom, {pan: options});
3422  	},
3423
3424  	// @method panBy(offset: Point, options?: Pan options): this
3425  	// Pans the map by a given number of pixels (animated).
3426  	panBy: function (offset, options) {
3427  		offset = toPoint(offset).round();
3428  		options = options || {};
3429
3430  		if (!offset.x && !offset.y) {
3431  			return this.fire('moveend');
3432  		}
3433  		// If we pan too far, Chrome gets issues with tiles
3434  		// and makes them disappear or appear in the wrong place (slightly offset) #2602
3435  		if (options.animate !== true && !this.getSize().contains(offset)) {
3436  			this._resetView(this.unproject(this.project(this.getCenter()).add(offset)), this.getZoom());
3437  			return this;
3438  		}
3439
3440  		if (!this._panAnim) {
3441  			this._panAnim = new PosAnimation();
3442
3443  			this._panAnim.on({
3444  				'step': this._onPanTransitionStep,
3445  				'end': this._onPanTransitionEnd
3446  			}, this);
3447  		}
3448
3449  		// don't fire movestart if animating inertia
3450  		if (!options.noMoveStart) {
3451  			this.fire('movestart');
3452  		}
3453
3454  		// animate pan unless animate: false specified
3455  		if (options.animate !== false) {
3456  			addClass(this._mapPane, 'leaflet-pan-anim');
3457
3458  			var newPos = this._getMapPanePos().subtract(offset).round();
3459  			this._panAnim.run(this._mapPane, newPos, options.duration || 0.25, options.easeLinearity);
3460  		} else {
3461  			this._rawPanBy(offset);
3462  			this.fire('move').fire('moveend');
3463  		}
3464
3465  		return this;
3466  	},
3467
3468  	// @method flyTo(latlng: LatLng, zoom?: Number, options?: Zoom/pan options): this
3469  	// Sets the view of the map (geographical center and zoom) performing a smooth
3470  	// pan-zoom animation.
3471  	flyTo: function (targetCenter, targetZoom, options) {
3472
3473  		options = options || {};
3474  		if (options.animate === false || !Browser.any3d) {
3475  			return this.setView(targetCenter, targetZoom, options);
3476  		}
3477
3478  		this._stop();
3479
3480  		var from = this.project(this.getCenter()),
3481  		    to = this.project(targetCenter),
3482  		    size = this.getSize(),
3483  		    startZoom = this._zoom;
3484
3485  		targetCenter = toLatLng(targetCenter);
3486  		targetZoom = targetZoom === undefined ? startZoom : targetZoom;
3487
3488  		var w0 = Math.max(size.x, size.y),
3489  		    w1 = w0 * this.getZoomScale(startZoom, targetZoom),
3490  		    u1 = (to.distanceTo(from)) || 1,
3491  		    rho = 1.42,
3492  		    rho2 = rho * rho;
3493
3494  		function r(i) {
3495  			var s1 = i ? -1 : 1,
3496  			    s2 = i ? w1 : w0,
3497  			    t1 = w1 * w1 - w0 * w0 + s1 * rho2 * rho2 * u1 * u1,
3498  			    b1 = 2 * s2 * rho2 * u1,
3499  			    b = t1 / b1,
3500  			    sq = Math.sqrt(b * b + 1) - b;
3501
3502  			    // workaround for floating point precision bug when sq = 0, log = -Infinite,
3503  			    // thus triggering an infinite loop in flyTo
3504  			    var log = sq < 0.000000001 ? -18 : Math.log(sq);
3505
3506  			return log;
3507  		}
3508
3509  		function sinh(n) { return (Math.exp(n) - Math.exp(-n)) / 2; }
3510  		function cosh(n) { return (Math.exp(n) + Math.exp(-n)) / 2; }
3511  		function tanh(n) { return sinh(n) / cosh(n); }
3512
3513  		var r0 = r(0);
3514
3515  		function w(s) { return w0 * (cosh(r0) / cosh(r0 + rho * s)); }
3516  		function u(s) { return w0 * (cosh(r0) * tanh(r0 + rho * s) - sinh(r0)) / rho2; }
3517
3518  		function easeOut(t) { return 1 - Math.pow(1 - t, 1.5); }
3519
3520  		var start = Date.now(),
3521  		    S = (r(1) - r0) / rho,
3522  		    duration = options.duration ? 1000 * options.duration : 1000 * S * 0.8;
3523
3524  		function frame() {
3525  			var t = (Date.now() - start) / duration,
3526  			    s = easeOut(t) * S;
3527
3528  			if (t <= 1) {
3529  				this._flyToFrame = requestAnimFrame(frame, this);
3530
3531  				this._move(
3532  					this.unproject(from.add(to.subtract(from).multiplyBy(u(s) / u1)), startZoom),
3533  					this.getScaleZoom(w0 / w(s), startZoom),
3534  					{flyTo: true});
3535
3536  			} else {
3537  				this
3538  					._move(targetCenter, targetZoom)
3539  					._moveEnd(true);
3540  			}
3541  		}
3542
3543  		this._moveStart(true, options.noMoveStart);
3544
3545  		frame.call(this);
3546  		return this;
3547  	},
3548
3549  	// @method flyToBounds(bounds: LatLngBounds, options?: fitBounds options): this
3550  	// Sets the view of the map with a smooth animation like [`flyTo`](#map-flyto),
3551  	// but takes a bounds parameter like [`fitBounds`](#map-fitbounds).
3552  	flyToBounds: function (bounds, options) {
3553  		var target = this._getBoundsCenterZoom(bounds, options);
3554  		return this.flyTo(target.center, target.zoom, options);
3555  	},
3556
3557  	// @method setMaxBounds(bounds: LatLngBounds): this
3558  	// Restricts the map view to the given bounds (see the [maxBounds](#map-maxbounds) option).
3559  	setMaxBounds: function (bounds) {
3560  		bounds = toLatLngBounds(bounds);
3561
3562  		if (this.listens('moveend', this._panInsideMaxBounds)) {
3563  			this.off('moveend', this._panInsideMaxBounds);
3564  		}
3565
3566  		if (!bounds.isValid()) {
3567  			this.options.maxBounds = null;
3568  			return this;
3569  		}
3570
3571  		this.options.maxBounds = bounds;
3572
3573  		if (this._loaded) {
3574  			this._panInsideMaxBounds();
3575  		}
3576
3577  		return this.on('moveend', this._panInsideMaxBounds);
3578  	},
3579
3580  	// @method setMinZoom(zoom: Number): this
3581  	// Sets the lower limit for the available zoom levels (see the [minZoom](#map-minzoom) option).
3582  	setMinZoom: function (zoom) {
3583  		var oldZoom = this.options.minZoom;
3584  		this.options.minZoom = zoom;
3585
3586  		if (this._loaded && oldZoom !== zoom) {
3587  			this.fire('zoomlevelschange');
3588
3589  			if (this.getZoom() < this.options.minZoom) {
3590  				return this.setZoom(zoom);
3591  			}
3592  		}
3593
3594  		return this;
3595  	},
3596
3597  	// @method setMaxZoom(zoom: Number): this
3598  	// Sets the upper limit for the available zoom levels (see the [maxZoom](#map-maxzoom) option).
3599  	setMaxZoom: function (zoom) {
3600  		var oldZoom = this.options.maxZoom;
3601  		this.options.maxZoom = zoom;
3602
3603  		if (this._loaded && oldZoom !== zoom) {
3604  			this.fire('zoomlevelschange');
3605
3606  			if (this.getZoom() > this.options.maxZoom) {
3607  				return this.setZoom(zoom);
3608  			}
3609  		}
3610
3611  		return this;
3612  	},
3613
3614  	// @method panInsideBounds(bounds: LatLngBounds, options?: Pan options): this
3615  	// 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.
3616  	panInsideBounds: function (bounds, options) {
3617  		this._enforcingBounds = true;
3618  		var center = this.getCenter(),
3619  		    newCenter = this._limitCenter(center, this._zoom, toLatLngBounds(bounds));
3620
3621  		if (!center.equals(newCenter)) {
3622  			this.panTo(newCenter, options);
3623  		}
3624
3625  		this._enforcingBounds = false;
3626  		return this;
3627  	},
3628
3629  	// @method panInside(latlng: LatLng, options?: padding options): this
3630  	// Pans the map the minimum amount to make the `latlng` visible. Use
3631  	// padding options to fit the display to more restricted bounds.
3632  	// If `latlng` is already within the (optionally padded) display bounds,
3633  	// the map will not be panned.
3634  	panInside: function (latlng, options) {
3635  		options = options || {};
3636
3637  		var paddingTL = toPoint(options.paddingTopLeft || options.padding || [0, 0]),
3638  		    paddingBR = toPoint(options.paddingBottomRight || options.padding || [0, 0]),
3639  		    pixelCenter = this.project(this.getCenter()),
3640  		    pixelPoint = this.project(latlng),
3641  		    pixelBounds = this.getPixelBounds(),
3642  		    paddedBounds = toBounds([pixelBounds.min.add(paddingTL), pixelBounds.max.subtract(paddingBR)]),
3643  		    paddedSize = paddedBounds.getSize();
3644
3645  		if (!paddedBounds.contains(pixelPoint)) {
3646  			this._enforcingBounds = true;
3647  			var centerOffset = pixelPoint.subtract(paddedBounds.getCenter());
3648  			var offset = paddedBounds.extend(pixelPoint).getSize().subtract(paddedSize);
3649  			pixelCenter.x += centerOffset.x < 0 ? -offset.x : offset.x;
3650  			pixelCenter.y += centerOffset.y < 0 ? -offset.y : offset.y;
3651  			this.panTo(this.unproject(pixelCenter), options);
3652  			this._enforcingBounds = false;
3653  		}
3654  		return this;
3655  	},
3656
3657  	// @method invalidateSize(options: Zoom/pan options): this
3658  	// Checks if the map container size changed and updates the map if so —
3659  	// call it after you've changed the map size dynamically, also animating
3660  	// pan by default. If `options.pan` is `false`, panning will not occur.
3661  	// If `options.debounceMoveend` is `true`, it will delay `moveend` event so
3662  	// that it doesn't happen often even if the method is called many
3663  	// times in a row.
3664
3665  	// @alternative
3666  	// @method invalidateSize(animate: Boolean): this
3667  	// Checks if the map container size changed and updates the map if so —
3668  	// call it after you've changed the map size dynamically, also animating
3669  	// pan by default.
3670  	invalidateSize: function (options) {
3671  		if (!this._loaded) { return this; }
3672
3673  		options = extend({
3674  			animate: false,
3675  			pan: true
3676  		}, options === true ? {animate: true} : options);
3677
3678  		var oldSize = this.getSize();
3679  		this._sizeChanged = true;
3680  		this._lastCenter = null;
3681
3682  		var newSize = this.getSize(),
3683  		    oldCenter = oldSize.divideBy(2).round(),
3684  		    newCenter = newSize.divideBy(2).round(),
3685  		    offset = oldCenter.subtract(newCenter);
3686
3687  		if (!offset.x && !offset.y) { return this; }
3688
3689  		if (options.animate && options.pan) {
3690  			this.panBy(offset);
3691
3692  		} else {
3693  			if (options.pan) {
3694  				this._rawPanBy(offset);
3695  			}
3696
3697  			this.fire('move');
3698
3699  			if (options.debounceMoveend) {
3700  				clearTimeout(this._sizeTimer);
3701  				this._sizeTimer = setTimeout(bind(this.fire, this, 'moveend'), 200);
3702  			} else {
3703  				this.fire('moveend');
3704  			}
3705  		}
3706
3707  		// @section Map state change events
3708  		// @event resize: ResizeEvent
3709  		// Fired when the map is resized.
3710  		return this.fire('resize', {
3711  			oldSize: oldSize,
3712  			newSize: newSize
3713  		});
3714  	},
3715
3716  	// @section Methods for modifying map state
3717  	// @method stop(): this
3718  	// Stops the currently running `panTo` or `flyTo` animation, if any.
3719  	stop: function () {
3720  		this.setZoom(this._limitZoom(this._zoom));
3721  		if (!this.options.zoomSnap) {
3722  			this.fire('viewreset');
3723  		}
3724  		return this._stop();
3725  	},
3726
3727  	// @section Geolocation methods
3728  	// @method locate(options?: Locate options): this
3729  	// Tries to locate the user using the Geolocation API, firing a [`locationfound`](#map-locationfound)
3730  	// event with location data on success or a [`locationerror`](#map-locationerror) event on failure,
3731  	// and optionally sets the map view to the user's location with respect to
3732  	// detection accuracy (or to the world view if geolocation failed).
3733  	// Note that, if your page doesn't use HTTPS, this method will fail in
3734  	// modern browsers ([Chrome 50 and newer](https://sites.google.com/a/chromium.org/dev/Home/chromium-security/deprecating-powerful-features-on-insecure-origins))
3735  	// See `Locate options` for more details.
3736  	locate: function (options) {
3737
3738  		options = this._locateOptions = extend({
3739  			timeout: 10000,
3740  			watch: false
3741  			// setView: false
3742  			// maxZoom: <Number>
3743  			// maximumAge: 0
3744  			// enableHighAccuracy: false
3745  		}, options);
3746
3747  		if (!('geolocation' in navigator)) {
3748  			this._handleGeolocationError({
3749  				code: 0,
3750  				message: 'Geolocation not supported.'
3751  			});
3752  			return this;
3753  		}
3754
3755  		var onResponse = bind(this._handleGeolocationResponse, this),
3756  		    onError = bind(this._handleGeolocationError, this);
3757
3758  		if (options.watch) {
3759  			this._locationWatchId =
3760  			        navigator.geolocation.watchPosition(onResponse, onError, options);
3761  		} else {
3762  			navigator.geolocation.getCurrentPosition(onResponse, onError, options);
3763  		}
3764  		return this;
3765  	},
3766
3767  	// @method stopLocate(): this
3768  	// Stops watching location previously initiated by `map.locate({watch: true})`
3769  	// and aborts resetting the map view if map.locate was called with
3770  	// `{setView: true}`.
3771  	stopLocate: function () {
3772  		if (navigator.geolocation && navigator.geolocation.clearWatch) {
3773  			navigator.geolocation.clearWatch(this._locationWatchId);
3774  		}
3775  		if (this._locateOptions) {
3776  			this._locateOptions.setView = false;
3777  		}
3778  		return this;
3779  	},
3780
3781  	_handleGeolocationError: function (error) {
3782  		if (!this._container._leaflet_id) { return; }
3783
3784  		var c = error.code,
3785  		    message = error.message ||
3786  		            (c === 1 ? 'permission denied' :
3787  		            (c === 2 ? 'position unavailable' : 'timeout'));
3788
3789  		if (this._locateOptions.setView && !this._loaded) {
3790  			this.fitWorld();
3791  		}
3792
3793  		// @section Location events
3794  		// @event locationerror: ErrorEvent
3795  		// Fired when geolocation (using the [`locate`](#map-locate) method) failed.
3796  		this.fire('locationerror', {
3797  			code: c,
3798  			message: 'Geolocation error: ' + message + '.'
3799  		});
3800  	},
3801
3802  	_handleGeolocationResponse: function (pos) {
3803  		if (!this._container._leaflet_id) { return; }
3804
3805  		var lat = pos.coords.latitude,
3806  		    lng = pos.coords.longitude,
3807  		    latlng = new LatLng(lat, lng),
3808  		    bounds = latlng.toBounds(pos.coords.accuracy * 2),
3809  		    options = this._locateOptions;
3810
3811  		if (options.setView) {
3812  			var zoom = this.getBoundsZoom(bounds);
3813  			this.setView(latlng, options.maxZoom ? Math.min(zoom, options.maxZoom) : zoom);
3814  		}
3815
3816  		var data = {
3817  			latlng: latlng,
3818  			bounds: bounds,
3819  			timestamp: pos.timestamp
3820  		};
3821
3822  		for (var i in pos.coords) {
3823  			if (typeof pos.coords[i] === 'number') {
3824  				data[i] = pos.coords[i];
3825  			}
3826  		}
3827
3828  		// @event locationfound: LocationEvent
3829  		// Fired when geolocation (using the [`locate`](#map-locate) method)
3830  		// went successfully.
3831  		this.fire('locationfound', data);
3832  	},
3833
3834  	// TODO Appropriate docs section?
3835  	// @section Other Methods
3836  	// @method addHandler(name: String, HandlerClass: Function): this
3837  	// Adds a new `Handler` to the map, given its name and constructor function.
3838  	addHandler: function (name, HandlerClass) {
3839  		if (!HandlerClass) { return this; }
3840
3841  		var handler = this[name] = new HandlerClass(this);
3842
3843  		this._handlers.push(handler);
3844
3845  		if (this.options[name]) {
3846  			handler.enable();
3847  		}
3848
3849  		return this;
3850  	},
3851
3852  	// @method remove(): this
3853  	// Destroys the map and clears all related event listeners.
3854  	remove: function () {
3855
3856  		this._initEvents(true);
3857  		if (this.options.maxBounds) { this.off('moveend', this._panInsideMaxBounds); }
3858
3859  		if (this._containerId !== this._container._leaflet_id) {
3860  			throw new Error('Map container is being reused by another instance');
3861  		}
3862
3863  		try {
3864  			// throws error in IE6-8
3865  			delete this._container._leaflet_id;
3866  			delete this._containerId;
3867  		} catch (e) {
3868  			/*eslint-disable */
3869  			this._container._leaflet_id = undefined;
3870  			/* eslint-enable */
3871  			this._containerId = undefined;
3872  		}
3873
3874  		if (this._locationWatchId !== undefined) {
3875  			this.stopLocate();
3876  		}
3877
3878  		this._stop();
3879
3880  		remove(this._mapPane);
3881
3882  		if (this._clearControlPos) {
3883  			this._clearControlPos();
3884  		}
3885  		if (this._resizeRequest) {
3886  			cancelAnimFrame(this._resizeRequest);
3887  			this._resizeRequest = null;
3888  		}
3889
3890  		this._clearHandlers();
3891
3892  		if (this._loaded) {
3893  			// @section Map state change events
3894  			// @event unload: Event
3895  			// Fired when the map is destroyed with [remove](#map-remove) method.
3896  			this.fire('unload');
3897  		}
3898
3899  		var i;
3900  		for (i in this._layers) {
3901  			this._layers[i].remove();
3902  		}
3903  		for (i in this._panes) {
3904  			remove(this._panes[i]);
3905  		}
3906
3907  		this._layers = [];
3908  		this._panes = [];
3909  		delete this._mapPane;
3910  		delete this._renderer;
3911
3912  		return this;
3913  	},
3914
3915  	// @section Other Methods
3916  	// @method createPane(name: String, container?: HTMLElement): HTMLElement
3917  	// Creates a new [map pane](#map-pane) with the given name if it doesn't exist already,
3918  	// then returns it. The pane is created as a child of `container`, or
3919  	// as a child of the main map pane if not set.
3920  	createPane: function (name, container) {
3921  		var className = 'leaflet-pane' + (name ? ' leaflet-' + name.replace('Pane', '') + '-pane' : ''),
3922  		    pane = create$1('div', className, container || this._mapPane);
3923
3924  		if (name) {
3925  			this._panes[name] = pane;
3926  		}
3927  		return pane;
3928  	},
3929
3930  	// @section Methods for Getting Map State
3931
3932  	// @method getCenter(): LatLng
3933  	// Returns the geographical center of the map view
3934  	getCenter: function () {
3935  		this._checkIfLoaded();
3936
3937  		if (this._lastCenter && !this._moved()) {
3938  			return this._lastCenter.clone();
3939  		}
3940  		return this.layerPointToLatLng(this._getCenterLayerPoint());
3941  	},
3942
3943  	// @method getZoom(): Number
3944  	// Returns the current zoom level of the map view
3945  	getZoom: function () {
3946  		return this._zoom;
3947  	},
3948
3949  	// @method getBounds(): LatLngBounds
3950  	// Returns the geographical bounds visible in the current map view
3951  	getBounds: function () {
3952  		var bounds = this.getPixelBounds(),
3953  		    sw = this.unproject(bounds.getBottomLeft()),
3954  		    ne = this.unproject(bounds.getTopRight());
3955
3956  		return new LatLngBounds(sw, ne);
3957  	},
3958
3959  	// @method getMinZoom(): Number
3960  	// 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.
3961  	getMinZoom: function () {
3962  		return this.options.minZoom === undefined ? this._layersMinZoom || 0 : this.options.minZoom;
3963  	},
3964
3965  	// @method getMaxZoom(): Number
3966  	// Returns the maximum zoom level of the map (if set in the `maxZoom` option of the map or of any layers).
3967  	getMaxZoom: function () {
3968  		return this.options.maxZoom === undefined ?
3969  			(this._layersMaxZoom === undefined ? Infinity : this._layersMaxZoom) :
3970  			this.options.maxZoom;
3971  	},
3972
3973  	// @method getBoundsZoom(bounds: LatLngBounds, inside?: Boolean, padding?: Point): Number
3974  	// Returns the maximum zoom level on which the given bounds fit to the map
3975  	// view in its entirety. If `inside` (optional) is set to `true`, the method
3976  	// instead returns the minimum zoom level on which the map view fits into
3977  	// the given bounds in its entirety.
3978  	getBoundsZoom: function (bounds, inside, padding) { // (LatLngBounds[, Boolean, Point]) -> Number
3979  		bounds = toLatLngBounds(bounds);
3980  		padding = toPoint(padding || [0, 0]);
3981
3982  		var zoom = this.getZoom() || 0,
3983  		    min = this.getMinZoom(),
3984  		    max = this.getMaxZoom(),
3985  		    nw = bounds.getNorthWest(),
3986  		    se = bounds.getSouthEast(),
3987  		    size = this.getSize().subtract(padding),
3988  		    boundsSize = toBounds(this.project(se, zoom), this.project(nw, zoom)).getSize(),
3989  		    snap = Browser.any3d ? this.options.zoomSnap : 1,
3990  		    scalex = size.x / boundsSize.x,
3991  		    scaley = size.y / boundsSize.y,
3992  		    scale = inside ? Math.max(scalex, scaley) : Math.min(scalex, scaley);
3993
3994  		zoom = this.getScaleZoom(scale, zoom);
3995
3996  		if (snap) {
3997  			zoom = Math.round(zoom / (snap / 100)) * (snap / 100); // don't jump if within 1% of a snap level
3998  			zoom = inside ? Math.ceil(zoom / snap) * snap : Math.floor(zoom / snap) * snap;
3999  		}
4000
4001  		return Math.max(min, Math.min(max, zoom));
4002  	},
4003
4004  	// @method getSize(): Point
4005  	// Returns the current size of the map container (in pixels).
4006  	getSize: function () {
4007  		if (!this._size || this._sizeChanged) {
4008  			this._size = new Point(
4009  				this._container.clientWidth || 0,
4010  				this._container.clientHeight || 0);
4011
4012  			this._sizeChanged = false;
4013  		}
4014  		return this._size.clone();
4015  	},
4016
4017  	// @method getPixelBounds(): Bounds
4018  	// Returns the bounds of the current map view in projected pixel
4019  	// coordinates (sometimes useful in layer and overlay implementations).
4020  	getPixelBounds: function (center, zoom) {
4021  		var topLeftPoint = this._getTopLeftPoint(center, zoom);
4022  		return new Bounds(topLeftPoint, topLeftPoint.add(this.getSize()));
4023  	},
4024
4025  	// TODO: Check semantics - isn't the pixel origin the 0,0 coord relative to
4026  	// the map pane? "left point of the map layer" can be confusing, specially
4027  	// since there can be negative offsets.
4028  	// @method getPixelOrigin(): Point
4029  	// Returns the projected pixel coordinates of the top left point of
4030  	// the map layer (useful in custom layer and overlay implementations).
4031  	getPixelOrigin: function () {
4032  		this._checkIfLoaded();
4033  		return this._pixelOrigin;
4034  	},
4035
4036  	// @method getPixelWorldBounds(zoom?: Number): Bounds
4037  	// Returns the world's bounds in pixel coordinates for zoom level `zoom`.
4038  	// If `zoom` is omitted, the map's current zoom level is used.
4039  	getPixelWorldBounds: function (zoom) {
4040  		return this.options.crs.getProjectedBounds(zoom === undefined ? this.getZoom() : zoom);
4041  	},
4042
4043  	// @section Other Methods
4044
4045  	// @method getPane(pane: String|HTMLElement): HTMLElement
4046  	// Returns a [map pane](#map-pane), given its name or its HTML element (its identity).
4047  	getPane: function (pane) {
4048  		return typeof pane === 'string' ? this._panes[pane] : pane;
4049  	},
4050
4051  	// @method getPanes(): Object
4052  	// Returns a plain object containing the names of all [panes](#map-pane) as keys and
4053  	// the panes as values.
4054  	getPanes: function () {
4055  		return this._panes;
4056  	},
4057
4058  	// @method getContainer: HTMLElement
4059  	// Returns the HTML element that contains the map.
4060  	getContainer: function () {
4061  		return this._container;
4062  	},
4063
4064
4065  	// @section Conversion Methods
4066
4067  	// @method getZoomScale(toZoom: Number, fromZoom: Number): Number
4068  	// Returns the scale factor to be applied to a map transition from zoom level
4069  	// `fromZoom` to `toZoom`. Used internally to help with zoom animations.
4070  	getZoomScale: function (toZoom, fromZoom) {
4071  		// TODO replace with universal implementation after refactoring projections
4072  		var crs = this.options.crs;
4073  		fromZoom = fromZoom === undefined ? this._zoom : fromZoom;
4074  		return crs.scale(toZoom) / crs.scale(fromZoom);
4075  	},
4076
4077  	// @method getScaleZoom(scale: Number, fromZoom: Number): Number
4078  	// Returns the zoom level that the map would end up at, if it is at `fromZoom`
4079  	// level and everything is scaled by a factor of `scale`. Inverse of
4080  	// [`getZoomScale`](#map-getZoomScale).
4081  	getScaleZoom: function (scale, fromZoom) {
4082  		var crs = this.options.crs;
4083  		fromZoom = fromZoom === undefined ? this._zoom : fromZoom;
4084  		var zoom = crs.zoom(scale * crs.scale(fromZoom));
4085  		return isNaN(zoom) ? Infinity : zoom;
4086  	},
4087
4088  	// @method project(latlng: LatLng, zoom: Number): Point
4089  	// Projects a geographical coordinate `LatLng` according to the projection
4090  	// of the map's CRS, then scales it according to `zoom` and the CRS's
4091  	// `Transformation`. The result is pixel coordinate relative to
4092  	// the CRS origin.
4093  	project: function (latlng, zoom) {
4094  		zoom = zoom === undefined ? this._zoom : zoom;
4095  		return this.options.crs.latLngToPoint(toLatLng(latlng), zoom);
4096  	},
4097
4098  	// @method unproject(point: Point, zoom: Number): LatLng
4099  	// Inverse of [`project`](#map-project).
4100  	unproject: function (point, zoom) {
4101  		zoom = zoom === undefined ? this._zoom : zoom;
4102  		return this.options.crs.pointToLatLng(toPoint(point), zoom);
4103  	},
4104
4105  	// @method layerPointToLatLng(point: Point): LatLng
4106  	// Given a pixel coordinate relative to the [origin pixel](#map-getpixelorigin),
4107  	// returns the corresponding geographical coordinate (for the current zoom level).
4108  	layerPointToLatLng: function (point) {
4109  		var projectedPoint = toPoint(point).add(this.getPixelOrigin());
4110  		return this.unproject(projectedPoint);
4111  	},
4112
4113  	// @method latLngToLayerPoint(latlng: LatLng): Point
4114  	// Given a geographical coordinate, returns the corresponding pixel coordinate
4115  	// relative to the [origin pixel](#map-getpixelorigin).
4116  	latLngToLayerPoint: function (latlng) {
4117  		var projectedPoint = this.project(toLatLng(latlng))._round();
4118  		return projectedPoint._subtract(this.getPixelOrigin());
4119  	},
4120
4121  	// @method wrapLatLng(latlng: LatLng): LatLng
4122  	// Returns a `LatLng` where `lat` and `lng` has been wrapped according to the
4123  	// map's CRS's `wrapLat` and `wrapLng` properties, if they are outside the
4124  	// CRS's bounds.
4125  	// By default this means longitude is wrapped around the dateline so its
4126  	// value is between -180 and +180 degrees.
4127  	wrapLatLng: function (latlng) {
4128  		return this.options.crs.wrapLatLng(toLatLng(latlng));
4129  	},
4130
4131  	// @method wrapLatLngBounds(bounds: LatLngBounds): LatLngBounds
4132  	// Returns a `LatLngBounds` with the same size as the given one, ensuring that
4133  	// its center is within the CRS's bounds.
4134  	// By default this means the center longitude is wrapped around the dateline so its
4135  	// value is between -180 and +180 degrees, and the majority of the bounds
4136  	// overlaps the CRS's bounds.
4137  	wrapLatLngBounds: function (latlng) {
4138  		return this.options.crs.wrapLatLngBounds(toLatLngBounds(latlng));
4139  	},
4140
4141  	// @method distance(latlng1: LatLng, latlng2: LatLng): Number
4142  	// Returns the distance between two geographical coordinates according to
4143  	// the map's CRS. By default this measures distance in meters.
4144  	distance: function (latlng1, latlng2) {
4145  		return this.options.crs.distance(toLatLng(latlng1), toLatLng(latlng2));
4146  	},
4147
4148  	// @method containerPointToLayerPoint(point: Point): Point
4149  	// Given a pixel coordinate relative to the map container, returns the corresponding
4150  	// pixel coordinate relative to the [origin pixel](#map-getpixelorigin).
4151  	containerPointToLayerPoint: function (point) { // (Point)
4152  		return toPoint(point).subtract(this._getMapPanePos());
4153  	},
4154
4155  	// @method layerPointToContainerPoint(point: Point): Point
4156  	// Given a pixel coordinate relative to the [origin pixel](#map-getpixelorigin),
4157  	// returns the corresponding pixel coordinate relative to the map container.
4158  	layerPointToContainerPoint: function (point) { // (Point)
4159  		return toPoint(point).add(this._getMapPanePos());
4160  	},
4161
4162  	// @method containerPointToLatLng(point: Point): LatLng
4163  	// Given a pixel coordinate relative to the map container, returns
4164  	// the corresponding geographical coordinate (for the current zoom level).
4165  	containerPointToLatLng: function (point) {
4166  		var layerPoint = this.containerPointToLayerPoint(toPoint(point));
4167  		return this.layerPointToLatLng(layerPoint);
4168  	},
4169
4170  	// @method latLngToContainerPoint(latlng: LatLng): Point
4171  	// Given a geographical coordinate, returns the corresponding pixel coordinate
4172  	// relative to the map container.
4173  	latLngToContainerPoint: function (latlng) {
4174  		return this.layerPointToContainerPoint(this.latLngToLayerPoint(toLatLng(latlng)));
4175  	},
4176
4177  	// @method mouseEventToContainerPoint(ev: MouseEvent): Point
4178  	// Given a MouseEvent object, returns the pixel coordinate relative to the
4179  	// map container where the event took place.
4180  	mouseEventToContainerPoint: function (e) {
4181  		return getMousePosition(e, this._container);
4182  	},
4183
4184  	// @method mouseEventToLayerPoint(ev: MouseEvent): Point
4185  	// Given a MouseEvent object, returns the pixel coordinate relative to
4186  	// the [origin pixel](#map-getpixelorigin) where the event took place.
4187  	mouseEventToLayerPoint: function (e) {
4188  		return this.containerPointToLayerPoint(this.mouseEventToContainerPoint(e));
4189  	},
4190
4191  	// @method mouseEventToLatLng(ev: MouseEvent): LatLng
4192  	// Given a MouseEvent object, returns geographical coordinate where the
4193  	// event took place.
4194  	mouseEventToLatLng: function (e) { // (MouseEvent)
4195  		return this.layerPointToLatLng(this.mouseEventToLayerPoint(e));
4196  	},
4197
4198
4199  	// map initialization methods
4200
4201  	_initContainer: function (id) {
4202  		var container = this._container = get(id);
4203
4204  		if (!container) {
4205  			throw new Error('Map container not found.');
4206  		} else if (container._leaflet_id) {
4207  			throw new Error('Map container is already initialized.');
4208  		}
4209
4210  		on(container, 'scroll', this._onScroll, this);
4211  		this._containerId = stamp(container);
4212  	},
4213
4214  	_initLayout: function () {
4215  		var container = this._container;
4216
4217  		this._fadeAnimated = this.options.fadeAnimation && Browser.any3d;
4218
4219  		addClass(container, 'leaflet-container' +
4220  			(Browser.touch ? ' leaflet-touch' : '') +
4221  			(Browser.retina ? ' leaflet-retina' : '') +
4222  			(Browser.ielt9 ? ' leaflet-oldie' : '') +
4223  			(Browser.safari ? ' leaflet-safari' : '') +
4224  			(this._fadeAnimated ? ' leaflet-fade-anim' : ''));
4225
4226  		var position = getStyle(container, 'position');
4227
4228  		if (position !== 'absolute' && position !== 'relative' && position !== 'fixed' && position !== 'sticky') {
4229  			container.style.position = 'relative';
4230  		}
4231
4232  		this._initPanes();
4233
4234  		if (this._initControlPos) {
4235  			this._initControlPos();
4236  		}
4237  	},
4238
4239  	_initPanes: function () {
4240  		var panes = this._panes = {};
4241  		this._paneRenderers = {};
4242
4243  		// @section
4244  		//
4245  		// Panes are DOM elements used to control the ordering of layers on the map. You
4246  		// can access panes with [`map.getPane`](#map-getpane) or
4247  		// [`map.getPanes`](#map-getpanes) methods. New panes can be created with the
4248  		// [`map.createPane`](#map-createpane) method.
4249  		//
4250  		// Every map has the following default panes that differ only in zIndex.
4251  		//
4252  		// @pane mapPane: HTMLElement = 'auto'
4253  		// Pane that contains all other map panes
4254
4255  		this._mapPane = this.createPane('mapPane', this._container);
4256  		setPosition(this._mapPane, new Point(0, 0));
4257
4258  		// @pane tilePane: HTMLElement = 200
4259  		// Pane for `GridLayer`s and `TileLayer`s
4260  		this.createPane('tilePane');
4261  		// @pane overlayPane: HTMLElement = 400
4262  		// Pane for vectors (`Path`s, like `Polyline`s and `Polygon`s), `ImageOverlay`s and `VideoOverlay`s
4263  		this.createPane('overlayPane');
4264  		// @pane shadowPane: HTMLElement = 500
4265  		// Pane for overlay shadows (e.g. `Marker` shadows)
4266  		this.createPane('shadowPane');
4267  		// @pane markerPane: HTMLElement = 600
4268  		// Pane for `Icon`s of `Marker`s
4269  		this.createPane('markerPane');
4270  		// @pane tooltipPane: HTMLElement = 650
4271  		// Pane for `Tooltip`s.
4272  		this.createPane('tooltipPane');
4273  		// @pane popupPane: HTMLElement = 700
4274  		// Pane for `Popup`s.
4275  		this.createPane('popupPane');
4276
4277  		if (!this.options.markerZoomAnimation) {
4278  			addClass(panes.markerPane, 'leaflet-zoom-hide');
4279  			addClass(panes.shadowPane, 'leaflet-zoom-hide');
4280  		}
4281  	},
4282
4283
4284  	// private methods that modify map state
4285
4286  	// @section Map state change events
4287  	_resetView: function (center, zoom, noMoveStart) {
4288  		setPosition(this._mapPane, new Point(0, 0));
4289
4290  		var loading = !this._loaded;
4291  		this._loaded = true;
4292  		zoom = this._limitZoom(zoom);
4293
4294  		this.fire('viewprereset');
4295
4296  		var zoomChanged = this._zoom !== zoom;
4297  		this
4298  			._moveStart(zoomChanged, noMoveStart)
4299  			._move(center, zoom)
4300  			._moveEnd(zoomChanged);
4301
4302  		// @event viewreset: Event
4303  		// Fired when the map needs to redraw its content (this usually happens
4304  		// on map zoom or load). Very useful for creating custom overlays.
4305  		this.fire('viewreset');
4306
4307  		// @event load: Event
4308  		// Fired when the map is initialized (when its center and zoom are set
4309  		// for the first time).
4310  		if (loading) {
4311  			this.fire('load');
4312  		}
4313  	},
4314
4315  	_moveStart: function (zoomChanged, noMoveStart) {
4316  		// @event zoomstart: Event
4317  		// Fired when the map zoom is about to change (e.g. before zoom animation).
4318  		// @event movestart: Event
4319  		// Fired when the view of the map starts changing (e.g. user starts dragging the map).
4320  		if (zoomChanged) {
4321  			this.fire('zoomstart');
4322  		}
4323  		if (!noMoveStart) {
4324  			this.fire('movestart');
4325  		}
4326  		return this;
4327  	},
4328
4329  	_move: function (center, zoom, data, supressEvent) {
4330  		if (zoom === undefined) {
4331  			zoom = this._zoom;
4332  		}
4333  		var zoomChanged = this._zoom !== zoom;
4334
4335  		this._zoom = zoom;
4336  		this._lastCenter = center;
4337  		this._pixelOrigin = this._getNewPixelOrigin(center);
4338
4339  		if (!supressEvent) {
4340  			// @event zoom: Event
4341  			// Fired repeatedly during any change in zoom level,
4342  			// including zoom and fly animations.
4343  			if (zoomChanged || (data && data.pinch)) {	// Always fire 'zoom' if pinching because #3530
4344  				this.fire('zoom', data);
4345  			}
4346
4347  			// @event move: Event
4348  			// Fired repeatedly during any movement of the map,
4349  			// including pan and fly animations.
4350  			this.fire('move', data);
4351  		} else if (data && data.pinch) {	// Always fire 'zoom' if pinching because #3530
4352  			this.fire('zoom', data);
4353  		}
4354  		return this;
4355  	},
4356
4357  	_moveEnd: function (zoomChanged) {
4358  		// @event zoomend: Event
4359  		// Fired when the map zoom changed, after any animations.
4360  		if (zoomChanged) {
4361  			this.fire('zoomend');
4362  		}
4363
4364  		// @event moveend: Event
4365  		// Fired when the center of the map stops changing
4366  		// (e.g. user stopped dragging the map or after non-centered zoom).
4367  		return this.fire('moveend');
4368  	},
4369
4370  	_stop: function () {
4371  		cancelAnimFrame(this._flyToFrame);
4372  		if (this._panAnim) {
4373  			this._panAnim.stop();
4374  		}
4375  		return this;
4376  	},
4377
4378  	_rawPanBy: function (offset) {
4379  		setPosition(this._mapPane, this._getMapPanePos().subtract(offset));
4380  	},
4381
4382  	_getZoomSpan: function () {
4383  		return this.getMaxZoom() - this.getMinZoom();
4384  	},
4385
4386  	_panInsideMaxBounds: function () {
4387  		if (!this._enforcingBounds) {
4388  			this.panInsideBounds(this.options.maxBounds);
4389  		}
4390  	},
4391
4392  	_checkIfLoaded: function () {
4393  		if (!this._loaded) {
4394  			throw new Error('Set map center and zoom first.');
4395  		}
4396  	},
4397
4398  	// DOM event handling
4399
4400  	// @section Interaction events
4401  	_initEvents: function (remove) {
4402  		this._targets = {};
4403  		this._targets[stamp(this._container)] = this;
4404
4405  		var onOff = remove ? off : on;
4406
4407  		// @event click: MouseEvent
4408  		// Fired when the user clicks (or taps) the map.
4409  		// @event dblclick: MouseEvent
4410  		// Fired when the user double-clicks (or double-taps) the map.
4411  		// @event mousedown: MouseEvent
4412  		// Fired when the user pushes the mouse button on the map.
4413  		// @event mouseup: MouseEvent
4414  		// Fired when the user releases the mouse button on the map.
4415  		// @event mouseover: MouseEvent
4416  		// Fired when the mouse enters the map.
4417  		// @event mouseout: MouseEvent
4418  		// Fired when the mouse leaves the map.
4419  		// @event mousemove: MouseEvent
4420  		// Fired while the mouse moves over the map.
4421  		// @event contextmenu: MouseEvent
4422  		// Fired when the user pushes the right mouse button on the map, prevents
4423  		// default browser context menu from showing if there are listeners on
4424  		// this event. Also fired on mobile when the user holds a single touch
4425  		// for a second (also called long press).
4426  		// @event keypress: KeyboardEvent
4427  		// Fired when the user presses a key from the keyboard that produces a character value while the map is focused.
4428  		// @event keydown: KeyboardEvent
4429  		// Fired when the user presses a key from the keyboard while the map is focused. Unlike the `keypress` event,
4430  		// the `keydown` event is fired for keys that produce a character value and for keys
4431  		// that do not produce a character value.
4432  		// @event keyup: KeyboardEvent
4433  		// Fired when the user releases a key from the keyboard while the map is focused.
4434  		onOff(this._container, 'click dblclick mousedown mouseup ' +
4435  			'mouseover mouseout mousemove contextmenu keypress keydown keyup', this._handleDOMEvent, this);
4436
4437  		if (this.options.trackResize) {
4438  			onOff(window, 'resize', this._onResize, this);
4439  		}
4440
4441  		if (Browser.any3d && this.options.transform3DLimit) {
4442  			(remove ? this.off : this.on).call(this, 'moveend', this._onMoveEnd);
4443  		}
4444  	},
4445
4446  	_onResize: function () {
4447  		cancelAnimFrame(this._resizeRequest);
4448  		this._resizeRequest = requestAnimFrame(
4449  		        function () { this.invalidateSize({debounceMoveend: true}); }, this);
4450  	},
4451
4452  	_onScroll: function () {
4453  		this._container.scrollTop  = 0;
4454  		this._container.scrollLeft = 0;
4455  	},
4456
4457  	_onMoveEnd: function () {
4458  		var pos = this._getMapPanePos();
4459  		if (Math.max(Math.abs(pos.x), Math.abs(pos.y)) >= this.options.transform3DLimit) {
4460  			// https://bugzilla.mozilla.org/show_bug.cgi?id=1203873 but Webkit also have
4461  			// a pixel offset on very high values, see: https://jsfiddle.net/dg6r5hhb/
4462  			this._resetView(this.getCenter(), this.getZoom());
4463  		}
4464  	},
4465
4466  	_findEventTargets: function (e, type) {
4467  		var targets = [],
4468  		    target,
4469  		    isHover = type === 'mouseout' || type === 'mouseover',
4470  		    src = e.target || e.srcElement,
4471  		    dragging = false;
4472
4473  		while (src) {
4474  			target = this._targets[stamp(src)];
4475  			if (target && (type === 'click' || type === 'preclick') && this._draggableMoved(target)) {
4476  				// Prevent firing click after you just dragged an object.
4477  				dragging = true;
4478  				break;
4479  			}
4480  			if (target && target.listens(type, true)) {
4481  				if (isHover && !isExternalTarget(src, e)) { break; }
4482  				targets.push(target);
4483  				if (isHover) { break; }
4484  			}
4485  			if (src === this._container) { break; }
4486  			src = src.parentNode;
4487  		}
4488  		if (!targets.length && !dragging && !isHover && this.listens(type, true)) {
4489  			targets = [this];
4490  		}
4491  		return targets;
4492  	},
4493
4494  	_isClickDisabled: function (el) {
4495  		while (el && el !== this._container) {
4496  			if (el['_leaflet_disable_click']) { return true; }
4497  			el = el.parentNode;
4498  		}
4499  	},
4500
4501  	_handleDOMEvent: function (e) {
4502  		var el = (e.target || e.srcElement);
4503  		if (!this._loaded || el['_leaflet_disable_events'] || e.type === 'click' && this._isClickDisabled(el)) {
4504  			return;
4505  		}
4506
4507  		var type = e.type;
4508
4509  		if (type === 'mousedown') {
4510  			// prevents outline when clicking on keyboard-focusable element
4511  			preventOutline(el);
4512  		}
4513
4514  		this._fireDOMEvent(e, type);
4515  	},
4516
4517  	_mouseEvents: ['click', 'dblclick', 'mouseover', 'mouseout', 'contextmenu'],
4518
4519  	_fireDOMEvent: function (e, type, canvasTargets) {
4520
4521  		if (e.type === 'click') {
4522  			// Fire a synthetic 'preclick' event which propagates up (mainly for closing popups).
4523  			// @event preclick: MouseEvent
4524  			// Fired before mouse click on the map (sometimes useful when you
4525  			// want something to happen on click before any existing click
4526  			// handlers start running).
4527  			var synth = extend({}, e);
4528  			synth.type = 'preclick';
4529  			this._fireDOMEvent(synth, synth.type, canvasTargets);
4530  		}
4531
4532  		// Find the layer the event is propagating from and its parents.
4533  		var targets = this._findEventTargets(e, type);
4534
4535  		if (canvasTargets) {
4536  			var filtered = []; // pick only targets with listeners
4537  			for (var i = 0; i < canvasTargets.length; i++) {
4538  				if (canvasTargets[i].listens(type, true)) {
4539  					filtered.push(canvasTargets[i]);
4540  				}
4541  			}
4542  			targets = filtered.concat(targets);
4543  		}
4544
4545  		if (!targets.length) { return; }
4546
4547  		if (type === 'contextmenu') {
4548  			preventDefault(e);
4549  		}
4550
4551  		var target = targets[0];
4552  		var data = {
4553  			originalEvent: e
4554  		};
4555
4556  		if (e.type !== 'keypress' && e.type !== 'keydown' && e.type !== 'keyup') {
4557  			var isMarker = target.getLatLng && (!target._radius || target._radius <= 10);
4558  			data.containerPoint = isMarker ?
4559  				this.latLngToContainerPoint(target.getLatLng()) : this.mouseEventToContainerPoint(e);
4560  			data.layerPoint = this.containerPointToLayerPoint(data.containerPoint);
4561  			data.latlng = isMarker ? target.getLatLng() : this.layerPointToLatLng(data.layerPoint);
4562  		}
4563
4564  		for (i = 0; i < targets.length; i++) {
4565  			targets[i].fire(type, data, true);
4566  			if (data.originalEvent._stopped ||
4567  				(targets[i].options.bubblingMouseEvents === false && indexOf(this._mouseEvents, type) !== -1)) { return; }
4568  		}
4569  	},
4570
4571  	_draggableMoved: function (obj) {
4572  		obj = obj.dragging && obj.dragging.enabled() ? obj : this;
4573  		return (obj.dragging && obj.dragging.moved()) || (this.boxZoom && this.boxZoom.moved());
4574  	},
4575
4576  	_clearHandlers: function () {
4577  		for (var i = 0, len = this._handlers.length; i < len; i++) {
4578  			this._handlers[i].disable();
4579  		}
4580  	},
4581
4582  	// @section Other Methods
4583
4584  	// @method whenReady(fn: Function, context?: Object): this
4585  	// Runs the given function `fn` when the map gets initialized with
4586  	// a view (center and zoom) and at least one layer, or immediately
4587  	// if it's already initialized, optionally passing a function context.
4588  	whenReady: function (callback, context) {
4589  		if (this._loaded) {
4590  			callback.call(context || this, {target: this});
4591  		} else {
4592  			this.on('load', callback, context);
4593  		}
4594  		return this;
4595  	},
4596
4597
4598  	// private methods for getting map state
4599
4600  	_getMapPanePos: function () {
4601  		return getPosition(this._mapPane) || new Point(0, 0);
4602  	},
4603
4604  	_moved: function () {
4605  		var pos = this._getMapPanePos();
4606  		return pos && !pos.equals([0, 0]);
4607  	},
4608
4609  	_getTopLeftPoint: function (center, zoom) {
4610  		var pixelOrigin = center && zoom !== undefined ?
4611  			this._getNewPixelOrigin(center, zoom) :
4612  			this.getPixelOrigin();
4613  		return pixelOrigin.subtract(this._getMapPanePos());
4614  	},
4615
4616  	_getNewPixelOrigin: function (center, zoom) {
4617  		var viewHalf = this.getSize()._divideBy(2);
4618  		return this.project(center, zoom)._subtract(viewHalf)._add(this._getMapPanePos())._round();
4619  	},
4620
4621  	_latLngToNewLayerPoint: function (latlng, zoom, center) {
4622  		var topLeft = this._getNewPixelOrigin(center, zoom);
4623  		return this.project(latlng, zoom)._subtract(topLeft);
4624  	},
4625
4626  	_latLngBoundsToNewLayerBounds: function (latLngBounds, zoom, center) {
4627  		var topLeft = this._getNewPixelOrigin(center, zoom);
4628  		return toBounds([
4629  			this.project(latLngBounds.getSouthWest(), zoom)._subtract(topLeft),
4630  			this.project(latLngBounds.getNorthWest(), zoom)._subtract(topLeft),
4631  			this.project(latLngBounds.getSouthEast(), zoom)._subtract(topLeft),
4632  			this.project(latLngBounds.getNorthEast(), zoom)._subtract(topLeft)
4633  		]);
4634  	},
4635
4636  	// layer point of the current center
4637  	_getCenterLayerPoint: function () {
4638  		return this.containerPointToLayerPoint(this.getSize()._divideBy(2));
4639  	},
4640
4641  	// offset of the specified place to the current center in pixels
4642  	_getCenterOffset: function (latlng) {
4643  		return this.latLngToLayerPoint(latlng).subtract(this._getCenterLayerPoint());
4644  	},
4645
4646  	// adjust center for view to get inside bounds
4647  	_limitCenter: function (center, zoom, bounds) {
4648
4649  		if (!bounds) { return center; }
4650
4651  		var centerPoint = this.project(center, zoom),
4652  		    viewHalf = this.getSize().divideBy(2),
4653  		    viewBounds = new Bounds(centerPoint.subtract(viewHalf), centerPoint.add(viewHalf)),
4654  		    offset = this._getBoundsOffset(viewBounds, bounds, zoom);
4655
4656  		// If offset is less than a pixel, ignore.
4657  		// This prevents unstable projections from getting into
4658  		// an infinite loop of tiny offsets.
4659  		if (Math.abs(offset.x) <= 1 && Math.abs(offset.y) <= 1) {
4660  			return center;
4661  		}
4662
4663  		return this.unproject(centerPoint.add(offset), zoom);
4664  	},
4665
4666  	// adjust offset for view to get inside bounds
4667  	_limitOffset: function (offset, bounds) {
4668  		if (!bounds) { return offset; }
4669
4670  		var viewBounds = this.getPixelBounds(),
4671  		    newBounds = new Bounds(viewBounds.min.add(offset), viewBounds.max.add(offset));
4672
4673  		return offset.add(this._getBoundsOffset(newBounds, bounds));
4674  	},
4675
4676  	// returns offset needed for pxBounds to get inside maxBounds at a specified zoom
4677  	_getBoundsOffset: function (pxBounds, maxBounds, zoom) {
4678  		var projectedMaxBounds = toBounds(
4679  		        this.project(maxBounds.getNorthEast(), zoom),
4680  		        this.project(maxBounds.getSouthWest(), zoom)
4681  		    ),
4682  		    minOffset = projectedMaxBounds.min.subtract(pxBounds.min),
4683  		    maxOffset = projectedMaxBounds.max.subtract(pxBounds.max),
4684
4685  		    dx = this._rebound(minOffset.x, -maxOffset.x),
4686  		    dy = this._rebound(minOffset.y, -maxOffset.y);
4687
4688  		return new Point(dx, dy);
4689  	},
4690
4691  	_rebound: function (left, right) {
4692  		return left + right > 0 ?
4693  			Math.round(left - right) / 2 :
4694  			Math.max(0, Math.ceil(left)) - Math.max(0, Math.floor(right));
4695  	},
4696
4697  	_limitZoom: function (zoom) {
4698  		var min = this.getMinZoom(),
4699  		    max = this.getMaxZoom(),
4700  		    snap = Browser.any3d ? this.options.zoomSnap : 1;
4701  		if (snap) {
4702  			zoom = Math.round(zoom / snap) * snap;
4703  		}
4704  		return Math.max(min, Math.min(max, zoom));
4705  	},
4706
4707  	_onPanTransitionStep: function () {
4708  		this.fire('move');
4709  	},
4710
4711  	_onPanTransitionEnd: function () {
4712  		removeClass(this._mapPane, 'leaflet-pan-anim');
4713  		this.fire('moveend');
4714  	},
4715
4716  	_tryAnimatedPan: function (center, options) {
4717  		// difference between the new and current centers in pixels
4718  		var offset = this._getCenterOffset(center)._trunc();
4719
4720  		// don't animate too far unless animate: true specified in options
4721  		if ((options && options.animate) !== true && !this.getSize().contains(offset)) { return false; }
4722
4723  		this.panBy(offset, options);
4724
4725  		return true;
4726  	},
4727
4728  	_createAnimProxy: function () {
4729
4730  		var proxy = this._proxy = create$1('div', 'leaflet-proxy leaflet-zoom-animated');
4731  		this._panes.mapPane.appendChild(proxy);
4732
4733  		this.on('zoomanim', function (e) {
4734  			var prop = TRANSFORM,
4735  			    transform = this._proxy.style[prop];
4736
4737  			setTransform(this._proxy, this.project(e.center, e.zoom), this.getZoomScale(e.zoom, 1));
4738
4739  			// workaround for case when transform is the same and so transitionend event is not fired
4740  			if (transform === this._proxy.style[prop] && this._animatingZoom) {
4741  				this._onZoomTransitionEnd();
4742  			}
4743  		}, this);
4744
4745  		this.on('load moveend', this._animMoveEnd, this);
4746
4747  		this._on('unload', this._destroyAnimProxy, this);
4748  	},
4749
4750  	_destroyAnimProxy: function () {
4751  		remove(this._proxy);
4752  		this.off('load moveend', this._animMoveEnd, this);
4753  		delete this._proxy;
4754  	},
4755
4756  	_animMoveEnd: function () {
4757  		var c = this.getCenter(),
4758  		    z = this.getZoom();
4759  		setTransform(this._proxy, this.project(c, z), this.getZoomScale(z, 1));
4760  	},
4761
4762  	_catchTransitionEnd: function (e) {
4763  		if (this._animatingZoom && e.propertyName.indexOf('transform') >= 0) {
4764  			this._onZoomTransitionEnd();
4765  		}
4766  	},
4767
4768  	_nothingToAnimate: function () {
4769  		return !this._container.getElementsByClassName('leaflet-zoom-animated').length;
4770  	},
4771
4772  	_tryAnimatedZoom: function (center, zoom, options) {
4773
4774  		if (this._animatingZoom) { return true; }
4775
4776  		options = options || {};
4777
4778  		// don't animate if disabled, not supported or zoom difference is too large
4779  		if (!this._zoomAnimated || options.animate === false || this._nothingToAnimate() ||
4780  		        Math.abs(zoom - this._zoom) > this.options.zoomAnimationThreshold) { return false; }
4781
4782  		// offset is the pixel coords of the zoom origin relative to the current center
4783  		var scale = this.getZoomScale(zoom),
4784  		    offset = this._getCenterOffset(center)._divideBy(1 - 1 / scale);
4785
4786  		// don't animate if the zoom origin isn't within one screen from the current center, unless forced
4787  		if (options.animate !== true && !this.getSize().contains(offset)) { return false; }
4788
4789  		requestAnimFrame(function () {
4790  			this
4791  			    ._moveStart(true, options.noMoveStart || false)
4792  			    ._animateZoom(center, zoom, true);
4793  		}, this);
4794
4795  		return true;
4796  	},
4797
4798  	_animateZoom: function (center, zoom, startAnim, noUpdate) {
4799  		if (!this._mapPane) { return; }
4800
4801  		if (startAnim) {
4802  			this._animatingZoom = true;
4803
4804  			// remember what center/zoom to set after animation
4805  			this._animateToCenter = center;
4806  			this._animateToZoom = zoom;
4807
4808  			addClass(this._mapPane, 'leaflet-zoom-anim');
4809  		}
4810
4811  		// @section Other Events
4812  		// @event zoomanim: ZoomAnimEvent
4813  		// Fired at least once per zoom animation. For continuous zoom, like pinch zooming, fired once per frame during zoom.
4814  		this.fire('zoomanim', {
4815  			center: center,
4816  			zoom: zoom,
4817  			noUpdate: noUpdate
4818  		});
4819
4820  		if (!this._tempFireZoomEvent) {
4821  			this._tempFireZoomEvent = this._zoom !== this._animateToZoom;
4822  		}
4823
4824  		this._move(this._animateToCenter, this._animateToZoom, undefined, true);
4825
4826  		// Work around webkit not firing 'transitionend', see https://github.com/Leaflet/Leaflet/issues/3689, 2693
4827  		setTimeout(bind(this._onZoomTransitionEnd, this), 250);
4828  	},
4829
4830  	_onZoomTransitionEnd: function () {
4831  		if (!this._animatingZoom) { return; }
4832
4833  		if (this._mapPane) {
4834  			removeClass(this._mapPane, 'leaflet-zoom-anim');
4835  		}
4836
4837  		this._animatingZoom = false;
4838
4839  		this._move(this._animateToCenter, this._animateToZoom, undefined, true);
4840
4841  		if (this._tempFireZoomEvent) {
4842  			this.fire('zoom');
4843  		}
4844  		delete this._tempFireZoomEvent;
4845
4846  		this.fire('move');
4847
4848  		this._moveEnd(true);
4849  	}
4850  });
4851
4852  // @section
4853
4854  // @factory L.map(id: String, options?: Map options)
4855  // Instantiates a map object given the DOM ID of a `<div>` element
4856  // and optionally an object literal with `Map options`.
4857  //
4858  // @alternative
4859  // @factory L.map(el: HTMLElement, options?: Map options)
4860  // Instantiates a map object given an instance of a `<div>` HTML element
4861  // and optionally an object literal with `Map options`.
4862  function createMap(id, options) {
4863  	return new Map(id, options);
4864  }
4865
4866  /*
4867   * @class Control
4868   * @aka L.Control
4869   * @inherits Class
4870   *
4871   * L.Control is a base class for implementing map controls. Handles positioning.
4872   * All other controls extend from this class.
4873   */
4874
4875  var Control = Class.extend({
4876  	// @section
4877  	// @aka Control Options
4878  	options: {
4879  		// @option position: String = 'topright'
4880  		// The position of the control (one of the map corners). Possible values are `'topleft'`,
4881  		// `'topright'`, `'bottomleft'` or `'bottomright'`
4882  		position: 'topright'
4883  	},
4884
4885  	initialize: function (options) {
4886  		setOptions(this, options);
4887  	},
4888
4889  	/* @section
4890  	 * Classes extending L.Control will inherit the following methods:
4891  	 *
4892  	 * @method getPosition: string
4893  	 * Returns the position of the control.
4894  	 */
4895  	getPosition: function () {
4896  		return this.options.position;
4897  	},
4898
4899  	// @method setPosition(position: string): this
4900  	// Sets the position of the control.
4901  	setPosition: function (position) {
4902  		var map = this._map;
4903
4904  		if (map) {
4905  			map.removeControl(this);
4906  		}
4907
4908  		this.options.position = position;
4909
4910  		if (map) {
4911  			map.addControl(this);
4912  		}
4913
4914  		return this;
4915  	},
4916
4917  	// @method getContainer: HTMLElement
4918  	// Returns the HTMLElement that contains the control.
4919  	getContainer: function () {
4920  		return this._container;
4921  	},
4922
4923  	// @method addTo(map: Map): this
4924  	// Adds the control to the given map.
4925  	addTo: function (map) {
4926  		this.remove();
4927  		this._map = map;
4928
4929  		var container = this._container = this.onAdd(map),
4930  		    pos = this.getPosition(),
4931  		    corner = map._controlCorners[pos];
4932
4933  		addClass(container, 'leaflet-control');
4934
4935  		if (pos.indexOf('bottom') !== -1) {
4936  			corner.insertBefore(container, corner.firstChild);
4937  		} else {
4938  			corner.appendChild(container);
4939  		}
4940
4941  		this._map.on('unload', this.remove, this);
4942
4943  		return this;
4944  	},
4945
4946  	// @method remove: this
4947  	// Removes the control from the map it is currently active on.
4948  	remove: function () {
4949  		if (!this._map) {
4950  			return this;
4951  		}
4952
4953  		remove(this._container);
4954
4955  		if (this.onRemove) {
4956  			this.onRemove(this._map);
4957  		}
4958
4959  		this._map.off('unload', this.remove, this);
4960  		this._map = null;
4961
4962  		return this;
4963  	},
4964
4965  	_refocusOnMap: function (e) {
4966  		// if map exists and event is not a keyboard event
4967  		if (this._map && e && e.screenX > 0 && e.screenY > 0) {
4968  			this._map.getContainer().focus();
4969  		}
4970  	}
4971  });
4972
4973  var control = function (options) {
4974  	return new Control(options);
4975  };
4976
4977  /* @section Extension methods
4978   * @uninheritable
4979   *
4980   * Every control should extend from `L.Control` and (re-)implement the following methods.
4981   *
4982   * @method onAdd(map: Map): HTMLElement
4983   * Should return the container DOM element for the control and add listeners on relevant map events. Called on [`control.addTo(map)`](#control-addTo).
4984   *
4985   * @method onRemove(map: Map)
4986   * Optional method. Should contain all clean up code that removes the listeners previously added in [`onAdd`](#control-onadd). Called on [`control.remove()`](#control-remove).
4987   */
4988
4989  /* @namespace Map
4990   * @section Methods for Layers and Controls
4991   */
4992  Map.include({
4993  	// @method addControl(control: Control): this
4994  	// Adds the given control to the map
4995  	addControl: function (control) {
4996  		control.addTo(this);
4997  		return this;
4998  	},
4999
5000  	// @method removeControl(control: Control): this
5001  	// Removes the given control from the map
5002  	removeControl: function (control) {
5003  		control.remove();
5004  		return this;
5005  	},
5006
5007  	_initControlPos: function () {
5008  		var corners = this._controlCorners = {},
5009  		    l = 'leaflet-',
5010  		    container = this._controlContainer =
5011  		            create$1('div', l + 'control-container', this._container);
5012
5013  		function createCorner(vSide, hSide) {
5014  			var className = l + vSide + ' ' + l + hSide;
5015
5016  			corners[vSide + hSide] = create$1('div', className, container);
5017  		}
5018
5019  		createCorner('top', 'left');
5020  		createCorner('top', 'right');
5021  		createCorner('bottom', 'left');
5022  		createCorner('bottom', 'right');
5023  	},
5024
5025  	_clearControlPos: function () {
5026  		for (var i in this._controlCorners) {
5027  			remove(this._controlCorners[i]);
5028  		}
5029  		remove(this._controlContainer);
5030  		delete this._controlCorners;
5031  		delete this._controlContainer;
5032  	}
5033  });
5034
5035  /*
5036   * @class Control.Layers
5037   * @aka L.Control.Layers
5038   * @inherits Control
5039   *
5040   * The layers control gives users the ability to switch between different base layers and switch overlays on/off (check out the [detailed example](https://leafletjs.com/examples/layers-control/)). Extends `Control`.
5041   *
5042   * @example
5043   *
5044   * ```js
5045   * var baseLayers = {
5046   * 	"Mapbox": mapbox,
5047   * 	"OpenStreetMap": osm
5048   * };
5049   *
5050   * var overlays = {
5051   * 	"Marker": marker,
5052   * 	"Roads": roadsLayer
5053   * };
5054   *
5055   * L.control.layers(baseLayers, overlays).addTo(map);
5056   * ```
5057   *
5058   * The `baseLayers` and `overlays` parameters are object literals with layer names as keys and `Layer` objects as values:
5059   *
5060   * ```js
5061   * {
5062   *     "<someName1>": layer1,
5063   *     "<someName2>": layer2
5064   * }
5065   * ```
5066   *
5067   * The layer names can contain HTML, which allows you to add additional styling to the items:
5068   *
5069   * ```js
5070   * {"<img src='my-layer-icon' /> <span class='my-layer-item'>My Layer</span>": myLayer}
5071   * ```
5072   */
5073
5074  var Layers = Control.extend({
5075  	// @section
5076  	// @aka Control.Layers options
5077  	options: {
5078  		// @option collapsed: Boolean = true
5079  		// If `true`, the control will be collapsed into an icon and expanded on mouse hover, touch, or keyboard activation.
5080  		collapsed: true,
5081  		position: 'topright',
5082
5083  		// @option autoZIndex: Boolean = true
5084  		// 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.
5085  		autoZIndex: true,
5086
5087  		// @option hideSingleBase: Boolean = false
5088  		// If `true`, the base layers in the control will be hidden when there is only one.
5089  		hideSingleBase: false,
5090
5091  		// @option sortLayers: Boolean = false
5092  		// Whether to sort the layers. When `false`, layers will keep the order
5093  		// in which they were added to the control.
5094  		sortLayers: false,
5095
5096  		// @option sortFunction: Function = *
5097  		// A [compare function](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Array/sort)
5098  		// that will be used for sorting the layers, when `sortLayers` is `true`.
5099  		// The function receives both the `L.Layer` instances and their names, as in
5100  		// `sortFunction(layerA, layerB, nameA, nameB)`.
5101  		// By default, it sorts layers alphabetically by their name.
5102  		sortFunction: function (layerA, layerB, nameA, nameB) {
5103  			return nameA < nameB ? -1 : (nameB < nameA ? 1 : 0);
5104  		}
5105  	},
5106
5107  	initialize: function (baseLayers, overlays, options) {
5108  		setOptions(this, options);
5109
5110  		this._layerControlInputs = [];
5111  		this._layers = [];
5112  		this._lastZIndex = 0;
5113  		this._handlingClick = false;
5114  		this._preventClick = false;
5115
5116  		for (var i in baseLayers) {
5117  			this._addLayer(baseLayers[i], i);
5118  		}
5119
5120  		for (i in overlays) {
5121  			this._addLayer(overlays[i], i, true);
5122  		}
5123  	},
5124
5125  	onAdd: function (map) {
5126  		this._initLayout();
5127  		this._update();
5128
5129  		this._map = map;
5130  		map.on('zoomend', this._checkDisabledLayers, this);
5131
5132  		for (var i = 0; i < this._layers.length; i++) {
5133  			this._layers[i].layer.on('add remove', this._onLayerChange, this);
5134  		}
5135
5136  		return this._container;
5137  	},
5138
5139  	addTo: function (map) {
5140  		Control.prototype.addTo.call(this, map);
5141  		// Trigger expand after Layers Control has been inserted into DOM so that is now has an actual height.
5142  		return this._expandIfNotCollapsed();
5143  	},
5144
5145  	onRemove: function () {
5146  		this._map.off('zoomend', this._checkDisabledLayers, this);
5147
5148  		for (var i = 0; i < this._layers.length; i++) {
5149  			this._layers[i].layer.off('add remove', this._onLayerChange, this);
5150  		}
5151  	},
5152
5153  	// @method addBaseLayer(layer: Layer, name: String): this
5154  	// Adds a base layer (radio button entry) with the given name to the control.
5155  	addBaseLayer: function (layer, name) {
5156  		this._addLayer(layer, name);
5157  		return (this._map) ? this._update() : this;
5158  	},
5159
5160  	// @method addOverlay(layer: Layer, name: String): this
5161  	// Adds an overlay (checkbox entry) with the given name to the control.
5162  	addOverlay: function (layer, name) {
5163  		this._addLayer(layer, name, true);
5164  		return (this._map) ? this._update() : this;
5165  	},
5166
5167  	// @method removeLayer(layer: Layer): this
5168  	// Remove the given layer from the control.
5169  	removeLayer: function (layer) {
5170  		layer.off('add remove', this._onLayerChange, this);
5171
5172  		var obj = this._getLayer(stamp(layer));
5173  		if (obj) {
5174  			this._layers.splice(this._layers.indexOf(obj), 1);
5175  		}
5176  		return (this._map) ? this._update() : this;
5177  	},
5178
5179  	// @method expand(): this
5180  	// Expand the control container if collapsed.
5181  	expand: function () {
5182  		addClass(this._container, 'leaflet-control-layers-expanded');
5183  		this._section.style.height = null;
5184  		var acceptableHeight = this._map.getSize().y - (this._container.offsetTop + 50);
5185  		if (acceptableHeight < this._section.clientHeight) {
5186  			addClass(this._section, 'leaflet-control-layers-scrollbar');
5187  			this._section.style.height = acceptableHeight + 'px';
5188  		} else {
5189  			removeClass(this._section, 'leaflet-control-layers-scrollbar');
5190  		}
5191  		this._checkDisabledLayers();
5192  		return this;
5193  	},
5194
5195  	// @method collapse(): this
5196  	// Collapse the control container if expanded.
5197  	collapse: function () {
5198  		removeClass(this._container, 'leaflet-control-layers-expanded');
5199  		return this;
5200  	},
5201
5202  	_initLayout: function () {
5203  		var className = 'leaflet-control-layers',
5204  		    container = this._container = create$1('div', className),
5205  		    collapsed = this.options.collapsed;
5206
5207  		// makes this work on IE touch devices by stopping it from firing a mouseout event when the touch is released
5208  		container.setAttribute('aria-haspopup', true);
5209
5210  		disableClickPropagation(container);
5211  		disableScrollPropagation(container);
5212
5213  		var section = this._section = create$1('section', className + '-list');
5214
5215  		if (collapsed) {
5216  			this._map.on('click', this.collapse, this);
5217
5218  			on(container, {
5219  				mouseenter: this._expandSafely,
5220  				mouseleave: this.collapse
5221  			}, this);
5222  		}
5223
5224  		var link = this._layersLink = create$1('a', className + '-toggle', container);
5225  		link.href = '#';
5226  		link.title = 'Layers';
5227  		link.setAttribute('role', 'button');
5228
5229  		on(link, {
5230  			keydown: function (e) {
5231  				if (e.keyCode === 13) {
5232  					this._expandSafely();
5233  				}
5234  			},
5235  			// Certain screen readers intercept the key event and instead send a click event
5236  			click: function (e) {
5237  				preventDefault(e);
5238  				this._expandSafely();
5239  			}
5240  		}, this);
5241
5242  		if (!collapsed) {
5243  			this.expand();
5244  		}
5245
5246  		this._baseLayersList = create$1('div', className + '-base', section);
5247  		this._separator = create$1('div', className + '-separator', section);
5248  		this._overlaysList = create$1('div', className + '-overlays', section);
5249
5250  		container.appendChild(section);
5251  	},
5252
5253  	_getLayer: function (id) {
5254  		for (var i = 0; i < this._layers.length; i++) {
5255
5256  			if (this._layers[i] && stamp(this._layers[i].layer) === id) {
5257  				return this._layers[i];
5258  			}
5259  		}
5260  	},
5261
5262  	_addLayer: function (layer, name, overlay) {
5263  		if (this._map) {
5264  			layer.on('add remove', this._onLayerChange, this);
5265  		}
5266
5267  		this._layers.push({
5268  			layer: layer,
5269  			name: name,
5270  			overlay: overlay
5271  		});
5272
5273  		if (this.options.sortLayers) {
5274  			this._layers.sort(bind(function (a, b) {
5275  				return this.options.sortFunction(a.layer, b.layer, a.name, b.name);
5276  			}, this));
5277  		}
5278
5279  		if (this.options.autoZIndex && layer.setZIndex) {
5280  			this._lastZIndex++;
5281  			layer.setZIndex(this._lastZIndex);
5282  		}
5283
5284  		this._expandIfNotCollapsed();
5285  	},
5286
5287  	_update: function () {
5288  		if (!this._container) { return this; }
5289
5290  		empty(this._baseLayersList);
5291  		empty(this._overlaysList);
5292
5293  		this._layerControlInputs = [];
5294  		var baseLayersPresent, overlaysPresent, i, obj, baseLayersCount = 0;
5295
5296  		for (i = 0; i < this._layers.length; i++) {
5297  			obj = this._layers[i];
5298  			this._addItem(obj);
5299  			overlaysPresent = overlaysPresent || obj.overlay;
5300  			baseLayersPresent = baseLayersPresent || !obj.overlay;
5301  			baseLayersCount += !obj.overlay ? 1 : 0;
5302  		}
5303
5304  		// Hide base layers section if there's only one layer.
5305  		if (this.options.hideSingleBase) {
5306  			baseLayersPresent = baseLayersPresent && baseLayersCount > 1;
5307  			this._baseLayersList.style.display = baseLayersPresent ? '' : 'none';
5308  		}
5309
5310  		this._separator.style.display = overlaysPresent && baseLayersPresent ? '' : 'none';
5311
5312  		return this;
5313  	},
5314
5315  	_onLayerChange: function (e) {
5316  		if (!this._handlingClick) {
5317  			this._update();
5318  		}
5319
5320  		var obj = this._getLayer(stamp(e.target));
5321
5322  		// @namespace Map
5323  		// @section Layer events
5324  		// @event baselayerchange: LayersControlEvent
5325  		// Fired when the base layer is changed through the [layers control](#control-layers).
5326  		// @event overlayadd: LayersControlEvent
5327  		// Fired when an overlay is selected through the [layers control](#control-layers).
5328  		// @event overlayremove: LayersControlEvent
5329  		// Fired when an overlay is deselected through the [layers control](#control-layers).
5330  		// @namespace Control.Layers
5331  		var type = obj.overlay ?
5332  			(e.type === 'add' ? 'overlayadd' : 'overlayremove') :
5333  			(e.type === 'add' ? 'baselayerchange' : null);
5334
5335  		if (type) {
5336  			this._map.fire(type, obj);
5337  		}
5338  	},
5339
5340  	// IE7 bugs out if you create a radio dynamically, so you have to do it this hacky way (see https://stackoverflow.com/a/119079)
5341  	_createRadioElement: function (name, checked) {
5342
5343  		var radioHtml = '<input type="radio" class="leaflet-control-layers-selector" name="' +
5344  				name + '"' + (checked ? ' checked="checked"' : '') + '/>';
5345
5346  		var radioFragment = document.createElement('div');
5347  		radioFragment.innerHTML = radioHtml;
5348
5349  		return radioFragment.firstChild;
5350  	},
5351
5352  	_addItem: function (obj) {
5353  		var label = document.createElement('label'),
5354  		    checked = this._map.hasLayer(obj.layer),
5355  		    input;
5356
5357  		if (obj.overlay) {
5358  			input = document.createElement('input');
5359  			input.type = 'checkbox';
5360  			input.className = 'leaflet-control-layers-selector';
5361  			input.defaultChecked = checked;
5362  		} else {
5363  			input = this._createRadioElement('leaflet-base-layers_' + stamp(this), checked);
5364  		}
5365
5366  		this._layerControlInputs.push(input);
5367  		input.layerId = stamp(obj.layer);
5368
5369  		on(input, 'click', this._onInputClick, this);
5370
5371  		var name = document.createElement('span');
5372  		name.innerHTML = ' ' + obj.name;
5373
5374  		// Helps from preventing layer control flicker when checkboxes are disabled
5375  		// https://github.com/Leaflet/Leaflet/issues/2771
5376  		var holder = document.createElement('span');
5377
5378  		label.appendChild(holder);
5379  		holder.appendChild(input);
5380  		holder.appendChild(name);
5381
5382  		var container = obj.overlay ? this._overlaysList : this._baseLayersList;
5383  		container.appendChild(label);
5384
5385  		this._checkDisabledLayers();
5386  		return label;
5387  	},
5388
5389  	_onInputClick: function () {
5390  		// expanding the control on mobile with a click can cause adding a layer - we don't want this
5391  		if (this._preventClick) {
5392  			return;
5393  		}
5394
5395  		var inputs = this._layerControlInputs,
5396  		    input, layer;
5397  		var addedLayers = [],
5398  		    removedLayers = [];
5399
5400  		this._handlingClick = true;
5401
5402  		for (var i = inputs.length - 1; i >= 0; i--) {
5403  			input = inputs[i];
5404  			layer = this._getLayer(input.layerId).layer;
5405
5406  			if (input.checked) {
5407  				addedLayers.push(layer);
5408  			} else if (!input.checked) {
5409  				removedLayers.push(layer);
5410  			}
5411  		}
5412
5413  		// Bugfix issue 2318: Should remove all old layers before readding new ones
5414  		for (i = 0; i < removedLayers.length; i++) {
5415  			if (this._map.hasLayer(removedLayers[i])) {
5416  				this._map.removeLayer(removedLayers[i]);
5417  			}
5418  		}
5419  		for (i = 0; i < addedLayers.length; i++) {
5420  			if (!this._map.hasLayer(addedLayers[i])) {
5421  				this._map.addLayer(addedLayers[i]);
5422  			}
5423  		}
5424
5425  		this._handlingClick = false;
5426
5427  		this._refocusOnMap();
5428  	},
5429
5430  	_checkDisabledLayers: function () {
5431  		var inputs = this._layerControlInputs,
5432  		    input,
5433  		    layer,
5434  		    zoom = this._map.getZoom();
5435
5436  		for (var i = inputs.length - 1; i >= 0; i--) {
5437  			input = inputs[i];
5438  			layer = this._getLayer(input.layerId).layer;
5439  			input.disabled = (layer.options.minZoom !== undefined && zoom < layer.options.minZoom) ||
5440  			                 (layer.options.maxZoom !== undefined && zoom > layer.options.maxZoom);
5441
5442  		}
5443  	},
5444
5445  	_expandIfNotCollapsed: function () {
5446  		if (this._map && !this.options.collapsed) {
5447  			this.expand();
5448  		}
5449  		return this;
5450  	},
5451
5452  	_expandSafely: function () {
5453  		var section = this._section;
5454  		this._preventClick = true;
5455  		on(section, 'click', preventDefault);
5456  		this.expand();
5457  		var that = this;
5458  		setTimeout(function () {
5459  			off(section, 'click', preventDefault);
5460  			that._preventClick = false;
5461  		});
5462  	}
5463
5464  });
5465
5466
5467  // @factory L.control.layers(baselayers?: Object, overlays?: Object, options?: Control.Layers options)
5468  // Creates a layers control with the given layers. Base layers will be switched with radio buttons, while overlays will be switched with checkboxes. Note that all base layers should be passed in the base layers object, but only one should be added to the map during map instantiation.
5469  var layers = function (baseLayers, overlays, options) {
5470  	return new Layers(baseLayers, overlays, options);
5471  };
5472
5473  /*
5474   * @class Control.Zoom
5475   * @aka L.Control.Zoom
5476   * @inherits Control
5477   *
5478   * 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`.
5479   */
5480
5481  var Zoom = Control.extend({
5482  	// @section
5483  	// @aka Control.Zoom options
5484  	options: {
5485  		position: 'topleft',
5486
5487  		// @option zoomInText: String = '<span aria-hidden="true">+</span>'
5488  		// The text set on the 'zoom in' button.
5489  		zoomInText: '<span aria-hidden="true">+</span>',
5490
5491  		// @option zoomInTitle: String = 'Zoom in'
5492  		// The title set on the 'zoom in' button.
5493  		zoomInTitle: 'Zoom in',
5494
5495  		// @option zoomOutText: String = '<span aria-hidden="true">&#x2212;</span>'
5496  		// The text set on the 'zoom out' button.
5497  		zoomOutText: '<span aria-hidden="true">&#x2212;</span>',
5498
5499  		// @option zoomOutTitle: String = 'Zoom out'
5500  		// The title set on the 'zoom out' button.
5501  		zoomOutTitle: 'Zoom out'
5502  	},
5503
5504  	onAdd: function (map) {
5505  		var zoomName = 'leaflet-control-zoom',
5506  		    container = create$1('div', zoomName + ' leaflet-bar'),
5507  		    options = this.options;
5508
5509  		this._zoomInButton  = this._createButton(options.zoomInText, options.zoomInTitle,
5510  		        zoomName + '-in',  container, this._zoomIn);
5511  		this._zoomOutButton = this._createButton(options.zoomOutText, options.zoomOutTitle,
5512  		        zoomName + '-out', container, this._zoomOut);
5513
5514  		this._updateDisabled();
5515  		map.on('zoomend zoomlevelschange', this._updateDisabled, this);
5516
5517  		return container;
5518  	},
5519
5520  	onRemove: function (map) {
5521  		map.off('zoomend zoomlevelschange', this._updateDisabled, this);
5522  	},
5523
5524  	disable: function () {
5525  		this._disabled = true;
5526  		this._updateDisabled();
5527  		return this;
5528  	},
5529
5530  	enable: function () {
5531  		this._disabled = false;
5532  		this._updateDisabled();
5533  		return this;
5534  	},
5535
5536  	_zoomIn: function (e) {
5537  		if (!this._disabled && this._map._zoom < this._map.getMaxZoom()) {
5538  			this._map.zoomIn(this._map.options.zoomDelta * (e.shiftKey ? 3 : 1));
5539  		}
5540  	},
5541
5542  	_zoomOut: function (e) {
5543  		if (!this._disabled && this._map._zoom > this._map.getMinZoom()) {
5544  			this._map.zoomOut(this._map.options.zoomDelta * (e.shiftKey ? 3 : 1));
5545  		}
5546  	},
5547
5548  	_createButton: function (html, title, className, container, fn) {
5549  		var link = create$1('a', className, container);
5550  		link.innerHTML = html;
5551  		link.href = '#';
5552  		link.title = title;
5553
5554  		/*
5555  		 * Will force screen readers like VoiceOver to read this as "Zoom in - button"
5556  		 */
5557  		link.setAttribute('role', 'button');
5558  		link.setAttribute('aria-label', title);
5559
5560  		disableClickPropagation(link);
5561  		on(link, 'click', stop);
5562  		on(link, 'click', fn, this);
5563  		on(link, 'click', this._refocusOnMap, this);
5564
5565  		return link;
5566  	},
5567
5568  	_updateDisabled: function () {
5569  		var map = this._map,
5570  		    className = 'leaflet-disabled';
5571
5572  		removeClass(this._zoomInButton, className);
5573  		removeClass(this._zoomOutButton, className);
5574  		this._zoomInButton.setAttribute('aria-disabled', 'false');
5575  		this._zoomOutButton.setAttribute('aria-disabled', 'false');
5576
5577  		if (this._disabled || map._zoom === map.getMinZoom()) {
5578  			addClass(this._zoomOutButton, className);
5579  			this._zoomOutButton.setAttribute('aria-disabled', 'true');
5580  		}
5581  		if (this._disabled || map._zoom === map.getMaxZoom()) {
5582  			addClass(this._zoomInButton, className);
5583  			this._zoomInButton.setAttribute('aria-disabled', 'true');
5584  		}
5585  	}
5586  });
5587
5588  // @namespace Map
5589  // @section Control options
5590  // @option zoomControl: Boolean = true
5591  // Whether a [zoom control](#control-zoom) is added to the map by default.
5592  Map.mergeOptions({
5593  	zoomControl: true
5594  });
5595
5596  Map.addInitHook(function () {
5597  	if (this.options.zoomControl) {
5598  		// @section Controls
5599  		// @property zoomControl: Control.Zoom
5600  		// The default zoom control (only available if the
5601  		// [`zoomControl` option](#map-zoomcontrol) was `true` when creating the map).
5602  		this.zoomControl = new Zoom();
5603  		this.addControl(this.zoomControl);
5604  	}
5605  });
5606
5607  // @namespace Control.Zoom
5608  // @factory L.control.zoom(options: Control.Zoom options)
5609  // Creates a zoom control
5610  var zoom = function (options) {
5611  	return new Zoom(options);
5612  };
5613
5614  /*
5615   * @class Control.Scale
5616   * @aka L.Control.Scale
5617   * @inherits Control
5618   *
5619   * 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`.
5620   *
5621   * @example
5622   *
5623   * ```js
5624   * L.control.scale().addTo(map);
5625   * ```
5626   */
5627
5628  var Scale = Control.extend({
5629  	// @section
5630  	// @aka Control.Scale options
5631  	options: {
5632  		position: 'bottomleft',
5633
5634  		// @option maxWidth: Number = 100
5635  		// Maximum width of the control in pixels. The width is set dynamically to show round values (e.g. 100, 200, 500).
5636  		maxWidth: 100,
5637
5638  		// @option metric: Boolean = True
5639  		// Whether to show the metric scale line (m/km).
5640  		metric: true,
5641
5642  		// @option imperial: Boolean = True
5643  		// Whether to show the imperial scale line (mi/ft).
5644  		imperial: true
5645
5646  		// @option updateWhenIdle: Boolean = false
5647  		// If `true`, the control is updated on [`moveend`](#map-moveend), otherwise it's always up-to-date (updated on [`move`](#map-move)).
5648  	},
5649
5650  	onAdd: function (map) {
5651  		var className = 'leaflet-control-scale',
5652  		    container = create$1('div', className),
5653  		    options = this.options;
5654
5655  		this._addScales(options, className + '-line', container);
5656
5657  		map.on(options.updateWhenIdle ? 'moveend' : 'move', this._update, this);
5658  		map.whenReady(this._update, this);
5659
5660  		return container;
5661  	},
5662
5663  	onRemove: function (map) {
5664  		map.off(this.options.updateWhenIdle ? 'moveend' : 'move', this._update, this);
5665  	},
5666
5667  	_addScales: function (options, className, container) {
5668  		if (options.metric) {
5669  			this._mScale = create$1('div', className, container);
5670  		}
5671  		if (options.imperial) {
5672  			this._iScale = create$1('div', className, container);
5673  		}
5674  	},
5675
5676  	_update: function () {
5677  		var map = this._map,
5678  		    y = map.getSize().y / 2;
5679
5680  		var maxMeters = map.distance(
5681  			map.containerPointToLatLng([0, y]),
5682  			map.containerPointToLatLng([this.options.maxWidth, y]));
5683
5684  		this._updateScales(maxMeters);
5685  	},
5686
5687  	_updateScales: function (maxMeters) {
5688  		if (this.options.metric && maxMeters) {
5689  			this._updateMetric(maxMeters);
5690  		}
5691  		if (this.options.imperial && maxMeters) {
5692  			this._updateImperial(maxMeters);
5693  		}
5694  	},
5695
5696  	_updateMetric: function (maxMeters) {
5697  		var meters = this._getRoundNum(maxMeters),
5698  		    label = meters < 1000 ? meters + ' m' : (meters / 1000) + ' km';
5699
5700  		this._updateScale(this._mScale, label, meters / maxMeters);
5701  	},
5702
5703  	_updateImperial: function (maxMeters) {
5704  		var maxFeet = maxMeters * 3.2808399,
5705  		    maxMiles, miles, feet;
5706
5707  		if (maxFeet > 5280) {
5708  			maxMiles = maxFeet / 5280;
5709  			miles = this._getRoundNum(maxMiles);
5710  			this._updateScale(this._iScale, miles + ' mi', miles / maxMiles);
5711
5712  		} else {
5713  			feet = this._getRoundNum(maxFeet);
5714  			this._updateScale(this._iScale, feet + ' ft', feet / maxFeet);
5715  		}
5716  	},
5717
5718  	_updateScale: function (scale, text, ratio) {
5719  		scale.style.width = Math.round(this.options.maxWidth * ratio) + 'px';
5720  		scale.innerHTML = text;
5721  	},
5722
5723  	_getRoundNum: function (num) {
5724  		var pow10 = Math.pow(10, (Math.floor(num) + '').length - 1),
5725  		    d = num / pow10;
5726
5727  		d = d >= 10 ? 10 :
5728  		    d >= 5 ? 5 :
5729  		    d >= 3 ? 3 :
5730  		    d >= 2 ? 2 : 1;
5731
5732  		return pow10 * d;
5733  	}
5734  });
5735
5736
5737  // @factory L.control.scale(options?: Control.Scale options)
5738  // Creates an scale control with the given options.
5739  var scale = function (options) {
5740  	return new Scale(options);
5741  };
5742
5743  var ukrainianFlag = '<svg aria-hidden="true" xmlns="http://www.w3.org/2000/svg" width="12" height="8" viewBox="0 0 12 8" class="leaflet-attribution-flag"><path fill="#4C7BE1" d="M0 0h12v4H0z"/><path fill="#FFD500" d="M0 4h12v3H0z"/><path fill="#E0BC00" d="M0 7h12v1H0z"/></svg>';
5744
5745
5746  /*
5747   * @class Control.Attribution
5748   * @aka L.Control.Attribution
5749   * @inherits Control
5750   *
5751   * 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.
5752   */
5753
5754  var Attribution = Control.extend({
5755  	// @section
5756  	// @aka Control.Attribution options
5757  	options: {
5758  		position: 'bottomright',
5759
5760  		// @option prefix: String|false = 'Leaflet'
5761  		// The HTML text shown before the attributions. Pass `false` to disable.
5762  		prefix: '<a href="https://leafletjs.com" title="A JavaScript library for interactive maps">' + (Browser.inlineSvg ? ukrainianFlag + ' ' : '') + 'Leaflet</a>'
5763  	},
5764
5765  	initialize: function (options) {
5766  		setOptions(this, options);
5767
5768  		this._attributions = {};
5769  	},
5770
5771  	onAdd: function (map) {
5772  		map.attributionControl = this;
5773  		this._container = create$1('div', 'leaflet-control-attribution');
5774  		disableClickPropagation(this._container);
5775
5776  		// TODO ugly, refactor
5777  		for (var i in map._layers) {
5778  			if (map._layers[i].getAttribution) {
5779  				this.addAttribution(map._layers[i].getAttribution());
5780  			}
5781  		}
5782
5783  		this._update();
5784
5785  		map.on('layeradd', this._addAttribution, this);
5786
5787  		return this._container;
5788  	},
5789
5790  	onRemove: function (map) {
5791  		map.off('layeradd', this._addAttribution, this);
5792  	},
5793
5794  	_addAttribution: function (ev) {
5795  		if (ev.layer.getAttribution) {
5796  			this.addAttribution(ev.layer.getAttribution());
5797  			ev.layer.once('remove', function () {
5798  				this.removeAttribution(ev.layer.getAttribution());
5799  			}, this);
5800  		}
5801  	},
5802
5803  	// @method setPrefix(prefix: String|false): this
5804  	// The HTML text shown before the attributions. Pass `false` to disable.
5805  	setPrefix: function (prefix) {
5806  		this.options.prefix = prefix;
5807  		this._update();
5808  		return this;
5809  	},
5810
5811  	// @method addAttribution(text: String): this
5812  	// Adds an attribution text (e.g. `'&copy; OpenStreetMap contributors'`).
5813  	addAttribution: function (text) {
5814  		if (!text) { return this; }
5815
5816  		if (!this._attributions[text]) {
5817  			this._attributions[text] = 0;
5818  		}
5819  		this._attributions[text]++;
5820
5821  		this._update();
5822
5823  		return this;
5824  	},
5825
5826  	// @method removeAttribution(text: String): this
5827  	// Removes an attribution text.
5828  	removeAttribution: function (text) {
5829  		if (!text) { return this; }
5830
5831  		if (this._attributions[text]) {
5832  			this._attributions[text]--;
5833  			this._update();
5834  		}
5835
5836  		return this;
5837  	},
5838
5839  	_update: function () {
5840  		if (!this._map) { return; }
5841
5842  		var attribs = [];
5843
5844  		for (var i in this._attributions) {
5845  			if (this._attributions[i]) {
5846  				attribs.push(i);
5847  			}
5848  		}
5849
5850  		var prefixAndAttribs = [];
5851
5852  		if (this.options.prefix) {
5853  			prefixAndAttribs.push(this.options.prefix);
5854  		}
5855  		if (attribs.length) {
5856  			prefixAndAttribs.push(attribs.join(', '));
5857  		}
5858
5859  		this._container.innerHTML = prefixAndAttribs.join(' <span aria-hidden="true">|</span> ');
5860  	}
5861  });
5862
5863  // @namespace Map
5864  // @section Control options
5865  // @option attributionControl: Boolean = true
5866  // Whether a [attribution control](#control-attribution) is added to the map by default.
5867  Map.mergeOptions({
5868  	attributionControl: true
5869  });
5870
5871  Map.addInitHook(function () {
5872  	if (this.options.attributionControl) {
5873  		new Attribution().addTo(this);
5874  	}
5875  });
5876
5877  // @namespace Control.Attribution
5878  // @factory L.control.attribution(options: Control.Attribution options)
5879  // Creates an attribution control.
5880  var attribution = function (options) {
5881  	return new Attribution(options);
5882  };
5883
5884  Control.Layers = Layers;
5885  Control.Zoom = Zoom;
5886  Control.Scale = Scale;
5887  Control.Attribution = Attribution;
5888
5889  control.layers = layers;
5890  control.zoom = zoom;
5891  control.scale = scale;
5892  control.attribution = attribution;
5893
5894  /*
5895  	L.Handler is a base class for handler classes that are used internally to inject
5896  	interaction features like dragging to classes like Map and Marker.
5897  */
5898
5899  // @class Handler
5900  // @aka L.Handler
5901  // Abstract class for map interaction handlers
5902
5903  var Handler = Class.extend({
5904  	initialize: function (map) {
5905  		this._map = map;
5906  	},
5907
5908  	// @method enable(): this
5909  	// Enables the handler
5910  	enable: function () {
5911  		if (this._enabled) { return this; }
5912
5913  		this._enabled = true;
5914  		this.addHooks();
5915  		return this;
5916  	},
5917
5918  	// @method disable(): this
5919  	// Disables the handler
5920  	disable: function () {
5921  		if (!this._enabled) { return this; }
5922
5923  		this._enabled = false;
5924  		this.removeHooks();
5925  		return this;
5926  	},
5927
5928  	// @method enabled(): Boolean
5929  	// Returns `true` if the handler is enabled
5930  	enabled: function () {
5931  		return !!this._enabled;
5932  	}
5933
5934  	// @section Extension methods
5935  	// Classes inheriting from `Handler` must implement the two following methods:
5936  	// @method addHooks()
5937  	// Called when the handler is enabled, should add event hooks.
5938  	// @method removeHooks()
5939  	// Called when the handler is disabled, should remove the event hooks added previously.
5940  });
5941
5942  // @section There is static function which can be called without instantiating L.Handler:
5943  // @function addTo(map: Map, name: String): this
5944  // Adds a new Handler to the given map with the given name.
5945  Handler.addTo = function (map, name) {
5946  	map.addHandler(name, this);
5947  	return this;
5948  };
5949
5950  var Mixin = {Events: Events};
5951
5952  /*
5953   * @class Draggable
5954   * @aka L.Draggable
5955   * @inherits Evented
5956   *
5957   * A class for making DOM elements draggable (including touch support).
5958   * Used internally for map and marker dragging. Only works for elements
5959   * that were positioned with [`L.DomUtil.setPosition`](#domutil-setposition).
5960   *
5961   * @example
5962   * ```js
5963   * var draggable = new L.Draggable(elementToDrag);
5964   * draggable.enable();
5965   * ```
5966   */
5967
5968  var START = Browser.touch ? 'touchstart mousedown' : 'mousedown';
5969
5970  var Draggable = Evented.extend({
5971
5972  	options: {
5973  		// @section
5974  		// @aka Draggable options
5975  		// @option clickTolerance: Number = 3
5976  		// The max number of pixels a user can shift the mouse pointer during a click
5977  		// for it to be considered a valid click (as opposed to a mouse drag).
5978  		clickTolerance: 3
5979  	},
5980
5981  	// @constructor L.Draggable(el: HTMLElement, dragHandle?: HTMLElement, preventOutline?: Boolean, options?: Draggable options)
5982  	// Creates a `Draggable` object for moving `el` when you start dragging the `dragHandle` element (equals `el` itself by default).
5983  	initialize: function (element, dragStartTarget, preventOutline, options) {
5984  		setOptions(this, options);
5985
5986  		this._element = element;
5987  		this._dragStartTarget = dragStartTarget || element;
5988  		this._preventOutline = preventOutline;
5989  	},
5990
5991  	// @method enable()
5992  	// Enables the dragging ability
5993  	enable: function () {
5994  		if (this._enabled) { return; }
5995
5996  		on(this._dragStartTarget, START, this._onDown, this);
5997
5998  		this._enabled = true;
5999  	},
6000
6001  	// @method disable()
6002  	// Disables the dragging ability
6003  	disable: function () {
6004  		if (!this._enabled) { return; }
6005
6006  		// If we're currently dragging this draggable,
6007  		// disabling it counts as first ending the drag.
6008  		if (Draggable._dragging === this) {
6009  			this.finishDrag(true);
6010  		}
6011
6012  		off(this._dragStartTarget, START, this._onDown, this);
6013
6014  		this._enabled = false;
6015  		this._moved = false;
6016  	},
6017
6018  	_onDown: function (e) {
6019  		// Ignore the event if disabled; this happens in IE11
6020  		// under some circumstances, see #3666.
6021  		if (!this._enabled) { return; }
6022
6023  		this._moved = false;
6024
6025  		if (hasClass(this._element, 'leaflet-zoom-anim')) { return; }
6026
6027  		if (e.touches && e.touches.length !== 1) {
6028  			// Finish dragging to avoid conflict with touchZoom
6029  			if (Draggable._dragging === this) {
6030  				this.finishDrag();
6031  			}
6032  			return;
6033  		}
6034
6035  		if (Draggable._dragging || e.shiftKey || ((e.which !== 1) && (e.button !== 1) && !e.touches)) { return; }
6036  		Draggable._dragging = this;  // Prevent dragging multiple objects at once.
6037
6038  		if (this._preventOutline) {
6039  			preventOutline(this._element);
6040  		}
6041
6042  		disableImageDrag();
6043  		disableTextSelection();
6044
6045  		if (this._moving) { return; }
6046
6047  		// @event down: Event
6048  		// Fired when a drag is about to start.
6049  		this.fire('down');
6050
6051  		var first = e.touches ? e.touches[0] : e,
6052  		    sizedParent = getSizedParentNode(this._element);
6053
6054  		this._startPoint = new Point(first.clientX, first.clientY);
6055  		this._startPos = getPosition(this._element);
6056
6057  		// Cache the scale, so that we can continuously compensate for it during drag (_onMove).
6058  		this._parentScale = getScale(sizedParent);
6059
6060  		var mouseevent = e.type === 'mousedown';
6061  		on(document, mouseevent ? 'mousemove' : 'touchmove', this._onMove, this);
6062  		on(document, mouseevent ? 'mouseup' : 'touchend touchcancel', this._onUp, this);
6063  	},
6064
6065  	_onMove: function (e) {
6066  		// Ignore the event if disabled; this happens in IE11
6067  		// under some circumstances, see #3666.
6068  		if (!this._enabled) { return; }
6069
6070  		if (e.touches && e.touches.length > 1) {
6071  			this._moved = true;
6072  			return;
6073  		}
6074
6075  		var first = (e.touches && e.touches.length === 1 ? e.touches[0] : e),
6076  		    offset = new Point(first.clientX, first.clientY)._subtract(this._startPoint);
6077
6078  		if (!offset.x && !offset.y) { return; }
6079  		if (Math.abs(offset.x) + Math.abs(offset.y) < this.options.clickTolerance) { return; }
6080
6081  		// We assume that the parent container's position, border and scale do not change for the duration of the drag.
6082  		// Therefore there is no need to account for the position and border (they are eliminated by the subtraction)
6083  		// and we can use the cached value for the scale.
6084  		offset.x /= this._parentScale.x;
6085  		offset.y /= this._parentScale.y;
6086
6087  		preventDefault(e);
6088
6089  		if (!this._moved) {
6090  			// @event dragstart: Event
6091  			// Fired when a drag starts
6092  			this.fire('dragstart');
6093
6094  			this._moved = true;
6095
6096  			addClass(document.body, 'leaflet-dragging');
6097
6098  			this._lastTarget = e.target || e.srcElement;
6099  			// IE and Edge do not give the <use> element, so fetch it
6100  			// if necessary
6101  			if (window.SVGElementInstance && this._lastTarget instanceof window.SVGElementInstance) {
6102  				this._lastTarget = this._lastTarget.correspondingUseElement;
6103  			}
6104  			addClass(this._lastTarget, 'leaflet-drag-target');
6105  		}
6106
6107  		this._newPos = this._startPos.add(offset);
6108  		this._moving = true;
6109
6110  		this._lastEvent = e;
6111  		this._updatePosition();
6112  	},
6113
6114  	_updatePosition: function () {
6115  		var e = {originalEvent: this._lastEvent};
6116
6117  		// @event predrag: Event
6118  		// Fired continuously during dragging *before* each corresponding
6119  		// update of the element's position.
6120  		this.fire('predrag', e);
6121  		setPosition(this._element, this._newPos);
6122
6123  		// @event drag: Event
6124  		// Fired continuously during dragging.
6125  		this.fire('drag', e);
6126  	},
6127
6128  	_onUp: function () {
6129  		// Ignore the event if disabled; this happens in IE11
6130  		// under some circumstances, see #3666.
6131  		if (!this._enabled) { return; }
6132  		this.finishDrag();
6133  	},
6134
6135  	finishDrag: function (noInertia) {
6136  		removeClass(document.body, 'leaflet-dragging');
6137
6138  		if (this._lastTarget) {
6139  			removeClass(this._lastTarget, 'leaflet-drag-target');
6140  			this._lastTarget = null;
6141  		}
6142
6143  		off(document, 'mousemove touchmove', this._onMove, this);
6144  		off(document, 'mouseup touchend touchcancel', this._onUp, this);
6145
6146  		enableImageDrag();
6147  		enableTextSelection();
6148
6149  		var fireDragend = this._moved && this._moving;
6150
6151  		this._moving = false;
6152  		Draggable._dragging = false;
6153
6154  		if (fireDragend) {
6155  			// @event dragend: DragEndEvent
6156  			// Fired when the drag ends.
6157  			this.fire('dragend', {
6158  				noInertia: noInertia,
6159  				distance: this._newPos.distanceTo(this._startPos)
6160  			});
6161  		}
6162  	}
6163
6164  });
6165
6166  /*
6167   * @namespace PolyUtil
6168   * Various utility functions for polygon geometries.
6169   */
6170
6171  /* @function clipPolygon(points: Point[], bounds: Bounds, round?: Boolean): Point[]
6172   * Clips the polygon geometry defined by the given `points` by the given bounds (using the [Sutherland-Hodgman algorithm](https://en.wikipedia.org/wiki/Sutherland%E2%80%93Hodgman_algorithm)).
6173   * Used by Leaflet to only show polygon points that are on the screen or near, increasing
6174   * performance. Note that polygon points needs different algorithm for clipping
6175   * than polyline, so there's a separate method for it.
6176   */
6177  function clipPolygon(points, bounds, round) {
6178  	var clippedPoints,
6179  	    edges = [1, 4, 2, 8],
6180  	    i, j, k,
6181  	    a, b,
6182  	    len, edge, p;
6183
6184  	for (i = 0, len = points.length; i < len; i++) {
6185  		points[i]._code = _getBitCode(points[i], bounds);
6186  	}
6187
6188  	// for each edge (left, bottom, right, top)
6189  	for (k = 0; k < 4; k++) {
6190  		edge = edges[k];
6191  		clippedPoints = [];
6192
6193  		for (i = 0, len = points.length, j = len - 1; i < len; j = i++) {
6194  			a = points[i];
6195  			b = points[j];
6196
6197  			// if a is inside the clip window
6198  			if (!(a._code & edge)) {
6199  				// if b is outside the clip window (a->b goes out of screen)
6200  				if (b._code & edge) {
6201  					p = _getEdgeIntersection(b, a, edge, bounds, round);
6202  					p._code = _getBitCode(p, bounds);
6203  					clippedPoints.push(p);
6204  				}
6205  				clippedPoints.push(a);
6206
6207  			// else if b is inside the clip window (a->b enters the screen)
6208  			} else if (!(b._code & edge)) {
6209  				p = _getEdgeIntersection(b, a, edge, bounds, round);
6210  				p._code = _getBitCode(p, bounds);
6211  				clippedPoints.push(p);
6212  			}
6213  		}
6214  		points = clippedPoints;
6215  	}
6216
6217  	return points;
6218  }
6219
6220  /* @function polygonCenter(latlngs: LatLng[], crs: CRS): LatLng
6221   * Returns the center ([centroid](http://en.wikipedia.org/wiki/Centroid)) of the passed LatLngs (first ring) from a polygon.
6222   */
6223  function polygonCenter(latlngs, crs) {
6224  	var i, j, p1, p2, f, area, x, y, center;
6225
6226  	if (!latlngs || latlngs.length === 0) {
6227  		throw new Error('latlngs not passed');
6228  	}
6229
6230  	if (!isFlat(latlngs)) {
6231  		console.warn('latlngs are not flat! Only the first ring will be used');
6232  		latlngs = latlngs[0];
6233  	}
6234
6235  	var centroidLatLng = toLatLng([0, 0]);
6236
6237  	var bounds = toLatLngBounds(latlngs);
6238  	var areaBounds = bounds.getNorthWest().distanceTo(bounds.getSouthWest()) * bounds.getNorthEast().distanceTo(bounds.getNorthWest());
6239  	// tests showed that below 1700 rounding errors are happening
6240  	if (areaBounds < 1700) {
6241  		// getting a inexact center, to move the latlngs near to [0, 0] to prevent rounding errors
6242  		centroidLatLng = centroid(latlngs);
6243  	}
6244
6245  	var len = latlngs.length;
6246  	var points = [];
6247  	for (i = 0; i < len; i++) {
6248  		var latlng = toLatLng(latlngs[i]);
6249  		points.push(crs.project(toLatLng([latlng.lat - centroidLatLng.lat, latlng.lng - centroidLatLng.lng])));
6250  	}
6251
6252  	area = x = y = 0;
6253
6254  	// polygon centroid algorithm;
6255  	for (i = 0, j = len - 1; i < len; j = i++) {
6256  		p1 = points[i];
6257  		p2 = points[j];
6258
6259  		f = p1.y * p2.x - p2.y * p1.x;
6260  		x += (p1.x + p2.x) * f;
6261  		y += (p1.y + p2.y) * f;
6262  		area += f * 3;
6263  	}
6264
6265  	if (area === 0) {
6266  		// Polygon is so small that all points are on same pixel.
6267  		center = points[0];
6268  	} else {
6269  		center = [x / area, y / area];
6270  	}
6271
6272  	var latlngCenter = crs.unproject(toPoint(center));
6273  	return toLatLng([latlngCenter.lat + centroidLatLng.lat, latlngCenter.lng + centroidLatLng.lng]);
6274  }
6275
6276  /* @function centroid(latlngs: LatLng[]): LatLng
6277   * Returns the 'center of mass' of the passed LatLngs.
6278   */
6279  function centroid(coords) {
6280  	var latSum = 0;
6281  	var lngSum = 0;
6282  	var len = 0;
6283  	for (var i = 0; i < coords.length; i++) {
6284  		var latlng = toLatLng(coords[i]);
6285  		latSum += latlng.lat;
6286  		lngSum += latlng.lng;
6287  		len++;
6288  	}
6289  	return toLatLng([latSum / len, lngSum / len]);
6290  }
6291
6292  var PolyUtil = {
6293    __proto__: null,
6294    clipPolygon: clipPolygon,
6295    polygonCenter: polygonCenter,
6296    centroid: centroid
6297  };
6298
6299  /*
6300   * @namespace LineUtil
6301   *
6302   * Various utility functions for polyline points processing, used by Leaflet internally to make polylines lightning-fast.
6303   */
6304
6305  // Simplify polyline with vertex reduction and Douglas-Peucker simplification.
6306  // Improves rendering performance dramatically by lessening the number of points to draw.
6307
6308  // @function simplify(points: Point[], tolerance: Number): Point[]
6309  // Dramatically reduces the number of points in a polyline while retaining
6310  // its shape and returns a new array of simplified points, using the
6311  // [Ramer-Douglas-Peucker algorithm](https://en.wikipedia.org/wiki/Ramer-Douglas-Peucker_algorithm).
6312  // Used for a huge performance boost when processing/displaying Leaflet polylines for
6313  // each zoom level and also reducing visual noise. tolerance affects the amount of
6314  // simplification (lesser value means higher quality but slower and with more points).
6315  // Also released as a separated micro-library [Simplify.js](https://mourner.github.io/simplify-js/).
6316  function simplify(points, tolerance) {
6317  	if (!tolerance || !points.length) {
6318  		return points.slice();
6319  	}
6320
6321  	var sqTolerance = tolerance * tolerance;
6322
6323  	    // stage 1: vertex reduction
6324  	    points = _reducePoints(points, sqTolerance);
6325
6326  	    // stage 2: Douglas-Peucker simplification
6327  	    points = _simplifyDP(points, sqTolerance);
6328
6329  	return points;
6330  }
6331
6332  // @function pointToSegmentDistance(p: Point, p1: Point, p2: Point): Number
6333  // Returns the distance between point `p` and segment `p1` to `p2`.
6334  function pointToSegmentDistance(p, p1, p2) {
6335  	return Math.sqrt(_sqClosestPointOnSegment(p, p1, p2, true));
6336  }
6337
6338  // @function closestPointOnSegment(p: Point, p1: Point, p2: Point): Number
6339  // Returns the closest point from a point `p` on a segment `p1` to `p2`.
6340  function closestPointOnSegment(p, p1, p2) {
6341  	return _sqClosestPointOnSegment(p, p1, p2);
6342  }
6343
6344  // Ramer-Douglas-Peucker simplification, see https://en.wikipedia.org/wiki/Ramer-Douglas-Peucker_algorithm
6345  function _simplifyDP(points, sqTolerance) {
6346
6347  	var len = points.length,
6348  	    ArrayConstructor = typeof Uint8Array !== undefined + '' ? Uint8Array : Array,
6349  	    markers = new ArrayConstructor(len);
6350
6351  	    markers[0] = markers[len - 1] = 1;
6352
6353  	_simplifyDPStep(points, markers, sqTolerance, 0, len - 1);
6354
6355  	var i,
6356  	    newPoints = [];
6357
6358  	for (i = 0; i < len; i++) {
6359  		if (markers[i]) {
6360  			newPoints.push(points[i]);
6361  		}
6362  	}
6363
6364  	return newPoints;
6365  }
6366
6367  function _simplifyDPStep(points, markers, sqTolerance, first, last) {
6368
6369  	var maxSqDist = 0,
6370  	index, i, sqDist;
6371
6372  	for (i = first + 1; i <= last - 1; i++) {
6373  		sqDist = _sqClosestPointOnSegment(points[i], points[first], points[last], true);
6374
6375  		if (sqDist > maxSqDist) {
6376  			index = i;
6377  			maxSqDist = sqDist;
6378  		}
6379  	}
6380
6381  	if (maxSqDist > sqTolerance) {
6382  		markers[index] = 1;
6383
6384  		_simplifyDPStep(points, markers, sqTolerance, first, index);
6385  		_simplifyDPStep(points, markers, sqTolerance, index, last);
6386  	}
6387  }
6388
6389  // reduce points that are too close to each other to a single point
6390  function _reducePoints(points, sqTolerance) {
6391  	var reducedPoints = [points[0]];
6392
6393  	for (var i = 1, prev = 0, len = points.length; i < len; i++) {
6394  		if (_sqDist(points[i], points[prev]) > sqTolerance) {
6395  			reducedPoints.push(points[i]);
6396  			prev = i;
6397  		}
6398  	}
6399  	if (prev < len - 1) {
6400  		reducedPoints.push(points[len - 1]);
6401  	}
6402  	return reducedPoints;
6403  }
6404
6405  var _lastCode;
6406
6407  // @function clipSegment(a: Point, b: Point, bounds: Bounds, useLastCode?: Boolean, round?: Boolean): Point[]|Boolean
6408  // Clips the segment a to b by rectangular bounds with the
6409  // [Cohen-Sutherland algorithm](https://en.wikipedia.org/wiki/Cohen%E2%80%93Sutherland_algorithm)
6410  // (modifying the segment points directly!). Used by Leaflet to only show polyline
6411  // points that are on the screen or near, increasing performance.
6412  function clipSegment(a, b, bounds, useLastCode, round) {
6413  	var codeA = useLastCode ? _lastCode : _getBitCode(a, bounds),
6414  	    codeB = _getBitCode(b, bounds),
6415
6416  	    codeOut, p, newCode;
6417
6418  	    // save 2nd code to avoid calculating it on the next segment
6419  	    _lastCode = codeB;
6420
6421  	while (true) {
6422  		// if a,b is inside the clip window (trivial accept)
6423  		if (!(codeA | codeB)) {
6424  			return [a, b];
6425  		}
6426
6427  		// if a,b is outside the clip window (trivial reject)
6428  		if (codeA & codeB) {
6429  			return false;
6430  		}
6431
6432  		// other cases
6433  		codeOut = codeA || codeB;
6434  		p = _getEdgeIntersection(a, b, codeOut, bounds, round);
6435  		newCode = _getBitCode(p, bounds);
6436
6437  		if (codeOut === codeA) {
6438  			a = p;
6439  			codeA = newCode;
6440  		} else {
6441  			b = p;
6442  			codeB = newCode;
6443  		}
6444  	}
6445  }
6446
6447  function _getEdgeIntersection(a, b, code, bounds, round) {
6448  	var dx = b.x - a.x,
6449  	    dy = b.y - a.y,
6450  	    min = bounds.min,
6451  	    max = bounds.max,
6452  	    x, y;
6453
6454  	if (code & 8) { // top
6455  		x = a.x + dx * (max.y - a.y) / dy;
6456  		y = max.y;
6457
6458  	} else if (code & 4) { // bottom
6459  		x = a.x + dx * (min.y - a.y) / dy;
6460  		y = min.y;
6461
6462  	} else if (code & 2) { // right
6463  		x = max.x;
6464  		y = a.y + dy * (max.x - a.x) / dx;
6465
6466  	} else if (code & 1) { // left
6467  		x = min.x;
6468  		y = a.y + dy * (min.x - a.x) / dx;
6469  	}
6470
6471  	return new Point(x, y, round);
6472  }
6473
6474  function _getBitCode(p, bounds) {
6475  	var code = 0;
6476
6477  	if (p.x < bounds.min.x) { // left
6478  		code |= 1;
6479  	} else if (p.x > bounds.max.x) { // right
6480  		code |= 2;
6481  	}
6482
6483  	if (p.y < bounds.min.y) { // bottom
6484  		code |= 4;
6485  	} else if (p.y > bounds.max.y) { // top
6486  		code |= 8;
6487  	}
6488
6489  	return code;
6490  }
6491
6492  // square distance (to avoid unnecessary Math.sqrt calls)
6493  function _sqDist(p1, p2) {
6494  	var dx = p2.x - p1.x,
6495  	    dy = p2.y - p1.y;
6496  	return dx * dx + dy * dy;
6497  }
6498
6499  // return closest point on segment or distance to that point
6500  function _sqClosestPointOnSegment(p, p1, p2, sqDist) {
6501  	var x = p1.x,
6502  	    y = p1.y,
6503  	    dx = p2.x - x,
6504  	    dy = p2.y - y,
6505  	    dot = dx * dx + dy * dy,
6506  	    t;
6507
6508  	if (dot > 0) {
6509  		t = ((p.x - x) * dx + (p.y - y) * dy) / dot;
6510
6511  		if (t > 1) {
6512  			x = p2.x;
6513  			y = p2.y;
6514  		} else if (t > 0) {
6515  			x += dx * t;
6516  			y += dy * t;
6517  		}
6518  	}
6519
6520  	dx = p.x - x;
6521  	dy = p.y - y;
6522
6523  	return sqDist ? dx * dx + dy * dy : new Point(x, y);
6524  }
6525
6526
6527  // @function isFlat(latlngs: LatLng[]): Boolean
6528  // Returns true if `latlngs` is a flat array, false is nested.
6529  function isFlat(latlngs) {
6530  	return !isArray(latlngs[0]) || (typeof latlngs[0][0] !== 'object' && typeof latlngs[0][0] !== 'undefined');
6531  }
6532
6533  function _flat(latlngs) {
6534  	console.warn('Deprecated use of _flat, please use L.LineUtil.isFlat instead.');
6535  	return isFlat(latlngs);
6536  }
6537
6538  /* @function polylineCenter(latlngs: LatLng[], crs: CRS): LatLng
6539   * Returns the center ([centroid](http://en.wikipedia.org/wiki/Centroid)) of the passed LatLngs (first ring) from a polyline.
6540   */
6541  function polylineCenter(latlngs, crs) {
6542  	var i, halfDist, segDist, dist, p1, p2, ratio, center;
6543
6544  	if (!latlngs || latlngs.length === 0) {
6545  		throw new Error('latlngs not passed');
6546  	}
6547
6548  	if (!isFlat(latlngs)) {
6549  		console.warn('latlngs are not flat! Only the first ring will be used');
6550  		latlngs = latlngs[0];
6551  	}
6552
6553  	var centroidLatLng = toLatLng([0, 0]);
6554
6555  	var bounds = toLatLngBounds(latlngs);
6556  	var areaBounds = bounds.getNorthWest().distanceTo(bounds.getSouthWest()) * bounds.getNorthEast().distanceTo(bounds.getNorthWest());
6557  	// tests showed that below 1700 rounding errors are happening
6558  	if (areaBounds < 1700) {
6559  		// getting a inexact center, to move the latlngs near to [0, 0] to prevent rounding errors
6560  		centroidLatLng = centroid(latlngs);
6561  	}
6562
6563  	var len = latlngs.length;
6564  	var points = [];
6565  	for (i = 0; i < len; i++) {
6566  		var latlng = toLatLng(latlngs[i]);
6567  		points.push(crs.project(toLatLng([latlng.lat - centroidLatLng.lat, latlng.lng - centroidLatLng.lng])));
6568  	}
6569
6570  	for (i = 0, halfDist = 0; i < len - 1; i++) {
6571  		halfDist += points[i].distanceTo(points[i + 1]) / 2;
6572  	}
6573
6574  	// The line is so small in the current view that all points are on the same pixel.
6575  	if (halfDist === 0) {
6576  		center = points[0];
6577  	} else {
6578  		for (i = 0, dist = 0; i < len - 1; i++) {
6579  			p1 = points[i];
6580  			p2 = points[i + 1];
6581  			segDist = p1.distanceTo(p2);
6582  			dist += segDist;
6583
6584  			if (dist > halfDist) {
6585  				ratio = (dist - halfDist) / segDist;
6586  				center = [
6587  					p2.x - ratio * (p2.x - p1.x),
6588  					p2.y - ratio * (p2.y - p1.y)
6589  				];
6590  				break;
6591  			}
6592  		}
6593  	}
6594
6595  	var latlngCenter = crs.unproject(toPoint(center));
6596  	return toLatLng([latlngCenter.lat + centroidLatLng.lat, latlngCenter.lng + centroidLatLng.lng]);
6597  }
6598
6599  var LineUtil = {
6600    __proto__: null,
6601    simplify: simplify,
6602    pointToSegmentDistance: pointToSegmentDistance,
6603    closestPointOnSegment: closestPointOnSegment,
6604    clipSegment: clipSegment,
6605    _getEdgeIntersection: _getEdgeIntersection,
6606    _getBitCode: _getBitCode,
6607    _sqClosestPointOnSegment: _sqClosestPointOnSegment,
6608    isFlat: isFlat,
6609    _flat: _flat,
6610    polylineCenter: polylineCenter
6611  };
6612
6613  /*
6614   * @namespace Projection
6615   * @section
6616   * Leaflet comes with a set of already defined Projections out of the box:
6617   *
6618   * @projection L.Projection.LonLat
6619   *
6620   * Equirectangular, or Plate Carree projection — the most simple projection,
6621   * mostly used by GIS enthusiasts. Directly maps `x` as longitude, and `y` as
6622   * latitude. Also suitable for flat worlds, e.g. game maps. Used by the
6623   * `EPSG:4326` and `Simple` CRS.
6624   */
6625
6626  var LonLat = {
6627  	project: function (latlng) {
6628  		return new Point(latlng.lng, latlng.lat);
6629  	},
6630
6631  	unproject: function (point) {
6632  		return new LatLng(point.y, point.x);
6633  	},
6634
6635  	bounds: new Bounds([-180, -90], [180, 90])
6636  };
6637
6638  /*
6639   * @namespace Projection
6640   * @projection L.Projection.Mercator
6641   *
6642   * Elliptical Mercator projection — more complex than Spherical Mercator. Assumes that Earth is an ellipsoid. Used by the EPSG:3395 CRS.
6643   */
6644
6645  var Mercator = {
6646  	R: 6378137,
6647  	R_MINOR: 6356752.314245179,
6648
6649  	bounds: new Bounds([-20037508.34279, -15496570.73972], [20037508.34279, 18764656.23138]),
6650
6651  	project: function (latlng) {
6652  		var d = Math.PI / 180,
6653  		    r = this.R,
6654  		    y = latlng.lat * d,
6655  		    tmp = this.R_MINOR / r,
6656  		    e = Math.sqrt(1 - tmp * tmp),
6657  		    con = e * Math.sin(y);
6658
6659  		var ts = Math.tan(Math.PI / 4 - y / 2) / Math.pow((1 - con) / (1 + con), e / 2);
6660  		y = -r * Math.log(Math.max(ts, 1E-10));
6661
6662  		return new Point(latlng.lng * d * r, y);
6663  	},
6664
6665  	unproject: function (point) {
6666  		var d = 180 / Math.PI,
6667  		    r = this.R,
6668  		    tmp = this.R_MINOR / r,
6669  		    e = Math.sqrt(1 - tmp * tmp),
6670  		    ts = Math.exp(-point.y / r),
6671  		    phi = Math.PI / 2 - 2 * Math.atan(ts);
6672
6673  		for (var i = 0, dphi = 0.1, con; i < 15 && Math.abs(dphi) > 1e-7; i++) {
6674  			con = e * Math.sin(phi);
6675  			con = Math.pow((1 - con) / (1 + con), e / 2);
6676  			dphi = Math.PI / 2 - 2 * Math.atan(ts * con) - phi;
6677  			phi += dphi;
6678  		}
6679
6680  		return new LatLng(phi * d, point.x * d / r);
6681  	}
6682  };
6683
6684  /*
6685   * @class Projection
6686
6687   * An object with methods for projecting geographical coordinates of the world onto
6688   * a flat surface (and back). See [Map projection](https://en.wikipedia.org/wiki/Map_projection).
6689
6690   * @property bounds: Bounds
6691   * The bounds (specified in CRS units) where the projection is valid
6692
6693   * @method project(latlng: LatLng): Point
6694   * Projects geographical coordinates into a 2D point.
6695   * Only accepts actual `L.LatLng` instances, not arrays.
6696
6697   * @method unproject(point: Point): LatLng
6698   * The inverse of `project`. Projects a 2D point into a geographical location.
6699   * Only accepts actual `L.Point` instances, not arrays.
6700
6701   * Note that the projection instances do not inherit from Leaflet's `Class` object,
6702   * and can't be instantiated. Also, new classes can't inherit from them,
6703   * and methods can't be added to them with the `include` function.
6704
6705   */
6706
6707  var index = {
6708    __proto__: null,
6709    LonLat: LonLat,
6710    Mercator: Mercator,
6711    SphericalMercator: SphericalMercator
6712  };
6713
6714  /*
6715   * @namespace CRS
6716   * @crs L.CRS.EPSG3395
6717   *
6718   * Rarely used by some commercial tile providers. Uses Elliptical Mercator projection.
6719   */
6720  var EPSG3395 = extend({}, Earth, {
6721  	code: 'EPSG:3395',
6722  	projection: Mercator,
6723
6724  	transformation: (function () {
6725  		var scale = 0.5 / (Math.PI * Mercator.R);
6726  		return toTransformation(scale, 0.5, -scale, 0.5);
6727  	}())
6728  });
6729
6730  /*
6731   * @namespace CRS
6732   * @crs L.CRS.EPSG4326
6733   *
6734   * A common CRS among GIS enthusiasts. Uses simple Equirectangular projection.
6735   *
6736   * Leaflet 1.0.x complies with the [TMS coordinate scheme for EPSG:4326](https://wiki.osgeo.org/wiki/Tile_Map_Service_Specification#global-geodetic),
6737   * which is a breaking change from 0.7.x behaviour.  If you are using a `TileLayer`
6738   * with this CRS, ensure that there are two 256x256 pixel tiles covering the
6739   * whole earth at zoom level zero, and that the tile coordinate origin is (-180,+90),
6740   * or (-180,-90) for `TileLayer`s with [the `tms` option](#tilelayer-tms) set.
6741   */
6742
6743  var EPSG4326 = extend({}, Earth, {
6744  	code: 'EPSG:4326',
6745  	projection: LonLat,
6746  	transformation: toTransformation(1 / 180, 1, -1 / 180, 0.5)
6747  });
6748
6749  /*
6750   * @namespace CRS
6751   * @crs L.CRS.Simple
6752   *
6753   * A simple CRS that maps longitude and latitude into `x` and `y` directly.
6754   * May be used for maps of flat surfaces (e.g. game maps). Note that the `y`
6755   * axis should still be inverted (going from bottom to top). `distance()` returns
6756   * simple euclidean distance.
6757   */
6758
6759  var Simple = extend({}, CRS, {
6760  	projection: LonLat,
6761  	transformation: toTransformation(1, 0, -1, 0),
6762
6763  	scale: function (zoom) {
6764  		return Math.pow(2, zoom);
6765  	},
6766
6767  	zoom: function (scale) {
6768  		return Math.log(scale) / Math.LN2;
6769  	},
6770
6771  	distance: function (latlng1, latlng2) {
6772  		var dx = latlng2.lng - latlng1.lng,
6773  		    dy = latlng2.lat - latlng1.lat;
6774
6775  		return Math.sqrt(dx * dx + dy * dy);
6776  	},
6777
6778  	infinite: true
6779  });
6780
6781  CRS.Earth = Earth;
6782  CRS.EPSG3395 = EPSG3395;
6783  CRS.EPSG3857 = EPSG3857;
6784  CRS.EPSG900913 = EPSG900913;
6785  CRS.EPSG4326 = EPSG4326;
6786  CRS.Simple = Simple;
6787
6788  /*
6789   * @class Layer
6790   * @inherits Evented
6791   * @aka L.Layer
6792   * @aka ILayer
6793   *
6794   * A set of methods from the Layer base class that all Leaflet layers use.
6795   * Inherits all methods, options and events from `L.Evented`.
6796   *
6797   * @example
6798   *
6799   * ```js
6800   * var layer = L.marker(latlng).addTo(map);
6801   * layer.addTo(map);
6802   * layer.remove();
6803   * ```
6804   *
6805   * @event add: Event
6806   * Fired after the layer is added to a map
6807   *
6808   * @event remove: Event
6809   * Fired after the layer is removed from a map
6810   */
6811
6812
6813  var Layer = Evented.extend({
6814
6815  	// Classes extending `L.Layer` will inherit the following options:
6816  	options: {
6817  		// @option pane: String = 'overlayPane'
6818  		// 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.
6819  		pane: 'overlayPane',
6820
6821  		// @option attribution: String = null
6822  		// String to be shown in the attribution control, e.g. "© OpenStreetMap contributors". It describes the layer data and is often a legal obligation towards copyright holders and tile providers.
6823  		attribution: null,
6824
6825  		bubblingMouseEvents: true
6826  	},
6827
6828  	/* @section
6829  	 * Classes extending `L.Layer` will inherit the following methods:
6830  	 *
6831  	 * @method addTo(map: Map|LayerGroup): this
6832  	 * Adds the layer to the given map or layer group.
6833  	 */
6834  	addTo: function (map) {
6835  		map.addLayer(this);
6836  		return this;
6837  	},
6838
6839  	// @method remove: this
6840  	// Removes the layer from the map it is currently active on.
6841  	remove: function () {
6842  		return this.removeFrom(this._map || this._mapToAdd);
6843  	},
6844
6845  	// @method removeFrom(map: Map): this
6846  	// Removes the layer from the given map
6847  	//
6848  	// @alternative
6849  	// @method removeFrom(group: LayerGroup): this
6850  	// Removes the layer from the given `LayerGroup`
6851  	removeFrom: function (obj) {
6852  		if (obj) {
6853  			obj.removeLayer(this);
6854  		}
6855  		return this;
6856  	},
6857
6858  	// @method getPane(name? : String): HTMLElement
6859  	// Returns the `HTMLElement` representing the named pane on the map. If `name` is omitted, returns the pane for this layer.
6860  	getPane: function (name) {
6861  		return this._map.getPane(name ? (this.options[name] || name) : this.options.pane);
6862  	},
6863
6864  	addInteractiveTarget: function (targetEl) {
6865  		this._map._targets[stamp(targetEl)] = this;
6866  		return this;
6867  	},
6868
6869  	removeInteractiveTarget: function (targetEl) {
6870  		delete this._map._targets[stamp(targetEl)];
6871  		return this;
6872  	},
6873
6874  	// @method getAttribution: String
6875  	// Used by the `attribution control`, returns the [attribution option](#gridlayer-attribution).
6876  	getAttribution: function () {
6877  		return this.options.attribution;
6878  	},
6879
6880  	_layerAdd: function (e) {
6881  		var map = e.target;
6882
6883  		// check in case layer gets added and then removed before the map is ready
6884  		if (!map.hasLayer(this)) { return; }
6885
6886  		this._map = map;
6887  		this._zoomAnimated = map._zoomAnimated;
6888
6889  		if (this.getEvents) {
6890  			var events = this.getEvents();
6891  			map.on(events, this);
6892  			this.once('remove', function () {
6893  				map.off(events, this);
6894  			}, this);
6895  		}
6896
6897  		this.onAdd(map);
6898
6899  		this.fire('add');
6900  		map.fire('layeradd', {layer: this});
6901  	}
6902  });
6903
6904  /* @section Extension methods
6905   * @uninheritable
6906   *
6907   * Every layer should extend from `L.Layer` and (re-)implement the following methods.
6908   *
6909   * @method onAdd(map: Map): this
6910   * 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).
6911   *
6912   * @method onRemove(map: Map): this
6913   * 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).
6914   *
6915   * @method getEvents(): Object
6916   * 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.
6917   *
6918   * @method getAttribution(): String
6919   * This optional method should return a string containing HTML to be shown on the `Attribution control` whenever the layer is visible.
6920   *
6921   * @method beforeAdd(map: Map): this
6922   * 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.
6923   */
6924
6925
6926  /* @namespace Map
6927   * @section Layer events
6928   *
6929   * @event layeradd: LayerEvent
6930   * Fired when a new layer is added to the map.
6931   *
6932   * @event layerremove: LayerEvent
6933   * Fired when some layer is removed from the map
6934   *
6935   * @section Methods for Layers and Controls
6936   */
6937  Map.include({
6938  	// @method addLayer(layer: Layer): this
6939  	// Adds the given layer to the map
6940  	addLayer: function (layer) {
6941  		if (!layer._layerAdd) {
6942  			throw new Error('The provided object is not a Layer.');
6943  		}
6944
6945  		var id = stamp(layer);
6946  		if (this._layers[id]) { return this; }
6947  		this._layers[id] = layer;
6948
6949  		layer._mapToAdd = this;
6950
6951  		if (layer.beforeAdd) {
6952  			layer.beforeAdd(this);
6953  		}
6954
6955  		this.whenReady(layer._layerAdd, layer);
6956
6957  		return this;
6958  	},
6959
6960  	// @method removeLayer(layer: Layer): this
6961  	// Removes the given layer from the map.
6962  	removeLayer: function (layer) {
6963  		var id = stamp(layer);
6964
6965  		if (!this._layers[id]) { return this; }
6966
6967  		if (this._loaded) {
6968  			layer.onRemove(this);
6969  		}
6970
6971  		delete this._layers[id];
6972
6973  		if (this._loaded) {
6974  			this.fire('layerremove', {layer: layer});
6975  			layer.fire('remove');
6976  		}
6977
6978  		layer._map = layer._mapToAdd = null;
6979
6980  		return this;
6981  	},
6982
6983  	// @method hasLayer(layer: Layer): Boolean
6984  	// Returns `true` if the given layer is currently added to the map
6985  	hasLayer: function (layer) {
6986  		return stamp(layer) in this._layers;
6987  	},
6988
6989  	/* @method eachLayer(fn: Function, context?: Object): this
6990  	 * Iterates over the layers of the map, optionally specifying context of the iterator function.
6991  	 * ```
6992  	 * map.eachLayer(function(layer){
6993  	 *     layer.bindPopup('Hello');
6994  	 * });
6995  	 * ```
6996  	 */
6997  	eachLayer: function (method, context) {
6998  		for (var i in this._layers) {
6999  			method.call(context, this._layers[i]);
7000  		}
7001  		return this;
7002  	},
7003
7004  	_addLayers: function (layers) {
7005  		layers = layers ? (isArray(layers) ? layers : [layers]) : [];
7006
7007  		for (var i = 0, len = layers.length; i < len; i++) {
7008  			this.addLayer(layers[i]);
7009  		}
7010  	},
7011
7012  	_addZoomLimit: function (layer) {
7013  		if (!isNaN(layer.options.maxZoom) || !isNaN(layer.options.minZoom)) {
7014  			this._zoomBoundLayers[stamp(layer)] = layer;
7015  			this._updateZoomLevels();
7016  		}
7017  	},
7018
7019  	_removeZoomLimit: function (layer) {
7020  		var id = stamp(layer);
7021
7022  		if (this._zoomBoundLayers[id]) {
7023  			delete this._zoomBoundLayers[id];
7024  			this._updateZoomLevels();
7025  		}
7026  	},
7027
7028  	_updateZoomLevels: function () {
7029  		var minZoom = Infinity,
7030  		    maxZoom = -Infinity,
7031  		    oldZoomSpan = this._getZoomSpan();
7032
7033  		for (var i in this._zoomBoundLayers) {
7034  			var options = this._zoomBoundLayers[i].options;
7035
7036  			minZoom = options.minZoom === undefined ? minZoom : Math.min(minZoom, options.minZoom);
7037  			maxZoom = options.maxZoom === undefined ? maxZoom : Math.max(maxZoom, options.maxZoom);
7038  		}
7039
7040  		this._layersMaxZoom = maxZoom === -Infinity ? undefined : maxZoom;
7041  		this._layersMinZoom = minZoom === Infinity ? undefined : minZoom;
7042
7043  		// @section Map state change events
7044  		// @event zoomlevelschange: Event
7045  		// Fired when the number of zoomlevels on the map is changed due
7046  		// to adding or removing a layer.
7047  		if (oldZoomSpan !== this._getZoomSpan()) {
7048  			this.fire('zoomlevelschange');
7049  		}
7050
7051  		if (this.options.maxZoom === undefined && this._layersMaxZoom && this.getZoom() > this._layersMaxZoom) {
7052  			this.setZoom(this._layersMaxZoom);
7053  		}
7054  		if (this.options.minZoom === undefined && this._layersMinZoom && this.getZoom() < this._layersMinZoom) {
7055  			this.setZoom(this._layersMinZoom);
7056  		}
7057  	}
7058  });
7059
7060  /*
7061   * @class LayerGroup
7062   * @aka L.LayerGroup
7063   * @inherits Interactive layer
7064   *
7065   * Used to group several layers and handle them as one. If you add it to the map,
7066   * any layers added or removed from the group will be added/removed on the map as
7067   * well. Extends `Layer`.
7068   *
7069   * @example
7070   *
7071   * ```js
7072   * L.layerGroup([marker1, marker2])
7073   * 	.addLayer(polyline)
7074   * 	.addTo(map);
7075   * ```
7076   */
7077
7078  var LayerGroup = Layer.extend({
7079
7080  	initialize: function (layers, options) {
7081  		setOptions(this, options);
7082
7083  		this._layers = {};
7084
7085  		var i, len;
7086
7087  		if (layers) {
7088  			for (i = 0, len = layers.length; i < len; i++) {
7089  				this.addLayer(layers[i]);
7090  			}
7091  		}
7092  	},
7093
7094  	// @method addLayer(layer: Layer): this
7095  	// Adds the given layer to the group.
7096  	addLayer: function (layer) {
7097  		var id = this.getLayerId(layer);
7098
7099  		this._layers[id] = layer;
7100
7101  		if (this._map) {
7102  			this._map.addLayer(layer);
7103  		}
7104
7105  		return this;
7106  	},
7107
7108  	// @method removeLayer(layer: Layer): this
7109  	// Removes the given layer from the group.
7110  	// @alternative
7111  	// @method removeLayer(id: Number): this
7112  	// Removes the layer with the given internal ID from the group.
7113  	removeLayer: function (layer) {
7114  		var id = layer in this._layers ? layer : this.getLayerId(layer);
7115
7116  		if (this._map && this._layers[id]) {
7117  			this._map.removeLayer(this._layers[id]);
7118  		}
7119
7120  		delete this._layers[id];
7121
7122  		return this;
7123  	},
7124
7125  	// @method hasLayer(layer: Layer): Boolean
7126  	// Returns `true` if the given layer is currently added to the group.
7127  	// @alternative
7128  	// @method hasLayer(id: Number): Boolean
7129  	// Returns `true` if the given internal ID is currently added to the group.
7130  	hasLayer: function (layer) {
7131  		var layerId = typeof layer === 'number' ? layer : this.getLayerId(layer);
7132  		return layerId in this._layers;
7133  	},
7134
7135  	// @method clearLayers(): this
7136  	// Removes all the layers from the group.
7137  	clearLayers: function () {
7138  		return this.eachLayer(this.removeLayer, this);
7139  	},
7140
7141  	// @method invoke(methodName: String, …): this
7142  	// Calls `methodName` on every layer contained in this group, passing any
7143  	// additional parameters. Has no effect if the layers contained do not
7144  	// implement `methodName`.
7145  	invoke: function (methodName) {
7146  		var args = Array.prototype.slice.call(arguments, 1),
7147  		    i, layer;
7148
7149  		for (i in this._layers) {
7150  			layer = this._layers[i];
7151
7152  			if (layer[methodName]) {
7153  				layer[methodName].apply(layer, args);
7154  			}
7155  		}
7156
7157  		return this;
7158  	},
7159
7160  	onAdd: function (map) {
7161  		this.eachLayer(map.addLayer, map);
7162  	},
7163
7164  	onRemove: function (map) {
7165  		this.eachLayer(map.removeLayer, map);
7166  	},
7167
7168  	// @method eachLayer(fn: Function, context?: Object): this
7169  	// Iterates over the layers of the group, optionally specifying context of the iterator function.
7170  	// ```js
7171  	// group.eachLayer(function (layer) {
7172  	// 	layer.bindPopup('Hello');
7173  	// });
7174  	// ```
7175  	eachLayer: function (method, context) {
7176  		for (var i in this._layers) {
7177  			method.call(context, this._layers[i]);
7178  		}
7179  		return this;
7180  	},
7181
7182  	// @method getLayer(id: Number): Layer
7183  	// Returns the layer with the given internal ID.
7184  	getLayer: function (id) {
7185  		return this._layers[id];
7186  	},
7187
7188  	// @method getLayers(): Layer[]
7189  	// Returns an array of all the layers added to the group.
7190  	getLayers: function () {
7191  		var layers = [];
7192  		this.eachLayer(layers.push, layers);
7193  		return layers;
7194  	},
7195
7196  	// @method setZIndex(zIndex: Number): this
7197  	// Calls `setZIndex` on every layer contained in this group, passing the z-index.
7198  	setZIndex: function (zIndex) {
7199  		return this.invoke('setZIndex', zIndex);
7200  	},
7201
7202  	// @method getLayerId(layer: Layer): Number
7203  	// Returns the internal ID for a layer
7204  	getLayerId: function (layer) {
7205  		return stamp(layer);
7206  	}
7207  });
7208
7209
7210  // @factory L.layerGroup(layers?: Layer[], options?: Object)
7211  // Create a layer group, optionally given an initial set of layers and an `options` object.
7212  var layerGroup = function (layers, options) {
7213  	return new LayerGroup(layers, options);
7214  };
7215
7216  /*
7217   * @class FeatureGroup
7218   * @aka L.FeatureGroup
7219   * @inherits LayerGroup
7220   *
7221   * Extended `LayerGroup` that makes it easier to do the same thing to all its member layers:
7222   *  * [`bindPopup`](#layer-bindpopup) binds a popup to all of the layers at once (likewise with [`bindTooltip`](#layer-bindtooltip))
7223   *  * Events are propagated to the `FeatureGroup`, so if the group has an event
7224   * handler, it will handle events from any of the layers. This includes mouse events
7225   * and custom events.
7226   *  * Has `layeradd` and `layerremove` events
7227   *
7228   * @example
7229   *
7230   * ```js
7231   * L.featureGroup([marker1, marker2, polyline])
7232   * 	.bindPopup('Hello world!')
7233   * 	.on('click', function() { alert('Clicked on a member of the group!'); })
7234   * 	.addTo(map);
7235   * ```
7236   */
7237
7238  var FeatureGroup = LayerGroup.extend({
7239
7240  	addLayer: function (layer) {
7241  		if (this.hasLayer(layer)) {
7242  			return this;
7243  		}
7244
7245  		layer.addEventParent(this);
7246
7247  		LayerGroup.prototype.addLayer.call(this, layer);
7248
7249  		// @event layeradd: LayerEvent
7250  		// Fired when a layer is added to this `FeatureGroup`
7251  		return this.fire('layeradd', {layer: layer});
7252  	},
7253
7254  	removeLayer: function (layer) {
7255  		if (!this.hasLayer(layer)) {
7256  			return this;
7257  		}
7258  		if (layer in this._layers) {
7259  			layer = this._layers[layer];
7260  		}
7261
7262  		layer.removeEventParent(this);
7263
7264  		LayerGroup.prototype.removeLayer.call(this, layer);
7265
7266  		// @event layerremove: LayerEvent
7267  		// Fired when a layer is removed from this `FeatureGroup`
7268  		return this.fire('layerremove', {layer: layer});
7269  	},
7270
7271  	// @method setStyle(style: Path options): this
7272  	// Sets the given path options to each layer of the group that has a `setStyle` method.
7273  	setStyle: function (style) {
7274  		return this.invoke('setStyle', style);
7275  	},
7276
7277  	// @method bringToFront(): this
7278  	// Brings the layer group to the top of all other layers
7279  	bringToFront: function () {
7280  		return this.invoke('bringToFront');
7281  	},
7282
7283  	// @method bringToBack(): this
7284  	// Brings the layer group to the back of all other layers
7285  	bringToBack: function () {
7286  		return this.invoke('bringToBack');
7287  	},
7288
7289  	// @method getBounds(): LatLngBounds
7290  	// Returns the LatLngBounds of the Feature Group (created from bounds and coordinates of its children).
7291  	getBounds: function () {
7292  		var bounds = new LatLngBounds();
7293
7294  		for (var id in this._layers) {
7295  			var layer = this._layers[id];
7296  			bounds.extend(layer.getBounds ? layer.getBounds() : layer.getLatLng());
7297  		}
7298  		return bounds;
7299  	}
7300  });
7301
7302  // @factory L.featureGroup(layers?: Layer[], options?: Object)
7303  // Create a feature group, optionally given an initial set of layers and an `options` object.
7304  var featureGroup = function (layers, options) {
7305  	return new FeatureGroup(layers, options);
7306  };
7307
7308  /*
7309   * @class Icon
7310   * @aka L.Icon
7311   *
7312   * Represents an icon to provide when creating a marker.
7313   *
7314   * @example
7315   *
7316   * ```js
7317   * var myIcon = L.icon({
7318   *     iconUrl: 'my-icon.png',
7319   *     iconRetinaUrl: '[email protected]',
7320   *     iconSize: [38, 95],
7321   *     iconAnchor: [22, 94],
7322   *     popupAnchor: [-3, -76],
7323   *     shadowUrl: 'my-icon-shadow.png',
7324   *     shadowRetinaUrl: '[email protected]',
7325   *     shadowSize: [68, 95],
7326   *     shadowAnchor: [22, 94]
7327   * });
7328   *
7329   * L.marker([50.505, 30.57], {icon: myIcon}).addTo(map);
7330   * ```
7331   *
7332   * `L.Icon.Default` extends `L.Icon` and is the blue icon Leaflet uses for markers by default.
7333   *
7334   */
7335
7336  var Icon = Class.extend({
7337
7338  	/* @section
7339  	 * @aka Icon options
7340  	 *
7341  	 * @option iconUrl: String = null
7342  	 * **(required)** The URL to the icon image (absolute or relative to your script path).
7343  	 *
7344  	 * @option iconRetinaUrl: String = null
7345  	 * The URL to a retina sized version of the icon image (absolute or relative to your
7346  	 * script path). Used for Retina screen devices.
7347  	 *
7348  	 * @option iconSize: Point = null
7349  	 * Size of the icon image in pixels.
7350  	 *
7351  	 * @option iconAnchor: Point = null
7352  	 * The coordinates of the "tip" of the icon (relative to its top left corner). The icon
7353  	 * will be aligned so that this point is at the marker's geographical location. Centered
7354  	 * by default if size is specified, also can be set in CSS with negative margins.
7355  	 *
7356  	 * @option popupAnchor: Point = [0, 0]
7357  	 * The coordinates of the point from which popups will "open", relative to the icon anchor.
7358  	 *
7359  	 * @option tooltipAnchor: Point = [0, 0]
7360  	 * The coordinates of the point from which tooltips will "open", relative to the icon anchor.
7361  	 *
7362  	 * @option shadowUrl: String = null
7363  	 * The URL to the icon shadow image. If not specified, no shadow image will be created.
7364  	 *
7365  	 * @option shadowRetinaUrl: String = null
7366  	 *
7367  	 * @option shadowSize: Point = null
7368  	 * Size of the shadow image in pixels.
7369  	 *
7370  	 * @option shadowAnchor: Point = null
7371  	 * The coordinates of the "tip" of the shadow (relative to its top left corner) (the same
7372  	 * as iconAnchor if not specified).
7373  	 *
7374  	 * @option className: String = ''
7375  	 * A custom class name to assign to both icon and shadow images. Empty by default.
7376  	 */
7377
7378  	options: {
7379  		popupAnchor: [0, 0],
7380  		tooltipAnchor: [0, 0],
7381
7382  		// @option crossOrigin: Boolean|String = false
7383  		// Whether the crossOrigin attribute will be added to the tiles.
7384  		// If a String is provided, all tiles will have their crossOrigin attribute set to the String provided. This is needed if you want to access tile pixel data.
7385  		// Refer to [CORS Settings](https://developer.mozilla.org/en-US/docs/Web/HTML/CORS_settings_attributes) for valid String values.
7386  		crossOrigin: false
7387  	},
7388
7389  	initialize: function (options) {
7390  		setOptions(this, options);
7391  	},
7392
7393  	// @method createIcon(oldIcon?: HTMLElement): HTMLElement
7394  	// Called internally when the icon has to be shown, returns a `<img>` HTML element
7395  	// styled according to the options.
7396  	createIcon: function (oldIcon) {
7397  		return this._createIcon('icon', oldIcon);
7398  	},
7399
7400  	// @method createShadow(oldIcon?: HTMLElement): HTMLElement
7401  	// As `createIcon`, but for the shadow beneath it.
7402  	createShadow: function (oldIcon) {
7403  		return this._createIcon('shadow', oldIcon);
7404  	},
7405
7406  	_createIcon: function (name, oldIcon) {
7407  		var src = this._getIconUrl(name);
7408
7409  		if (!src) {
7410  			if (name === 'icon') {
7411  				throw new Error('iconUrl not set in Icon options (see the docs).');
7412  			}
7413  			return null;
7414  		}
7415
7416  		var img = this._createImg(src, oldIcon && oldIcon.tagName === 'IMG' ? oldIcon : null);
7417  		this._setIconStyles(img, name);
7418
7419  		if (this.options.crossOrigin || this.options.crossOrigin === '') {
7420  			img.crossOrigin = this.options.crossOrigin === true ? '' : this.options.crossOrigin;
7421  		}
7422
7423  		return img;
7424  	},
7425
7426  	_setIconStyles: function (img, name) {
7427  		var options = this.options;
7428  		var sizeOption = options[name + 'Size'];
7429
7430  		if (typeof sizeOption === 'number') {
7431  			sizeOption = [sizeOption, sizeOption];
7432  		}
7433
7434  		var size = toPoint(sizeOption),
7435  		    anchor = toPoint(name === 'shadow' && options.shadowAnchor || options.iconAnchor ||
7436  		            size && size.divideBy(2, true));
7437
7438  		img.className = 'leaflet-marker-' + name + ' ' + (options.className || '');
7439
7440  		if (anchor) {
7441  			img.style.marginLeft = (-anchor.x) + 'px';
7442  			img.style.marginTop  = (-anchor.y) + 'px';
7443  		}
7444
7445  		if (size) {
7446  			img.style.width  = size.x + 'px';
7447  			img.style.height = size.y + 'px';
7448  		}
7449  	},
7450
7451  	_createImg: function (src, el) {
7452  		el = el || document.createElement('img');
7453  		el.src = src;
7454  		return el;
7455  	},
7456
7457  	_getIconUrl: function (name) {
7458  		return Browser.retina && this.options[name + 'RetinaUrl'] || this.options[name + 'Url'];
7459  	}
7460  });
7461
7462
7463  // @factory L.icon(options: Icon options)
7464  // Creates an icon instance with the given options.
7465  function icon(options) {
7466  	return new Icon(options);
7467  }
7468
7469  /*
7470   * @miniclass Icon.Default (Icon)
7471   * @aka L.Icon.Default
7472   * @section
7473   *
7474   * A trivial subclass of `Icon`, represents the icon to use in `Marker`s when
7475   * no icon is specified. Points to the blue marker image distributed with Leaflet
7476   * releases.
7477   *
7478   * In order to customize the default icon, just change the properties of `L.Icon.Default.prototype.options`
7479   * (which is a set of `Icon options`).
7480   *
7481   * If you want to _completely_ replace the default icon, override the
7482   * `L.Marker.prototype.options.icon` with your own icon instead.
7483   */
7484
7485  var IconDefault = Icon.extend({
7486
7487  	options: {
7488  		iconUrl:       'marker-icon.png',
7489  		iconRetinaUrl: 'marker-icon-2x.png',
7490  		shadowUrl:     'marker-shadow.png',
7491  		iconSize:    [25, 41],
7492  		iconAnchor:  [12, 41],
7493  		popupAnchor: [1, -34],
7494  		tooltipAnchor: [16, -28],
7495  		shadowSize:  [41, 41]
7496  	},
7497
7498  	_getIconUrl: function (name) {
7499  		if (typeof IconDefault.imagePath !== 'string') {	// Deprecated, backwards-compatibility only
7500  			IconDefault.imagePath = this._detectIconPath();
7501  		}
7502
7503  		// @option imagePath: String
7504  		// `Icon.Default` will try to auto-detect the location of the
7505  		// blue icon images. If you are placing these images in a non-standard
7506  		// way, set this option to point to the right path.
7507  		return (this.options.imagePath || IconDefault.imagePath) + Icon.prototype._getIconUrl.call(this, name);
7508  	},
7509
7510  	_stripUrl: function (path) {	// separate function to use in tests
7511  		var strip = function (str, re, idx) {
7512  			var match = re.exec(str);
7513  			return match && match[idx];
7514  		};
7515  		path = strip(path, /^url\((['"])?(.+)\1\)$/, 2);
7516  		return path && strip(path, /^(.*)marker-icon\.png$/, 1);
7517  	},
7518
7519  	_detectIconPath: function () {
7520  		var el = create$1('div',  'leaflet-default-icon-path', document.body);
7521  		var path = getStyle(el, 'background-image') ||
7522  		           getStyle(el, 'backgroundImage');	// IE8
7523
7524  		document.body.removeChild(el);
7525  		path = this._stripUrl(path);
7526  		if (path) { return path; }
7527  		var link = document.querySelector('link[href$="leaflet.css"]');
7528  		if (!link) { return ''; }
7529  		return link.href.substring(0, link.href.length - 'leaflet.css'.length - 1);
7530  	}
7531  });
7532
7533  /*
7534   * L.Handler.MarkerDrag is used internally by L.Marker to make the markers draggable.
7535   */
7536
7537
7538  /* @namespace Marker
7539   * @section Interaction handlers
7540   *
7541   * 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:
7542   *
7543   * ```js
7544   * marker.dragging.disable();
7545   * ```
7546   *
7547   * @property dragging: Handler
7548   * Marker dragging handler (by both mouse and touch). Only valid when the marker is on the map (Otherwise set [`marker.options.draggable`](#marker-draggable)).
7549   */
7550
7551  var MarkerDrag = Handler.extend({
7552  	initialize: function (marker) {
7553  		this._marker = marker;
7554  	},
7555
7556  	addHooks: function () {
7557  		var icon = this._marker._icon;
7558
7559  		if (!this._draggable) {
7560  			this._draggable = new Draggable(icon, icon, true);
7561  		}
7562
7563  		this._draggable.on({
7564  			dragstart: this._onDragStart,
7565  			predrag: this._onPreDrag,
7566  			drag: this._onDrag,
7567  			dragend: this._onDragEnd
7568  		}, this).enable();
7569
7570  		addClass(icon, 'leaflet-marker-draggable');
7571  	},
7572
7573  	removeHooks: function () {
7574  		this._draggable.off({
7575  			dragstart: this._onDragStart,
7576  			predrag: this._onPreDrag,
7577  			drag: this._onDrag,
7578  			dragend: this._onDragEnd
7579  		}, this).disable();
7580
7581  		if (this._marker._icon) {
7582  			removeClass(this._marker._icon, 'leaflet-marker-draggable');
7583  		}
7584  	},
7585
7586  	moved: function () {
7587  		return this._draggable && this._draggable._moved;
7588  	},
7589
7590  	_adjustPan: function (e) {
7591  		var marker = this._marker,
7592  		    map = marker._map,
7593  		    speed = this._marker.options.autoPanSpeed,
7594  		    padding = this._marker.options.autoPanPadding,
7595  		    iconPos = getPosition(marker._icon),
7596  		    bounds = map.getPixelBounds(),
7597  		    origin = map.getPixelOrigin();
7598
7599  		var panBounds = toBounds(
7600  			bounds.min._subtract(origin).add(padding),
7601  			bounds.max._subtract(origin).subtract(padding)
7602  		);
7603
7604  		if (!panBounds.contains(iconPos)) {
7605  			// Compute incremental movement
7606  			var movement = toPoint(
7607  				(Math.max(panBounds.max.x, iconPos.x) - panBounds.max.x) / (bounds.max.x - panBounds.max.x) -
7608  				(Math.min(panBounds.min.x, iconPos.x) - panBounds.min.x) / (bounds.min.x - panBounds.min.x),
7609
7610  				(Math.max(panBounds.max.y, iconPos.y) - panBounds.max.y) / (bounds.max.y - panBounds.max.y) -
7611  				(Math.min(panBounds.min.y, iconPos.y) - panBounds.min.y) / (bounds.min.y - panBounds.min.y)
7612  			).multiplyBy(speed);
7613
7614  			map.panBy(movement, {animate: false});
7615
7616  			this._draggable._newPos._add(movement);
7617  			this._draggable._startPos._add(movement);
7618
7619  			setPosition(marker._icon, this._draggable._newPos);
7620  			this._onDrag(e);
7621
7622  			this._panRequest = requestAnimFrame(this._adjustPan.bind(this, e));
7623  		}
7624  	},
7625
7626  	_onDragStart: function () {
7627  		// @section Dragging events
7628  		// @event dragstart: Event
7629  		// Fired when the user starts dragging the marker.
7630
7631  		// @event movestart: Event
7632  		// Fired when the marker starts moving (because of dragging).
7633
7634  		this._oldLatLng = this._marker.getLatLng();
7635
7636  		// When using ES6 imports it could not be set when `Popup` was not imported as well
7637  		this._marker.closePopup && this._marker.closePopup();
7638
7639  		this._marker
7640  			.fire('movestart')
7641  			.fire('dragstart');
7642  	},
7643
7644  	_onPreDrag: function (e) {
7645  		if (this._marker.options.autoPan) {
7646  			cancelAnimFrame(this._panRequest);
7647  			this._panRequest = requestAnimFrame(this._adjustPan.bind(this, e));
7648  		}
7649  	},
7650
7651  	_onDrag: function (e) {
7652  		var marker = this._marker,
7653  		    shadow = marker._shadow,
7654  		    iconPos = getPosition(marker._icon),
7655  		    latlng = marker._map.layerPointToLatLng(iconPos);
7656
7657  		// update shadow position
7658  		if (shadow) {
7659  			setPosition(shadow, iconPos);
7660  		}
7661
7662  		marker._latlng = latlng;
7663  		e.latlng = latlng;
7664  		e.oldLatLng = this._oldLatLng;
7665
7666  		// @event drag: Event
7667  		// Fired repeatedly while the user drags the marker.
7668  		marker
7669  		    .fire('move', e)
7670  		    .fire('drag', e);
7671  	},
7672
7673  	_onDragEnd: function (e) {
7674  		// @event dragend: DragEndEvent
7675  		// Fired when the user stops dragging the marker.
7676
7677  		 cancelAnimFrame(this._panRequest);
7678
7679  		// @event moveend: Event
7680  		// Fired when the marker stops moving (because of dragging).
7681  		delete this._oldLatLng;
7682  		this._marker
7683  		    .fire('moveend')
7684  		    .fire('dragend', e);
7685  	}
7686  });
7687
7688  /*
7689   * @class Marker
7690   * @inherits Interactive layer
7691   * @aka L.Marker
7692   * L.Marker is used to display clickable/draggable icons on the map. Extends `Layer`.
7693   *
7694   * @example
7695   *
7696   * ```js
7697   * L.marker([50.5, 30.5]).addTo(map);
7698   * ```
7699   */
7700
7701  var Marker = Layer.extend({
7702
7703  	// @section
7704  	// @aka Marker options
7705  	options: {
7706  		// @option icon: Icon = *
7707  		// Icon instance to use for rendering the marker.
7708  		// See [Icon documentation](#L.Icon) for details on how to customize the marker icon.
7709  		// If not specified, a common instance of `L.Icon.Default` is used.
7710  		icon: new IconDefault(),
7711
7712  		// Option inherited from "Interactive layer" abstract class
7713  		interactive: true,
7714
7715  		// @option keyboard: Boolean = true
7716  		// Whether the marker can be tabbed to with a keyboard and clicked by pressing enter.
7717  		keyboard: true,
7718
7719  		// @option title: String = ''
7720  		// Text for the browser tooltip that appear on marker hover (no tooltip by default).
7721  		// [Useful for accessibility](https://leafletjs.com/examples/accessibility/#markers-must-be-labelled).
7722  		title: '',
7723
7724  		// @option alt: String = 'Marker'
7725  		// Text for the `alt` attribute of the icon image.
7726  		// [Useful for accessibility](https://leafletjs.com/examples/accessibility/#markers-must-be-labelled).
7727  		alt: 'Marker',
7728
7729  		// @option zIndexOffset: Number = 0
7730  		// 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).
7731  		zIndexOffset: 0,
7732
7733  		// @option opacity: Number = 1.0
7734  		// The opacity of the marker.
7735  		opacity: 1,
7736
7737  		// @option riseOnHover: Boolean = false
7738  		// If `true`, the marker will get on top of others when you hover the mouse over it.
7739  		riseOnHover: false,
7740
7741  		// @option riseOffset: Number = 250
7742  		// The z-index offset used for the `riseOnHover` feature.
7743  		riseOffset: 250,
7744
7745  		// @option pane: String = 'markerPane'
7746  		// `Map pane` where the markers icon will be added.
7747  		pane: 'markerPane',
7748
7749  		// @option shadowPane: String = 'shadowPane'
7750  		// `Map pane` where the markers shadow will be added.
7751  		shadowPane: 'shadowPane',
7752
7753  		// @option bubblingMouseEvents: Boolean = false
7754  		// When `true`, a mouse event on this marker will trigger the same event on the map
7755  		// (unless [`L.DomEvent.stopPropagation`](#domevent-stoppropagation) is used).
7756  		bubblingMouseEvents: false,
7757
7758  		// @option autoPanOnFocus: Boolean = true
7759  		// When `true`, the map will pan whenever the marker is focused (via
7760  		// e.g. pressing `tab` on the keyboard) to ensure the marker is
7761  		// visible within the map's bounds
7762  		autoPanOnFocus: true,
7763
7764  		// @section Draggable marker options
7765  		// @option draggable: Boolean = false
7766  		// Whether the marker is draggable with mouse/touch or not.
7767  		draggable: false,
7768
7769  		// @option autoPan: Boolean = false
7770  		// Whether to pan the map when dragging this marker near its edge or not.
7771  		autoPan: false,
7772
7773  		// @option autoPanPadding: Point = Point(50, 50)
7774  		// Distance (in pixels to the left/right and to the top/bottom) of the
7775  		// map edge to start panning the map.
7776  		autoPanPadding: [50, 50],
7777
7778  		// @option autoPanSpeed: Number = 10
7779  		// Number of pixels the map should pan by.
7780  		autoPanSpeed: 10
7781  	},
7782
7783  	/* @section
7784  	 *
7785  	 * In addition to [shared layer methods](#Layer) like `addTo()` and `remove()` and [popup methods](#Popup) like bindPopup() you can also use the following methods:
7786  	 */
7787
7788  	initialize: function (latlng, options) {
7789  		setOptions(this, options);
7790  		this._latlng = toLatLng(latlng);
7791  	},
7792
7793  	onAdd: function (map) {
7794  		this._zoomAnimated = this._zoomAnimated && map.options.markerZoomAnimation;
7795
7796  		if (this._zoomAnimated) {
7797  			map.on('zoomanim', this._animateZoom, this);
7798  		}
7799
7800  		this._initIcon();
7801  		this.update();
7802  	},
7803
7804  	onRemove: function (map) {
7805  		if (this.dragging && this.dragging.enabled()) {
7806  			this.options.draggable = true;
7807  			this.dragging.removeHooks();
7808  		}
7809  		delete this.dragging;
7810
7811  		if (this._zoomAnimated) {
7812  			map.off('zoomanim', this._animateZoom, this);
7813  		}
7814
7815  		this._removeIcon();
7816  		this._removeShadow();
7817  	},
7818
7819  	getEvents: function () {
7820  		return {
7821  			zoom: this.update,
7822  			viewreset: this.update
7823  		};
7824  	},
7825
7826  	// @method getLatLng: LatLng
7827  	// Returns the current geographical position of the marker.
7828  	getLatLng: function () {
7829  		return this._latlng;
7830  	},
7831
7832  	// @method setLatLng(latlng: LatLng): this
7833  	// Changes the marker position to the given point.
7834  	setLatLng: function (latlng) {
7835  		var oldLatLng = this._latlng;
7836  		this._latlng = toLatLng(latlng);
7837  		this.update();
7838
7839  		// @event move: Event
7840  		// 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`.
7841  		return this.fire('move', {oldLatLng: oldLatLng, latlng: this._latlng});
7842  	},
7843
7844  	// @method setZIndexOffset(offset: Number): this
7845  	// Changes the [zIndex offset](#marker-zindexoffset) of the marker.
7846  	setZIndexOffset: function (offset) {
7847  		this.options.zIndexOffset = offset;
7848  		return this.update();
7849  	},
7850
7851  	// @method getIcon: Icon
7852  	// Returns the current icon used by the marker
7853  	getIcon: function () {
7854  		return this.options.icon;
7855  	},
7856
7857  	// @method setIcon(icon: Icon): this
7858  	// Changes the marker icon.
7859  	setIcon: function (icon) {
7860
7861  		this.options.icon = icon;
7862
7863  		if (this._map) {
7864  			this._initIcon();
7865  			this.update();
7866  		}
7867
7868  		if (this._popup) {
7869  			this.bindPopup(this._popup, this._popup.options);
7870  		}
7871
7872  		return this;
7873  	},
7874
7875  	getElement: function () {
7876  		return this._icon;
7877  	},
7878
7879  	update: function () {
7880
7881  		if (this._icon && this._map) {
7882  			var pos = this._map.latLngToLayerPoint(this._latlng).round();
7883  			this._setPos(pos);
7884  		}
7885
7886  		return this;
7887  	},
7888
7889  	_initIcon: function () {
7890  		var options = this.options,
7891  		    classToAdd = 'leaflet-zoom-' + (this._zoomAnimated ? 'animated' : 'hide');
7892
7893  		var icon = options.icon.createIcon(this._icon),
7894  		    addIcon = false;
7895
7896  		// if we're not reusing the icon, remove the old one and init new one
7897  		if (icon !== this._icon) {
7898  			if (this._icon) {
7899  				this._removeIcon();
7900  			}
7901  			addIcon = true;
7902
7903  			if (options.title) {
7904  				icon.title = options.title;
7905  			}
7906
7907  			if (icon.tagName === 'IMG') {
7908  				icon.alt = options.alt || '';
7909  			}
7910  		}
7911
7912  		addClass(icon, classToAdd);
7913
7914  		if (options.keyboard) {
7915  			icon.tabIndex = '0';
7916  			icon.setAttribute('role', 'button');
7917  		}
7918
7919  		this._icon = icon;
7920
7921  		if (options.riseOnHover) {
7922  			this.on({
7923  				mouseover: this._bringToFront,
7924  				mouseout: this._resetZIndex
7925  			});
7926  		}
7927
7928  		if (this.options.autoPanOnFocus) {
7929  			on(icon, 'focus', this._panOnFocus, this);
7930  		}
7931
7932  		var newShadow = options.icon.createShadow(this._shadow),
7933  		    addShadow = false;
7934
7935  		if (newShadow !== this._shadow) {
7936  			this._removeShadow();
7937  			addShadow = true;
7938  		}
7939
7940  		if (newShadow) {
7941  			addClass(newShadow, classToAdd);
7942  			newShadow.alt = '';
7943  		}
7944  		this._shadow = newShadow;
7945
7946
7947  		if (options.opacity < 1) {
7948  			this._updateOpacity();
7949  		}
7950
7951
7952  		if (addIcon) {
7953  			this.getPane().appendChild(this._icon);
7954  		}
7955  		this._initInteraction();
7956  		if (newShadow && addShadow) {
7957  			this.getPane(options.shadowPane).appendChild(this._shadow);
7958  		}
7959  	},
7960
7961  	_removeIcon: function () {
7962  		if (this.options.riseOnHover) {
7963  			this.off({
7964  				mouseover: this._bringToFront,
7965  				mouseout: this._resetZIndex
7966  			});
7967  		}
7968
7969  		if (this.options.autoPanOnFocus) {
7970  			off(this._icon, 'focus', this._panOnFocus, this);
7971  		}
7972
7973  		remove(this._icon);
7974  		this.removeInteractiveTarget(this._icon);
7975
7976  		this._icon = null;
7977  	},
7978
7979  	_removeShadow: function () {
7980  		if (this._shadow) {
7981  			remove(this._shadow);
7982  		}
7983  		this._shadow = null;
7984  	},
7985
7986  	_setPos: function (pos) {
7987
7988  		if (this._icon) {
7989  			setPosition(this._icon, pos);
7990  		}
7991
7992  		if (this._shadow) {
7993  			setPosition(this._shadow, pos);
7994  		}
7995
7996  		this._zIndex = pos.y + this.options.zIndexOffset;
7997
7998  		this._resetZIndex();
7999  	},
8000
8001  	_updateZIndex: function (offset) {
8002  		if (this._icon) {
8003  			this._icon.style.zIndex = this._zIndex + offset;
8004  		}
8005  	},
8006
8007  	_animateZoom: function (opt) {
8008  		var pos = this._map._latLngToNewLayerPoint(this._latlng, opt.zoom, opt.center).round();
8009
8010  		this._setPos(pos);
8011  	},
8012
8013  	_initInteraction: function () {
8014
8015  		if (!this.options.interactive) { return; }
8016
8017  		addClass(this._icon, 'leaflet-interactive');
8018
8019  		this.addInteractiveTarget(this._icon);
8020
8021  		if (MarkerDrag) {
8022  			var draggable = this.options.draggable;
8023  			if (this.dragging) {
8024  				draggable = this.dragging.enabled();
8025  				this.dragging.disable();
8026  			}
8027
8028  			this.dragging = new MarkerDrag(this);
8029
8030  			if (draggable) {
8031  				this.dragging.enable();
8032  			}
8033  		}
8034  	},
8035
8036  	// @method setOpacity(opacity: Number): this
8037  	// Changes the opacity of the marker.
8038  	setOpacity: function (opacity) {
8039  		this.options.opacity = opacity;
8040  		if (this._map) {
8041  			this._updateOpacity();
8042  		}
8043
8044  		return this;
8045  	},
8046
8047  	_updateOpacity: function () {
8048  		var opacity = this.options.opacity;
8049
8050  		if (this._icon) {
8051  			setOpacity(this._icon, opacity);
8052  		}
8053
8054  		if (this._shadow) {
8055  			setOpacity(this._shadow, opacity);
8056  		}
8057  	},
8058
8059  	_bringToFront: function () {
8060  		this._updateZIndex(this.options.riseOffset);
8061  	},
8062
8063  	_resetZIndex: function () {
8064  		this._updateZIndex(0);
8065  	},
8066
8067  	_panOnFocus: function () {
8068  		var map = this._map;
8069  		if (!map) { return; }
8070
8071  		var iconOpts = this.options.icon.options;
8072  		var size = iconOpts.iconSize ? toPoint(iconOpts.iconSize) : toPoint(0, 0);
8073  		var anchor = iconOpts.iconAnchor ? toPoint(iconOpts.iconAnchor) : toPoint(0, 0);
8074
8075  		map.panInside(this._latlng, {
8076  			paddingTopLeft: anchor,
8077  			paddingBottomRight: size.subtract(anchor)
8078  		});
8079  	},
8080
8081  	_getPopupAnchor: function () {
8082  		return this.options.icon.options.popupAnchor;
8083  	},
8084
8085  	_getTooltipAnchor: function () {
8086  		return this.options.icon.options.tooltipAnchor;
8087  	}
8088  });
8089
8090
8091  // factory L.marker(latlng: LatLng, options? : Marker options)
8092
8093  // @factory L.marker(latlng: LatLng, options? : Marker options)
8094  // Instantiates a Marker object given a geographical point and optionally an options object.
8095  function marker(latlng, options) {
8096  	return new Marker(latlng, options);
8097  }
8098
8099  /*
8100   * @class Path
8101   * @aka L.Path
8102   * @inherits Interactive layer
8103   *
8104   * An abstract class that contains options and constants shared between vector
8105   * overlays (Polygon, Polyline, Circle). Do not use it directly. Extends `Layer`.
8106   */
8107
8108  var Path = Layer.extend({
8109
8110  	// @section
8111  	// @aka Path options
8112  	options: {
8113  		// @option stroke: Boolean = true
8114  		// Whether to draw stroke along the path. Set it to `false` to disable borders on polygons or circles.
8115  		stroke: true,
8116
8117  		// @option color: String = '#3388ff'
8118  		// Stroke color
8119  		color: '#3388ff',
8120
8121  		// @option weight: Number = 3
8122  		// Stroke width in pixels
8123  		weight: 3,
8124
8125  		// @option opacity: Number = 1.0
8126  		// Stroke opacity
8127  		opacity: 1,
8128
8129  		// @option lineCap: String= 'round'
8130  		// A string that defines [shape to be used at the end](https://developer.mozilla.org/docs/Web/SVG/Attribute/stroke-linecap) of the stroke.
8131  		lineCap: 'round',
8132
8133  		// @option lineJoin: String = 'round'
8134  		// A string that defines [shape to be used at the corners](https://developer.mozilla.org/docs/Web/SVG/Attribute/stroke-linejoin) of the stroke.
8135  		lineJoin: 'round',
8136
8137  		// @option dashArray: String = null
8138  		// 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).
8139  		dashArray: null,
8140
8141  		// @option dashOffset: String = null
8142  		// 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).
8143  		dashOffset: null,
8144
8145  		// @option fill: Boolean = depends
8146  		// Whether to fill the path with color. Set it to `false` to disable filling on polygons or circles.
8147  		fill: false,
8148
8149  		// @option fillColor: String = *
8150  		// Fill color. Defaults to the value of the [`color`](#path-color) option
8151  		fillColor: null,
8152
8153  		// @option fillOpacity: Number = 0.2
8154  		// Fill opacity.
8155  		fillOpacity: 0.2,
8156
8157  		// @option fillRule: String = 'evenodd'
8158  		// A string that defines [how the inside of a shape](https://developer.mozilla.org/docs/Web/SVG/Attribute/fill-rule) is determined.
8159  		fillRule: 'evenodd',
8160
8161  		// className: '',
8162
8163  		// Option inherited from "Interactive layer" abstract class
8164  		interactive: true,
8165
8166  		// @option bubblingMouseEvents: Boolean = true
8167  		// When `true`, a mouse event on this path will trigger the same event on the map
8168  		// (unless [`L.DomEvent.stopPropagation`](#domevent-stoppropagation) is used).
8169  		bubblingMouseEvents: true
8170  	},
8171
8172  	beforeAdd: function (map) {
8173  		// Renderer is set here because we need to call renderer.getEvents
8174  		// before this.getEvents.
8175  		this._renderer = map.getRenderer(this);
8176  	},
8177
8178  	onAdd: function () {
8179  		this._renderer._initPath(this);
8180  		this._reset();
8181  		this._renderer._addPath(this);
8182  	},
8183
8184  	onRemove: function () {
8185  		this._renderer._removePath(this);
8186  	},
8187
8188  	// @method redraw(): this
8189  	// Redraws the layer. Sometimes useful after you changed the coordinates that the path uses.
8190  	redraw: function () {
8191  		if (this._map) {
8192  			this._renderer._updatePath(this);
8193  		}
8194  		return this;
8195  	},
8196
8197  	// @method setStyle(style: Path options): this
8198  	// Changes the appearance of a Path based on the options in the `Path options` object.
8199  	setStyle: function (style) {
8200  		setOptions(this, style);
8201  		if (this._renderer) {
8202  			this._renderer._updateStyle(this);
8203  			if (this.options.stroke && style && Object.prototype.hasOwnProperty.call(style, 'weight')) {
8204  				this._updateBounds();
8205  			}
8206  		}
8207  		return this;
8208  	},
8209
8210  	// @method bringToFront(): this
8211  	// Brings the layer to the top of all path layers.
8212  	bringToFront: function () {
8213  		if (this._renderer) {
8214  			this._renderer._bringToFront(this);
8215  		}
8216  		return this;
8217  	},
8218
8219  	// @method bringToBack(): this
8220  	// Brings the layer to the bottom of all path layers.
8221  	bringToBack: function () {
8222  		if (this._renderer) {
8223  			this._renderer._bringToBack(this);
8224  		}
8225  		return this;
8226  	},
8227
8228  	getElement: function () {
8229  		return this._path;
8230  	},
8231
8232  	_reset: function () {
8233  		// defined in child classes
8234  		this._project();
8235  		this._update();
8236  	},
8237
8238  	_clickTolerance: function () {
8239  		// used when doing hit detection for Canvas layers
8240  		return (this.options.stroke ? this.options.weight / 2 : 0) +
8241  		  (this._renderer.options.tolerance || 0);
8242  	}
8243  });
8244
8245  /*
8246   * @class CircleMarker
8247   * @aka L.CircleMarker
8248   * @inherits Path
8249   *
8250   * A circle of a fixed size with radius specified in pixels. Extends `Path`.
8251   */
8252
8253  var CircleMarker = Path.extend({
8254
8255  	// @section
8256  	// @aka CircleMarker options
8257  	options: {
8258  		fill: true,
8259
8260  		// @option radius: Number = 10
8261  		// Radius of the circle marker, in pixels
8262  		radius: 10
8263  	},
8264
8265  	initialize: function (latlng, options) {
8266  		setOptions(this, options);
8267  		this._latlng = toLatLng(latlng);
8268  		this._radius = this.options.radius;
8269  	},
8270
8271  	// @method setLatLng(latLng: LatLng): this
8272  	// Sets the position of a circle marker to a new location.
8273  	setLatLng: function (latlng) {
8274  		var oldLatLng = this._latlng;
8275  		this._latlng = toLatLng(latlng);
8276  		this.redraw();
8277
8278  		// @event move: Event
8279  		// Fired when the marker is moved via [`setLatLng`](#circlemarker-setlatlng). Old and new coordinates are included in event arguments as `oldLatLng`, `latlng`.
8280  		return this.fire('move', {oldLatLng: oldLatLng, latlng: this._latlng});
8281  	},
8282
8283  	// @method getLatLng(): LatLng
8284  	// Returns the current geographical position of the circle marker
8285  	getLatLng: function () {
8286  		return this._latlng;
8287  	},
8288
8289  	// @method setRadius(radius: Number): this
8290  	// Sets the radius of a circle marker. Units are in pixels.
8291  	setRadius: function (radius) {
8292  		this.options.radius = this._radius = radius;
8293  		return this.redraw();
8294  	},
8295
8296  	// @method getRadius(): Number
8297  	// Returns the current radius of the circle
8298  	getRadius: function () {
8299  		return this._radius;
8300  	},
8301
8302  	setStyle : function (options) {
8303  		var radius = options && options.radius || this._radius;
8304  		Path.prototype.setStyle.call(this, options);
8305  		this.setRadius(radius);
8306  		return this;
8307  	},
8308
8309  	_project: function () {
8310  		this._point = this._map.latLngToLayerPoint(this._latlng);
8311  		this._updateBounds();
8312  	},
8313
8314  	_updateBounds: function () {
8315  		var r = this._radius,
8316  		    r2 = this._radiusY || r,
8317  		    w = this._clickTolerance(),
8318  		    p = [r + w, r2 + w];
8319  		this._pxBounds = new Bounds(this._point.subtract(p), this._point.add(p));
8320  	},
8321
8322  	_update: function () {
8323  		if (this._map) {
8324  			this._updatePath();
8325  		}
8326  	},
8327
8328  	_updatePath: function () {
8329  		this._renderer._updateCircle(this);
8330  	},
8331
8332  	_empty: function () {
8333  		return this._radius && !this._renderer._bounds.intersects(this._pxBounds);
8334  	},
8335
8336  	// Needed by the `Canvas` renderer for interactivity
8337  	_containsPoint: function (p) {
8338  		return p.distanceTo(this._point) <= this._radius + this._clickTolerance();
8339  	}
8340  });
8341
8342
8343  // @factory L.circleMarker(latlng: LatLng, options?: CircleMarker options)
8344  // Instantiates a circle marker object given a geographical point, and an optional options object.
8345  function circleMarker(latlng, options) {
8346  	return new CircleMarker(latlng, options);
8347  }
8348
8349  /*
8350   * @class Circle
8351   * @aka L.Circle
8352   * @inherits CircleMarker
8353   *
8354   * A class for drawing circle overlays on a map. Extends `CircleMarker`.
8355   *
8356   * It's an approximation and starts to diverge from a real circle closer to poles (due to projection distortion).
8357   *
8358   * @example
8359   *
8360   * ```js
8361   * L.circle([50.5, 30.5], {radius: 200}).addTo(map);
8362   * ```
8363   */
8364
8365  var Circle = CircleMarker.extend({
8366
8367  	initialize: function (latlng, options, legacyOptions) {
8368  		if (typeof options === 'number') {
8369  			// Backwards compatibility with 0.7.x factory (latlng, radius, options?)
8370  			options = extend({}, legacyOptions, {radius: options});
8371  		}
8372  		setOptions(this, options);
8373  		this._latlng = toLatLng(latlng);
8374
8375  		if (isNaN(this.options.radius)) { throw new Error('Circle radius cannot be NaN'); }
8376
8377  		// @section
8378  		// @aka Circle options
8379  		// @option radius: Number; Radius of the circle, in meters.
8380  		this._mRadius = this.options.radius;
8381  	},
8382
8383  	// @method setRadius(radius: Number): this
8384  	// Sets the radius of a circle. Units are in meters.
8385  	setRadius: function (radius) {
8386  		this._mRadius = radius;
8387  		return this.redraw();
8388  	},
8389
8390  	// @method getRadius(): Number
8391  	// Returns the current radius of a circle. Units are in meters.
8392  	getRadius: function () {
8393  		return this._mRadius;
8394  	},
8395
8396  	// @method getBounds(): LatLngBounds
8397  	// Returns the `LatLngBounds` of the path.
8398  	getBounds: function () {
8399  		var half = [this._radius, this._radiusY || this._radius];
8400
8401  		return new LatLngBounds(
8402  			this._map.layerPointToLatLng(this._point.subtract(half)),
8403  			this._map.layerPointToLatLng(this._point.add(half)));
8404  	},
8405
8406  	setStyle: Path.prototype.setStyle,
8407
8408  	_project: function () {
8409
8410  		var lng = this._latlng.lng,
8411  		    lat = this._latlng.lat,
8412  		    map = this._map,
8413  		    crs = map.options.crs;
8414
8415  		if (crs.distance === Earth.distance) {
8416  			var d = Math.PI / 180,
8417  			    latR = (this._mRadius / Earth.R) / d,
8418  			    top = map.project([lat + latR, lng]),
8419  			    bottom = map.project([lat - latR, lng]),
8420  			    p = top.add(bottom).divideBy(2),
8421  			    lat2 = map.unproject(p).lat,
8422  			    lngR = Math.acos((Math.cos(latR * d) - Math.sin(lat * d) * Math.sin(lat2 * d)) /
8423  			            (Math.cos(lat * d) * Math.cos(lat2 * d))) / d;
8424
8425  			if (isNaN(lngR) || lngR === 0) {
8426  				lngR = latR / Math.cos(Math.PI / 180 * lat); // Fallback for edge case, #2425
8427  			}
8428
8429  			this._point = p.subtract(map.getPixelOrigin());
8430  			this._radius = isNaN(lngR) ? 0 : p.x - map.project([lat2, lng - lngR]).x;
8431  			this._radiusY = p.y - top.y;
8432
8433  		} else {
8434  			var latlng2 = crs.unproject(crs.project(this._latlng).subtract([this._mRadius, 0]));
8435
8436  			this._point = map.latLngToLayerPoint(this._latlng);
8437  			this._radius = this._point.x - map.latLngToLayerPoint(latlng2).x;
8438  		}
8439
8440  		this._updateBounds();
8441  	}
8442  });
8443
8444  // @factory L.circle(latlng: LatLng, options?: Circle options)
8445  // Instantiates a circle object given a geographical point, and an options object
8446  // which contains the circle radius.
8447  // @alternative
8448  // @factory L.circle(latlng: LatLng, radius: Number, options?: Circle options)
8449  // Obsolete way of instantiating a circle, for compatibility with 0.7.x code.
8450  // Do not use in new applications or plugins.
8451  function circle(latlng, options, legacyOptions) {
8452  	return new Circle(latlng, options, legacyOptions);
8453  }
8454
8455  /*
8456   * @class Polyline
8457   * @aka L.Polyline
8458   * @inherits Path
8459   *
8460   * A class for drawing polyline overlays on a map. Extends `Path`.
8461   *
8462   * @example
8463   *
8464   * ```js
8465   * // create a red polyline from an array of LatLng points
8466   * var latlngs = [
8467   * 	[45.51, -122.68],
8468   * 	[37.77, -122.43],
8469   * 	[34.04, -118.2]
8470   * ];
8471   *
8472   * var polyline = L.polyline(latlngs, {color: 'red'}).addTo(map);
8473   *
8474   * // zoom the map to the polyline
8475   * map.fitBounds(polyline.getBounds());
8476   * ```
8477   *
8478   * You can also pass a multi-dimensional array to represent a `MultiPolyline` shape:
8479   *
8480   * ```js
8481   * // create a red polyline from an array of arrays of LatLng points
8482   * var latlngs = [
8483   * 	[[45.51, -122.68],
8484   * 	 [37.77, -122.43],
8485   * 	 [34.04, -118.2]],
8486   * 	[[40.78, -73.91],
8487   * 	 [41.83, -87.62],
8488   * 	 [32.76, -96.72]]
8489   * ];
8490   * ```
8491   */
8492
8493
8494  var Polyline = Path.extend({
8495
8496  	// @section
8497  	// @aka Polyline options
8498  	options: {
8499  		// @option smoothFactor: Number = 1.0
8500  		// How much to simplify the polyline on each zoom level. More means
8501  		// better performance and smoother look, and less means more accurate representation.
8502  		smoothFactor: 1.0,
8503
8504  		// @option noClip: Boolean = false
8505  		// Disable polyline clipping.
8506  		noClip: false
8507  	},
8508
8509  	initialize: function (latlngs, options) {
8510  		setOptions(this, options);
8511  		this._setLatLngs(latlngs);
8512  	},
8513
8514  	// @method getLatLngs(): LatLng[]
8515  	// Returns an array of the points in the path, or nested arrays of points in case of multi-polyline.
8516  	getLatLngs: function () {
8517  		return this._latlngs;
8518  	},
8519
8520  	// @method setLatLngs(latlngs: LatLng[]): this
8521  	// Replaces all the points in the polyline with the given array of geographical points.
8522  	setLatLngs: function (latlngs) {
8523  		this._setLatLngs(latlngs);
8524  		return this.redraw();
8525  	},
8526
8527  	// @method isEmpty(): Boolean
8528  	// Returns `true` if the Polyline has no LatLngs.
8529  	isEmpty: function () {
8530  		return !this._latlngs.length;
8531  	},
8532
8533  	// @method closestLayerPoint(p: Point): Point
8534  	// Returns the point closest to `p` on the Polyline.
8535  	closestLayerPoint: function (p) {
8536  		var minDistance = Infinity,
8537  		    minPoint = null,
8538  		    closest = _sqClosestPointOnSegment,
8539  		    p1, p2;
8540
8541  		for (var j = 0, jLen = this._parts.length; j < jLen; j++) {
8542  			var points = this._parts[j];
8543
8544  			for (var i = 1, len = points.length; i < len; i++) {
8545  				p1 = points[i - 1];
8546  				p2 = points[i];
8547
8548  				var sqDist = closest(p, p1, p2, true);
8549
8550  				if (sqDist < minDistance) {
8551  					minDistance = sqDist;
8552  					minPoint = closest(p, p1, p2);
8553  				}
8554  			}
8555  		}
8556  		if (minPoint) {
8557  			minPoint.distance = Math.sqrt(minDistance);
8558  		}
8559  		return minPoint;
8560  	},
8561
8562  	// @method getCenter(): LatLng
8563  	// Returns the center ([centroid](https://en.wikipedia.org/wiki/Centroid)) of the polyline.
8564  	getCenter: function () {
8565  		// throws error when not yet added to map as this center calculation requires projected coordinates
8566  		if (!this._map) {
8567  			throw new Error('Must add layer to map before using getCenter()');
8568  		}
8569  		return polylineCenter(this._defaultShape(), this._map.options.crs);
8570  	},
8571
8572  	// @method getBounds(): LatLngBounds
8573  	// Returns the `LatLngBounds` of the path.
8574  	getBounds: function () {
8575  		return this._bounds;
8576  	},
8577
8578  	// @method addLatLng(latlng: LatLng, latlngs?: LatLng[]): this
8579  	// Adds a given point to the polyline. By default, adds to the first ring of
8580  	// the polyline in case of a multi-polyline, but can be overridden by passing
8581  	// a specific ring as a LatLng array (that you can earlier access with [`getLatLngs`](#polyline-getlatlngs)).
8582  	addLatLng: function (latlng, latlngs) {
8583  		latlngs = latlngs || this._defaultShape();
8584  		latlng = toLatLng(latlng);
8585  		latlngs.push(latlng);
8586  		this._bounds.extend(latlng);
8587  		return this.redraw();
8588  	},
8589
8590  	_setLatLngs: function (latlngs) {
8591  		this._bounds = new LatLngBounds();
8592  		this._latlngs = this._convertLatLngs(latlngs);
8593  	},
8594
8595  	_defaultShape: function () {
8596  		return isFlat(this._latlngs) ? this._latlngs : this._latlngs[0];
8597  	},
8598
8599  	// recursively convert latlngs input into actual LatLng instances; calculate bounds along the way
8600  	_convertLatLngs: function (latlngs) {
8601  		var result = [],
8602  		    flat = isFlat(latlngs);
8603
8604  		for (var i = 0, len = latlngs.length; i < len; i++) {
8605  			if (flat) {
8606  				result[i] = toLatLng(latlngs[i]);
8607  				this._bounds.extend(result[i]);
8608  			} else {
8609  				result[i] = this._convertLatLngs(latlngs[i]);
8610  			}
8611  		}
8612
8613  		return result;
8614  	},
8615
8616  	_project: function () {
8617  		var pxBounds = new Bounds();
8618  		this._rings = [];
8619  		this._projectLatlngs(this._latlngs, this._rings, pxBounds);
8620
8621  		if (this._bounds.isValid() && pxBounds.isValid()) {
8622  			this._rawPxBounds = pxBounds;
8623  			this._updateBounds();
8624  		}
8625  	},
8626
8627  	_updateBounds: function () {
8628  		var w = this._clickTolerance(),
8629  		    p = new Point(w, w);
8630
8631  		if (!this._rawPxBounds) {
8632  			return;
8633  		}
8634
8635  		this._pxBounds = new Bounds([
8636  			this._rawPxBounds.min.subtract(p),
8637  			this._rawPxBounds.max.add(p)
8638  		]);
8639  	},
8640
8641  	// recursively turns latlngs into a set of rings with projected coordinates
8642  	_projectLatlngs: function (latlngs, result, projectedBounds) {
8643  		var flat = latlngs[0] instanceof LatLng,
8644  		    len = latlngs.length,
8645  		    i, ring;
8646
8647  		if (flat) {
8648  			ring = [];
8649  			for (i = 0; i < len; i++) {
8650  				ring[i] = this._map.latLngToLayerPoint(latlngs[i]);
8651  				projectedBounds.extend(ring[i]);
8652  			}
8653  			result.push(ring);
8654  		} else {
8655  			for (i = 0; i < len; i++) {
8656  				this._projectLatlngs(latlngs[i], result, projectedBounds);
8657  			}
8658  		}
8659  	},
8660
8661  	// clip polyline by renderer bounds so that we have less to render for performance
8662  	_clipPoints: function () {
8663  		var bounds = this._renderer._bounds;
8664
8665  		this._parts = [];
8666  		if (!this._pxBounds || !this._pxBounds.intersects(bounds)) {
8667  			return;
8668  		}
8669
8670  		if (this.options.noClip) {
8671  			this._parts = this._rings;
8672  			return;
8673  		}
8674
8675  		var parts = this._parts,
8676  		    i, j, k, len, len2, segment, points;
8677
8678  		for (i = 0, k = 0, len = this._rings.length; i < len; i++) {
8679  			points = this._rings[i];
8680
8681  			for (j = 0, len2 = points.length; j < len2 - 1; j++) {
8682  				segment = clipSegment(points[j], points[j + 1], bounds, j, true);
8683
8684  				if (!segment) { continue; }
8685
8686  				parts[k] = parts[k] || [];
8687  				parts[k].push(segment[0]);
8688
8689  				// if segment goes out of screen, or it's the last one, it's the end of the line part
8690  				if ((segment[1] !== points[j + 1]) || (j === len2 - 2)) {
8691  					parts[k].push(segment[1]);
8692  					k++;
8693  				}
8694  			}
8695  		}
8696  	},
8697
8698  	// simplify each clipped part of the polyline for performance
8699  	_simplifyPoints: function () {
8700  		var parts = this._parts,
8701  		    tolerance = this.options.smoothFactor;
8702
8703  		for (var i = 0, len = parts.length; i < len; i++) {
8704  			parts[i] = simplify(parts[i], tolerance);
8705  		}
8706  	},
8707
8708  	_update: function () {
8709  		if (!this._map) { return; }
8710
8711  		this._clipPoints();
8712  		this._simplifyPoints();
8713  		this._updatePath();
8714  	},
8715
8716  	_updatePath: function () {
8717  		this._renderer._updatePoly(this);
8718  	},
8719
8720  	// Needed by the `Canvas` renderer for interactivity
8721  	_containsPoint: function (p, closed) {
8722  		var i, j, k, len, len2, part,
8723  		    w = this._clickTolerance();
8724
8725  		if (!this._pxBounds || !this._pxBounds.contains(p)) { return false; }
8726
8727  		// hit detection for polylines
8728  		for (i = 0, len = this._parts.length; i < len; i++) {
8729  			part = this._parts[i];
8730
8731  			for (j = 0, len2 = part.length, k = len2 - 1; j < len2; k = j++) {
8732  				if (!closed && (j === 0)) { continue; }
8733
8734  				if (pointToSegmentDistance(p, part[k], part[j]) <= w) {
8735  					return true;
8736  				}
8737  			}
8738  		}
8739  		return false;
8740  	}
8741  });
8742
8743  // @factory L.polyline(latlngs: LatLng[], options?: Polyline options)
8744  // Instantiates a polyline object given an array of geographical points and
8745  // optionally an options object. You can create a `Polyline` object with
8746  // multiple separate lines (`MultiPolyline`) by passing an array of arrays
8747  // of geographic points.
8748  function polyline(latlngs, options) {
8749  	return new Polyline(latlngs, options);
8750  }
8751
8752  // Retrocompat. Allow plugins to support Leaflet versions before and after 1.1.
8753  Polyline._flat = _flat;
8754
8755  /*
8756   * @class Polygon
8757   * @aka L.Polygon
8758   * @inherits Polyline
8759   *
8760   * A class for drawing polygon overlays on a map. Extends `Polyline`.
8761   *
8762   * 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.
8763   *
8764   *
8765   * @example
8766   *
8767   * ```js
8768   * // create a red polygon from an array of LatLng points
8769   * var latlngs = [[37, -109.05],[41, -109.03],[41, -102.05],[37, -102.04]];
8770   *
8771   * var polygon = L.polygon(latlngs, {color: 'red'}).addTo(map);
8772   *
8773   * // zoom the map to the polygon
8774   * map.fitBounds(polygon.getBounds());
8775   * ```
8776   *
8777   * 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:
8778   *
8779   * ```js
8780   * var latlngs = [
8781   *   [[37, -109.05],[41, -109.03],[41, -102.05],[37, -102.04]], // outer ring
8782   *   [[37.29, -108.58],[40.71, -108.58],[40.71, -102.50],[37.29, -102.50]] // hole
8783   * ];
8784   * ```
8785   *
8786   * Additionally, you can pass a multi-dimensional array to represent a MultiPolygon shape.
8787   *
8788   * ```js
8789   * var latlngs = [
8790   *   [ // first polygon
8791   *     [[37, -109.05],[41, -109.03],[41, -102.05],[37, -102.04]], // outer ring
8792   *     [[37.29, -108.58],[40.71, -108.58],[40.71, -102.50],[37.29, -102.50]] // hole
8793   *   ],
8794   *   [ // second polygon
8795   *     [[41, -111.03],[45, -111.04],[45, -104.05],[41, -104.05]]
8796   *   ]
8797   * ];
8798   * ```
8799   */
8800
8801  var Polygon = Polyline.extend({
8802
8803  	options: {
8804  		fill: true
8805  	},
8806
8807  	isEmpty: function () {
8808  		return !this._latlngs.length || !this._latlngs[0].length;
8809  	},
8810
8811  	// @method getCenter(): LatLng
8812  	// Returns the center ([centroid](http://en.wikipedia.org/wiki/Centroid)) of the Polygon.
8813  	getCenter: function () {
8814  		// throws error when not yet added to map as this center calculation requires projected coordinates
8815  		if (!this._map) {
8816  			throw new Error('Must add layer to map before using getCenter()');
8817  		}
8818  		return polygonCenter(this._defaultShape(), this._map.options.crs);
8819  	},
8820
8821  	_convertLatLngs: function (latlngs) {
8822  		var result = Polyline.prototype._convertLatLngs.call(this, latlngs),
8823  		    len = result.length;
8824
8825  		// remove last point if it equals first one
8826  		if (len >= 2 && result[0] instanceof LatLng && result[0].equals(result[len - 1])) {
8827  			result.pop();
8828  		}
8829  		return result;
8830  	},
8831
8832  	_setLatLngs: function (latlngs) {
8833  		Polyline.prototype._setLatLngs.call(this, latlngs);
8834  		if (isFlat(this._latlngs)) {
8835  			this._latlngs = [this._latlngs];
8836  		}
8837  	},
8838
8839  	_defaultShape: function () {
8840  		return isFlat(this._latlngs[0]) ? this._latlngs[0] : this._latlngs[0][0];
8841  	},
8842
8843  	_clipPoints: function () {
8844  		// polygons need a different clipping algorithm so we redefine that
8845
8846  		var bounds = this._renderer._bounds,
8847  		    w = this.options.weight,
8848  		    p = new Point(w, w);
8849
8850  		// increase clip padding by stroke width to avoid stroke on clip edges
8851  		bounds = new Bounds(bounds.min.subtract(p), bounds.max.add(p));
8852
8853  		this._parts = [];
8854  		if (!this._pxBounds || !this._pxBounds.intersects(bounds)) {
8855  			return;
8856  		}
8857
8858  		if (this.options.noClip) {
8859  			this._parts = this._rings;
8860  			return;
8861  		}
8862
8863  		for (var i = 0, len = this._rings.length, clipped; i < len; i++) {
8864  			clipped = clipPolygon(this._rings[i], bounds, true);
8865  			if (clipped.length) {
8866  				this._parts.push(clipped);
8867  			}
8868  		}
8869  	},
8870
8871  	_updatePath: function () {
8872  		this._renderer._updatePoly(this, true);
8873  	},
8874
8875  	// Needed by the `Canvas` renderer for interactivity
8876  	_containsPoint: function (p) {
8877  		var inside = false,
8878  		    part, p1, p2, i, j, k, len, len2;
8879
8880  		if (!this._pxBounds || !this._pxBounds.contains(p)) { return false; }
8881
8882  		// ray casting algorithm for detecting if point is in polygon
8883  		for (i = 0, len = this._parts.length; i < len; i++) {
8884  			part = this._parts[i];
8885
8886  			for (j = 0, len2 = part.length, k = len2 - 1; j < len2; k = j++) {
8887  				p1 = part[j];
8888  				p2 = part[k];
8889
8890  				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)) {
8891  					inside = !inside;
8892  				}
8893  			}
8894  		}
8895
8896  		// also check if it's on polygon stroke
8897  		return inside || Polyline.prototype._containsPoint.call(this, p, true);
8898  	}
8899
8900  });
8901
8902
8903  // @factory L.polygon(latlngs: LatLng[], options?: Polyline options)
8904  function polygon(latlngs, options) {
8905  	return new Polygon(latlngs, options);
8906  }
8907
8908  /*
8909   * @class GeoJSON
8910   * @aka L.GeoJSON
8911   * @inherits FeatureGroup
8912   *
8913   * Represents a GeoJSON object or an array of GeoJSON objects. Allows you to parse
8914   * GeoJSON data and display it on the map. Extends `FeatureGroup`.
8915   *
8916   * @example
8917   *
8918   * ```js
8919   * L.geoJSON(data, {
8920   * 	style: function (feature) {
8921   * 		return {color: feature.properties.color};
8922   * 	}
8923   * }).bindPopup(function (layer) {
8924   * 	return layer.feature.properties.description;
8925   * }).addTo(map);
8926   * ```
8927   */
8928
8929  var GeoJSON = FeatureGroup.extend({
8930
8931  	/* @section
8932  	 * @aka GeoJSON options
8933  	 *
8934  	 * @option pointToLayer: Function = *
8935  	 * A `Function` defining how GeoJSON points spawn Leaflet layers. It is internally
8936  	 * called when data is added, passing the GeoJSON point feature and its `LatLng`.
8937  	 * The default is to spawn a default `Marker`:
8938  	 * ```js
8939  	 * function(geoJsonPoint, latlng) {
8940  	 * 	return L.marker(latlng);
8941  	 * }
8942  	 * ```
8943  	 *
8944  	 * @option style: Function = *
8945  	 * A `Function` defining the `Path options` for styling GeoJSON lines and polygons,
8946  	 * called internally when data is added.
8947  	 * The default value is to not override any defaults:
8948  	 * ```js
8949  	 * function (geoJsonFeature) {
8950  	 * 	return {}
8951  	 * }
8952  	 * ```
8953  	 *
8954  	 * @option onEachFeature: Function = *
8955  	 * A `Function` that will be called once for each created `Feature`, after it has
8956  	 * been created and styled. Useful for attaching events and popups to features.
8957  	 * The default is to do nothing with the newly created layers:
8958  	 * ```js
8959  	 * function (feature, layer) {}
8960  	 * ```
8961  	 *
8962  	 * @option filter: Function = *
8963  	 * A `Function` that will be used to decide whether to include a feature or not.
8964  	 * The default is to include all features:
8965  	 * ```js
8966  	 * function (geoJsonFeature) {
8967  	 * 	return true;
8968  	 * }
8969  	 * ```
8970  	 * Note: dynamically changing the `filter` option will have effect only on newly
8971  	 * added data. It will _not_ re-evaluate already included features.
8972  	 *
8973  	 * @option coordsToLatLng: Function = *
8974  	 * A `Function` that will be used for converting GeoJSON coordinates to `LatLng`s.
8975  	 * The default is the `coordsToLatLng` static method.
8976  	 *
8977  	 * @option markersInheritOptions: Boolean = false
8978  	 * Whether default Markers for "Point" type Features inherit from group options.
8979  	 */
8980
8981  	initialize: function (geojson, options) {
8982  		setOptions(this, options);
8983
8984  		this._layers = {};
8985
8986  		if (geojson) {
8987  			this.addData(geojson);
8988  		}
8989  	},
8990
8991  	// @method addData( <GeoJSON> data ): this
8992  	// Adds a GeoJSON object to the layer.
8993  	addData: function (geojson) {
8994  		var features = isArray(geojson) ? geojson : geojson.features,
8995  		    i, len, feature;
8996
8997  		if (features) {
8998  			for (i = 0, len = features.length; i < len; i++) {
8999  				// only add this if geometry or geometries are set and not null
9000  				feature = features[i];
9001  				if (feature.geometries || feature.geometry || feature.features || feature.coordinates) {
9002  					this.addData(feature);
9003  				}
9004  			}
9005  			return this;
9006  		}
9007
9008  		var options = this.options;
9009
9010  		if (options.filter && !options.filter(geojson)) { return this; }
9011
9012  		var layer = geometryToLayer(geojson, options);
9013  		if (!layer) {
9014  			return this;
9015  		}
9016  		layer.feature = asFeature(geojson);
9017
9018  		layer.defaultOptions = layer.options;
9019  		this.resetStyle(layer);
9020
9021  		if (options.onEachFeature) {
9022  			options.onEachFeature(geojson, layer);
9023  		}
9024
9025  		return this.addLayer(layer);
9026  	},
9027
9028  	// @method resetStyle( <Path> layer? ): this
9029  	// Resets the given vector layer's style to the original GeoJSON style, useful for resetting style after hover events.
9030  	// If `layer` is omitted, the style of all features in the current layer is reset.
9031  	resetStyle: function (layer) {
9032  		if (layer === undefined) {
9033  			return this.eachLayer(this.resetStyle, this);
9034  		}
9035  		// reset any custom styles
9036  		layer.options = extend({}, layer.defaultOptions);
9037  		this._setLayerStyle(layer, this.options.style);
9038  		return this;
9039  	},
9040
9041  	// @method setStyle( <Function> style ): this
9042  	// Changes styles of GeoJSON vector layers with the given style function.
9043  	setStyle: function (style) {
9044  		return this.eachLayer(function (layer) {
9045  			this._setLayerStyle(layer, style);
9046  		}, this);
9047  	},
9048
9049  	_setLayerStyle: function (layer, style) {
9050  		if (layer.setStyle) {
9051  			if (typeof style === 'function') {
9052  				style = style(layer.feature);
9053  			}
9054  			layer.setStyle(style);
9055  		}
9056  	}
9057  });
9058
9059  // @section
9060  // There are several static functions which can be called without instantiating L.GeoJSON:
9061
9062  // @function geometryToLayer(featureData: Object, options?: GeoJSON options): Layer
9063  // Creates a `Layer` from a given GeoJSON feature. Can use a custom
9064  // [`pointToLayer`](#geojson-pointtolayer) and/or [`coordsToLatLng`](#geojson-coordstolatlng)
9065  // functions if provided as options.
9066  function geometryToLayer(geojson, options) {
9067
9068  	var geometry = geojson.type === 'Feature' ? geojson.geometry : geojson,
9069  	    coords = geometry ? geometry.coordinates : null,
9070  	    layers = [],
9071  	    pointToLayer = options && options.pointToLayer,
9072  	    _coordsToLatLng = options && options.coordsToLatLng || coordsToLatLng,
9073  	    latlng, latlngs, i, len;
9074
9075  	if (!coords && !geometry) {
9076  		return null;
9077  	}
9078
9079  	switch (geometry.type) {
9080  	case 'Point':
9081  		latlng = _coordsToLatLng(coords);
9082  		return _pointToLayer(pointToLayer, geojson, latlng, options);
9083
9084  	case 'MultiPoint':
9085  		for (i = 0, len = coords.length; i < len; i++) {
9086  			latlng = _coordsToLatLng(coords[i]);
9087  			layers.push(_pointToLayer(pointToLayer, geojson, latlng, options));
9088  		}
9089  		return new FeatureGroup(layers);
9090
9091  	case 'LineString':
9092  	case 'MultiLineString':
9093  		latlngs = coordsToLatLngs(coords, geometry.type === 'LineString' ? 0 : 1, _coordsToLatLng);
9094  		return new Polyline(latlngs, options);
9095
9096  	case 'Polygon':
9097  	case 'MultiPolygon':
9098  		latlngs = coordsToLatLngs(coords, geometry.type === 'Polygon' ? 1 : 2, _coordsToLatLng);
9099  		return new Polygon(latlngs, options);
9100
9101  	case 'GeometryCollection':
9102  		for (i = 0, len = geometry.geometries.length; i < len; i++) {
9103  			var geoLayer = geometryToLayer({
9104  				geometry: geometry.geometries[i],
9105  				type: 'Feature',
9106  				properties: geojson.properties
9107  			}, options);
9108
9109  			if (geoLayer) {
9110  				layers.push(geoLayer);
9111  			}
9112  		}
9113  		return new FeatureGroup(layers);
9114
9115  	case 'FeatureCollection':
9116  		for (i = 0, len = geometry.features.length; i < len; i++) {
9117  			var featureLayer = geometryToLayer(geometry.features[i], options);
9118
9119  			if (featureLayer) {
9120  				layers.push(featureLayer);
9121  			}
9122  		}
9123  		return new FeatureGroup(layers);
9124
9125  	default:
9126  		throw new Error('Invalid GeoJSON object.');
9127  	}
9128  }
9129
9130  function _pointToLayer(pointToLayerFn, geojson, latlng, options) {
9131  	return pointToLayerFn ?
9132  		pointToLayerFn(geojson, latlng) :
9133  		new Marker(latlng, options && options.markersInheritOptions && options);
9134  }
9135
9136  // @function coordsToLatLng(coords: Array): LatLng
9137  // Creates a `LatLng` object from an array of 2 numbers (longitude, latitude)
9138  // or 3 numbers (longitude, latitude, altitude) used in GeoJSON for points.
9139  function coordsToLatLng(coords) {
9140  	return new LatLng(coords[1], coords[0], coords[2]);
9141  }
9142
9143  // @function coordsToLatLngs(coords: Array, levelsDeep?: Number, coordsToLatLng?: Function): Array
9144  // Creates a multidimensional array of `LatLng`s from a GeoJSON coordinates array.
9145  // `levelsDeep` specifies the nesting level (0 is for an array of points, 1 for an array of arrays of points, etc., 0 by default).
9146  // Can use a custom [`coordsToLatLng`](#geojson-coordstolatlng) function.
9147  function coordsToLatLngs(coords, levelsDeep, _coordsToLatLng) {
9148  	var latlngs = [];
9149
9150  	for (var i = 0, len = coords.length, latlng; i < len; i++) {
9151  		latlng = levelsDeep ?
9152  			coordsToLatLngs(coords[i], levelsDeep - 1, _coordsToLatLng) :
9153  			(_coordsToLatLng || coordsToLatLng)(coords[i]);
9154
9155  		latlngs.push(latlng);
9156  	}
9157
9158  	return latlngs;
9159  }
9160
9161  // @function latLngToCoords(latlng: LatLng, precision?: Number|false): Array
9162  // Reverse of [`coordsToLatLng`](#geojson-coordstolatlng)
9163  // Coordinates values are rounded with [`formatNum`](#util-formatnum) function.
9164  function latLngToCoords(latlng, precision) {
9165  	latlng = toLatLng(latlng);
9166  	return latlng.alt !== undefined ?
9167  		[formatNum(latlng.lng, precision), formatNum(latlng.lat, precision), formatNum(latlng.alt, precision)] :
9168  		[formatNum(latlng.lng, precision), formatNum(latlng.lat, precision)];
9169  }
9170
9171  // @function latLngsToCoords(latlngs: Array, levelsDeep?: Number, closed?: Boolean, precision?: Number|false): Array
9172  // Reverse of [`coordsToLatLngs`](#geojson-coordstolatlngs)
9173  // `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.
9174  // Coordinates values are rounded with [`formatNum`](#util-formatnum) function.
9175  function latLngsToCoords(latlngs, levelsDeep, closed, precision) {
9176  	var coords = [];
9177
9178  	for (var i = 0, len = latlngs.length; i < len; i++) {
9179  		// Check for flat arrays required to ensure unbalanced arrays are correctly converted in recursion
9180  		coords.push(levelsDeep ?
9181  			latLngsToCoords(latlngs[i], isFlat(latlngs[i]) ? 0 : levelsDeep - 1, closed, precision) :
9182  			latLngToCoords(latlngs[i], precision));
9183  	}
9184
9185  	if (!levelsDeep && closed && coords.length > 0) {
9186  		coords.push(coords[0].slice());
9187  	}
9188
9189  	return coords;
9190  }
9191
9192  function getFeature(layer, newGeometry) {
9193  	return layer.feature ?
9194  		extend({}, layer.feature, {geometry: newGeometry}) :
9195  		asFeature(newGeometry);
9196  }
9197
9198  // @function asFeature(geojson: Object): Object
9199  // Normalize GeoJSON geometries/features into GeoJSON features.
9200  function asFeature(geojson) {
9201  	if (geojson.type === 'Feature' || geojson.type === 'FeatureCollection') {
9202  		return geojson;
9203  	}
9204
9205  	return {
9206  		type: 'Feature',
9207  		properties: {},
9208  		geometry: geojson
9209  	};
9210  }
9211
9212  var PointToGeoJSON = {
9213  	toGeoJSON: function (precision) {
9214  		return getFeature(this, {
9215  			type: 'Point',
9216  			coordinates: latLngToCoords(this.getLatLng(), precision)
9217  		});
9218  	}
9219  };
9220
9221  // @namespace Marker
9222  // @section Other methods
9223  // @method toGeoJSON(precision?: Number|false): Object
9224  // Coordinates values are rounded with [`formatNum`](#util-formatnum) function with given `precision`.
9225  // Returns a [`GeoJSON`](https://en.wikipedia.org/wiki/GeoJSON) representation of the marker (as a GeoJSON `Point` Feature).
9226  Marker.include(PointToGeoJSON);
9227
9228  // @namespace CircleMarker
9229  // @method toGeoJSON(precision?: Number|false): Object
9230  // Coordinates values are rounded with [`formatNum`](#util-formatnum) function with given `precision`.
9231  // Returns a [`GeoJSON`](https://en.wikipedia.org/wiki/GeoJSON) representation of the circle marker (as a GeoJSON `Point` Feature).
9232  Circle.include(PointToGeoJSON);
9233  CircleMarker.include(PointToGeoJSON);
9234
9235
9236  // @namespace Polyline
9237  // @method toGeoJSON(precision?: Number|false): Object
9238  // Coordinates values are rounded with [`formatNum`](#util-formatnum) function with given `precision`.
9239  // Returns a [`GeoJSON`](https://en.wikipedia.org/wiki/GeoJSON) representation of the polyline (as a GeoJSON `LineString` or `MultiLineString` Feature).
9240  Polyline.include({
9241  	toGeoJSON: function (precision) {
9242  		var multi = !isFlat(this._latlngs);
9243
9244  		var coords = latLngsToCoords(this._latlngs, multi ? 1 : 0, false, precision);
9245
9246  		return getFeature(this, {
9247  			type: (multi ? 'Multi' : '') + 'LineString',
9248  			coordinates: coords
9249  		});
9250  	}
9251  });
9252
9253  // @namespace Polygon
9254  // @method toGeoJSON(precision?: Number|false): Object
9255  // Coordinates values are rounded with [`formatNum`](#util-formatnum) function with given `precision`.
9256  // Returns a [`GeoJSON`](https://en.wikipedia.org/wiki/GeoJSON) representation of the polygon (as a GeoJSON `Polygon` or `MultiPolygon` Feature).
9257  Polygon.include({
9258  	toGeoJSON: function (precision) {
9259  		var holes = !isFlat(this._latlngs),
9260  		    multi = holes && !isFlat(this._latlngs[0]);
9261
9262  		var coords = latLngsToCoords(this._latlngs, multi ? 2 : holes ? 1 : 0, true, precision);
9263
9264  		if (!holes) {
9265  			coords = [coords];
9266  		}
9267
9268  		return getFeature(this, {
9269  			type: (multi ? 'Multi' : '') + 'Polygon',
9270  			coordinates: coords
9271  		});
9272  	}
9273  });
9274
9275
9276  // @namespace LayerGroup
9277  LayerGroup.include({
9278  	toMultiPoint: function (precision) {
9279  		var coords = [];
9280
9281  		this.eachLayer(function (layer) {
9282  			coords.push(layer.toGeoJSON(precision).geometry.coordinates);
9283  		});
9284
9285  		return getFeature(this, {
9286  			type: 'MultiPoint',
9287  			coordinates: coords
9288  		});
9289  	},
9290
9291  	// @method toGeoJSON(precision?: Number|false): Object
9292  	// Coordinates values are rounded with [`formatNum`](#util-formatnum) function with given `precision`.
9293  	// Returns a [`GeoJSON`](https://en.wikipedia.org/wiki/GeoJSON) representation of the layer group (as a GeoJSON `FeatureCollection`, `GeometryCollection`, or `MultiPoint`).
9294  	toGeoJSON: function (precision) {
9295
9296  		var type = this.feature && this.feature.geometry && this.feature.geometry.type;
9297
9298  		if (type === 'MultiPoint') {
9299  			return this.toMultiPoint(precision);
9300  		}
9301
9302  		var isGeometryCollection = type === 'GeometryCollection',
9303  		    jsons = [];
9304
9305  		this.eachLayer(function (layer) {
9306  			if (layer.toGeoJSON) {
9307  				var json = layer.toGeoJSON(precision);
9308  				if (isGeometryCollection) {
9309  					jsons.push(json.geometry);
9310  				} else {
9311  					var feature = asFeature(json);
9312  					// Squash nested feature collections
9313  					if (feature.type === 'FeatureCollection') {
9314  						jsons.push.apply(jsons, feature.features);
9315  					} else {
9316  						jsons.push(feature);
9317  					}
9318  				}
9319  			}
9320  		});
9321
9322  		if (isGeometryCollection) {
9323  			return getFeature(this, {
9324  				geometries: jsons,
9325  				type: 'GeometryCollection'
9326  			});
9327  		}
9328
9329  		return {
9330  			type: 'FeatureCollection',
9331  			features: jsons
9332  		};
9333  	}
9334  });
9335
9336  // @namespace GeoJSON
9337  // @factory L.geoJSON(geojson?: Object, options?: GeoJSON options)
9338  // Creates a GeoJSON layer. Optionally accepts an object in
9339  // [GeoJSON format](https://tools.ietf.org/html/rfc7946) to display on the map
9340  // (you can alternatively add it later with `addData` method) and an `options` object.
9341  function geoJSON(geojson, options) {
9342  	return new GeoJSON(geojson, options);
9343  }
9344
9345  // Backward compatibility.
9346  var geoJson = geoJSON;
9347
9348  /*
9349   * @class ImageOverlay
9350   * @aka L.ImageOverlay
9351   * @inherits Interactive layer
9352   *
9353   * Used to load and display a single image over specific bounds of the map. Extends `Layer`.
9354   *
9355   * @example
9356   *
9357   * ```js
9358   * var imageUrl = 'https://maps.lib.utexas.edu/maps/historical/newark_nj_1922.jpg',
9359   * 	imageBounds = [[40.712216, -74.22655], [40.773941, -74.12544]];
9360   * L.imageOverlay(imageUrl, imageBounds).addTo(map);
9361   * ```
9362   */
9363
9364  var ImageOverlay = Layer.extend({
9365
9366  	// @section
9367  	// @aka ImageOverlay options
9368  	options: {
9369  		// @option opacity: Number = 1.0
9370  		// The opacity of the image overlay.
9371  		opacity: 1,
9372
9373  		// @option alt: String = ''
9374  		// Text for the `alt` attribute of the image (useful for accessibility).
9375  		alt: '',
9376
9377  		// @option interactive: Boolean = false
9378  		// If `true`, the image overlay will emit [mouse events](#interactive-layer) when clicked or hovered.
9379  		interactive: false,
9380
9381  		// @option crossOrigin: Boolean|String = false
9382  		// Whether the crossOrigin attribute will be added to the image.
9383  		// If a String is provided, the image will have its crossOrigin attribute set to the String provided. This is needed if you want to access image pixel data.
9384  		// Refer to [CORS Settings](https://developer.mozilla.org/en-US/docs/Web/HTML/CORS_settings_attributes) for valid String values.
9385  		crossOrigin: false,
9386
9387  		// @option errorOverlayUrl: String = ''
9388  		// URL to the overlay image to show in place of the overlay that failed to load.
9389  		errorOverlayUrl: '',
9390
9391  		// @option zIndex: Number = 1
9392  		// The explicit [zIndex](https://developer.mozilla.org/docs/Web/CSS/CSS_Positioning/Understanding_z_index) of the overlay layer.
9393  		zIndex: 1,
9394
9395  		// @option className: String = ''
9396  		// A custom class name to assign to the image. Empty by default.
9397  		className: ''
9398  	},
9399
9400  	initialize: function (url, bounds, options) { // (String, LatLngBounds, Object)
9401  		this._url = url;
9402  		this._bounds = toLatLngBounds(bounds);
9403
9404  		setOptions(this, options);
9405  	},
9406
9407  	onAdd: function () {
9408  		if (!this._image) {
9409  			this._initImage();
9410
9411  			if (this.options.opacity < 1) {
9412  				this._updateOpacity();
9413  			}
9414  		}
9415
9416  		if (this.options.interactive) {
9417  			addClass(this._image, 'leaflet-interactive');
9418  			this.addInteractiveTarget(this._image);
9419  		}
9420
9421  		this.getPane().appendChild(this._image);
9422  		this._reset();
9423  	},
9424
9425  	onRemove: function () {
9426  		remove(this._image);
9427  		if (this.options.interactive) {
9428  			this.removeInteractiveTarget(this._image);
9429  		}
9430  	},
9431
9432  	// @method setOpacity(opacity: Number): this
9433  	// Sets the opacity of the overlay.
9434  	setOpacity: function (opacity) {
9435  		this.options.opacity = opacity;
9436
9437  		if (this._image) {
9438  			this._updateOpacity();
9439  		}
9440  		return this;
9441  	},
9442
9443  	setStyle: function (styleOpts) {
9444  		if (styleOpts.opacity) {
9445  			this.setOpacity(styleOpts.opacity);
9446  		}
9447  		return this;
9448  	},
9449
9450  	// @method bringToFront(): this
9451  	// Brings the layer to the top of all overlays.
9452  	bringToFront: function () {
9453  		if (this._map) {
9454  			toFront(this._image);
9455  		}
9456  		return this;
9457  	},
9458
9459  	// @method bringToBack(): this
9460  	// Brings the layer to the bottom of all overlays.
9461  	bringToBack: function () {
9462  		if (this._map) {
9463  			toBack(this._image);
9464  		}
9465  		return this;
9466  	},
9467
9468  	// @method setUrl(url: String): this
9469  	// Changes the URL of the image.
9470  	setUrl: function (url) {
9471  		this._url = url;
9472
9473  		if (this._image) {
9474  			this._image.src = url;
9475  		}
9476  		return this;
9477  	},
9478
9479  	// @method setBounds(bounds: LatLngBounds): this
9480  	// Update the bounds that this ImageOverlay covers
9481  	setBounds: function (bounds) {
9482  		this._bounds = toLatLngBounds(bounds);
9483
9484  		if (this._map) {
9485  			this._reset();
9486  		}
9487  		return this;
9488  	},
9489
9490  	getEvents: function () {
9491  		var events = {
9492  			zoom: this._reset,
9493  			viewreset: this._reset
9494  		};
9495
9496  		if (this._zoomAnimated) {
9497  			events.zoomanim = this._animateZoom;
9498  		}
9499
9500  		return events;
9501  	},
9502
9503  	// @method setZIndex(value: Number): this
9504  	// Changes the [zIndex](#imageoverlay-zindex) of the image overlay.
9505  	setZIndex: function (value) {
9506  		this.options.zIndex = value;
9507  		this._updateZIndex();
9508  		return this;
9509  	},
9510
9511  	// @method getBounds(): LatLngBounds
9512  	// Get the bounds that this ImageOverlay covers
9513  	getBounds: function () {
9514  		return this._bounds;
9515  	},
9516
9517  	// @method getElement(): HTMLElement
9518  	// Returns the instance of [`HTMLImageElement`](https://developer.mozilla.org/docs/Web/API/HTMLImageElement)
9519  	// used by this overlay.
9520  	getElement: function () {
9521  		return this._image;
9522  	},
9523
9524  	_initImage: function () {
9525  		var wasElementSupplied = this._url.tagName === 'IMG';
9526  		var img = this._image = wasElementSupplied ? this._url : create$1('img');
9527
9528  		addClass(img, 'leaflet-image-layer');
9529  		if (this._zoomAnimated) { addClass(img, 'leaflet-zoom-animated'); }
9530  		if (this.options.className) { addClass(img, this.options.className); }
9531
9532  		img.onselectstart = falseFn;
9533  		img.onmousemove = falseFn;
9534
9535  		// @event load: Event
9536  		// Fired when the ImageOverlay layer has loaded its image
9537  		img.onload = bind(this.fire, this, 'load');
9538  		img.onerror = bind(this._overlayOnError, this, 'error');
9539
9540  		if (this.options.crossOrigin || this.options.crossOrigin === '') {
9541  			img.crossOrigin = this.options.crossOrigin === true ? '' : this.options.crossOrigin;
9542  		}
9543
9544  		if (this.options.zIndex) {
9545  			this._updateZIndex();
9546  		}
9547
9548  		if (wasElementSupplied) {
9549  			this._url = img.src;
9550  			return;
9551  		}
9552
9553  		img.src = this._url;
9554  		img.alt = this.options.alt;
9555  	},
9556
9557  	_animateZoom: function (e) {
9558  		var scale = this._map.getZoomScale(e.zoom),
9559  		    offset = this._map._latLngBoundsToNewLayerBounds(this._bounds, e.zoom, e.center).min;
9560
9561  		setTransform(this._image, offset, scale);
9562  	},
9563
9564  	_reset: function () {
9565  		var image = this._image,
9566  		    bounds = new Bounds(
9567  		        this._map.latLngToLayerPoint(this._bounds.getNorthWest()),
9568  		        this._map.latLngToLayerPoint(this._bounds.getSouthEast())),
9569  		    size = bounds.getSize();
9570
9571  		setPosition(image, bounds.min);
9572
9573  		image.style.width  = size.x + 'px';
9574  		image.style.height = size.y + 'px';
9575  	},
9576
9577  	_updateOpacity: function () {
9578  		setOpacity(this._image, this.options.opacity);
9579  	},
9580
9581  	_updateZIndex: function () {
9582  		if (this._image && this.options.zIndex !== undefined && this.options.zIndex !== null) {
9583  			this._image.style.zIndex = this.options.zIndex;
9584  		}
9585  	},
9586
9587  	_overlayOnError: function () {
9588  		// @event error: Event
9589  		// Fired when the ImageOverlay layer fails to load its image
9590  		this.fire('error');
9591
9592  		var errorUrl = this.options.errorOverlayUrl;
9593  		if (errorUrl && this._url !== errorUrl) {
9594  			this._url = errorUrl;
9595  			this._image.src = errorUrl;
9596  		}
9597  	},
9598
9599  	// @method getCenter(): LatLng
9600  	// Returns the center of the ImageOverlay.
9601  	getCenter: function () {
9602  		return this._bounds.getCenter();
9603  	}
9604  });
9605
9606  // @factory L.imageOverlay(imageUrl: String, bounds: LatLngBounds, options?: ImageOverlay options)
9607  // Instantiates an image overlay object given the URL of the image and the
9608  // geographical bounds it is tied to.
9609  var imageOverlay = function (url, bounds, options) {
9610  	return new ImageOverlay(url, bounds, options);
9611  };
9612
9613  /*
9614   * @class VideoOverlay
9615   * @aka L.VideoOverlay
9616   * @inherits ImageOverlay
9617   *
9618   * Used to load and display a video player over specific bounds of the map. Extends `ImageOverlay`.
9619   *
9620   * A video overlay uses the [`<video>`](https://developer.mozilla.org/docs/Web/HTML/Element/video)
9621   * HTML5 element.
9622   *
9623   * @example
9624   *
9625   * ```js
9626   * var videoUrl = 'https://www.mapbox.com/bites/00188/patricia_nasa.webm',
9627   * 	videoBounds = [[ 32, -130], [ 13, -100]];
9628   * L.videoOverlay(videoUrl, videoBounds ).addTo(map);
9629   * ```
9630   */
9631
9632  var VideoOverlay = ImageOverlay.extend({
9633
9634  	// @section
9635  	// @aka VideoOverlay options
9636  	options: {
9637  		// @option autoplay: Boolean = true
9638  		// Whether the video starts playing automatically when loaded.
9639  		// On some browsers autoplay will only work with `muted: true`
9640  		autoplay: true,
9641
9642  		// @option loop: Boolean = true
9643  		// Whether the video will loop back to the beginning when played.
9644  		loop: true,
9645
9646  		// @option keepAspectRatio: Boolean = true
9647  		// Whether the video will save aspect ratio after the projection.
9648  		// Relevant for supported browsers. See [browser compatibility](https://developer.mozilla.org/en-US/docs/Web/CSS/object-fit)
9649  		keepAspectRatio: true,
9650
9651  		// @option muted: Boolean = false
9652  		// Whether the video starts on mute when loaded.
9653  		muted: false,
9654
9655  		// @option playsInline: Boolean = true
9656  		// Mobile browsers will play the video right where it is instead of open it up in fullscreen mode.
9657  		playsInline: true
9658  	},
9659
9660  	_initImage: function () {
9661  		var wasElementSupplied = this._url.tagName === 'VIDEO';
9662  		var vid = this._image = wasElementSupplied ? this._url : create$1('video');
9663
9664  		addClass(vid, 'leaflet-image-layer');
9665  		if (this._zoomAnimated) { addClass(vid, 'leaflet-zoom-animated'); }
9666  		if (this.options.className) { addClass(vid, this.options.className); }
9667
9668  		vid.onselectstart = falseFn;
9669  		vid.onmousemove = falseFn;
9670
9671  		// @event load: Event
9672  		// Fired when the video has finished loading the first frame
9673  		vid.onloadeddata = bind(this.fire, this, 'load');
9674
9675  		if (wasElementSupplied) {
9676  			var sourceElements = vid.getElementsByTagName('source');
9677  			var sources = [];
9678  			for (var j = 0; j < sourceElements.length; j++) {
9679  				sources.push(sourceElements[j].src);
9680  			}
9681
9682  			this._url = (sourceElements.length > 0) ? sources : [vid.src];
9683  			return;
9684  		}
9685
9686  		if (!isArray(this._url)) { this._url = [this._url]; }
9687
9688  		if (!this.options.keepAspectRatio && Object.prototype.hasOwnProperty.call(vid.style, 'objectFit')) {
9689  			vid.style['objectFit'] = 'fill';
9690  		}
9691  		vid.autoplay = !!this.options.autoplay;
9692  		vid.loop = !!this.options.loop;
9693  		vid.muted = !!this.options.muted;
9694  		vid.playsInline = !!this.options.playsInline;
9695  		for (var i = 0; i < this._url.length; i++) {
9696  			var source = create$1('source');
9697  			source.src = this._url[i];
9698  			vid.appendChild(source);
9699  		}
9700  	}
9701
9702  	// @method getElement(): HTMLVideoElement
9703  	// Returns the instance of [`HTMLVideoElement`](https://developer.mozilla.org/docs/Web/API/HTMLVideoElement)
9704  	// used by this overlay.
9705  });
9706
9707
9708  // @factory L.videoOverlay(video: String|Array|HTMLVideoElement, bounds: LatLngBounds, options?: VideoOverlay options)
9709  // Instantiates an image overlay object given the URL of the video (or array of URLs, or even a video element) and the
9710  // geographical bounds it is tied to.
9711
9712  function videoOverlay(video, bounds, options) {
9713  	return new VideoOverlay(video, bounds, options);
9714  }
9715
9716  /*
9717   * @class SVGOverlay
9718   * @aka L.SVGOverlay
9719   * @inherits ImageOverlay
9720   *
9721   * Used to load, display and provide DOM access to an SVG file over specific bounds of the map. Extends `ImageOverlay`.
9722   *
9723   * An SVG overlay uses the [`<svg>`](https://developer.mozilla.org/docs/Web/SVG/Element/svg) element.
9724   *
9725   * @example
9726   *
9727   * ```js
9728   * var svgElement = document.createElementNS("http://www.w3.org/2000/svg", "svg");
9729   * svgElement.setAttribute('xmlns', "http://www.w3.org/2000/svg");
9730   * svgElement.setAttribute('viewBox', "0 0 200 200");
9731   * svgElement.innerHTML = '<rect width="200" height="200"/><rect x="75" y="23" width="50" height="50" style="fill:red"/><rect x="75" y="123" width="50" height="50" style="fill:#0013ff"/>';
9732   * var svgElementBounds = [ [ 32, -130 ], [ 13, -100 ] ];
9733   * L.svgOverlay(svgElement, svgElementBounds).addTo(map);
9734   * ```
9735   */
9736
9737  var SVGOverlay = ImageOverlay.extend({
9738  	_initImage: function () {
9739  		var el = this._image = this._url;
9740
9741  		addClass(el, 'leaflet-image-layer');
9742  		if (this._zoomAnimated) { addClass(el, 'leaflet-zoom-animated'); }
9743  		if (this.options.className) { addClass(el, this.options.className); }
9744
9745  		el.onselectstart = falseFn;
9746  		el.onmousemove = falseFn;
9747  	}
9748
9749  	// @method getElement(): SVGElement
9750  	// Returns the instance of [`SVGElement`](https://developer.mozilla.org/docs/Web/API/SVGElement)
9751  	// used by this overlay.
9752  });
9753
9754
9755  // @factory L.svgOverlay(svg: String|SVGElement, bounds: LatLngBounds, options?: SVGOverlay options)
9756  // Instantiates an image overlay object given an SVG element and the geographical bounds it is tied to.
9757  // A viewBox attribute is required on the SVG element to zoom in and out properly.
9758
9759  function svgOverlay(el, bounds, options) {
9760  	return new SVGOverlay(el, bounds, options);
9761  }
9762
9763  /*
9764   * @class DivOverlay
9765   * @inherits Interactive layer
9766   * @aka L.DivOverlay
9767   * Base model for L.Popup and L.Tooltip. Inherit from it for custom overlays like plugins.
9768   */
9769
9770  // @namespace DivOverlay
9771  var DivOverlay = Layer.extend({
9772
9773  	// @section
9774  	// @aka DivOverlay options
9775  	options: {
9776  		// @option interactive: Boolean = false
9777  		// If true, the popup/tooltip will listen to the mouse events.
9778  		interactive: false,
9779
9780  		// @option offset: Point = Point(0, 0)
9781  		// The offset of the overlay position.
9782  		offset: [0, 0],
9783
9784  		// @option className: String = ''
9785  		// A custom CSS class name to assign to the overlay.
9786  		className: '',
9787
9788  		// @option pane: String = undefined
9789  		// `Map pane` where the overlay will be added.
9790  		pane: undefined,
9791
9792  		// @option content: String|HTMLElement|Function = ''
9793  		// Sets the HTML content of the overlay while initializing. If a function is passed the source layer will be
9794  		// passed to the function. The function should return a `String` or `HTMLElement` to be used in the overlay.
9795  		content: ''
9796  	},
9797
9798  	initialize: function (options, source) {
9799  		if (options && (options instanceof LatLng || isArray(options))) {
9800  			this._latlng = toLatLng(options);
9801  			setOptions(this, source);
9802  		} else {
9803  			setOptions(this, options);
9804  			this._source = source;
9805  		}
9806  		if (this.options.content) {
9807  			this._content = this.options.content;
9808  		}
9809  	},
9810
9811  	// @method openOn(map: Map): this
9812  	// Adds the overlay to the map.
9813  	// Alternative to `map.openPopup(popup)`/`.openTooltip(tooltip)`.
9814  	openOn: function (map) {
9815  		map = arguments.length ? map : this._source._map; // experimental, not the part of public api
9816  		if (!map.hasLayer(this)) {
9817  			map.addLayer(this);
9818  		}
9819  		return this;
9820  	},
9821
9822  	// @method close(): this
9823  	// Closes the overlay.
9824  	// Alternative to `map.closePopup(popup)`/`.closeTooltip(tooltip)`
9825  	// and `layer.closePopup()`/`.closeTooltip()`.
9826  	close: function () {
9827  		if (this._map) {
9828  			this._map.removeLayer(this);
9829  		}
9830  		return this;
9831  	},
9832
9833  	// @method toggle(layer?: Layer): this
9834  	// Opens or closes the overlay bound to layer depending on its current state.
9835  	// Argument may be omitted only for overlay bound to layer.
9836  	// Alternative to `layer.togglePopup()`/`.toggleTooltip()`.
9837  	toggle: function (layer) {
9838  		if (this._map) {
9839  			this.close();
9840  		} else {
9841  			if (arguments.length) {
9842  				this._source = layer;
9843  			} else {
9844  				layer = this._source;
9845  			}
9846  			this._prepareOpen();
9847
9848  			// open the overlay on the map
9849  			this.openOn(layer._map);
9850  		}
9851  		return this;
9852  	},
9853
9854  	onAdd: function (map) {
9855  		this._zoomAnimated = map._zoomAnimated;
9856
9857  		if (!this._container) {
9858  			this._initLayout();
9859  		}
9860
9861  		if (map._fadeAnimated) {
9862  			setOpacity(this._container, 0);
9863  		}
9864
9865  		clearTimeout(this._removeTimeout);
9866  		this.getPane().appendChild(this._container);
9867  		this.update();
9868
9869  		if (map._fadeAnimated) {
9870  			setOpacity(this._container, 1);
9871  		}
9872
9873  		this.bringToFront();
9874
9875  		if (this.options.interactive) {
9876  			addClass(this._container, 'leaflet-interactive');
9877  			this.addInteractiveTarget(this._container);
9878  		}
9879  	},
9880
9881  	onRemove: function (map) {
9882  		if (map._fadeAnimated) {
9883  			setOpacity(this._container, 0);
9884  			this._removeTimeout = setTimeout(bind(remove, undefined, this._container), 200);
9885  		} else {
9886  			remove(this._container);
9887  		}
9888
9889  		if (this.options.interactive) {
9890  			removeClass(this._container, 'leaflet-interactive');
9891  			this.removeInteractiveTarget(this._container);
9892  		}
9893  	},
9894
9895  	// @namespace DivOverlay
9896  	// @method getLatLng: LatLng
9897  	// Returns the geographical point of the overlay.
9898  	getLatLng: function () {
9899  		return this._latlng;
9900  	},
9901
9902  	// @method setLatLng(latlng: LatLng): this
9903  	// Sets the geographical point where the overlay will open.
9904  	setLatLng: function (latlng) {
9905  		this._latlng = toLatLng(latlng);
9906  		if (this._map) {
9907  			this._updatePosition();
9908  			this._adjustPan();
9909  		}
9910  		return this;
9911  	},
9912
9913  	// @method getContent: String|HTMLElement
9914  	// Returns the content of the overlay.
9915  	getContent: function () {
9916  		return this._content;
9917  	},
9918
9919  	// @method setContent(htmlContent: String|HTMLElement|Function): this
9920  	// Sets the HTML content of the overlay. If a function is passed the source layer will be passed to the function.
9921  	// The function should return a `String` or `HTMLElement` to be used in the overlay.
9922  	setContent: function (content) {
9923  		this._content = content;
9924  		this.update();
9925  		return this;
9926  	},
9927
9928  	// @method getElement: String|HTMLElement
9929  	// Returns the HTML container of the overlay.
9930  	getElement: function () {
9931  		return this._container;
9932  	},
9933
9934  	// @method update: null
9935  	// Updates the overlay content, layout and position. Useful for updating the overlay after something inside changed, e.g. image loaded.
9936  	update: function () {
9937  		if (!this._map) { return; }
9938
9939  		this._container.style.visibility = 'hidden';
9940
9941  		this._updateContent();
9942  		this._updateLayout();
9943  		this._updatePosition();
9944
9945  		this._container.style.visibility = '';
9946
9947  		this._adjustPan();
9948  	},
9949
9950  	getEvents: function () {
9951  		var events = {
9952  			zoom: this._updatePosition,
9953  			viewreset: this._updatePosition
9954  		};
9955
9956  		if (this._zoomAnimated) {
9957  			events.zoomanim = this._animateZoom;
9958  		}
9959  		return events;
9960  	},
9961
9962  	// @method isOpen: Boolean
9963  	// Returns `true` when the overlay is visible on the map.
9964  	isOpen: function () {
9965  		return !!this._map && this._map.hasLayer(this);
9966  	},
9967
9968  	// @method bringToFront: this
9969  	// Brings this overlay in front of other overlays (in the same map pane).
9970  	bringToFront: function () {
9971  		if (this._map) {
9972  			toFront(this._container);
9973  		}
9974  		return this;
9975  	},
9976
9977  	// @method bringToBack: this
9978  	// Brings this overlay to the back of other overlays (in the same map pane).
9979  	bringToBack: function () {
9980  		if (this._map) {
9981  			toBack(this._container);
9982  		}
9983  		return this;
9984  	},
9985
9986  	// prepare bound overlay to open: update latlng pos / content source (for FeatureGroup)
9987  	_prepareOpen: function (latlng) {
9988  		var source = this._source;
9989  		if (!source._map) { return false; }
9990
9991  		if (source instanceof FeatureGroup) {
9992  			source = null;
9993  			var layers = this._source._layers;
9994  			for (var id in layers) {
9995  				if (layers[id]._map) {
9996  					source = layers[id];
9997  					break;
9998  				}
9999  			}
10000  			if (!source) { return false; } // Unable to get source layer.
10001
10002  			// set overlay source to this layer
10003  			this._source = source;
10004  		}
10005
10006  		if (!latlng) {
10007  			if (source.getCenter) {
10008  				latlng = source.getCenter();
10009  			} else if (source.getLatLng) {
10010  				latlng = source.getLatLng();
10011  			} else if (source.getBounds) {
10012  				latlng = source.getBounds().getCenter();
10013  			} else {
10014  				throw new Error('Unable to get source layer LatLng.');
10015  			}
10016  		}
10017  		this.setLatLng(latlng);
10018
10019  		if (this._map) {
10020  			// update the overlay (content, layout, etc...)
10021  			this.update();
10022  		}
10023
10024  		return true;
10025  	},
10026
10027  	_updateContent: function () {
10028  		if (!this._content) { return; }
10029
10030  		var node = this._contentNode;
10031  		var content = (typeof this._content === 'function') ? this._content(this._source || this) : this._content;
10032
10033  		if (typeof content === 'string') {
10034  			node.innerHTML = content;
10035  		} else {
10036  			while (node.hasChildNodes()) {
10037  				node.removeChild(node.firstChild);
10038  			}
10039  			node.appendChild(content);
10040  		}
10041
10042  		// @namespace DivOverlay
10043  		// @section DivOverlay events
10044  		// @event contentupdate: Event
10045  		// Fired when the content of the overlay is updated
10046  		this.fire('contentupdate');
10047  	},
10048
10049  	_updatePosition: function () {
10050  		if (!this._map) { return; }
10051
10052  		var pos = this._map.latLngToLayerPoint(this._latlng),
10053  		    offset = toPoint(this.options.offset),
10054  		    anchor = this._getAnchor();
10055
10056  		if (this._zoomAnimated) {
10057  			setPosition(this._container, pos.add(anchor));
10058  		} else {
10059  			offset = offset.add(pos).add(anchor);
10060  		}
10061
10062  		var bottom = this._containerBottom = -offset.y,
10063  		    left = this._containerLeft = -Math.round(this._containerWidth / 2) + offset.x;
10064
10065  		// bottom position the overlay in case the height of the overlay changes (images loading etc)
10066  		this._container.style.bottom = bottom + 'px';
10067  		this._container.style.left = left + 'px';
10068  	},
10069
10070  	_getAnchor: function () {
10071  		return [0, 0];
10072  	}
10073
10074  });
10075
10076  Map.include({
10077  	_initOverlay: function (OverlayClass, content, latlng, options) {
10078  		var overlay = content;
10079  		if (!(overlay instanceof OverlayClass)) {
10080  			overlay = new OverlayClass(options).setContent(content);
10081  		}
10082  		if (latlng) {
10083  			overlay.setLatLng(latlng);
10084  		}
10085  		return overlay;
10086  	}
10087  });
10088
10089
10090  Layer.include({
10091  	_initOverlay: function (OverlayClass, old, content, options) {
10092  		var overlay = content;
10093  		if (overlay instanceof OverlayClass) {
10094  			setOptions(overlay, options);
10095  			overlay._source = this;
10096  		} else {
10097  			overlay = (old && !options) ? old : new OverlayClass(options, this);
10098  			overlay.setContent(content);
10099  		}
10100  		return overlay;
10101  	}
10102  });
10103
10104  /*
10105   * @class Popup
10106   * @inherits DivOverlay
10107   * @aka L.Popup
10108   * Used to open popups in certain places of the map. Use [Map.openPopup](#map-openpopup) to
10109   * open popups while making sure that only one popup is open at one time
10110   * (recommended for usability), or use [Map.addLayer](#map-addlayer) to open as many as you want.
10111   *
10112   * @example
10113   *
10114   * If you want to just bind a popup to marker click and then open it, it's really easy:
10115   *
10116   * ```js
10117   * marker.bindPopup(popupContent).openPopup();
10118   * ```
10119   * Path overlays like polylines also have a `bindPopup` method.
10120   *
10121   * A popup can be also standalone:
10122   *
10123   * ```js
10124   * var popup = L.popup()
10125   * 	.setLatLng(latlng)
10126   * 	.setContent('<p>Hello world!<br />This is a nice popup.</p>')
10127   * 	.openOn(map);
10128   * ```
10129   * or
10130   * ```js
10131   * var popup = L.popup(latlng, {content: '<p>Hello world!<br />This is a nice popup.</p>')
10132   * 	.openOn(map);
10133   * ```
10134   */
10135
10136
10137  // @namespace Popup
10138  var Popup = DivOverlay.extend({
10139
10140  	// @section
10141  	// @aka Popup options
10142  	options: {
10143  		// @option pane: String = 'popupPane'
10144  		// `Map pane` where the popup will be added.
10145  		pane: 'popupPane',
10146
10147  		// @option offset: Point = Point(0, 7)
10148  		// The offset of the popup position.
10149  		offset: [0, 7],
10150
10151  		// @option maxWidth: Number = 300
10152  		// Max width of the popup, in pixels.
10153  		maxWidth: 300,
10154
10155  		// @option minWidth: Number = 50
10156  		// Min width of the popup, in pixels.
10157  		minWidth: 50,
10158
10159  		// @option maxHeight: Number = null
10160  		// If set, creates a scrollable container of the given height
10161  		// inside a popup if its content exceeds it.
10162  		// The scrollable container can be styled using the
10163  		// `leaflet-popup-scrolled` CSS class selector.
10164  		maxHeight: null,
10165
10166  		// @option autoPan: Boolean = true
10167  		// Set it to `false` if you don't want the map to do panning animation
10168  		// to fit the opened popup.
10169  		autoPan: true,
10170
10171  		// @option autoPanPaddingTopLeft: Point = null
10172  		// The margin between the popup and the top left corner of the map
10173  		// view after autopanning was performed.
10174  		autoPanPaddingTopLeft: null,
10175
10176  		// @option autoPanPaddingBottomRight: Point = null
10177  		// The margin between the popup and the bottom right corner of the map
10178  		// view after autopanning was performed.
10179  		autoPanPaddingBottomRight: null,
10180
10181  		// @option autoPanPadding: Point = Point(5, 5)
10182  		// Equivalent of setting both top left and bottom right autopan padding to the same value.
10183  		autoPanPadding: [5, 5],
10184
10185  		// @option keepInView: Boolean = false
10186  		// Set it to `true` if you want to prevent users from panning the popup
10187  		// off of the screen while it is open.
10188  		keepInView: false,
10189
10190  		// @option closeButton: Boolean = true
10191  		// Controls the presence of a close button in the popup.
10192  		closeButton: true,
10193
10194  		// @option autoClose: Boolean = true
10195  		// Set it to `false` if you want to override the default behavior of
10196  		// the popup closing when another popup is opened.
10197  		autoClose: true,
10198
10199  		// @option closeOnEscapeKey: Boolean = true
10200  		// Set it to `false` if you want to override the default behavior of
10201  		// the ESC key for closing of the popup.
10202  		closeOnEscapeKey: true,
10203
10204  		// @option closeOnClick: Boolean = *
10205  		// Set it if you want to override the default behavior of the popup closing when user clicks
10206  		// on the map. Defaults to the map's [`closePopupOnClick`](#map-closepopuponclick) option.
10207
10208  		// @option className: String = ''
10209  		// A custom CSS class name to assign to the popup.
10210  		className: ''
10211  	},
10212
10213  	// @namespace Popup
10214  	// @method openOn(map: Map): this
10215  	// Alternative to `map.openPopup(popup)`.
10216  	// Adds the popup to the map and closes the previous one.
10217  	openOn: function (map) {
10218  		map = arguments.length ? map : this._source._map; // experimental, not the part of public api
10219
10220  		if (!map.hasLayer(this) && map._popup && map._popup.options.autoClose) {
10221  			map.removeLayer(map._popup);
10222  		}
10223  		map._popup = this;
10224
10225  		return DivOverlay.prototype.openOn.call(this, map);
10226  	},
10227
10228  	onAdd: function (map) {
10229  		DivOverlay.prototype.onAdd.call(this, map);
10230
10231  		// @namespace Map
10232  		// @section Popup events
10233  		// @event popupopen: PopupEvent
10234  		// Fired when a popup is opened in the map
10235  		map.fire('popupopen', {popup: this});
10236
10237  		if (this._source) {
10238  			// @namespace Layer
10239  			// @section Popup events
10240  			// @event popupopen: PopupEvent
10241  			// Fired when a popup bound to this layer is opened
10242  			this._source.fire('popupopen', {popup: this}, true);
10243  			// For non-path layers, we toggle the popup when clicking
10244  			// again the layer, so prevent the map to reopen it.
10245  			if (!(this._source instanceof Path)) {
10246  				this._source.on('preclick', stopPropagation);
10247  			}
10248  		}
10249  	},
10250
10251  	onRemove: function (map) {
10252  		DivOverlay.prototype.onRemove.call(this, map);
10253
10254  		// @namespace Map
10255  		// @section Popup events
10256  		// @event popupclose: PopupEvent
10257  		// Fired when a popup in the map is closed
10258  		map.fire('popupclose', {popup: this});
10259
10260  		if (this._source) {
10261  			// @namespace Layer
10262  			// @section Popup events
10263  			// @event popupclose: PopupEvent
10264  			// Fired when a popup bound to this layer is closed
10265  			this._source.fire('popupclose', {popup: this}, true);
10266  			if (!(this._source instanceof Path)) {
10267  				this._source.off('preclick', stopPropagation);
10268  			}
10269  		}
10270  	},
10271
10272  	getEvents: function () {
10273  		var events = DivOverlay.prototype.getEvents.call(this);
10274
10275  		if (this.options.closeOnClick !== undefined ? this.options.closeOnClick : this._map.options.closePopupOnClick) {
10276  			events.preclick = this.close;
10277  		}
10278
10279  		if (this.options.keepInView) {
10280  			events.moveend = this._adjustPan;
10281  		}
10282
10283  		return events;
10284  	},
10285
10286  	_initLayout: function () {
10287  		var prefix = 'leaflet-popup',
10288  		    container = this._container = create$1('div',
10289  			prefix + ' ' + (this.options.className || '') +
10290  			' leaflet-zoom-animated');
10291
10292  		var wrapper = this._wrapper = create$1('div', prefix + '-content-wrapper', container);
10293  		this._contentNode = create$1('div', prefix + '-content', wrapper);
10294
10295  		disableClickPropagation(container);
10296  		disableScrollPropagation(this._contentNode);
10297  		on(container, 'contextmenu', stopPropagation);
10298
10299  		this._tipContainer = create$1('div', prefix + '-tip-container', container);
10300  		this._tip = create$1('div', prefix + '-tip', this._tipContainer);
10301
10302  		if (this.options.closeButton) {
10303  			var closeButton = this._closeButton = create$1('a', prefix + '-close-button', container);
10304  			closeButton.setAttribute('role', 'button'); // overrides the implicit role=link of <a> elements #7399
10305  			closeButton.setAttribute('aria-label', 'Close popup');
10306  			closeButton.href = '#close';
10307  			closeButton.innerHTML = '<span aria-hidden="true">&#215;</span>';
10308
10309  			on(closeButton, 'click', function (ev) {
10310  				preventDefault(ev);
10311  				this.close();
10312  			}, this);
10313  		}
10314  	},
10315
10316  	_updateLayout: function () {
10317  		var container = this._contentNode,
10318  		    style = container.style;
10319
10320  		style.width = '';
10321  		style.whiteSpace = 'nowrap';
10322
10323  		var width = container.offsetWidth;
10324  		width = Math.min(width, this.options.maxWidth);
10325  		width = Math.max(width, this.options.minWidth);
10326
10327  		style.width = (width + 1) + 'px';
10328  		style.whiteSpace = '';
10329
10330  		style.height = '';
10331
10332  		var height = container.offsetHeight,
10333  		    maxHeight = this.options.maxHeight,
10334  		    scrolledClass = 'leaflet-popup-scrolled';
10335
10336  		if (maxHeight && height > maxHeight) {
10337  			style.height = maxHeight + 'px';
10338  			addClass(container, scrolledClass);
10339  		} else {
10340  			removeClass(container, scrolledClass);
10341  		}
10342
10343  		this._containerWidth = this._container.offsetWidth;
10344  	},
10345
10346  	_animateZoom: function (e) {
10347  		var pos = this._map._latLngToNewLayerPoint(this._latlng, e.zoom, e.center),
10348  		    anchor = this._getAnchor();
10349  		setPosition(this._container, pos.add(anchor));
10350  	},
10351
10352  	_adjustPan: function () {
10353  		if (!this.options.autoPan) { return; }
10354  		if (this._map._panAnim) { this._map._panAnim.stop(); }
10355
10356  		// We can endlessly recurse if keepInView is set and the view resets.
10357  		// Let's guard against that by exiting early if we're responding to our own autopan.
10358  		if (this._autopanning) {
10359  			this._autopanning = false;
10360  			return;
10361  		}
10362
10363  		var map = this._map,
10364  		    marginBottom = parseInt(getStyle(this._container, 'marginBottom'), 10) || 0,
10365  		    containerHeight = this._container.offsetHeight + marginBottom,
10366  		    containerWidth = this._containerWidth,
10367  		    layerPos = new Point(this._containerLeft, -containerHeight - this._containerBottom);
10368
10369  		layerPos._add(getPosition(this._container));
10370
10371  		var containerPos = map.layerPointToContainerPoint(layerPos),
10372  		    padding = toPoint(this.options.autoPanPadding),
10373  		    paddingTL = toPoint(this.options.autoPanPaddingTopLeft || padding),
10374  		    paddingBR = toPoint(this.options.autoPanPaddingBottomRight || padding),
10375  		    size = map.getSize(),
10376  		    dx = 0,
10377  		    dy = 0;
10378
10379  		if (containerPos.x + containerWidth + paddingBR.x > size.x) { // right
10380  			dx = containerPos.x + containerWidth - size.x + paddingBR.x;
10381  		}
10382  		if (containerPos.x - dx - paddingTL.x < 0) { // left
10383  			dx = containerPos.x - paddingTL.x;
10384  		}
10385  		if (containerPos.y + containerHeight + paddingBR.y > size.y) { // bottom
10386  			dy = containerPos.y + containerHeight - size.y + paddingBR.y;
10387  		}
10388  		if (containerPos.y - dy - paddingTL.y < 0) { // top
10389  			dy = containerPos.y - paddingTL.y;
10390  		}
10391
10392  		// @namespace Map
10393  		// @section Popup events
10394  		// @event autopanstart: Event
10395  		// Fired when the map starts autopanning when opening a popup.
10396  		if (dx || dy) {
10397  			// Track that we're autopanning, as this function will be re-ran on moveend
10398  			if (this.options.keepInView) {
10399  				this._autopanning = true;
10400  			}
10401
10402  			map
10403  			    .fire('autopanstart')
10404  			    .panBy([dx, dy]);
10405  		}
10406  	},
10407
10408  	_getAnchor: function () {
10409  		// Where should we anchor the popup on the source layer?
10410  		return toPoint(this._source && this._source._getPopupAnchor ? this._source._getPopupAnchor() : [0, 0]);
10411  	}
10412
10413  });
10414
10415  // @namespace Popup
10416  // @factory L.popup(options?: Popup options, source?: Layer)
10417  // 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.
10418  // @alternative
10419  // @factory L.popup(latlng: LatLng, options?: Popup options)
10420  // Instantiates a `Popup` object given `latlng` where the popup will open and an optional `options` object that describes its appearance and location.
10421  var popup = function (options, source) {
10422  	return new Popup(options, source);
10423  };
10424
10425
10426  /* @namespace Map
10427   * @section Interaction Options
10428   * @option closePopupOnClick: Boolean = true
10429   * Set it to `false` if you don't want popups to close when user clicks the map.
10430   */
10431  Map.mergeOptions({
10432  	closePopupOnClick: true
10433  });
10434
10435
10436  // @namespace Map
10437  // @section Methods for Layers and Controls
10438  Map.include({
10439  	// @method openPopup(popup: Popup): this
10440  	// Opens the specified popup while closing the previously opened (to make sure only one is opened at one time for usability).
10441  	// @alternative
10442  	// @method openPopup(content: String|HTMLElement, latlng: LatLng, options?: Popup options): this
10443  	// Creates a popup with the specified content and options and opens it in the given point on a map.
10444  	openPopup: function (popup, latlng, options) {
10445  		this._initOverlay(Popup, popup, latlng, options)
10446  		  .openOn(this);
10447
10448  		return this;
10449  	},
10450
10451  	// @method closePopup(popup?: Popup): this
10452  	// Closes the popup previously opened with [openPopup](#map-openpopup) (or the given one).
10453  	closePopup: function (popup) {
10454  		popup = arguments.length ? popup : this._popup;
10455  		if (popup) {
10456  			popup.close();
10457  		}
10458  		return this;
10459  	}
10460  });
10461
10462  /*
10463   * @namespace Layer
10464   * @section Popup methods example
10465   *
10466   * All layers share a set of methods convenient for binding popups to it.
10467   *
10468   * ```js
10469   * var layer = L.Polygon(latlngs).bindPopup('Hi There!').addTo(map);
10470   * layer.openPopup();
10471   * layer.closePopup();
10472   * ```
10473   *
10474   * 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.
10475   */
10476
10477  // @section Popup methods
10478  Layer.include({
10479
10480  	// @method bindPopup(content: String|HTMLElement|Function|Popup, options?: Popup options): this
10481  	// Binds a popup to the layer with the passed `content` and sets up the
10482  	// necessary event listeners. If a `Function` is passed it will receive
10483  	// the layer as the first argument and should return a `String` or `HTMLElement`.
10484  	bindPopup: function (content, options) {
10485  		this._popup = this._initOverlay(Popup, this._popup, content, options);
10486  		if (!this._popupHandlersAdded) {
10487  			this.on({
10488  				click: this._openPopup,
10489  				keypress: this._onKeyPress,
10490  				remove: this.closePopup,
10491  				move: this._movePopup
10492  			});
10493  			this._popupHandlersAdded = true;
10494  		}
10495
10496  		return this;
10497  	},
10498
10499  	// @method unbindPopup(): this
10500  	// Removes the popup previously bound with `bindPopup`.
10501  	unbindPopup: function () {
10502  		if (this._popup) {
10503  			this.off({
10504  				click: this._openPopup,
10505  				keypress: this._onKeyPress,
10506  				remove: this.closePopup,
10507  				move: this._movePopup
10508  			});
10509  			this._popupHandlersAdded = false;
10510  			this._popup = null;
10511  		}
10512  		return this;
10513  	},
10514
10515  	// @method openPopup(latlng?: LatLng): this
10516  	// Opens the bound popup at the specified `latlng` or at the default popup anchor if no `latlng` is passed.
10517  	openPopup: function (latlng) {
10518  		if (this._popup) {
10519  			if (!(this instanceof FeatureGroup)) {
10520  				this._popup._source = this;
10521  			}
10522  			if (this._popup._prepareOpen(latlng || this._latlng)) {
10523  				// open the popup on the map
10524  				this._popup.openOn(this._map);
10525  			}
10526  		}
10527  		return this;
10528  	},
10529
10530  	// @method closePopup(): this
10531  	// Closes the popup bound to this layer if it is open.
10532  	closePopup: function () {
10533  		if (this._popup) {
10534  			this._popup.close();
10535  		}
10536  		return this;
10537  	},
10538
10539  	// @method togglePopup(): this
10540  	// Opens or closes the popup bound to this layer depending on its current state.
10541  	togglePopup: function () {
10542  		if (this._popup) {
10543  			this._popup.toggle(this);
10544  		}
10545  		return this;
10546  	},
10547
10548  	// @method isPopupOpen(): boolean
10549  	// Returns `true` if the popup bound to this layer is currently open.
10550  	isPopupOpen: function () {
10551  		return (this._popup ? this._popup.isOpen() : false);
10552  	},
10553
10554  	// @method setPopupContent(content: String|HTMLElement|Popup): this
10555  	// Sets the content of the popup bound to this layer.
10556  	setPopupContent: function (content) {
10557  		if (this._popup) {
10558  			this._popup.setContent(content);
10559  		}
10560  		return this;
10561  	},
10562
10563  	// @method getPopup(): Popup
10564  	// Returns the popup bound to this layer.
10565  	getPopup: function () {
10566  		return this._popup;
10567  	},
10568
10569  	_openPopup: function (e) {
10570  		if (!this._popup || !this._map) {
10571  			return;
10572  		}
10573  		// prevent map click
10574  		stop(e);
10575
10576  		var target = e.layer || e.target;
10577  		if (this._popup._source === target && !(target instanceof Path)) {
10578  			// treat it like a marker and figure out
10579  			// if we should toggle it open/closed
10580  			if (this._map.hasLayer(this._popup)) {
10581  				this.closePopup();
10582  			} else {
10583  				this.openPopup(e.latlng);
10584  			}
10585  			return;
10586  		}
10587  		this._popup._source = target;
10588  		this.openPopup(e.latlng);
10589  	},
10590
10591  	_movePopup: function (e) {
10592  		this._popup.setLatLng(e.latlng);
10593  	},
10594
10595  	_onKeyPress: function (e) {
10596  		if (e.originalEvent.keyCode === 13) {
10597  			this._openPopup(e);
10598  		}
10599  	}
10600  });
10601
10602  /*
10603   * @class Tooltip
10604   * @inherits DivOverlay
10605   * @aka L.Tooltip
10606   * Used to display small texts on top of map layers.
10607   *
10608   * @example
10609   * If you want to just bind a tooltip to marker:
10610   *
10611   * ```js
10612   * marker.bindTooltip("my tooltip text").openTooltip();
10613   * ```
10614   * Path overlays like polylines also have a `bindTooltip` method.
10615   *
10616   * A tooltip can be also standalone:
10617   *
10618   * ```js
10619   * var tooltip = L.tooltip()
10620   * 	.setLatLng(latlng)
10621   * 	.setContent('Hello world!<br />This is a nice tooltip.')
10622   * 	.addTo(map);
10623   * ```
10624   * or
10625   * ```js
10626   * var tooltip = L.tooltip(latlng, {content: 'Hello world!<br />This is a nice tooltip.'})
10627   * 	.addTo(map);
10628   * ```
10629   *
10630   *
10631   * Note about tooltip offset. Leaflet takes two options in consideration
10632   * for computing tooltip offsetting:
10633   * - the `offset` Tooltip option: it defaults to [0, 0], and it's specific to one tooltip.
10634   *   Add a positive x offset to move the tooltip to the right, and a positive y offset to
10635   *   move it to the bottom. Negatives will move to the left and top.
10636   * - the `tooltipAnchor` Icon option: this will only be considered for Marker. You
10637   *   should adapt this value if you use a custom icon.
10638   */
10639
10640
10641  // @namespace Tooltip
10642  var Tooltip = DivOverlay.extend({
10643
10644  	// @section
10645  	// @aka Tooltip options
10646  	options: {
10647  		// @option pane: String = 'tooltipPane'
10648  		// `Map pane` where the tooltip will be added.
10649  		pane: 'tooltipPane',
10650
10651  		// @option offset: Point = Point(0, 0)
10652  		// Optional offset of the tooltip position.
10653  		offset: [0, 0],
10654
10655  		// @option direction: String = 'auto'
10656  		// Direction where to open the tooltip. Possible values are: `right`, `left`,
10657  		// `top`, `bottom`, `center`, `auto`.
10658  		// `auto` will dynamically switch between `right` and `left` according to the tooltip
10659  		// position on the map.
10660  		direction: 'auto',
10661
10662  		// @option permanent: Boolean = false
10663  		// Whether to open the tooltip permanently or only on mouseover.
10664  		permanent: false,
10665
10666  		// @option sticky: Boolean = false
10667  		// If true, the tooltip will follow the mouse instead of being fixed at the feature center.
10668  		sticky: false,
10669
10670  		// @option opacity: Number = 0.9
10671  		// Tooltip container opacity.
10672  		opacity: 0.9
10673  	},
10674
10675  	onAdd: function (map) {
10676  		DivOverlay.prototype.onAdd.call(this, map);
10677  		this.setOpacity(this.options.opacity);
10678
10679  		// @namespace Map
10680  		// @section Tooltip events
10681  		// @event tooltipopen: TooltipEvent
10682  		// Fired when a tooltip is opened in the map.
10683  		map.fire('tooltipopen', {tooltip: this});
10684
10685  		if (this._source) {
10686  			this.addEventParent(this._source);
10687
10688  			// @namespace Layer
10689  			// @section Tooltip events
10690  			// @event tooltipopen: TooltipEvent
10691  			// Fired when a tooltip bound to this layer is opened.
10692  			this._source.fire('tooltipopen', {tooltip: this}, true);
10693  		}
10694  	},
10695
10696  	onRemove: function (map) {
10697  		DivOverlay.prototype.onRemove.call(this, map);
10698
10699  		// @namespace Map
10700  		// @section Tooltip events
10701  		// @event tooltipclose: TooltipEvent
10702  		// Fired when a tooltip in the map is closed.
10703  		map.fire('tooltipclose', {tooltip: this});
10704
10705  		if (this._source) {
10706  			this.removeEventParent(this._source);
10707
10708  			// @namespace Layer
10709  			// @section Tooltip events
10710  			// @event tooltipclose: TooltipEvent
10711  			// Fired when a tooltip bound to this layer is closed.
10712  			this._source.fire('tooltipclose', {tooltip: this}, true);
10713  		}
10714  	},
10715
10716  	getEvents: function () {
10717  		var events = DivOverlay.prototype.getEvents.call(this);
10718
10719  		if (!this.options.permanent) {
10720  			events.preclick = this.close;
10721  		}
10722
10723  		return events;
10724  	},
10725
10726  	_initLayout: function () {
10727  		var prefix = 'leaflet-tooltip',
10728  		    className = prefix + ' ' + (this.options.className || '') + ' leaflet-zoom-' + (this._zoomAnimated ? 'animated' : 'hide');
10729
10730  		this._contentNode = this._container = create$1('div', className);
10731
10732  		this._container.setAttribute('role', 'tooltip');
10733  		this._container.setAttribute('id', 'leaflet-tooltip-' + stamp(this));
10734  	},
10735
10736  	_updateLayout: function () {},
10737
10738  	_adjustPan: function () {},
10739
10740  	_setPosition: function (pos) {
10741  		var subX, subY,
10742  		    map = this._map,
10743  		    container = this._container,
10744  		    centerPoint = map.latLngToContainerPoint(map.getCenter()),
10745  		    tooltipPoint = map.layerPointToContainerPoint(pos),
10746  		    direction = this.options.direction,
10747  		    tooltipWidth = container.offsetWidth,
10748  		    tooltipHeight = container.offsetHeight,
10749  		    offset = toPoint(this.options.offset),
10750  		    anchor = this._getAnchor();
10751
10752  		if (direction === 'top') {
10753  			subX = tooltipWidth / 2;
10754  			subY = tooltipHeight;
10755  		} else if (direction === 'bottom') {
10756  			subX = tooltipWidth / 2;
10757  			subY = 0;
10758  		} else if (direction === 'center') {
10759  			subX = tooltipWidth / 2;
10760  			subY = tooltipHeight / 2;
10761  		} else if (direction === 'right') {
10762  			subX = 0;
10763  			subY = tooltipHeight / 2;
10764  		} else if (direction === 'left') {
10765  			subX = tooltipWidth;
10766  			subY = tooltipHeight / 2;
10767  		} else if (tooltipPoint.x < centerPoint.x) {
10768  			direction = 'right';
10769  			subX = 0;
10770  			subY = tooltipHeight / 2;
10771  		} else {
10772  			direction = 'left';
10773  			subX = tooltipWidth + (offset.x + anchor.x) * 2;
10774  			subY = tooltipHeight / 2;
10775  		}
10776
10777  		pos = pos.subtract(toPoint(subX, subY, true)).add(offset).add(anchor);
10778
10779  		removeClass(container, 'leaflet-tooltip-right');
10780  		removeClass(container, 'leaflet-tooltip-left');
10781  		removeClass(container, 'leaflet-tooltip-top');
10782  		removeClass(container, 'leaflet-tooltip-bottom');
10783  		addClass(container, 'leaflet-tooltip-' + direction);
10784  		setPosition(container, pos);
10785  	},
10786
10787  	_updatePosition: function () {
10788  		var pos = this._map.latLngToLayerPoint(this._latlng);
10789  		this._setPosition(pos);
10790  	},
10791
10792  	setOpacity: function (opacity) {
10793  		this.options.opacity = opacity;
10794
10795  		if (this._container) {
10796  			setOpacity(this._container, opacity);
10797  		}
10798  	},
10799
10800  	_animateZoom: function (e) {
10801  		var pos = this._map._latLngToNewLayerPoint(this._latlng, e.zoom, e.center);
10802  		this._setPosition(pos);
10803  	},
10804
10805  	_getAnchor: function () {
10806  		// Where should we anchor the tooltip on the source layer?
10807  		return toPoint(this._source && this._source._getTooltipAnchor && !this.options.sticky ? this._source._getTooltipAnchor() : [0, 0]);
10808  	}
10809
10810  });
10811
10812  // @namespace Tooltip
10813  // @factory L.tooltip(options?: Tooltip options, source?: Layer)
10814  // 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.
10815  // @alternative
10816  // @factory L.tooltip(latlng: LatLng, options?: Tooltip options)
10817  // Instantiates a `Tooltip` object given `latlng` where the tooltip will open and an optional `options` object that describes its appearance and location.
10818  var tooltip = function (options, source) {
10819  	return new Tooltip(options, source);
10820  };
10821
10822  // @namespace Map
10823  // @section Methods for Layers and Controls
10824  Map.include({
10825
10826  	// @method openTooltip(tooltip: Tooltip): this
10827  	// Opens the specified tooltip.
10828  	// @alternative
10829  	// @method openTooltip(content: String|HTMLElement, latlng: LatLng, options?: Tooltip options): this
10830  	// Creates a tooltip with the specified content and options and open it.
10831  	openTooltip: function (tooltip, latlng, options) {
10832  		this._initOverlay(Tooltip, tooltip, latlng, options)
10833  		  .openOn(this);
10834
10835  		return this;
10836  	},
10837
10838  	// @method closeTooltip(tooltip: Tooltip): this
10839  	// Closes the tooltip given as parameter.
10840  	closeTooltip: function (tooltip) {
10841  		tooltip.close();
10842  		return this;
10843  	}
10844
10845  });
10846
10847  /*
10848   * @namespace Layer
10849   * @section Tooltip methods example
10850   *
10851   * All layers share a set of methods convenient for binding tooltips to it.
10852   *
10853   * ```js
10854   * var layer = L.Polygon(latlngs).bindTooltip('Hi There!').addTo(map);
10855   * layer.openTooltip();
10856   * layer.closeTooltip();
10857   * ```
10858   */
10859
10860  // @section Tooltip methods
10861  Layer.include({
10862
10863  	// @method bindTooltip(content: String|HTMLElement|Function|Tooltip, options?: Tooltip options): this
10864  	// Binds a tooltip to the layer with the passed `content` and sets up the
10865  	// necessary event listeners. If a `Function` is passed it will receive
10866  	// the layer as the first argument and should return a `String` or `HTMLElement`.
10867  	bindTooltip: function (content, options) {
10868
10869  		if (this._tooltip && this.isTooltipOpen()) {
10870  			this.unbindTooltip();
10871  		}
10872
10873  		this._tooltip = this._initOverlay(Tooltip, this._tooltip, content, options);
10874  		this._initTooltipInteractions();
10875
10876  		if (this._tooltip.options.permanent && this._map && this._map.hasLayer(this)) {
10877  			this.openTooltip();
10878  		}
10879
10880  		return this;
10881  	},
10882
10883  	// @method unbindTooltip(): this
10884  	// Removes the tooltip previously bound with `bindTooltip`.
10885  	unbindTooltip: function () {
10886  		if (this._tooltip) {
10887  			this._initTooltipInteractions(true);
10888  			this.closeTooltip();
10889  			this._tooltip = null;
10890  		}
10891  		return this;
10892  	},
10893
10894  	_initTooltipInteractions: function (remove) {
10895  		if (!remove && this._tooltipHandlersAdded) { return; }
10896  		var onOff = remove ? 'off' : 'on',
10897  		    events = {
10898  			remove: this.closeTooltip,
10899  			move: this._moveTooltip
10900  		    };
10901  		if (!this._tooltip.options.permanent) {
10902  			events.mouseover = this._openTooltip;
10903  			events.mouseout = this.closeTooltip;
10904  			events.click = this._openTooltip;
10905  			if (this._map) {
10906  				this._addFocusListeners();
10907  			} else {
10908  				events.add = this._addFocusListeners;
10909  			}
10910  		} else {
10911  			events.add = this._openTooltip;
10912  		}
10913  		if (this._tooltip.options.sticky) {
10914  			events.mousemove = this._moveTooltip;
10915  		}
10916  		this[onOff](events);
10917  		this._tooltipHandlersAdded = !remove;
10918  	},
10919
10920  	// @method openTooltip(latlng?: LatLng): this
10921  	// Opens the bound tooltip at the specified `latlng` or at the default tooltip anchor if no `latlng` is passed.
10922  	openTooltip: function (latlng) {
10923  		if (this._tooltip) {
10924  			if (!(this instanceof FeatureGroup)) {
10925  				this._tooltip._source = this;
10926  			}
10927  			if (this._tooltip._prepareOpen(latlng)) {
10928  				// open the tooltip on the map
10929  				this._tooltip.openOn(this._map);
10930
10931  				if (this.getElement) {
10932  					this._setAriaDescribedByOnLayer(this);
10933  				} else if (this.eachLayer) {
10934  					this.eachLayer(this._setAriaDescribedByOnLayer, this);
10935  				}
10936  			}
10937  		}
10938  		return this;
10939  	},
10940
10941  	// @method closeTooltip(): this
10942  	// Closes the tooltip bound to this layer if it is open.
10943  	closeTooltip: function () {
10944  		if (this._tooltip) {
10945  			return this._tooltip.close();
10946  		}
10947  	},
10948
10949  	// @method toggleTooltip(): this
10950  	// Opens or closes the tooltip bound to this layer depending on its current state.
10951  	toggleTooltip: function () {
10952  		if (this._tooltip) {
10953  			this._tooltip.toggle(this);
10954  		}
10955  		return this;
10956  	},
10957
10958  	// @method isTooltipOpen(): boolean
10959  	// Returns `true` if the tooltip bound to this layer is currently open.
10960  	isTooltipOpen: function () {
10961  		return this._tooltip.isOpen();
10962  	},
10963
10964  	// @method setTooltipContent(content: String|HTMLElement|Tooltip): this
10965  	// Sets the content of the tooltip bound to this layer.
10966  	setTooltipContent: function (content) {
10967  		if (this._tooltip) {
10968  			this._tooltip.setContent(content);
10969  		}
10970  		return this;
10971  	},
10972
10973  	// @method getTooltip(): Tooltip
10974  	// Returns the tooltip bound to this layer.
10975  	getTooltip: function () {
10976  		return this._tooltip;
10977  	},
10978
10979  	_addFocusListeners: function () {
10980  		if (this.getElement) {
10981  			this._addFocusListenersOnLayer(this);
10982  		} else if (this.eachLayer) {
10983  			this.eachLayer(this._addFocusListenersOnLayer, this);
10984  		}
10985  	},
10986
10987  	_addFocusListenersOnLayer: function (layer) {
10988  		var el = typeof layer.getElement === 'function' && layer.getElement();
10989  		if (el) {
10990  			on(el, 'focus', function () {
10991  				this._tooltip._source = layer;
10992  				this.openTooltip();
10993  			}, this);
10994  			on(el, 'blur', this.closeTooltip, this);
10995  		}
10996  	},
10997
10998  	_setAriaDescribedByOnLayer: function (layer) {
10999  		var el = typeof layer.getElement === 'function' && layer.getElement();
11000  		if (el) {
11001  			el.setAttribute('aria-describedby', this._tooltip._container.id);
11002  		}
11003  	},
11004
11005
11006  	_openTooltip: function (e) {
11007  		if (!this._tooltip || !this._map) {
11008  			return;
11009  		}
11010
11011  		// If the map is moving, we will show the tooltip after it's done.
11012  		if (this._map.dragging && this._map.dragging.moving() && !this._openOnceFlag) {
11013  			this._openOnceFlag = true;
11014  			var that = this;
11015  			this._map.once('moveend', function () {
11016  				that._openOnceFlag = false;
11017  				that._openTooltip(e);
11018  			});
11019  			return;
11020  		}
11021
11022  		this._tooltip._source = e.layer || e.target;
11023
11024  		this.openTooltip(this._tooltip.options.sticky ? e.latlng : undefined);
11025  	},
11026
11027  	_moveTooltip: function (e) {
11028  		var latlng = e.latlng, containerPoint, layerPoint;
11029  		if (this._tooltip.options.sticky && e.originalEvent) {
11030  			containerPoint = this._map.mouseEventToContainerPoint(e.originalEvent);
11031  			layerPoint = this._map.containerPointToLayerPoint(containerPoint);
11032  			latlng = this._map.layerPointToLatLng(layerPoint);
11033  		}
11034  		this._tooltip.setLatLng(latlng);
11035  	}
11036  });
11037
11038  /*
11039   * @class DivIcon
11040   * @aka L.DivIcon
11041   * @inherits Icon
11042   *
11043   * Represents a lightweight icon for markers that uses a simple `<div>`
11044   * element instead of an image. Inherits from `Icon` but ignores the `iconUrl` and shadow options.
11045   *
11046   * @example
11047   * ```js
11048   * var myIcon = L.divIcon({className: 'my-div-icon'});
11049   * // you can set .my-div-icon styles in CSS
11050   *
11051   * L.marker([50.505, 30.57], {icon: myIcon}).addTo(map);
11052   * ```
11053   *
11054   * By default, it has a 'leaflet-div-icon' CSS class and is styled as a little white square with a shadow.
11055   */
11056
11057  var DivIcon = Icon.extend({
11058  	options: {
11059  		// @section
11060  		// @aka DivIcon options
11061  		iconSize: [12, 12], // also can be set through CSS
11062
11063  		// iconAnchor: (Point),
11064  		// popupAnchor: (Point),
11065
11066  		// @option html: String|HTMLElement = ''
11067  		// Custom HTML code to put inside the div element, empty by default. Alternatively,
11068  		// an instance of `HTMLElement`.
11069  		html: false,
11070
11071  		// @option bgPos: Point = [0, 0]
11072  		// Optional relative position of the background, in pixels
11073  		bgPos: null,
11074
11075  		className: 'leaflet-div-icon'
11076  	},
11077
11078  	createIcon: function (oldIcon) {
11079  		var div = (oldIcon && oldIcon.tagName === 'DIV') ? oldIcon : document.createElement('div'),
11080  		    options = this.options;
11081
11082  		if (options.html instanceof Element) {
11083  			empty(div);
11084  			div.appendChild(options.html);
11085  		} else {
11086  			div.innerHTML = options.html !== false ? options.html : '';
11087  		}
11088
11089  		if (options.bgPos) {
11090  			var bgPos = toPoint(options.bgPos);
11091  			div.style.backgroundPosition = (-bgPos.x) + 'px ' + (-bgPos.y) + 'px';
11092  		}
11093  		this._setIconStyles(div, 'icon');
11094
11095  		return div;
11096  	},
11097
11098  	createShadow: function () {
11099  		return null;
11100  	}
11101  });
11102
11103  // @factory L.divIcon(options: DivIcon options)
11104  // Creates a `DivIcon` instance with the given options.
11105  function divIcon(options) {
11106  	return new DivIcon(options);
11107  }
11108
11109  Icon.Default = IconDefault;
11110
11111  /*
11112   * @class GridLayer
11113   * @inherits Layer
11114   * @aka L.GridLayer
11115   *
11116   * Generic class for handling a tiled grid of HTML elements. This is the base class for all tile layers and replaces `TileLayer.Canvas`.
11117   * 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.
11118   *
11119   *
11120   * @section Synchronous usage
11121   * @example
11122   *
11123   * 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.
11124   *
11125   * ```js
11126   * var CanvasLayer = L.GridLayer.extend({
11127   *     createTile: function(coords){
11128   *         // create a <canvas> element for drawing
11129   *         var tile = L.DomUtil.create('canvas', 'leaflet-tile');
11130   *
11131   *         // setup tile width and height according to the options
11132   *         var size = this.getTileSize();
11133   *         tile.width = size.x;
11134   *         tile.height = size.y;
11135   *
11136   *         // get a canvas context and draw something on it using coords.x, coords.y and coords.z
11137   *         var ctx = tile.getContext('2d');
11138   *
11139   *         // return the tile so it can be rendered on screen
11140   *         return tile;
11141   *     }
11142   * });
11143   * ```
11144   *
11145   * @section Asynchronous usage
11146   * @example
11147   *
11148   * 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.
11149   *
11150   * ```js
11151   * var CanvasLayer = L.GridLayer.extend({
11152   *     createTile: function(coords, done){
11153   *         var error;
11154   *
11155   *         // create a <canvas> element for drawing
11156   *         var tile = L.DomUtil.create('canvas', 'leaflet-tile');
11157   *
11158   *         // setup tile width and height according to the options
11159   *         var size = this.getTileSize();
11160   *         tile.width = size.x;
11161   *         tile.height = size.y;
11162   *
11163   *         // draw something asynchronously and pass the tile to the done() callback
11164   *         setTimeout(function() {
11165   *             done(error, tile);
11166   *         }, 1000);
11167   *
11168   *         return tile;
11169   *     }
11170   * });
11171   * ```
11172   *
11173   * @section
11174   */
11175
11176
11177  var GridLayer = Layer.extend({
11178
11179  	// @section
11180  	// @aka GridLayer options
11181  	options: {
11182  		// @option tileSize: Number|Point = 256
11183  		// Width and height of tiles in the grid. Use a number if width and height are equal, or `L.point(width, height)` otherwise.
11184  		tileSize: 256,
11185
11186  		// @option opacity: Number = 1.0
11187  		// Opacity of the tiles. Can be used in the `createTile()` function.
11188  		opacity: 1,
11189
11190  		// @option updateWhenIdle: Boolean = (depends)
11191  		// Load new tiles only when panning ends.
11192  		// `true` by default on mobile browsers, in order to avoid too many requests and keep smooth navigation.
11193  		// `false` otherwise in order to display new tiles _during_ panning, since it is easy to pan outside the
11194  		// [`keepBuffer`](#gridlayer-keepbuffer) option in desktop browsers.
11195  		updateWhenIdle: Browser.mobile,
11196
11197  		// @option updateWhenZooming: Boolean = true
11198  		// 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.
11199  		updateWhenZooming: true,
11200
11201  		// @option updateInterval: Number = 200
11202  		// Tiles will not update more than once every `updateInterval` milliseconds when panning.
11203  		updateInterval: 200,
11204
11205  		// @option zIndex: Number = 1
11206  		// The explicit zIndex of the tile layer.
11207  		zIndex: 1,
11208
11209  		// @option bounds: LatLngBounds = undefined
11210  		// If set, tiles will only be loaded inside the set `LatLngBounds`.
11211  		bounds: null,
11212
11213  		// @option minZoom: Number = 0
11214  		// The minimum zoom level down to which this layer will be displayed (inclusive).
11215  		minZoom: 0,
11216
11217  		// @option maxZoom: Number = undefined
11218  		// The maximum zoom level up to which this layer will be displayed (inclusive).
11219  		maxZoom: undefined,
11220
11221  		// @option maxNativeZoom: Number = undefined
11222  		// Maximum zoom number the tile source has available. If it is specified,
11223  		// the tiles on all zoom levels higher than `maxNativeZoom` will be loaded
11224  		// from `maxNativeZoom` level and auto-scaled.
11225  		maxNativeZoom: undefined,
11226
11227  		// @option minNativeZoom: Number = undefined
11228  		// Minimum zoom number the tile source has available. If it is specified,
11229  		// the tiles on all zoom levels lower than `minNativeZoom` will be loaded
11230  		// from `minNativeZoom` level and auto-scaled.
11231  		minNativeZoom: undefined,
11232
11233  		// @option noWrap: Boolean = false
11234  		// Whether the layer is wrapped around the antimeridian. If `true`, the
11235  		// GridLayer will only be displayed once at low zoom levels. Has no
11236  		// effect when the [map CRS](#map-crs) doesn't wrap around. Can be used
11237  		// in combination with [`bounds`](#gridlayer-bounds) to prevent requesting
11238  		// tiles outside the CRS limits.
11239  		noWrap: false,
11240
11241  		// @option pane: String = 'tilePane'
11242  		// `Map pane` where the grid layer will be added.
11243  		pane: 'tilePane',
11244
11245  		// @option className: String = ''
11246  		// A custom class name to assign to the tile layer. Empty by default.
11247  		className: '',
11248
11249  		// @option keepBuffer: Number = 2
11250  		// When panning the map, keep this many rows and columns of tiles before unloading them.
11251  		keepBuffer: 2
11252  	},
11253
11254  	initialize: function (options) {
11255  		setOptions(this, options);
11256  	},
11257
11258  	onAdd: function () {
11259  		this._initContainer();
11260
11261  		this._levels = {};
11262  		this._tiles = {};
11263
11264  		this._resetView(); // implicit _update() call
11265  	},
11266
11267  	beforeAdd: function (map) {
11268  		map._addZoomLimit(this);
11269  	},
11270
11271  	onRemove: function (map) {
11272  		this._removeAllTiles();
11273  		remove(this._container);
11274  		map._removeZoomLimit(this);
11275  		this._container = null;
11276  		this._tileZoom = undefined;
11277  	},
11278
11279  	// @method bringToFront: this
11280  	// Brings the tile layer to the top of all tile layers.
11281  	bringToFront: function () {
11282  		if (this._map) {
11283  			toFront(this._container);
11284  			this._setAutoZIndex(Math.max);
11285  		}
11286  		return this;
11287  	},
11288
11289  	// @method bringToBack: this
11290  	// Brings the tile layer to the bottom of all tile layers.
11291  	bringToBack: function () {
11292  		if (this._map) {
11293  			toBack(this._container);
11294  			this._setAutoZIndex(Math.min);
11295  		}
11296  		return this;
11297  	},
11298
11299  	// @method getContainer: HTMLElement
11300  	// Returns the HTML element that contains the tiles for this layer.
11301  	getContainer: function () {
11302  		return this._container;
11303  	},
11304
11305  	// @method setOpacity(opacity: Number): this
11306  	// Changes the [opacity](#gridlayer-opacity) of the grid layer.
11307  	setOpacity: function (opacity) {
11308  		this.options.opacity = opacity;
11309  		this._updateOpacity();
11310  		return this;
11311  	},
11312
11313  	// @method setZIndex(zIndex: Number): this
11314  	// Changes the [zIndex](#gridlayer-zindex) of the grid layer.
11315  	setZIndex: function (zIndex) {
11316  		this.options.zIndex = zIndex;
11317  		this._updateZIndex();
11318
11319  		return this;
11320  	},
11321
11322  	// @method isLoading: Boolean
11323  	// Returns `true` if any tile in the grid layer has not finished loading.
11324  	isLoading: function () {
11325  		return this._loading;
11326  	},
11327
11328  	// @method redraw: this
11329  	// Causes the layer to clear all the tiles and request them again.
11330  	redraw: function () {
11331  		if (this._map) {
11332  			this._removeAllTiles();
11333  			var tileZoom = this._clampZoom(this._map.getZoom());
11334  			if (tileZoom !== this._tileZoom) {
11335  				this._tileZoom = tileZoom;
11336  				this._updateLevels();
11337  			}
11338  			this._update();
11339  		}
11340  		return this;
11341  	},
11342
11343  	getEvents: function () {
11344  		var events = {
11345  			viewprereset: this._invalidateAll,
11346  			viewreset: this._resetView,
11347  			zoom: this._resetView,
11348  			moveend: this._onMoveEnd
11349  		};
11350
11351  		if (!this.options.updateWhenIdle) {
11352  			// update tiles on move, but not more often than once per given interval
11353  			if (!this._onMove) {
11354  				this._onMove = throttle(this._onMoveEnd, this.options.updateInterval, this);
11355  			}
11356
11357  			events.move = this._onMove;
11358  		}
11359
11360  		if (this._zoomAnimated) {
11361  			events.zoomanim = this._animateZoom;
11362  		}
11363
11364  		return events;
11365  	},
11366
11367  	// @section Extension methods
11368  	// Layers extending `GridLayer` shall reimplement the following method.
11369  	// @method createTile(coords: Object, done?: Function): HTMLElement
11370  	// Called only internally, must be overridden by classes extending `GridLayer`.
11371  	// Returns the `HTMLElement` corresponding to the given `coords`. If the `done` callback
11372  	// is specified, it must be called when the tile has finished loading and drawing.
11373  	createTile: function () {
11374  		return document.createElement('div');
11375  	},
11376
11377  	// @section
11378  	// @method getTileSize: Point
11379  	// Normalizes the [tileSize option](#gridlayer-tilesize) into a point. Used by the `createTile()` method.
11380  	getTileSize: function () {
11381  		var s = this.options.tileSize;
11382  		return s instanceof Point ? s : new Point(s, s);
11383  	},
11384
11385  	_updateZIndex: function () {
11386  		if (this._container && this.options.zIndex !== undefined && this.options.zIndex !== null) {
11387  			this._container.style.zIndex = this.options.zIndex;
11388  		}
11389  	},
11390
11391  	_setAutoZIndex: function (compare) {
11392  		// go through all other layers of the same pane, set zIndex to max + 1 (front) or min - 1 (back)
11393
11394  		var layers = this.getPane().children,
11395  		    edgeZIndex = -compare(-Infinity, Infinity); // -Infinity for max, Infinity for min
11396
11397  		for (var i = 0, len = layers.length, zIndex; i < len; i++) {
11398
11399  			zIndex = layers[i].style.zIndex;
11400
11401  			if (layers[i] !== this._container && zIndex) {
11402  				edgeZIndex = compare(edgeZIndex, +zIndex);
11403  			}
11404  		}
11405
11406  		if (isFinite(edgeZIndex)) {
11407  			this.options.zIndex = edgeZIndex + compare(-1, 1);
11408  			this._updateZIndex();
11409  		}
11410  	},
11411
11412  	_updateOpacity: function () {
11413  		if (!this._map) { return; }
11414
11415  		// IE doesn't inherit filter opacity properly, so we're forced to set it on tiles
11416  		if (Browser.ielt9) { return; }
11417
11418  		setOpacity(this._container, this.options.opacity);
11419
11420  		var now = +new Date(),
11421  		    nextFrame = false,
11422  		    willPrune = false;
11423
11424  		for (var key in this._tiles) {
11425  			var tile = this._tiles[key];
11426  			if (!tile.current || !tile.loaded) { continue; }
11427
11428  			var fade = Math.min(1, (now - tile.loaded) / 200);
11429
11430  			setOpacity(tile.el, fade);
11431  			if (fade < 1) {
11432  				nextFrame = true;
11433  			} else {
11434  				if (tile.active) {
11435  					willPrune = true;
11436  				} else {
11437  					this._onOpaqueTile(tile);
11438  				}
11439  				tile.active = true;
11440  			}
11441  		}
11442
11443  		if (willPrune && !this._noPrune) { this._pruneTiles(); }
11444
11445  		if (nextFrame) {
11446  			cancelAnimFrame(this._fadeFrame);
11447  			this._fadeFrame = requestAnimFrame(this._updateOpacity, this);
11448  		}
11449  	},
11450
11451  	_onOpaqueTile: falseFn,
11452
11453  	_initContainer: function () {
11454  		if (this._container) { return; }
11455
11456  		this._container = create$1('div', 'leaflet-layer ' + (this.options.className || ''));
11457  		this._updateZIndex();
11458
11459  		if (this.options.opacity < 1) {
11460  			this._updateOpacity();
11461  		}
11462
11463  		this.getPane().appendChild(this._container);
11464  	},
11465
11466  	_updateLevels: function () {
11467
11468  		var zoom = this._tileZoom,
11469  		    maxZoom = this.options.maxZoom;
11470
11471  		if (zoom === undefined) { return undefined; }
11472
11473  		for (var z in this._levels) {
11474  			z = Number(z);
11475  			if (this._levels[z].el.children.length || z === zoom) {
11476  				this._levels[z].el.style.zIndex = maxZoom - Math.abs(zoom - z);
11477  				this._onUpdateLevel(z);
11478  			} else {
11479  				remove(this._levels[z].el);
11480  				this._removeTilesAtZoom(z);
11481  				this._onRemoveLevel(z);
11482  				delete this._levels[z];
11483  			}
11484  		}
11485
11486  		var level = this._levels[zoom],
11487  		    map = this._map;
11488
11489  		if (!level) {
11490  			level = this._levels[zoom] = {};
11491
11492  			level.el = create$1('div', 'leaflet-tile-container leaflet-zoom-animated', this._container);
11493  			level.el.style.zIndex = maxZoom;
11494
11495  			level.origin = map.project(map.unproject(map.getPixelOrigin()), zoom).round();
11496  			level.zoom = zoom;
11497
11498  			this._setZoomTransform(level, map.getCenter(), map.getZoom());
11499
11500  			// force the browser to consider the newly added element for transition
11501  			falseFn(level.el.offsetWidth);
11502
11503  			this._onCreateLevel(level);
11504  		}
11505
11506  		this._level = level;
11507
11508  		return level;
11509  	},
11510
11511  	_onUpdateLevel: falseFn,
11512
11513  	_onRemoveLevel: falseFn,
11514
11515  	_onCreateLevel: falseFn,
11516
11517  	_pruneTiles: function () {
11518  		if (!this._map) {
11519  			return;
11520  		}
11521
11522  		var key, tile;
11523
11524  		var zoom = this._map.getZoom();
11525  		if (zoom > this.options.maxZoom ||
11526  			zoom < this.options.minZoom) {
11527  			this._removeAllTiles();
11528  			return;
11529  		}
11530
11531  		for (key in this._tiles) {
11532  			tile = this._tiles[key];
11533  			tile.retain = tile.current;
11534  		}
11535
11536  		for (key in this._tiles) {
11537  			tile = this._tiles[key];
11538  			if (tile.current && !tile.active) {
11539  				var coords = tile.coords;
11540  				if (!this._retainParent(coords.x, coords.y, coords.z, coords.z - 5)) {
11541  					this._retainChildren(coords.x, coords.y, coords.z, coords.z + 2);
11542  				}
11543  			}
11544  		}
11545
11546  		for (key in this._tiles) {
11547  			if (!this._tiles[key].retain) {
11548  				this._removeTile(key);
11549  			}
11550  		}
11551  	},
11552
11553  	_removeTilesAtZoom: function (zoom) {
11554  		for (var key in this._tiles) {
11555  			if (this._tiles[key].coords.z !== zoom) {
11556  				continue;
11557  			}
11558  			this._removeTile(key);
11559  		}
11560  	},
11561
11562  	_removeAllTiles: function () {
11563  		for (var key in this._tiles) {
11564  			this._removeTile(key);
11565  		}
11566  	},
11567
11568  	_invalidateAll: function () {
11569  		for (var z in this._levels) {
11570  			remove(this._levels[z].el);
11571  			this._onRemoveLevel(Number(z));
11572  			delete this._levels[z];
11573  		}
11574  		this._removeAllTiles();
11575
11576  		this._tileZoom = undefined;
11577  	},
11578
11579  	_retainParent: function (x, y, z, minZoom) {
11580  		var x2 = Math.floor(x / 2),
11581  		    y2 = Math.floor(y / 2),
11582  		    z2 = z - 1,
11583  		    coords2 = new Point(+x2, +y2);
11584  		coords2.z = +z2;
11585
11586  		var key = this._tileCoordsToKey(coords2),
11587  		    tile = this._tiles[key];
11588
11589  		if (tile && tile.active) {
11590  			tile.retain = true;
11591  			return true;
11592
11593  		} else if (tile && tile.loaded) {
11594  			tile.retain = true;
11595  		}
11596
11597  		if (z2 > minZoom) {
11598  			return this._retainParent(x2, y2, z2, minZoom);
11599  		}
11600
11601  		return false;
11602  	},
11603
11604  	_retainChildren: function (x, y, z, maxZoom) {
11605
11606  		for (var i = 2 * x; i < 2 * x + 2; i++) {
11607  			for (var j = 2 * y; j < 2 * y + 2; j++) {
11608
11609  				var coords = new Point(i, j);
11610  				coords.z = z + 1;
11611
11612  				var key = this._tileCoordsToKey(coords),
11613  				    tile = this._tiles[key];
11614
11615  				if (tile && tile.active) {
11616  					tile.retain = true;
11617  					continue;
11618
11619  				} else if (tile && tile.loaded) {
11620  					tile.retain = true;
11621  				}
11622
11623  				if (z + 1 < maxZoom) {
11624  					this._retainChildren(i, j, z + 1, maxZoom);
11625  				}
11626  			}
11627  		}
11628  	},
11629
11630  	_resetView: function (e) {
11631  		var animating = e && (e.pinch || e.flyTo);
11632  		this._setView(this._map.getCenter(), this._map.getZoom(), animating, animating);
11633  	},
11634
11635  	_animateZoom: function (e) {
11636  		this._setView(e.center, e.zoom, true, e.noUpdate);
11637  	},
11638
11639  	_clampZoom: function (zoom) {
11640  		var options = this.options;
11641
11642  		if (undefined !== options.minNativeZoom && zoom < options.minNativeZoom) {
11643  			return options.minNativeZoom;
11644  		}
11645
11646  		if (undefined !== options.maxNativeZoom && options.maxNativeZoom < zoom) {
11647  			return options.maxNativeZoom;
11648  		}
11649
11650  		return zoom;
11651  	},
11652
11653  	_setView: function (center, zoom, noPrune, noUpdate) {
11654  		var tileZoom = Math.round(zoom);
11655  		if ((this.options.maxZoom !== undefined && tileZoom > this.options.maxZoom) ||
11656  		    (this.options.minZoom !== undefined && tileZoom < this.options.minZoom)) {
11657  			tileZoom = undefined;
11658  		} else {
11659  			tileZoom = this._clampZoom(tileZoom);
11660  		}
11661
11662  		var tileZoomChanged = this.options.updateWhenZooming && (tileZoom !== this._tileZoom);
11663
11664  		if (!noUpdate || tileZoomChanged) {
11665
11666  			this._tileZoom = tileZoom;
11667
11668  			if (this._abortLoading) {
11669  				this._abortLoading();
11670  			}
11671
11672  			this._updateLevels();
11673  			this._resetGrid();
11674
11675  			if (tileZoom !== undefined) {
11676  				this._update(center);
11677  			}
11678
11679  			if (!noPrune) {
11680  				this._pruneTiles();
11681  			}
11682
11683  			// Flag to prevent _updateOpacity from pruning tiles during
11684  			// a zoom anim or a pinch gesture
11685  			this._noPrune = !!noPrune;
11686  		}
11687
11688  		this._setZoomTransforms(center, zoom);
11689  	},
11690
11691  	_setZoomTransforms: function (center, zoom) {
11692  		for (var i in this._levels) {
11693  			this._setZoomTransform(this._levels[i], center, zoom);
11694  		}
11695  	},
11696
11697  	_setZoomTransform: function (level, center, zoom) {
11698  		var scale = this._map.getZoomScale(zoom, level.zoom),
11699  		    translate = level.origin.multiplyBy(scale)
11700  		        .subtract(this._map._getNewPixelOrigin(center, zoom)).round();
11701
11702  		if (Browser.any3d) {
11703  			setTransform(level.el, translate, scale);
11704  		} else {
11705  			setPosition(level.el, translate);
11706  		}
11707  	},
11708
11709  	_resetGrid: function () {
11710  		var map = this._map,
11711  		    crs = map.options.crs,
11712  		    tileSize = this._tileSize = this.getTileSize(),
11713  		    tileZoom = this._tileZoom;
11714
11715  		var bounds = this._map.getPixelWorldBounds(this._tileZoom);
11716  		if (bounds) {
11717  			this._globalTileRange = this._pxBoundsToTileRange(bounds);
11718  		}
11719
11720  		this._wrapX = crs.wrapLng && !this.options.noWrap && [
11721  			Math.floor(map.project([0, crs.wrapLng[0]], tileZoom).x / tileSize.x),
11722  			Math.ceil(map.project([0, crs.wrapLng[1]], tileZoom).x / tileSize.y)
11723  		];
11724  		this._wrapY = crs.wrapLat && !this.options.noWrap && [
11725  			Math.floor(map.project([crs.wrapLat[0], 0], tileZoom).y / tileSize.x),
11726  			Math.ceil(map.project([crs.wrapLat[1], 0], tileZoom).y / tileSize.y)
11727  		];
11728  	},
11729
11730  	_onMoveEnd: function () {
11731  		if (!this._map || this._map._animatingZoom) { return; }
11732
11733  		this._update();
11734  	},
11735
11736  	_getTiledPixelBounds: function (center) {
11737  		var map = this._map,
11738  		    mapZoom = map._animatingZoom ? Math.max(map._animateToZoom, map.getZoom()) : map.getZoom(),
11739  		    scale = map.getZoomScale(mapZoom, this._tileZoom),
11740  		    pixelCenter = map.project(center, this._tileZoom).floor(),
11741  		    halfSize = map.getSize().divideBy(scale * 2);
11742
11743  		return new Bounds(pixelCenter.subtract(halfSize), pixelCenter.add(halfSize));
11744  	},
11745
11746  	// Private method to load tiles in the grid's active zoom level according to map bounds
11747  	_update: function (center) {
11748  		var map = this._map;
11749  		if (!map) { return; }
11750  		var zoom = this._clampZoom(map.getZoom());
11751
11752  		if (center === undefined) { center = map.getCenter(); }
11753  		if (this._tileZoom === undefined) { return; }	// if out of minzoom/maxzoom
11754
11755  		var pixelBounds = this._getTiledPixelBounds(center),
11756  		    tileRange = this._pxBoundsToTileRange(pixelBounds),
11757  		    tileCenter = tileRange.getCenter(),
11758  		    queue = [],
11759  		    margin = this.options.keepBuffer,
11760  		    noPruneRange = new Bounds(tileRange.getBottomLeft().subtract([margin, -margin]),
11761  		                              tileRange.getTopRight().add([margin, -margin]));
11762
11763  		// Sanity check: panic if the tile range contains Infinity somewhere.
11764  		if (!(isFinite(tileRange.min.x) &&
11765  		      isFinite(tileRange.min.y) &&
11766  		      isFinite(tileRange.max.x) &&
11767  		      isFinite(tileRange.max.y))) { throw new Error('Attempted to load an infinite number of tiles'); }
11768
11769  		for (var key in this._tiles) {
11770  			var c = this._tiles[key].coords;
11771  			if (c.z !== this._tileZoom || !noPruneRange.contains(new Point(c.x, c.y))) {
11772  				this._tiles[key].current = false;
11773  			}
11774  		}
11775
11776  		// _update just loads more tiles. If the tile zoom level differs too much
11777  		// from the map's, let _setView reset levels and prune old tiles.
11778  		if (Math.abs(zoom - this._tileZoom) > 1) { this._setView(center, zoom); return; }
11779
11780  		// create a queue of coordinates to load tiles from
11781  		for (var j = tileRange.min.y; j <= tileRange.max.y; j++) {
11782  			for (var i = tileRange.min.x; i <= tileRange.max.x; i++) {
11783  				var coords = new Point(i, j);
11784  				coords.z = this._tileZoom;
11785
11786  				if (!this._isValidTile(coords)) { continue; }
11787
11788  				var tile = this._tiles[this._tileCoordsToKey(coords)];
11789  				if (tile) {
11790  					tile.current = true;
11791  				} else {
11792  					queue.push(coords);
11793  				}
11794  			}
11795  		}
11796
11797  		// sort tile queue to load tiles in order of their distance to center
11798  		queue.sort(function (a, b) {
11799  			return a.distanceTo(tileCenter) - b.distanceTo(tileCenter);
11800  		});
11801
11802  		if (queue.length !== 0) {
11803  			// if it's the first batch of tiles to load
11804  			if (!this._loading) {
11805  				this._loading = true;
11806  				// @event loading: Event
11807  				// Fired when the grid layer starts loading tiles.
11808  				this.fire('loading');
11809  			}
11810
11811  			// create DOM fragment to append tiles in one batch
11812  			var fragment = document.createDocumentFragment();
11813
11814  			for (i = 0; i < queue.length; i++) {
11815  				this._addTile(queue[i], fragment);
11816  			}
11817
11818  			this._level.el.appendChild(fragment);
11819  		}
11820  	},
11821
11822  	_isValidTile: function (coords) {
11823  		var crs = this._map.options.crs;
11824
11825  		if (!crs.infinite) {
11826  			// don't load tile if it's out of bounds and not wrapped
11827  			var bounds = this._globalTileRange;
11828  			if ((!crs.wrapLng && (coords.x < bounds.min.x || coords.x > bounds.max.x)) ||
11829  			    (!crs.wrapLat && (coords.y < bounds.min.y || coords.y > bounds.max.y))) { return false; }
11830  		}
11831
11832  		if (!this.options.bounds) { return true; }
11833
11834  		// don't load tile if it doesn't intersect the bounds in options
11835  		var tileBounds = this._tileCoordsToBounds(coords);
11836  		return toLatLngBounds(this.options.bounds).overlaps(tileBounds);
11837  	},
11838
11839  	_keyToBounds: function (key) {
11840  		return this._tileCoordsToBounds(this._keyToTileCoords(key));
11841  	},
11842
11843  	_tileCoordsToNwSe: function (coords) {
11844  		var map = this._map,
11845  		    tileSize = this.getTileSize(),
11846  		    nwPoint = coords.scaleBy(tileSize),
11847  		    sePoint = nwPoint.add(tileSize),
11848  		    nw = map.unproject(nwPoint, coords.z),
11849  		    se = map.unproject(sePoint, coords.z);
11850  		return [nw, se];
11851  	},
11852
11853  	// converts tile coordinates to its geographical bounds
11854  	_tileCoordsToBounds: function (coords) {
11855  		var bp = this._tileCoordsToNwSe(coords),
11856  		    bounds = new LatLngBounds(bp[0], bp[1]);
11857
11858  		if (!this.options.noWrap) {
11859  			bounds = this._map.wrapLatLngBounds(bounds);
11860  		}
11861  		return bounds;
11862  	},
11863  	// converts tile coordinates to key for the tile cache
11864  	_tileCoordsToKey: function (coords) {
11865  		return coords.x + ':' + coords.y + ':' + coords.z;
11866  	},
11867
11868  	// converts tile cache key to coordinates
11869  	_keyToTileCoords: function (key) {
11870  		var k = key.split(':'),
11871  		    coords = new Point(+k[0], +k[1]);
11872  		coords.z = +k[2];
11873  		return coords;
11874  	},
11875
11876  	_removeTile: function (key) {
11877  		var tile = this._tiles[key];
11878  		if (!tile) { return; }
11879
11880  		remove(tile.el);
11881
11882  		delete this._tiles[key];
11883
11884  		// @event tileunload: TileEvent
11885  		// Fired when a tile is removed (e.g. when a tile goes off the screen).
11886  		this.fire('tileunload', {
11887  			tile: tile.el,
11888  			coords: this._keyToTileCoords(key)
11889  		});
11890  	},
11891
11892  	_initTile: function (tile) {
11893  		addClass(tile, 'leaflet-tile');
11894
11895  		var tileSize = this.getTileSize();
11896  		tile.style.width = tileSize.x + 'px';
11897  		tile.style.height = tileSize.y + 'px';
11898
11899  		tile.onselectstart = falseFn;
11900  		tile.onmousemove = falseFn;
11901
11902  		// update opacity on tiles in IE7-8 because of filter inheritance problems
11903  		if (Browser.ielt9 && this.options.opacity < 1) {
11904  			setOpacity(tile, this.options.opacity);
11905  		}
11906  	},
11907
11908  	_addTile: function (coords, container) {
11909  		var tilePos = this._getTilePos(coords),
11910  		    key = this._tileCoordsToKey(coords);
11911
11912  		var tile = this.createTile(this._wrapCoords(coords), bind(this._tileReady, this, coords));
11913
11914  		this._initTile(tile);
11915
11916  		// if createTile is defined with a second argument ("done" callback),
11917  		// we know that tile is async and will be ready later; otherwise
11918  		if (this.createTile.length < 2) {
11919  			// mark tile as ready, but delay one frame for opacity animation to happen
11920  			requestAnimFrame(bind(this._tileReady, this, coords, null, tile));
11921  		}
11922
11923  		setPosition(tile, tilePos);
11924
11925  		// save tile in cache
11926  		this._tiles[key] = {
11927  			el: tile,
11928  			coords: coords,
11929  			current: true
11930  		};
11931
11932  		container.appendChild(tile);
11933  		// @event tileloadstart: TileEvent
11934  		// Fired when a tile is requested and starts loading.
11935  		this.fire('tileloadstart', {
11936  			tile: tile,
11937  			coords: coords
11938  		});
11939  	},
11940
11941  	_tileReady: function (coords, err, tile) {
11942  		if (err) {
11943  			// @event tileerror: TileErrorEvent
11944  			// Fired when there is an error loading a tile.
11945  			this.fire('tileerror', {
11946  				error: err,
11947  				tile: tile,
11948  				coords: coords
11949  			});
11950  		}
11951
11952  		var key = this._tileCoordsToKey(coords);
11953
11954  		tile = this._tiles[key];
11955  		if (!tile) { return; }
11956
11957  		tile.loaded = +new Date();
11958  		if (this._map._fadeAnimated) {
11959  			setOpacity(tile.el, 0);
11960  			cancelAnimFrame(this._fadeFrame);
11961  			this._fadeFrame = requestAnimFrame(this._updateOpacity, this);
11962  		} else {
11963  			tile.active = true;
11964  			this._pruneTiles();
11965  		}
11966
11967  		if (!err) {
11968  			addClass(tile.el, 'leaflet-tile-loaded');
11969
11970  			// @event tileload: TileEvent
11971  			// Fired when a tile loads.
11972  			this.fire('tileload', {
11973  				tile: tile.el,
11974  				coords: coords
11975  			});
11976  		}
11977
11978  		if (this._noTilesToLoad()) {
11979  			this._loading = false;
11980  			// @event load: Event
11981  			// Fired when the grid layer loaded all visible tiles.
11982  			this.fire('load');
11983
11984  			if (Browser.ielt9 || !this._map._fadeAnimated) {
11985  				requestAnimFrame(this._pruneTiles, this);
11986  			} else {
11987  				// Wait a bit more than 0.2 secs (the duration of the tile fade-in)
11988  				// to trigger a pruning.
11989  				setTimeout(bind(this._pruneTiles, this), 250);
11990  			}
11991  		}
11992  	},
11993
11994  	_getTilePos: function (coords) {
11995  		return coords.scaleBy(this.getTileSize()).subtract(this._level.origin);
11996  	},
11997
11998  	_wrapCoords: function (coords) {
11999  		var newCoords = new Point(
12000  			this._wrapX ? wrapNum(coords.x, this._wrapX) : coords.x,
12001  			this._wrapY ? wrapNum(coords.y, this._wrapY) : coords.y);
12002  		newCoords.z = coords.z;
12003  		return newCoords;
12004  	},
12005
12006  	_pxBoundsToTileRange: function (bounds) {
12007  		var tileSize = this.getTileSize();
12008  		return new Bounds(
12009  			bounds.min.unscaleBy(tileSize).floor(),
12010  			bounds.max.unscaleBy(tileSize).ceil().subtract([1, 1]));
12011  	},
12012
12013  	_noTilesToLoad: function () {
12014  		for (var key in this._tiles) {
12015  			if (!this._tiles[key].loaded) { return false; }
12016  		}
12017  		return true;
12018  	}
12019  });
12020
12021  // @factory L.gridLayer(options?: GridLayer options)
12022  // Creates a new instance of GridLayer with the supplied options.
12023  function gridLayer(options) {
12024  	return new GridLayer(options);
12025  }
12026
12027  /*
12028   * @class TileLayer
12029   * @inherits GridLayer
12030   * @aka L.TileLayer
12031   * Used to load and display tile layers on the map. Note that most tile servers require attribution, which you can set under `Layer`. Extends `GridLayer`.
12032   *
12033   * @example
12034   *
12035   * ```js
12036   * L.tileLayer('https://tile.openstreetmap.org/{z}/{x}/{y}.png?{foo}', {foo: 'bar', attribution: '&copy; <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors'}).addTo(map);
12037   * ```
12038   *
12039   * @section URL template
12040   * @example
12041   *
12042   * A string of the following form:
12043   *
12044   * ```
12045   * 'https://{s}.somedomain.com/blabla/{z}/{x}/{y}{r}.png'
12046   * ```
12047   *
12048   * `{s}` means one of the available subdomains (used sequentially to help with browser parallel requests per domain limitation; subdomain values are specified in options; `a`, `b` or `c` by default, can be omitted), `{z}` — zoom level, `{x}` and `{y}` — tile coordinates. `{r}` can be used to add "&commat;2x" to the URL to load retina tiles.
12049   *
12050   * You can use custom keys in the template, which will be [evaluated](#util-template) from TileLayer options, like this:
12051   *
12052   * ```
12053   * L.tileLayer('https://{s}.somedomain.com/{foo}/{z}/{x}/{y}.png', {foo: 'bar'});
12054   * ```
12055   */
12056
12057
12058  var TileLayer = GridLayer.extend({
12059
12060  	// @section
12061  	// @aka TileLayer options
12062  	options: {
12063  		// @option minZoom: Number = 0
12064  		// The minimum zoom level down to which this layer will be displayed (inclusive).
12065  		minZoom: 0,
12066
12067  		// @option maxZoom: Number = 18
12068  		// The maximum zoom level up to which this layer will be displayed (inclusive).
12069  		maxZoom: 18,
12070
12071  		// @option subdomains: String|String[] = 'abc'
12072  		// 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.
12073  		subdomains: 'abc',
12074
12075  		// @option errorTileUrl: String = ''
12076  		// URL to the tile image to show in place of the tile that failed to load.
12077  		errorTileUrl: '',
12078
12079  		// @option zoomOffset: Number = 0
12080  		// The zoom number used in tile URLs will be offset with this value.
12081  		zoomOffset: 0,
12082
12083  		// @option tms: Boolean = false
12084  		// If `true`, inverses Y axis numbering for tiles (turn this on for [TMS](https://en.wikipedia.org/wiki/Tile_Map_Service) services).
12085  		tms: false,
12086
12087  		// @option zoomReverse: Boolean = false
12088  		// If set to true, the zoom number used in tile URLs will be reversed (`maxZoom - zoom` instead of `zoom`)
12089  		zoomReverse: false,
12090
12091  		// @option detectRetina: Boolean = false
12092  		// 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.
12093  		detectRetina: false,
12094
12095  		// @option crossOrigin: Boolean|String = false
12096  		// Whether the crossOrigin attribute will be added to the tiles.
12097  		// If a String is provided, all tiles will have their crossOrigin attribute set to the String provided. This is needed if you want to access tile pixel data.
12098  		// Refer to [CORS Settings](https://developer.mozilla.org/en-US/docs/Web/HTML/CORS_settings_attributes) for valid String values.
12099  		crossOrigin: false,
12100
12101  		// @option referrerPolicy: Boolean|String = false
12102  		// Whether the referrerPolicy attribute will be added to the tiles.
12103  		// If a String is provided, all tiles will have their referrerPolicy attribute set to the String provided.
12104  		// This may be needed if your map's rendering context has a strict default but your tile provider expects a valid referrer
12105  		// (e.g. to validate an API token).
12106  		// Refer to [HTMLImageElement.referrerPolicy](https://developer.mozilla.org/en-US/docs/Web/API/HTMLImageElement/referrerPolicy) for valid String values.
12107  		referrerPolicy: false
12108  	},
12109
12110  	initialize: function (url, options) {
12111
12112  		this._url = url;
12113
12114  		options = setOptions(this, options);
12115
12116  		// detecting retina displays, adjusting tileSize and zoom levels
12117  		if (options.detectRetina && Browser.retina && options.maxZoom > 0) {
12118
12119  			options.tileSize = Math.floor(options.tileSize / 2);
12120
12121  			if (!options.zoomReverse) {
12122  				options.zoomOffset++;
12123  				options.maxZoom = Math.max(options.minZoom, options.maxZoom - 1);
12124  			} else {
12125  				options.zoomOffset--;
12126  				options.minZoom = Math.min(options.maxZoom, options.minZoom + 1);
12127  			}
12128
12129  			options.minZoom = Math.max(0, options.minZoom);
12130  		} else if (!options.zoomReverse) {
12131  			// make sure maxZoom is gte minZoom
12132  			options.maxZoom = Math.max(options.minZoom, options.maxZoom);
12133  		} else {
12134  			// make sure minZoom is lte maxZoom
12135  			options.minZoom = Math.min(options.maxZoom, options.minZoom);
12136  		}
12137
12138  		if (typeof options.subdomains === 'string') {
12139  			options.subdomains = options.subdomains.split('');
12140  		}
12141
12142  		this.on('tileunload', this._onTileRemove);
12143  	},
12144
12145  	// @method setUrl(url: String, noRedraw?: Boolean): this
12146  	// Updates the layer's URL template and redraws it (unless `noRedraw` is set to `true`).
12147  	// If the URL does not change, the layer will not be redrawn unless
12148  	// the noRedraw parameter is set to false.
12149  	setUrl: function (url, noRedraw) {
12150  		if (this._url === url && noRedraw === undefined) {
12151  			noRedraw = true;
12152  		}
12153
12154  		this._url = url;
12155
12156  		if (!noRedraw) {
12157  			this.redraw();
12158  		}
12159  		return this;
12160  	},
12161
12162  	// @method createTile(coords: Object, done?: Function): HTMLElement
12163  	// Called only internally, overrides GridLayer's [`createTile()`](#gridlayer-createtile)
12164  	// to return an `<img>` HTML element with the appropriate image URL given `coords`. The `done`
12165  	// callback is called when the tile has been loaded.
12166  	createTile: function (coords, done) {
12167  		var tile = document.createElement('img');
12168
12169  		on(tile, 'load', bind(this._tileOnLoad, this, done, tile));
12170  		on(tile, 'error', bind(this._tileOnError, this, done, tile));
12171
12172  		if (this.options.crossOrigin || this.options.crossOrigin === '') {
12173  			tile.crossOrigin = this.options.crossOrigin === true ? '' : this.options.crossOrigin;
12174  		}
12175
12176  		// for this new option we follow the documented behavior
12177  		// more closely by only setting the property when string
12178  		if (typeof this.options.referrerPolicy === 'string') {
12179  			tile.referrerPolicy = this.options.referrerPolicy;
12180  		}
12181
12182  		// The alt attribute is set to the empty string,
12183  		// allowing screen readers to ignore the decorative image tiles.
12184  		// https://www.w3.org/WAI/tutorials/images/decorative/
12185  		// https://www.w3.org/TR/html-aria/#el-img-empty-alt
12186  		tile.alt = '';
12187
12188  		tile.src = this.getTileUrl(coords);
12189
12190  		return tile;
12191  	},
12192
12193  	// @section Extension methods
12194  	// @uninheritable
12195  	// Layers extending `TileLayer` might reimplement the following method.
12196  	// @method getTileUrl(coords: Object): String
12197  	// Called only internally, returns the URL for a tile given its coordinates.
12198  	// Classes extending `TileLayer` can override this function to provide custom tile URL naming schemes.
12199  	getTileUrl: function (coords) {
12200  		var data = {
12201  			r: Browser.retina ? '@2x' : '',
12202  			s: this._getSubdomain(coords),
12203  			x: coords.x,
12204  			y: coords.y,
12205  			z: this._getZoomForUrl()
12206  		};
12207  		if (this._map && !this._map.options.crs.infinite) {
12208  			var invertedY = this._globalTileRange.max.y - coords.y;
12209  			if (this.options.tms) {
12210  				data['y'] = invertedY;
12211  			}
12212  			data['-y'] = invertedY;
12213  		}
12214
12215  		return template(this._url, extend(data, this.options));
12216  	},
12217
12218  	_tileOnLoad: function (done, tile) {
12219  		// For https://github.com/Leaflet/Leaflet/issues/3332
12220  		if (Browser.ielt9) {
12221  			setTimeout(bind(done, this, null, tile), 0);
12222  		} else {
12223  			done(null, tile);
12224  		}
12225  	},
12226
12227  	_tileOnError: function (done, tile, e) {
12228  		var errorUrl = this.options.errorTileUrl;
12229  		if (errorUrl && tile.getAttribute('src') !== errorUrl) {
12230  			tile.src = errorUrl;
12231  		}
12232  		done(e, tile);
12233  	},
12234
12235  	_onTileRemove: function (e) {
12236  		e.tile.onload = null;
12237  	},
12238
12239  	_getZoomForUrl: function () {
12240  		var zoom = this._tileZoom,
12241  		maxZoom = this.options.maxZoom,
12242  		zoomReverse = this.options.zoomReverse,
12243  		zoomOffset = this.options.zoomOffset;
12244
12245  		if (zoomReverse) {
12246  			zoom = maxZoom - zoom;
12247  		}
12248
12249  		return zoom + zoomOffset;
12250  	},
12251
12252  	_getSubdomain: function (tilePoint) {
12253  		var index = Math.abs(tilePoint.x + tilePoint.y) % this.options.subdomains.length;
12254  		return this.options.subdomains[index];
12255  	},
12256
12257  	// stops loading all tiles in the background layer
12258  	_abortLoading: function () {
12259  		var i, tile;
12260  		for (i in this._tiles) {
12261  			if (this._tiles[i].coords.z !== this._tileZoom) {
12262  				tile = this._tiles[i].el;
12263
12264  				tile.onload = falseFn;
12265  				tile.onerror = falseFn;
12266
12267  				if (!tile.complete) {
12268  					tile.src = emptyImageUrl;
12269  					var coords = this._tiles[i].coords;
12270  					remove(tile);
12271  					delete this._tiles[i];
12272  					// @event tileabort: TileEvent
12273  					// Fired when a tile was loading but is now not wanted.
12274  					this.fire('tileabort', {
12275  						tile: tile,
12276  						coords: coords
12277  					});
12278  				}
12279  			}
12280  		}
12281  	},
12282
12283  	_removeTile: function (key) {
12284  		var tile = this._tiles[key];
12285  		if (!tile) { return; }
12286
12287  		// Cancels any pending http requests associated with the tile
12288  		tile.el.setAttribute('src', emptyImageUrl);
12289
12290  		return GridLayer.prototype._removeTile.call(this, key);
12291  	},
12292
12293  	_tileReady: function (coords, err, tile) {
12294  		if (!this._map || (tile && tile.getAttribute('src') === emptyImageUrl)) {
12295  			return;
12296  		}
12297
12298  		return GridLayer.prototype._tileReady.call(this, coords, err, tile);
12299  	}
12300  });
12301
12302
12303  // @factory L.tilelayer(urlTemplate: String, options?: TileLayer options)
12304  // Instantiates a tile layer object given a `URL template` and optionally an options object.
12305
12306  function tileLayer(url, options) {
12307  	return new TileLayer(url, options);
12308  }
12309
12310  /*
12311   * @class TileLayer.WMS
12312   * @inherits TileLayer
12313   * @aka L.TileLayer.WMS
12314   * Used to display [WMS](https://en.wikipedia.org/wiki/Web_Map_Service) services as tile layers on the map. Extends `TileLayer`.
12315   *
12316   * @example
12317   *
12318   * ```js
12319   * var nexrad = L.tileLayer.wms("http://mesonet.agron.iastate.edu/cgi-bin/wms/nexrad/n0r.cgi", {
12320   * 	layers: 'nexrad-n0r-900913',
12321   * 	format: 'image/png',
12322   * 	transparent: true,
12323   * 	attribution: "Weather data © 2012 IEM Nexrad"
12324   * });
12325   * ```
12326   */
12327
12328  var TileLayerWMS = TileLayer.extend({
12329
12330  	// @section
12331  	// @aka TileLayer.WMS options
12332  	// If any custom options not documented here are used, they will be sent to the
12333  	// WMS server as extra parameters in each request URL. This can be useful for
12334  	// [non-standard vendor WMS parameters](https://docs.geoserver.org/stable/en/user/services/wms/vendor.html).
12335  	defaultWmsParams: {
12336  		service: 'WMS',
12337  		request: 'GetMap',
12338
12339  		// @option layers: String = ''
12340  		// **(required)** Comma-separated list of WMS layers to show.
12341  		layers: '',
12342
12343  		// @option styles: String = ''
12344  		// Comma-separated list of WMS styles.
12345  		styles: '',
12346
12347  		// @option format: String = 'image/jpeg'
12348  		// WMS image format (use `'image/png'` for layers with transparency).
12349  		format: 'image/jpeg',
12350
12351  		// @option transparent: Boolean = false
12352  		// If `true`, the WMS service will return images with transparency.
12353  		transparent: false,
12354
12355  		// @option version: String = '1.1.1'
12356  		// Version of the WMS service to use
12357  		version: '1.1.1'
12358  	},
12359
12360  	options: {
12361  		// @option crs: CRS = null
12362  		// Coordinate Reference System to use for the WMS requests, defaults to
12363  		// map CRS. Don't change this if you're not sure what it means.
12364  		crs: null,
12365
12366  		// @option uppercase: Boolean = false
12367  		// If `true`, WMS request parameter keys will be uppercase.
12368  		uppercase: false
12369  	},
12370
12371  	initialize: function (url, options) {
12372
12373  		this._url = url;
12374
12375  		var wmsParams = extend({}, this.defaultWmsParams);
12376
12377  		// all keys that are not TileLayer options go to WMS params
12378  		for (var i in options) {
12379  			if (!(i in this.options)) {
12380  				wmsParams[i] = options[i];
12381  			}
12382  		}
12383
12384  		options = setOptions(this, options);
12385
12386  		var realRetina = options.detectRetina && Browser.retina ? 2 : 1;
12387  		var tileSize = this.getTileSize();
12388  		wmsParams.width = tileSize.x * realRetina;
12389  		wmsParams.height = tileSize.y * realRetina;
12390
12391  		this.wmsParams = wmsParams;
12392  	},
12393
12394  	onAdd: function (map) {
12395
12396  		this._crs = this.options.crs || map.options.crs;
12397  		this._wmsVersion = parseFloat(this.wmsParams.version);
12398
12399  		var projectionKey = this._wmsVersion >= 1.3 ? 'crs' : 'srs';
12400  		this.wmsParams[projectionKey] = this._crs.code;
12401
12402  		TileLayer.prototype.onAdd.call(this, map);
12403  	},
12404
12405  	getTileUrl: function (coords) {
12406
12407  		var tileBounds = this._tileCoordsToNwSe(coords),
12408  		    crs = this._crs,
12409  		    bounds = toBounds(crs.project(tileBounds[0]), crs.project(tileBounds[1])),
12410  		    min = bounds.min,
12411  		    max = bounds.max,
12412  		    bbox = (this._wmsVersion >= 1.3 && this._crs === EPSG4326 ?
12413  		    [min.y, min.x, max.y, max.x] :
12414  		    [min.x, min.y, max.x, max.y]).join(','),
12415  		    url = TileLayer.prototype.getTileUrl.call(this, coords);
12416  		return url +
12417  			getParamString(this.wmsParams, url, this.options.uppercase) +
12418  			(this.options.uppercase ? '&BBOX=' : '&bbox=') + bbox;
12419  	},
12420
12421  	// @method setParams(params: Object, noRedraw?: Boolean): this
12422  	// Merges an object with the new parameters and re-requests tiles on the current screen (unless `noRedraw` was set to true).
12423  	setParams: function (params, noRedraw) {
12424
12425  		extend(this.wmsParams, params);
12426
12427  		if (!noRedraw) {
12428  			this.redraw();
12429  		}
12430
12431  		return this;
12432  	}
12433  });
12434
12435
12436  // @factory L.tileLayer.wms(baseUrl: String, options: TileLayer.WMS options)
12437  // Instantiates a WMS tile layer object given a base URL of the WMS service and a WMS parameters/options object.
12438  function tileLayerWMS(url, options) {
12439  	return new TileLayerWMS(url, options);
12440  }
12441
12442  TileLayer.WMS = TileLayerWMS;
12443  tileLayer.wms = tileLayerWMS;
12444
12445  /*
12446   * @class Renderer
12447   * @inherits Layer
12448   * @aka L.Renderer
12449   *
12450   * Base class for vector renderer implementations (`SVG`, `Canvas`). Handles the
12451   * DOM container of the renderer, its bounds, and its zoom animation.
12452   *
12453   * A `Renderer` works as an implicit layer group for all `Path`s - the renderer
12454   * itself can be added or removed to the map. All paths use a renderer, which can
12455   * be implicit (the map will decide the type of renderer and use it automatically)
12456   * or explicit (using the [`renderer`](#path-renderer) option of the path).
12457   *
12458   * Do not use this class directly, use `SVG` and `Canvas` instead.
12459   *
12460   * @event update: Event
12461   * Fired when the renderer updates its bounds, center and zoom, for example when
12462   * its map has moved
12463   */
12464
12465  var Renderer = Layer.extend({
12466
12467  	// @section
12468  	// @aka Renderer options
12469  	options: {
12470  		// @option padding: Number = 0.1
12471  		// How much to extend the clip area around the map view (relative to its size)
12472  		// e.g. 0.1 would be 10% of map view in each direction
12473  		padding: 0.1
12474  	},
12475
12476  	initialize: function (options) {
12477  		setOptions(this, options);
12478  		stamp(this);
12479  		this._layers = this._layers || {};
12480  	},
12481
12482  	onAdd: function () {
12483  		if (!this._container) {
12484  			this._initContainer(); // defined by renderer implementations
12485
12486  			// always keep transform-origin as 0 0
12487  			addClass(this._container, 'leaflet-zoom-animated');
12488  		}
12489
12490  		this.getPane().appendChild(this._container);
12491  		this._update();
12492  		this.on('update', this._updatePaths, this);
12493  	},
12494
12495  	onRemove: function () {
12496  		this.off('update', this._updatePaths, this);
12497  		this._destroyContainer();
12498  	},
12499
12500  	getEvents: function () {
12501  		var events = {
12502  			viewreset: this._reset,
12503  			zoom: this._onZoom,
12504  			moveend: this._update,
12505  			zoomend: this._onZoomEnd
12506  		};
12507  		if (this._zoomAnimated) {
12508  			events.zoomanim = this._onAnimZoom;
12509  		}
12510  		return events;
12511  	},
12512
12513  	_onAnimZoom: function (ev) {
12514  		this._updateTransform(ev.center, ev.zoom);
12515  	},
12516
12517  	_onZoom: function () {
12518  		this._updateTransform(this._map.getCenter(), this._map.getZoom());
12519  	},
12520
12521  	_updateTransform: function (center, zoom) {
12522  		var scale = this._map.getZoomScale(zoom, this._zoom),
12523  		    viewHalf = this._map.getSize().multiplyBy(0.5 + this.options.padding),
12524  		    currentCenterPoint = this._map.project(this._center, zoom),
12525
12526  		    topLeftOffset = viewHalf.multiplyBy(-scale).add(currentCenterPoint)
12527  				  .subtract(this._map._getNewPixelOrigin(center, zoom));
12528
12529  		if (Browser.any3d) {
12530  			setTransform(this._container, topLeftOffset, scale);
12531  		} else {
12532  			setPosition(this._container, topLeftOffset);
12533  		}
12534  	},
12535
12536  	_reset: function () {
12537  		this._update();
12538  		this._updateTransform(this._center, this._zoom);
12539
12540  		for (var id in this._layers) {
12541  			this._layers[id]._reset();
12542  		}
12543  	},
12544
12545  	_onZoomEnd: function () {
12546  		for (var id in this._layers) {
12547  			this._layers[id]._project();
12548  		}
12549  	},
12550
12551  	_updatePaths: function () {
12552  		for (var id in this._layers) {
12553  			this._layers[id]._update();
12554  		}
12555  	},
12556
12557  	_update: function () {
12558  		// Update pixel bounds of renderer container (for positioning/sizing/clipping later)
12559  		// Subclasses are responsible of firing the 'update' event.
12560  		var p = this.options.padding,
12561  		    size = this._map.getSize(),
12562  		    min = this._map.containerPointToLayerPoint(size.multiplyBy(-p)).round();
12563
12564  		this._bounds = new Bounds(min, min.add(size.multiplyBy(1 + p * 2)).round());
12565
12566  		this._center = this._map.getCenter();
12567  		this._zoom = this._map.getZoom();
12568  	}
12569  });
12570
12571  /*
12572   * @class Canvas
12573   * @inherits Renderer
12574   * @aka L.Canvas
12575   *
12576   * Allows vector layers to be displayed with [`<canvas>`](https://developer.mozilla.org/docs/Web/API/Canvas_API).
12577   * Inherits `Renderer`.
12578   *
12579   * Due to [technical limitations](https://caniuse.com/canvas), Canvas is not
12580   * available in all web browsers, notably IE8, and overlapping geometries might
12581   * not display properly in some edge cases.
12582   *
12583   * @example
12584   *
12585   * Use Canvas by default for all paths in the map:
12586   *
12587   * ```js
12588   * var map = L.map('map', {
12589   * 	renderer: L.canvas()
12590   * });
12591   * ```
12592   *
12593   * Use a Canvas renderer with extra padding for specific vector geometries:
12594   *
12595   * ```js
12596   * var map = L.map('map');
12597   * var myRenderer = L.canvas({ padding: 0.5 });
12598   * var line = L.polyline( coordinates, { renderer: myRenderer } );
12599   * var circle = L.circle( center, { renderer: myRenderer } );
12600   * ```
12601   */
12602
12603  var Canvas = Renderer.extend({
12604
12605  	// @section
12606  	// @aka Canvas options
12607  	options: {
12608  		// @option tolerance: Number = 0
12609  		// How much to extend the click tolerance around a path/object on the map.
12610  		tolerance: 0
12611  	},
12612
12613  	getEvents: function () {
12614  		var events = Renderer.prototype.getEvents.call(this);
12615  		events.viewprereset = this._onViewPreReset;
12616  		return events;
12617  	},
12618
12619  	_onViewPreReset: function () {
12620  		// Set a flag so that a viewprereset+moveend+viewreset only updates&redraws once
12621  		this._postponeUpdatePaths = true;
12622  	},
12623
12624  	onAdd: function () {
12625  		Renderer.prototype.onAdd.call(this);
12626
12627  		// Redraw vectors since canvas is cleared upon removal,
12628  		// in case of removing the renderer itself from the map.
12629  		this._draw();
12630  	},
12631
12632  	_initContainer: function () {
12633  		var container = this._container = document.createElement('canvas');
12634
12635  		on(container, 'mousemove', this._onMouseMove, this);
12636  		on(container, 'click dblclick mousedown mouseup contextmenu', this._onClick, this);
12637  		on(container, 'mouseout', this._handleMouseOut, this);
12638  		container['_leaflet_disable_events'] = true;
12639
12640  		this._ctx = container.getContext('2d');
12641  	},
12642
12643  	_destroyContainer: function () {
12644  		cancelAnimFrame(this._redrawRequest);
12645  		delete this._ctx;
12646  		remove(this._container);
12647  		off(this._container);
12648  		delete this._container;
12649  	},
12650
12651  	_updatePaths: function () {
12652  		if (this._postponeUpdatePaths) { return; }
12653
12654  		var layer;
12655  		this._redrawBounds = null;
12656  		for (var id in this._layers) {
12657  			layer = this._layers[id];
12658  			layer._update();
12659  		}
12660  		this._redraw();
12661  	},
12662
12663  	_update: function () {
12664  		if (this._map._animatingZoom && this._bounds) { return; }
12665
12666  		Renderer.prototype._update.call(this);
12667
12668  		var b = this._bounds,
12669  		    container = this._container,
12670  		    size = b.getSize(),
12671  		    m = Browser.retina ? 2 : 1;
12672
12673  		setPosition(container, b.min);
12674
12675  		// set canvas size (also clearing it); use double size on retina
12676  		container.width = m * size.x;
12677  		container.height = m * size.y;
12678  		container.style.width = size.x + 'px';
12679  		container.style.height = size.y + 'px';
12680
12681  		if (Browser.retina) {
12682  			this._ctx.scale(2, 2);
12683  		}
12684
12685  		// translate so we use the same path coordinates after canvas element moves
12686  		this._ctx.translate(-b.min.x, -b.min.y);
12687
12688  		// Tell paths to redraw themselves
12689  		this.fire('update');
12690  	},
12691
12692  	_reset: function () {
12693  		Renderer.prototype._reset.call(this);
12694
12695  		if (this._postponeUpdatePaths) {
12696  			this._postponeUpdatePaths = false;
12697  			this._updatePaths();
12698  		}
12699  	},
12700
12701  	_initPath: function (layer) {
12702  		this._updateDashArray(layer);
12703  		this._layers[stamp(layer)] = layer;
12704
12705  		var order = layer._order = {
12706  			layer: layer,
12707  			prev: this._drawLast,
12708  			next: null
12709  		};
12710  		if (this._drawLast) { this._drawLast.next = order; }
12711  		this._drawLast = order;
12712  		this._drawFirst = this._drawFirst || this._drawLast;
12713  	},
12714
12715  	_addPath: function (layer) {
12716  		this._requestRedraw(layer);
12717  	},
12718
12719  	_removePath: function (layer) {
12720  		var order = layer._order;
12721  		var next = order.next;
12722  		var prev = order.prev;
12723
12724  		if (next) {
12725  			next.prev = prev;
12726  		} else {
12727  			this._drawLast = prev;
12728  		}
12729  		if (prev) {
12730  			prev.next = next;
12731  		} else {
12732  			this._drawFirst = next;
12733  		}
12734
12735  		delete layer._order;
12736
12737  		delete this._layers[stamp(layer)];
12738
12739  		this._requestRedraw(layer);
12740  	},
12741
12742  	_updatePath: function (layer) {
12743  		// Redraw the union of the layer's old pixel
12744  		// bounds and the new pixel bounds.
12745  		this._extendRedrawBounds(layer);
12746  		layer._project();
12747  		layer._update();
12748  		// The redraw will extend the redraw bounds
12749  		// with the new pixel bounds.
12750  		this._requestRedraw(layer);
12751  	},
12752
12753  	_updateStyle: function (layer) {
12754  		this._updateDashArray(layer);
12755  		this._requestRedraw(layer);
12756  	},
12757
12758  	_updateDashArray: function (layer) {
12759  		if (typeof layer.options.dashArray === 'string') {
12760  			var parts = layer.options.dashArray.split(/[, ]+/),
12761  			    dashArray = [],
12762  			    dashValue,
12763  			    i;
12764  			for (i = 0; i < parts.length; i++) {
12765  				dashValue = Number(parts[i]);
12766  				// Ignore dash array containing invalid lengths
12767  				if (isNaN(dashValue)) { return; }
12768  				dashArray.push(dashValue);
12769  			}
12770  			layer.options._dashArray = dashArray;
12771  		} else {
12772  			layer.options._dashArray = layer.options.dashArray;
12773  		}
12774  	},
12775
12776  	_requestRedraw: function (layer) {
12777  		if (!this._map) { return; }
12778
12779  		this._extendRedrawBounds(layer);
12780  		this._redrawRequest = this._redrawRequest || requestAnimFrame(this._redraw, this);
12781  	},
12782
12783  	_extendRedrawBounds: function (layer) {
12784  		if (layer._pxBounds) {
12785  			var padding = (layer.options.weight || 0) + 1;
12786  			this._redrawBounds = this._redrawBounds || new Bounds();
12787  			this._redrawBounds.extend(layer._pxBounds.min.subtract([padding, padding]));
12788  			this._redrawBounds.extend(layer._pxBounds.max.add([padding, padding]));
12789  		}
12790  	},
12791
12792  	_redraw: function () {
12793  		this._redrawRequest = null;
12794
12795  		if (this._redrawBounds) {
12796  			this._redrawBounds.min._floor();
12797  			this._redrawBounds.max._ceil();
12798  		}
12799
12800  		this._clear(); // clear layers in redraw bounds
12801  		this._draw(); // draw layers
12802
12803  		this._redrawBounds = null;
12804  	},
12805
12806  	_clear: function () {
12807  		var bounds = this._redrawBounds;
12808  		if (bounds) {
12809  			var size = bounds.getSize();
12810  			this._ctx.clearRect(bounds.min.x, bounds.min.y, size.x, size.y);
12811  		} else {
12812  			this._ctx.save();
12813  			this._ctx.setTransform(1, 0, 0, 1, 0, 0);
12814  			this._ctx.clearRect(0, 0, this._container.width, this._container.height);
12815  			this._ctx.restore();
12816  		}
12817  	},
12818
12819  	_draw: function () {
12820  		var layer, bounds = this._redrawBounds;
12821  		this._ctx.save();
12822  		if (bounds) {
12823  			var size = bounds.getSize();
12824  			this._ctx.beginPath();
12825  			this._ctx.rect(bounds.min.x, bounds.min.y, size.x, size.y);
12826  			this._ctx.clip();
12827  		}
12828
12829  		this._drawing = true;
12830
12831  		for (var order = this._drawFirst; order; order = order.next) {
12832  			layer = order.layer;
12833  			if (!bounds || (layer._pxBounds && layer._pxBounds.intersects(bounds))) {
12834  				layer._updatePath();
12835  			}
12836  		}
12837
12838  		this._drawing = false;
12839
12840  		this._ctx.restore();  // Restore state before clipping.
12841  	},
12842
12843  	_updatePoly: function (layer, closed) {
12844  		if (!this._drawing) { return; }
12845
12846  		var i, j, len2, p,
12847  		    parts = layer._parts,
12848  		    len = parts.length,
12849  		    ctx = this._ctx;
12850
12851  		if (!len) { return; }
12852
12853  		ctx.beginPath();
12854
12855  		for (i = 0; i < len; i++) {
12856  			for (j = 0, len2 = parts[i].length; j < len2; j++) {
12857  				p = parts[i][j];
12858  				ctx[j ? 'lineTo' : 'moveTo'](p.x, p.y);
12859  			}
12860  			if (closed) {
12861  				ctx.closePath();
12862  			}
12863  		}
12864
12865  		this._fillStroke(ctx, layer);
12866
12867  		// TODO optimization: 1 fill/stroke for all features with equal style instead of 1 for each feature
12868  	},
12869
12870  	_updateCircle: function (layer) {
12871
12872  		if (!this._drawing || layer._empty()) { return; }
12873
12874  		var p = layer._point,
12875  		    ctx = this._ctx,
12876  		    r = Math.max(Math.round(layer._radius), 1),
12877  		    s = (Math.max(Math.round(layer._radiusY), 1) || r) / r;
12878
12879  		if (s !== 1) {
12880  			ctx.save();
12881  			ctx.scale(1, s);
12882  		}
12883
12884  		ctx.beginPath();
12885  		ctx.arc(p.x, p.y / s, r, 0, Math.PI * 2, false);
12886
12887  		if (s !== 1) {
12888  			ctx.restore();
12889  		}
12890
12891  		this._fillStroke(ctx, layer);
12892  	},
12893
12894  	_fillStroke: function (ctx, layer) {
12895  		var options = layer.options;
12896
12897  		if (options.fill) {
12898  			ctx.globalAlpha = options.fillOpacity;
12899  			ctx.fillStyle = options.fillColor || options.color;
12900  			ctx.fill(options.fillRule || 'evenodd');
12901  		}
12902
12903  		if (options.stroke && options.weight !== 0) {
12904  			if (ctx.setLineDash) {
12905  				ctx.setLineDash(layer.options && layer.options._dashArray || []);
12906  			}
12907  			ctx.globalAlpha = options.opacity;
12908  			ctx.lineWidth = options.weight;
12909  			ctx.strokeStyle = options.color;
12910  			ctx.lineCap = options.lineCap;
12911  			ctx.lineJoin = options.lineJoin;
12912  			ctx.stroke();
12913  		}
12914  	},
12915
12916  	// Canvas obviously doesn't have mouse events for individual drawn objects,
12917  	// so we emulate that by calculating what's under the mouse on mousemove/click manually
12918
12919  	_onClick: function (e) {
12920  		var point = this._map.mouseEventToLayerPoint(e), layer, clickedLayer;
12921
12922  		for (var order = this._drawFirst; order; order = order.next) {
12923  			layer = order.layer;
12924  			if (layer.options.interactive && layer._containsPoint(point)) {
12925  				if (!(e.type === 'click' || e.type === 'preclick') || !this._map._draggableMoved(layer)) {
12926  					clickedLayer = layer;
12927  				}
12928  			}
12929  		}
12930  		this._fireEvent(clickedLayer ? [clickedLayer] : false, e);
12931  	},
12932
12933  	_onMouseMove: function (e) {
12934  		if (!this._map || this._map.dragging.moving() || this._map._animatingZoom) { return; }
12935
12936  		var point = this._map.mouseEventToLayerPoint(e);
12937  		this._handleMouseHover(e, point);
12938  	},
12939
12940
12941  	_handleMouseOut: function (e) {
12942  		var layer = this._hoveredLayer;
12943  		if (layer) {
12944  			// if we're leaving the layer, fire mouseout
12945  			removeClass(this._container, 'leaflet-interactive');
12946  			this._fireEvent([layer], e, 'mouseout');
12947  			this._hoveredLayer = null;
12948  			this._mouseHoverThrottled = false;
12949  		}
12950  	},
12951
12952  	_handleMouseHover: function (e, point) {
12953  		if (this._mouseHoverThrottled) {
12954  			return;
12955  		}
12956
12957  		var layer, candidateHoveredLayer;
12958
12959  		for (var order = this._drawFirst; order; order = order.next) {
12960  			layer = order.layer;
12961  			if (layer.options.interactive && layer._containsPoint(point)) {
12962  				candidateHoveredLayer = layer;
12963  			}
12964  		}
12965
12966  		if (candidateHoveredLayer !== this._hoveredLayer) {
12967  			this._handleMouseOut(e);
12968
12969  			if (candidateHoveredLayer) {
12970  				addClass(this._container, 'leaflet-interactive'); // change cursor
12971  				this._fireEvent([candidateHoveredLayer], e, 'mouseover');
12972  				this._hoveredLayer = candidateHoveredLayer;
12973  			}
12974  		}
12975
12976  		this._fireEvent(this._hoveredLayer ? [this._hoveredLayer] : false, e);
12977
12978  		this._mouseHoverThrottled = true;
12979  		setTimeout(bind(function () {
12980  			this._mouseHoverThrottled = false;
12981  		}, this), 32);
12982  	},
12983
12984  	_fireEvent: function (layers, e, type) {
12985  		this._map._fireDOMEvent(e, type || e.type, layers);
12986  	},
12987
12988  	_bringToFront: function (layer) {
12989  		var order = layer._order;
12990
12991  		if (!order) { return; }
12992
12993  		var next = order.next;
12994  		var prev = order.prev;
12995
12996  		if (next) {
12997  			next.prev = prev;
12998  		} else {
12999  			// Already last
13000  			return;
13001  		}
13002  		if (prev) {
13003  			prev.next = next;
13004  		} else if (next) {
13005  			// Update first entry unless this is the
13006  			// single entry
13007  			this._drawFirst = next;
13008  		}
13009
13010  		order.prev = this._drawLast;
13011  		this._drawLast.next = order;
13012
13013  		order.next = null;
13014  		this._drawLast = order;
13015
13016  		this._requestRedraw(layer);
13017  	},
13018
13019  	_bringToBack: function (layer) {
13020  		var order = layer._order;
13021
13022  		if (!order) { return; }
13023
13024  		var next = order.next;
13025  		var prev = order.prev;
13026
13027  		if (prev) {
13028  			prev.next = next;
13029  		} else {
13030  			// Already first
13031  			return;
13032  		}
13033  		if (next) {
13034  			next.prev = prev;
13035  		} else if (prev) {
13036  			// Update last entry unless this is the
13037  			// single entry
13038  			this._drawLast = prev;
13039  		}
13040
13041  		order.prev = null;
13042
13043  		order.next = this._drawFirst;
13044  		this._drawFirst.prev = order;
13045  		this._drawFirst = order;
13046
13047  		this._requestRedraw(layer);
13048  	}
13049  });
13050
13051  // @factory L.canvas(options?: Renderer options)
13052  // Creates a Canvas renderer with the given options.
13053  function canvas(options) {
13054  	return Browser.canvas ? new Canvas(options) : null;
13055  }
13056
13057  /*
13058   * Thanks to Dmitry Baranovsky and his Raphael library for inspiration!
13059   */
13060
13061
13062  var vmlCreate = (function () {
13063  	try {
13064  		document.namespaces.add('lvml', 'urn:schemas-microsoft-com:vml');
13065  		return function (name) {
13066  			return document.createElement('<lvml:' + name + ' class="lvml">');
13067  		};
13068  	} catch (e) {
13069  		// Do not return fn from catch block so `e` can be garbage collected
13070  		// See https://github.com/Leaflet/Leaflet/pull/7279
13071  	}
13072  	return function (name) {
13073  		return document.createElement('<' + name + ' xmlns="urn:schemas-microsoft.com:vml" class="lvml">');
13074  	};
13075  })();
13076
13077
13078  /*
13079   * @class SVG
13080   *
13081   *
13082   * VML was deprecated in 2012, which means VML functionality exists only for backwards compatibility
13083   * with old versions of Internet Explorer.
13084   */
13085
13086  // mixin to redefine some SVG methods to handle VML syntax which is similar but with some differences
13087  var vmlMixin = {
13088
13089  	_initContainer: function () {
13090  		this._container = create$1('div', 'leaflet-vml-container');
13091  	},
13092
13093  	_update: function () {
13094  		if (this._map._animatingZoom) { return; }
13095  		Renderer.prototype._update.call(this);
13096  		this.fire('update');
13097  	},
13098
13099  	_initPath: function (layer) {
13100  		var container = layer._container = vmlCreate('shape');
13101
13102  		addClass(container, 'leaflet-vml-shape ' + (this.options.className || ''));
13103
13104  		container.coordsize = '1 1';
13105
13106  		layer._path = vmlCreate('path');
13107  		container.appendChild(layer._path);
13108
13109  		this._updateStyle(layer);
13110  		this._layers[stamp(layer)] = layer;
13111  	},
13112
13113  	_addPath: function (layer) {
13114  		var container = layer._container;
13115  		this._container.appendChild(container);
13116
13117  		if (layer.options.interactive) {
13118  			layer.addInteractiveTarget(container);
13119  		}
13120  	},
13121
13122  	_removePath: function (layer) {
13123  		var container = layer._container;
13124  		remove(container);
13125  		layer.removeInteractiveTarget(container);
13126  		delete this._layers[stamp(layer)];
13127  	},
13128
13129  	_updateStyle: function (layer) {
13130  		var stroke = layer._stroke,
13131  		    fill = layer._fill,
13132  		    options = layer.options,
13133  		    container = layer._container;
13134
13135  		container.stroked = !!options.stroke;
13136  		container.filled = !!options.fill;
13137
13138  		if (options.stroke) {
13139  			if (!stroke) {
13140  				stroke = layer._stroke = vmlCreate('stroke');
13141  			}
13142  			container.appendChild(stroke);
13143  			stroke.weight = options.weight + 'px';
13144  			stroke.color = options.color;
13145  			stroke.opacity = options.opacity;
13146
13147  			if (options.dashArray) {
13148  				stroke.dashStyle = isArray(options.dashArray) ?
13149  				    options.dashArray.join(' ') :
13150  				    options.dashArray.replace(/( *, *)/g, ' ');
13151  			} else {
13152  				stroke.dashStyle = '';
13153  			}
13154  			stroke.endcap = options.lineCap.replace('butt', 'flat');
13155  			stroke.joinstyle = options.lineJoin;
13156
13157  		} else if (stroke) {
13158  			container.removeChild(stroke);
13159  			layer._stroke = null;
13160  		}
13161
13162  		if (options.fill) {
13163  			if (!fill) {
13164  				fill = layer._fill = vmlCreate('fill');
13165  			}
13166  			container.appendChild(fill);
13167  			fill.color = options.fillColor || options.color;
13168  			fill.opacity = options.fillOpacity;
13169
13170  		} else if (fill) {
13171  			container.removeChild(fill);
13172  			layer._fill = null;
13173  		}
13174  	},
13175
13176  	_updateCircle: function (layer) {
13177  		var p = layer._point.round(),
13178  		    r = Math.round(layer._radius),
13179  		    r2 = Math.round(layer._radiusY || r);
13180
13181  		this._setPath(layer, layer._empty() ? 'M0 0' :
13182  			'AL ' + p.x + ',' + p.y + ' ' + r + ',' + r2 + ' 0,' + (65535 * 360));
13183  	},
13184
13185  	_setPath: function (layer, path) {
13186  		layer._path.v = path;
13187  	},
13188
13189  	_bringToFront: function (layer) {
13190  		toFront(layer._container);
13191  	},
13192
13193  	_bringToBack: function (layer) {
13194  		toBack(layer._container);
13195  	}
13196  };
13197
13198  var create = Browser.vml ? vmlCreate : svgCreate;
13199
13200  /*
13201   * @class SVG
13202   * @inherits Renderer
13203   * @aka L.SVG
13204   *
13205   * Allows vector layers to be displayed with [SVG](https://developer.mozilla.org/docs/Web/SVG).
13206   * Inherits `Renderer`.
13207   *
13208   * Due to [technical limitations](https://caniuse.com/svg), SVG is not
13209   * available in all web browsers, notably Android 2.x and 3.x.
13210   *
13211   * Although SVG is not available on IE7 and IE8, these browsers support
13212   * [VML](https://en.wikipedia.org/wiki/Vector_Markup_Language)
13213   * (a now deprecated technology), and the SVG renderer will fall back to VML in
13214   * this case.
13215   *
13216   * @example
13217   *
13218   * Use SVG by default for all paths in the map:
13219   *
13220   * ```js
13221   * var map = L.map('map', {
13222   * 	renderer: L.svg()
13223   * });
13224   * ```
13225   *
13226   * Use a SVG renderer with extra padding for specific vector geometries:
13227   *
13228   * ```js
13229   * var map = L.map('map');
13230   * var myRenderer = L.svg({ padding: 0.5 });
13231   * var line = L.polyline( coordinates, { renderer: myRenderer } );
13232   * var circle = L.circle( center, { renderer: myRenderer } );
13233   * ```
13234   */
13235
13236  var SVG = Renderer.extend({
13237
13238  	_initContainer: function () {
13239  		this._container = create('svg');
13240
13241  		// makes it possible to click through svg root; we'll reset it back in individual paths
13242  		this._container.setAttribute('pointer-events', 'none');
13243
13244  		this._rootGroup = create('g');
13245  		this._container.appendChild(this._rootGroup);
13246  	},
13247
13248  	_destroyContainer: function () {
13249  		remove(this._container);
13250  		off(this._container);
13251  		delete this._container;
13252  		delete this._rootGroup;
13253  		delete this._svgSize;
13254  	},
13255
13256  	_update: function () {
13257  		if (this._map._animatingZoom && this._bounds) { return; }
13258
13259  		Renderer.prototype._update.call(this);
13260
13261  		var b = this._bounds,
13262  		    size = b.getSize(),
13263  		    container = this._container;
13264
13265  		// set size of svg-container if changed
13266  		if (!this._svgSize || !this._svgSize.equals(size)) {
13267  			this._svgSize = size;
13268  			container.setAttribute('width', size.x);
13269  			container.setAttribute('height', size.y);
13270  		}
13271
13272  		// movement: update container viewBox so that we don't have to change coordinates of individual layers
13273  		setPosition(container, b.min);
13274  		container.setAttribute('viewBox', [b.min.x, b.min.y, size.x, size.y].join(' '));
13275
13276  		this.fire('update');
13277  	},
13278
13279  	// methods below are called by vector layers implementations
13280
13281  	_initPath: function (layer) {
13282  		var path = layer._path = create('path');
13283
13284  		// @namespace Path
13285  		// @option className: String = null
13286  		// Custom class name set on an element. Only for SVG renderer.
13287  		if (layer.options.className) {
13288  			addClass(path, layer.options.className);
13289  		}
13290
13291  		if (layer.options.interactive) {
13292  			addClass(path, 'leaflet-interactive');
13293  		}
13294
13295  		this._updateStyle(layer);
13296  		this._layers[stamp(layer)] = layer;
13297  	},
13298
13299  	_addPath: function (layer) {
13300  		if (!this._rootGroup) { this._initContainer(); }
13301  		this._rootGroup.appendChild(layer._path);
13302  		layer.addInteractiveTarget(layer._path);
13303  	},
13304
13305  	_removePath: function (layer) {
13306  		remove(layer._path);
13307  		layer.removeInteractiveTarget(layer._path);
13308  		delete this._layers[stamp(layer)];
13309  	},
13310
13311  	_updatePath: function (layer) {
13312  		layer._project();
13313  		layer._update();
13314  	},
13315
13316  	_updateStyle: function (layer) {
13317  		var path = layer._path,
13318  		    options = layer.options;
13319
13320  		if (!path) { return; }
13321
13322  		if (options.stroke) {
13323  			path.setAttribute('stroke', options.color);
13324  			path.setAttribute('stroke-opacity', options.opacity);
13325  			path.setAttribute('stroke-width', options.weight);
13326  			path.setAttribute('stroke-linecap', options.lineCap);
13327  			path.setAttribute('stroke-linejoin', options.lineJoin);
13328
13329  			if (options.dashArray) {
13330  				path.setAttribute('stroke-dasharray', options.dashArray);
13331  			} else {
13332  				path.removeAttribute('stroke-dasharray');
13333  			}
13334
13335  			if (options.dashOffset) {
13336  				path.setAttribute('stroke-dashoffset', options.dashOffset);
13337  			} else {
13338  				path.removeAttribute('stroke-dashoffset');
13339  			}
13340  		} else {
13341  			path.setAttribute('stroke', 'none');
13342  		}
13343
13344  		if (options.fill) {
13345  			path.setAttribute('fill', options.fillColor || options.color);
13346  			path.setAttribute('fill-opacity', options.fillOpacity);
13347  			path.setAttribute('fill-rule', options.fillRule || 'evenodd');
13348  		} else {
13349  			path.setAttribute('fill', 'none');
13350  		}
13351  	},
13352
13353  	_updatePoly: function (layer, closed) {
13354  		this._setPath(layer, pointsToPath(layer._parts, closed));
13355  	},
13356
13357  	_updateCircle: function (layer) {
13358  		var p = layer._point,
13359  		    r = Math.max(Math.round(layer._radius), 1),
13360  		    r2 = Math.max(Math.round(layer._radiusY), 1) || r,
13361  		    arc = 'a' + r + ',' + r2 + ' 0 1,0 ';
13362
13363  		// drawing a circle with two half-arcs
13364  		var d = layer._empty() ? 'M0 0' :
13365  			'M' + (p.x - r) + ',' + p.y +
13366  			arc + (r * 2) + ',0 ' +
13367  			arc + (-r * 2) + ',0 ';
13368
13369  		this._setPath(layer, d);
13370  	},
13371
13372  	_setPath: function (layer, path) {
13373  		layer._path.setAttribute('d', path);
13374  	},
13375
13376  	// SVG does not have the concept of zIndex so we resort to changing the DOM order of elements
13377  	_bringToFront: function (layer) {
13378  		toFront(layer._path);
13379  	},
13380
13381  	_bringToBack: function (layer) {
13382  		toBack(layer._path);
13383  	}
13384  });
13385
13386  if (Browser.vml) {
13387  	SVG.include(vmlMixin);
13388  }
13389
13390  // @namespace SVG
13391  // @factory L.svg(options?: Renderer options)
13392  // Creates a SVG renderer with the given options.
13393  function svg(options) {
13394  	return Browser.svg || Browser.vml ? new SVG(options) : null;
13395  }
13396
13397  Map.include({
13398  	// @namespace Map; @method getRenderer(layer: Path): Renderer
13399  	// Returns the instance of `Renderer` that should be used to render the given
13400  	// `Path`. It will ensure that the `renderer` options of the map and paths
13401  	// are respected, and that the renderers do exist on the map.
13402  	getRenderer: function (layer) {
13403  		// @namespace Path; @option renderer: Renderer
13404  		// Use this specific instance of `Renderer` for this path. Takes
13405  		// precedence over the map's [default renderer](#map-renderer).
13406  		var renderer = layer.options.renderer || this._getPaneRenderer(layer.options.pane) || this.options.renderer || this._renderer;
13407
13408  		if (!renderer) {
13409  			renderer = this._renderer = this._createRenderer();
13410  		}
13411
13412  		if (!this.hasLayer(renderer)) {
13413  			this.addLayer(renderer);
13414  		}
13415  		return renderer;
13416  	},
13417
13418  	_getPaneRenderer: function (name) {
13419  		if (name === 'overlayPane' || name === undefined) {
13420  			return false;
13421  		}
13422
13423  		var renderer = this._paneRenderers[name];
13424  		if (renderer === undefined) {
13425  			renderer = this._createRenderer({pane: name});
13426  			this._paneRenderers[name] = renderer;
13427  		}
13428  		return renderer;
13429  	},
13430
13431  	_createRenderer: function (options) {
13432  		// @namespace Map; @option preferCanvas: Boolean = false
13433  		// Whether `Path`s should be rendered on a `Canvas` renderer.
13434  		// By default, all `Path`s are rendered in a `SVG` renderer.
13435  		return (this.options.preferCanvas && canvas(options)) || svg(options);
13436  	}
13437  });
13438
13439  /*
13440   * L.Rectangle extends Polygon and creates a rectangle when passed a LatLngBounds object.
13441   */
13442
13443  /*
13444   * @class Rectangle
13445   * @aka L.Rectangle
13446   * @inherits Polygon
13447   *
13448   * A class for drawing rectangle overlays on a map. Extends `Polygon`.
13449   *
13450   * @example
13451   *
13452   * ```js
13453   * // define rectangle geographical bounds
13454   * var bounds = [[54.559322, -5.767822], [56.1210604, -3.021240]];
13455   *
13456   * // create an orange rectangle
13457   * L.rectangle(bounds, {color: "#ff7800", weight: 1}).addTo(map);
13458   *
13459   * // zoom the map to the rectangle bounds
13460   * map.fitBounds(bounds);
13461   * ```
13462   *
13463   */
13464
13465
13466  var Rectangle = Polygon.extend({
13467  	initialize: function (latLngBounds, options) {
13468  		Polygon.prototype.initialize.call(this, this._boundsToLatLngs(latLngBounds), options);
13469  	},
13470
13471  	// @method setBounds(latLngBounds: LatLngBounds): this
13472  	// Redraws the rectangle with the passed bounds.
13473  	setBounds: function (latLngBounds) {
13474  		return this.setLatLngs(this._boundsToLatLngs(latLngBounds));
13475  	},
13476
13477  	_boundsToLatLngs: function (latLngBounds) {
13478  		latLngBounds = toLatLngBounds(latLngBounds);
13479  		return [
13480  			latLngBounds.getSouthWest(),
13481  			latLngBounds.getNorthWest(),
13482  			latLngBounds.getNorthEast(),
13483  			latLngBounds.getSouthEast()
13484  		];
13485  	}
13486  });
13487
13488
13489  // @factory L.rectangle(latLngBounds: LatLngBounds, options?: Polyline options)
13490  function rectangle(latLngBounds, options) {
13491  	return new Rectangle(latLngBounds, options);
13492  }
13493
13494  SVG.create = create;
13495  SVG.pointsToPath = pointsToPath;
13496
13497  GeoJSON.geometryToLayer = geometryToLayer;
13498  GeoJSON.coordsToLatLng = coordsToLatLng;
13499  GeoJSON.coordsToLatLngs = coordsToLatLngs;
13500  GeoJSON.latLngToCoords = latLngToCoords;
13501  GeoJSON.latLngsToCoords = latLngsToCoords;
13502  GeoJSON.getFeature = getFeature;
13503  GeoJSON.asFeature = asFeature;
13504
13505  /*
13506   * L.Handler.BoxZoom is used to add shift-drag zoom interaction to the map
13507   * (zoom to a selected bounding box), enabled by default.
13508   */
13509
13510  // @namespace Map
13511  // @section Interaction Options
13512  Map.mergeOptions({
13513  	// @option boxZoom: Boolean = true
13514  	// Whether the map can be zoomed to a rectangular area specified by
13515  	// dragging the mouse while pressing the shift key.
13516  	boxZoom: true
13517  });
13518
13519  var BoxZoom = Handler.extend({
13520  	initialize: function (map) {
13521  		this._map = map;
13522  		this._container = map._container;
13523  		this._pane = map._panes.overlayPane;
13524  		this._resetStateTimeout = 0;
13525  		map.on('unload', this._destroy, this);
13526  	},
13527
13528  	addHooks: function () {
13529  		on(this._container, 'mousedown', this._onMouseDown, this);
13530  	},
13531
13532  	removeHooks: function () {
13533  		off(this._container, 'mousedown', this._onMouseDown, this);
13534  	},
13535
13536  	moved: function () {
13537  		return this._moved;
13538  	},
13539
13540  	_destroy: function () {
13541  		remove(this._pane);
13542  		delete this._pane;
13543  	},
13544
13545  	_resetState: function () {
13546  		this._resetStateTimeout = 0;
13547  		this._moved = false;
13548  	},
13549
13550  	_clearDeferredResetState: function () {
13551  		if (this._resetStateTimeout !== 0) {
13552  			clearTimeout(this._resetStateTimeout);
13553  			this._resetStateTimeout = 0;
13554  		}
13555  	},
13556
13557  	_onMouseDown: function (e) {
13558  		if (!e.shiftKey || ((e.which !== 1) && (e.button !== 1))) { return false; }
13559
13560  		// Clear the deferred resetState if it hasn't executed yet, otherwise it
13561  		// will interrupt the interaction and orphan a box element in the container.
13562  		this._clearDeferredResetState();
13563  		this._resetState();
13564
13565  		disableTextSelection();
13566  		disableImageDrag();
13567
13568  		this._startPoint = this._map.mouseEventToContainerPoint(e);
13569
13570  		on(document, {
13571  			contextmenu: stop,
13572  			mousemove: this._onMouseMove,
13573  			mouseup: this._onMouseUp,
13574  			keydown: this._onKeyDown
13575  		}, this);
13576  	},
13577
13578  	_onMouseMove: function (e) {
13579  		if (!this._moved) {
13580  			this._moved = true;
13581
13582  			this._box = create$1('div', 'leaflet-zoom-box', this._container);
13583  			addClass(this._container, 'leaflet-crosshair');
13584
13585  			this._map.fire('boxzoomstart');
13586  		}
13587
13588  		this._point = this._map.mouseEventToContainerPoint(e);
13589
13590  		var bounds = new Bounds(this._point, this._startPoint),
13591  		    size = bounds.getSize();
13592
13593  		setPosition(this._box, bounds.min);
13594
13595  		this._box.style.width  = size.x + 'px';
13596  		this._box.style.height = size.y + 'px';
13597  	},
13598
13599  	_finish: function () {
13600  		if (this._moved) {
13601  			remove(this._box);
13602  			removeClass(this._container, 'leaflet-crosshair');
13603  		}
13604
13605  		enableTextSelection();
13606  		enableImageDrag();
13607
13608  		off(document, {
13609  			contextmenu: stop,
13610  			mousemove: this._onMouseMove,
13611  			mouseup: this._onMouseUp,
13612  			keydown: this._onKeyDown
13613  		}, this);
13614  	},
13615
13616  	_onMouseUp: function (e) {
13617  		if ((e.which !== 1) && (e.button !== 1)) { return; }
13618
13619  		this._finish();
13620
13621  		if (!this._moved) { return; }
13622  		// Postpone to next JS tick so internal click event handling
13623  		// still see it as "moved".
13624  		this._clearDeferredResetState();
13625  		this._resetStateTimeout = setTimeout(bind(this._resetState, this), 0);
13626
13627  		var bounds = new LatLngBounds(
13628  		        this._map.containerPointToLatLng(this._startPoint),
13629  		        this._map.containerPointToLatLng(this._point));
13630
13631  		this._map
13632  			.fitBounds(bounds)
13633  			.fire('boxzoomend', {boxZoomBounds: bounds});
13634  	},
13635
13636  	_onKeyDown: function (e) {
13637  		if (e.keyCode === 27) {
13638  			this._finish();
13639  			this._clearDeferredResetState();
13640  			this._resetState();
13641  		}
13642  	}
13643  });
13644
13645  // @section Handlers
13646  // @property boxZoom: Handler
13647  // Box (shift-drag with mouse) zoom handler.
13648  Map.addInitHook('addHandler', 'boxZoom', BoxZoom);
13649
13650  /*
13651   * L.Handler.DoubleClickZoom is used to handle double-click zoom on the map, enabled by default.
13652   */
13653
13654  // @namespace Map
13655  // @section Interaction Options
13656
13657  Map.mergeOptions({
13658  	// @option doubleClickZoom: Boolean|String = true
13659  	// Whether the map can be zoomed in by double clicking on it and
13660  	// zoomed out by double clicking while holding shift. If passed
13661  	// `'center'`, double-click zoom will zoom to the center of the
13662  	//  view regardless of where the mouse was.
13663  	doubleClickZoom: true
13664  });
13665
13666  var DoubleClickZoom = Handler.extend({
13667  	addHooks: function () {
13668  		this._map.on('dblclick', this._onDoubleClick, this);
13669  	},
13670
13671  	removeHooks: function () {
13672  		this._map.off('dblclick', this._onDoubleClick, this);
13673  	},
13674
13675  	_onDoubleClick: function (e) {
13676  		var map = this._map,
13677  		    oldZoom = map.getZoom(),
13678  		    delta = map.options.zoomDelta,
13679  		    zoom = e.originalEvent.shiftKey ? oldZoom - delta : oldZoom + delta;
13680
13681  		if (map.options.doubleClickZoom === 'center') {
13682  			map.setZoom(zoom);
13683  		} else {
13684  			map.setZoomAround(e.containerPoint, zoom);
13685  		}
13686  	}
13687  });
13688
13689  // @section Handlers
13690  //
13691  // Map properties include interaction handlers that allow you to control
13692  // interaction behavior in runtime, enabling or disabling certain features such
13693  // as dragging or touch zoom (see `Handler` methods). For example:
13694  //
13695  // ```js
13696  // map.doubleClickZoom.disable();
13697  // ```
13698  //
13699  // @property doubleClickZoom: Handler
13700  // Double click zoom handler.
13701  Map.addInitHook('addHandler', 'doubleClickZoom', DoubleClickZoom);
13702
13703  /*
13704   * L.Handler.MapDrag is used to make the map draggable (with panning inertia), enabled by default.
13705   */
13706
13707  // @namespace Map
13708  // @section Interaction Options
13709  Map.mergeOptions({
13710  	// @option dragging: Boolean = true
13711  	// Whether the map is draggable with mouse/touch or not.
13712  	dragging: true,
13713
13714  	// @section Panning Inertia Options
13715  	// @option inertia: Boolean = *
13716  	// If enabled, panning of the map will have an inertia effect where
13717  	// the map builds momentum while dragging and continues moving in
13718  	// the same direction for some time. Feels especially nice on touch
13719  	// devices. Enabled by default.
13720  	inertia: true,
13721
13722  	// @option inertiaDeceleration: Number = 3000
13723  	// The rate with which the inertial movement slows down, in pixels/second².
13724  	inertiaDeceleration: 3400, // px/s^2
13725
13726  	// @option inertiaMaxSpeed: Number = Infinity
13727  	// Max speed of the inertial movement, in pixels/second.
13728  	inertiaMaxSpeed: Infinity, // px/s
13729
13730  	// @option easeLinearity: Number = 0.2
13731  	easeLinearity: 0.2,
13732
13733  	// TODO refactor, move to CRS
13734  	// @option worldCopyJump: Boolean = false
13735  	// With this option enabled, the map tracks when you pan to another "copy"
13736  	// of the world and seamlessly jumps to the original one so that all overlays
13737  	// like markers and vector layers are still visible.
13738  	worldCopyJump: false,
13739
13740  	// @option maxBoundsViscosity: Number = 0.0
13741  	// If `maxBounds` is set, this option will control how solid the bounds
13742  	// are when dragging the map around. The default value of `0.0` allows the
13743  	// user to drag outside the bounds at normal speed, higher values will
13744  	// slow down map dragging outside bounds, and `1.0` makes the bounds fully
13745  	// solid, preventing the user from dragging outside the bounds.
13746  	maxBoundsViscosity: 0.0
13747  });
13748
13749  var Drag = Handler.extend({
13750  	addHooks: function () {
13751  		if (!this._draggable) {
13752  			var map = this._map;
13753
13754  			this._draggable = new Draggable(map._mapPane, map._container);
13755
13756  			this._draggable.on({
13757  				dragstart: this._onDragStart,
13758  				drag: this._onDrag,
13759  				dragend: this._onDragEnd
13760  			}, this);
13761
13762  			this._draggable.on('predrag', this._onPreDragLimit, this);
13763  			if (map.options.worldCopyJump) {
13764  				this._draggable.on('predrag', this._onPreDragWrap, this);
13765  				map.on('zoomend', this._onZoomEnd, this);
13766
13767  				map.whenReady(this._onZoomEnd, this);
13768  			}
13769  		}
13770  		addClass(this._map._container, 'leaflet-grab leaflet-touch-drag');
13771  		this._draggable.enable();
13772  		this._positions = [];
13773  		this._times = [];
13774  	},
13775
13776  	removeHooks: function () {
13777  		removeClass(this._map._container, 'leaflet-grab');
13778  		removeClass(this._map._container, 'leaflet-touch-drag');
13779  		this._draggable.disable();
13780  	},
13781
13782  	moved: function () {
13783  		return this._draggable && this._draggable._moved;
13784  	},
13785
13786  	moving: function () {
13787  		return this._draggable && this._draggable._moving;
13788  	},
13789
13790  	_onDragStart: function () {
13791  		var map = this._map;
13792
13793  		map._stop();
13794  		if (this._map.options.maxBounds && this._map.options.maxBoundsViscosity) {
13795  			var bounds = toLatLngBounds(this._map.options.maxBounds);
13796
13797  			this._offsetLimit = toBounds(
13798  				this._map.latLngToContainerPoint(bounds.getNorthWest()).multiplyBy(-1),
13799  				this._map.latLngToContainerPoint(bounds.getSouthEast()).multiplyBy(-1)
13800  					.add(this._map.getSize()));
13801
13802  			this._viscosity = Math.min(1.0, Math.max(0.0, this._map.options.maxBoundsViscosity));
13803  		} else {
13804  			this._offsetLimit = null;
13805  		}
13806
13807  		map
13808  		    .fire('movestart')
13809  		    .fire('dragstart');
13810
13811  		if (map.options.inertia) {
13812  			this._positions = [];
13813  			this._times = [];
13814  		}
13815  	},
13816
13817  	_onDrag: function (e) {
13818  		if (this._map.options.inertia) {
13819  			var time = this._lastTime = +new Date(),
13820  			    pos = this._lastPos = this._draggable._absPos || this._draggable._newPos;
13821
13822  			this._positions.push(pos);
13823  			this._times.push(time);
13824
13825  			this._prunePositions(time);
13826  		}
13827
13828  		this._map
13829  		    .fire('move', e)
13830  		    .fire('drag', e);
13831  	},
13832
13833  	_prunePositions: function (time) {
13834  		while (this._positions.length > 1 && time - this._times[0] > 50) {
13835  			this._positions.shift();
13836  			this._times.shift();
13837  		}
13838  	},
13839
13840  	_onZoomEnd: function () {
13841  		var pxCenter = this._map.getSize().divideBy(2),
13842  		    pxWorldCenter = this._map.latLngToLayerPoint([0, 0]);
13843
13844  		this._initialWorldOffset = pxWorldCenter.subtract(pxCenter).x;
13845  		this._worldWidth = this._map.getPixelWorldBounds().getSize().x;
13846  	},
13847
13848  	_viscousLimit: function (value, threshold) {
13849  		return value - (value - threshold) * this._viscosity;
13850  	},
13851
13852  	_onPreDragLimit: function () {
13853  		if (!this._viscosity || !this._offsetLimit) { return; }
13854
13855  		var offset = this._draggable._newPos.subtract(this._draggable._startPos);
13856
13857  		var limit = this._offsetLimit;
13858  		if (offset.x < limit.min.x) { offset.x = this._viscousLimit(offset.x, limit.min.x); }
13859  		if (offset.y < limit.min.y) { offset.y = this._viscousLimit(offset.y, limit.min.y); }
13860  		if (offset.x > limit.max.x) { offset.x = this._viscousLimit(offset.x, limit.max.x); }
13861  		if (offset.y > limit.max.y) { offset.y = this._viscousLimit(offset.y, limit.max.y); }
13862
13863  		this._draggable._newPos = this._draggable._startPos.add(offset);
13864  	},
13865
13866  	_onPreDragWrap: function () {
13867  		// TODO refactor to be able to adjust map pane position after zoom
13868  		var worldWidth = this._worldWidth,
13869  		    halfWidth = Math.round(worldWidth / 2),
13870  		    dx = this._initialWorldOffset,
13871  		    x = this._draggable._newPos.x,
13872  		    newX1 = (x - halfWidth + dx) % worldWidth + halfWidth - dx,
13873  		    newX2 = (x + halfWidth + dx) % worldWidth - halfWidth - dx,
13874  		    newX = Math.abs(newX1 + dx) < Math.abs(newX2 + dx) ? newX1 : newX2;
13875
13876  		this._draggable._absPos = this._draggable._newPos.clone();
13877  		this._draggable._newPos.x = newX;
13878  	},
13879
13880  	_onDragEnd: function (e) {
13881  		var map = this._map,
13882  		    options = map.options,
13883
13884  		    noInertia = !options.inertia || e.noInertia || this._times.length < 2;
13885
13886  		map.fire('dragend', e);
13887
13888  		if (noInertia) {
13889  			map.fire('moveend');
13890
13891  		} else {
13892  			this._prunePositions(+new Date());
13893
13894  			var direction = this._lastPos.subtract(this._positions[0]),
13895  			    duration = (this._lastTime - this._times[0]) / 1000,
13896  			    ease = options.easeLinearity,
13897
13898  			    speedVector = direction.multiplyBy(ease / duration),
13899  			    speed = speedVector.distanceTo([0, 0]),
13900
13901  			    limitedSpeed = Math.min(options.inertiaMaxSpeed, speed),
13902  			    limitedSpeedVector = speedVector.multiplyBy(limitedSpeed / speed),
13903
13904  			    decelerationDuration = limitedSpeed / (options.inertiaDeceleration * ease),
13905  			    offset = limitedSpeedVector.multiplyBy(-decelerationDuration / 2).round();
13906
13907  			if (!offset.x && !offset.y) {
13908  				map.fire('moveend');
13909
13910  			} else {
13911  				offset = map._limitOffset(offset, map.options.maxBounds);
13912
13913  				requestAnimFrame(function () {
13914  					map.panBy(offset, {
13915  						duration: decelerationDuration,
13916  						easeLinearity: ease,
13917  						noMoveStart: true,
13918  						animate: true
13919  					});
13920  				});
13921  			}
13922  		}
13923  	}
13924  });
13925
13926  // @section Handlers
13927  // @property dragging: Handler
13928  // Map dragging handler (by both mouse and touch).
13929  Map.addInitHook('addHandler', 'dragging', Drag);
13930
13931  /*
13932   * L.Map.Keyboard is handling keyboard interaction with the map, enabled by default.
13933   */
13934
13935  // @namespace Map
13936  // @section Keyboard Navigation Options
13937  Map.mergeOptions({
13938  	// @option keyboard: Boolean = true
13939  	// Makes the map focusable and allows users to navigate the map with keyboard
13940  	// arrows and `+`/`-` keys.
13941  	keyboard: true,
13942
13943  	// @option keyboardPanDelta: Number = 80
13944  	// Amount of pixels to pan when pressing an arrow key.
13945  	keyboardPanDelta: 80
13946  });
13947
13948  var Keyboard = Handler.extend({
13949
13950  	keyCodes: {
13951  		left:    [37],
13952  		right:   [39],
13953  		down:    [40],
13954  		up:      [38],
13955  		zoomIn:  [187, 107, 61, 171],
13956  		zoomOut: [189, 109, 54, 173]
13957  	},
13958
13959  	initialize: function (map) {
13960  		this._map = map;
13961
13962  		this._setPanDelta(map.options.keyboardPanDelta);
13963  		this._setZoomDelta(map.options.zoomDelta);
13964  	},
13965
13966  	addHooks: function () {
13967  		var container = this._map._container;
13968
13969  		// make the container focusable by tabbing
13970  		if (container.tabIndex <= 0) {
13971  			container.tabIndex = '0';
13972  		}
13973
13974  		on(container, {
13975  			focus: this._onFocus,
13976  			blur: this._onBlur,
13977  			mousedown: this._onMouseDown
13978  		}, this);
13979
13980  		this._map.on({
13981  			focus: this._addHooks,
13982  			blur: this._removeHooks
13983  		}, this);
13984  	},
13985
13986  	removeHooks: function () {
13987  		this._removeHooks();
13988
13989  		off(this._map._container, {
13990  			focus: this._onFocus,
13991  			blur: this._onBlur,
13992  			mousedown: this._onMouseDown
13993  		}, this);
13994
13995  		this._map.off({
13996  			focus: this._addHooks,
13997  			blur: this._removeHooks
13998  		}, this);
13999  	},
14000
14001  	_onMouseDown: function () {
14002  		if (this._focused) { return; }
14003
14004  		var body = document.body,
14005  		    docEl = document.documentElement,
14006  		    top = body.scrollTop || docEl.scrollTop,
14007  		    left = body.scrollLeft || docEl.scrollLeft;
14008
14009  		this._map._container.focus();
14010
14011  		window.scrollTo(left, top);
14012  	},
14013
14014  	_onFocus: function () {
14015  		this._focused = true;
14016  		this._map.fire('focus');
14017  	},
14018
14019  	_onBlur: function () {
14020  		this._focused = false;
14021  		this._map.fire('blur');
14022  	},
14023
14024  	_setPanDelta: function (panDelta) {
14025  		var keys = this._panKeys = {},
14026  		    codes = this.keyCodes,
14027  		    i, len;
14028
14029  		for (i = 0, len = codes.left.length; i < len; i++) {
14030  			keys[codes.left[i]] = [-1 * panDelta, 0];
14031  		}
14032  		for (i = 0, len = codes.right.length; i < len; i++) {
14033  			keys[codes.right[i]] = [panDelta, 0];
14034  		}
14035  		for (i = 0, len = codes.down.length; i < len; i++) {
14036  			keys[codes.down[i]] = [0, panDelta];
14037  		}
14038  		for (i = 0, len = codes.up.length; i < len; i++) {
14039  			keys[codes.up[i]] = [0, -1 * panDelta];
14040  		}
14041  	},
14042
14043  	_setZoomDelta: function (zoomDelta) {
14044  		var keys = this._zoomKeys = {},
14045  		    codes = this.keyCodes,
14046  		    i, len;
14047
14048  		for (i = 0, len = codes.zoomIn.length; i < len; i++) {
14049  			keys[codes.zoomIn[i]] = zoomDelta;
14050  		}
14051  		for (i = 0, len = codes.zoomOut.length; i < len; i++) {
14052  			keys[codes.zoomOut[i]] = -zoomDelta;
14053  		}
14054  	},
14055
14056  	_addHooks: function () {
14057  		on(document, 'keydown', this._onKeyDown, this);
14058  	},
14059
14060  	_removeHooks: function () {
14061  		off(document, 'keydown', this._onKeyDown, this);
14062  	},
14063
14064  	_onKeyDown: function (e) {
14065  		if (e.altKey || e.ctrlKey || e.metaKey) { return; }
14066
14067  		var key = e.keyCode,
14068  		    map = this._map,
14069  		    offset;
14070
14071  		if (key in this._panKeys) {
14072  			if (!map._panAnim || !map._panAnim._inProgress) {
14073  				offset = this._panKeys[key];
14074  				if (e.shiftKey) {
14075  					offset = toPoint(offset).multiplyBy(3);
14076  				}
14077
14078  				if (map.options.maxBounds) {
14079  					offset = map._limitOffset(toPoint(offset), map.options.maxBounds);
14080  				}
14081
14082  				if (map.options.worldCopyJump) {
14083  					var newLatLng = map.wrapLatLng(map.unproject(map.project(map.getCenter()).add(offset)));
14084  					map.panTo(newLatLng);
14085  				} else {
14086  					map.panBy(offset);
14087  				}
14088  			}
14089  		} else if (key in this._zoomKeys) {
14090  			map.setZoom(map.getZoom() + (e.shiftKey ? 3 : 1) * this._zoomKeys[key]);
14091
14092  		} else if (key === 27 && map._popup && map._popup.options.closeOnEscapeKey) {
14093  			map.closePopup();
14094
14095  		} else {
14096  			return;
14097  		}
14098
14099  		stop(e);
14100  	}
14101  });
14102
14103  // @section Handlers
14104  // @section Handlers
14105  // @property keyboard: Handler
14106  // Keyboard navigation handler.
14107  Map.addInitHook('addHandler', 'keyboard', Keyboard);
14108
14109  /*
14110   * L.Handler.ScrollWheelZoom is used by L.Map to enable mouse scroll wheel zoom on the map.
14111   */
14112
14113  // @namespace Map
14114  // @section Interaction Options
14115  Map.mergeOptions({
14116  	// @section Mouse wheel options
14117  	// @option scrollWheelZoom: Boolean|String = true
14118  	// Whether the map can be zoomed by using the mouse wheel. If passed `'center'`,
14119  	// it will zoom to the center of the view regardless of where the mouse was.
14120  	scrollWheelZoom: true,
14121
14122  	// @option wheelDebounceTime: Number = 40
14123  	// Limits the rate at which a wheel can fire (in milliseconds). By default
14124  	// user can't zoom via wheel more often than once per 40 ms.
14125  	wheelDebounceTime: 40,
14126
14127  	// @option wheelPxPerZoomLevel: Number = 60
14128  	// How many scroll pixels (as reported by [L.DomEvent.getWheelDelta](#domevent-getwheeldelta))
14129  	// mean a change of one full zoom level. Smaller values will make wheel-zooming
14130  	// faster (and vice versa).
14131  	wheelPxPerZoomLevel: 60
14132  });
14133
14134  var ScrollWheelZoom = Handler.extend({
14135  	addHooks: function () {
14136  		on(this._map._container, 'wheel', this._onWheelScroll, this);
14137
14138  		this._delta = 0;
14139  	},
14140
14141  	removeHooks: function () {
14142  		off(this._map._container, 'wheel', this._onWheelScroll, this);
14143  	},
14144
14145  	_onWheelScroll: function (e) {
14146  		var delta = getWheelDelta(e);
14147
14148  		var debounce = this._map.options.wheelDebounceTime;
14149
14150  		this._delta += delta;
14151  		this._lastMousePos = this._map.mouseEventToContainerPoint(e);
14152
14153  		if (!this._startTime) {
14154  			this._startTime = +new Date();
14155  		}
14156
14157  		var left = Math.max(debounce - (+new Date() - this._startTime), 0);
14158
14159  		clearTimeout(this._timer);
14160  		this._timer = setTimeout(bind(this._performZoom, this), left);
14161
14162  		stop(e);
14163  	},
14164
14165  	_performZoom: function () {
14166  		var map = this._map,
14167  		    zoom = map.getZoom(),
14168  		    snap = this._map.options.zoomSnap || 0;
14169
14170  		map._stop(); // stop panning and fly animations if any
14171
14172  		// map the delta with a sigmoid function to -4..4 range leaning on -1..1
14173  		var d2 = this._delta / (this._map.options.wheelPxPerZoomLevel * 4),
14174  		    d3 = 4 * Math.log(2 / (1 + Math.exp(-Math.abs(d2)))) / Math.LN2,
14175  		    d4 = snap ? Math.ceil(d3 / snap) * snap : d3,
14176  		    delta = map._limitZoom(zoom + (this._delta > 0 ? d4 : -d4)) - zoom;
14177
14178  		this._delta = 0;
14179  		this._startTime = null;
14180
14181  		if (!delta) { return; }
14182
14183  		if (map.options.scrollWheelZoom === 'center') {
14184  			map.setZoom(zoom + delta);
14185  		} else {
14186  			map.setZoomAround(this._lastMousePos, zoom + delta);
14187  		}
14188  	}
14189  });
14190
14191  // @section Handlers
14192  // @property scrollWheelZoom: Handler
14193  // Scroll wheel zoom handler.
14194  Map.addInitHook('addHandler', 'scrollWheelZoom', ScrollWheelZoom);
14195
14196  /*
14197   * L.Map.TapHold is used to simulate `contextmenu` event on long hold,
14198   * which otherwise is not fired by mobile Safari.
14199   */
14200
14201  var tapHoldDelay = 600;
14202
14203  // @namespace Map
14204  // @section Interaction Options
14205  Map.mergeOptions({
14206  	// @section Touch interaction options
14207  	// @option tapHold: Boolean
14208  	// Enables simulation of `contextmenu` event, default is `true` for mobile Safari.
14209  	tapHold: Browser.touchNative && Browser.safari && Browser.mobile,
14210
14211  	// @option tapTolerance: Number = 15
14212  	// The max number of pixels a user can shift his finger during touch
14213  	// for it to be considered a valid tap.
14214  	tapTolerance: 15
14215  });
14216
14217  var TapHold = Handler.extend({
14218  	addHooks: function () {
14219  		on(this._map._container, 'touchstart', this._onDown, this);
14220  	},
14221
14222  	removeHooks: function () {
14223  		off(this._map._container, 'touchstart', this._onDown, this);
14224  	},
14225
14226  	_onDown: function (e) {
14227  		clearTimeout(this._holdTimeout);
14228  		if (e.touches.length !== 1) { return; }
14229
14230  		var first = e.touches[0];
14231  		this._startPos = this._newPos = new Point(first.clientX, first.clientY);
14232
14233  		this._holdTimeout = setTimeout(bind(function () {
14234  			this._cancel();
14235  			if (!this._isTapValid()) { return; }
14236
14237  			// prevent simulated mouse events https://w3c.github.io/touch-events/#mouse-events
14238  			on(document, 'touchend', preventDefault);
14239  			on(document, 'touchend touchcancel', this._cancelClickPrevent);
14240  			this._simulateEvent('contextmenu', first);
14241  		}, this), tapHoldDelay);
14242
14243  		on(document, 'touchend touchcancel contextmenu', this._cancel, this);
14244  		on(document, 'touchmove', this._onMove, this);
14245  	},
14246
14247  	_cancelClickPrevent: function cancelClickPrevent() {
14248  		off(document, 'touchend', preventDefault);
14249  		off(document, 'touchend touchcancel', cancelClickPrevent);
14250  	},
14251
14252  	_cancel: function () {
14253  		clearTimeout(this._holdTimeout);
14254  		off(document, 'touchend touchcancel contextmenu', this._cancel, this);
14255  		off(document, 'touchmove', this._onMove, this);
14256  	},
14257
14258  	_onMove: function (e) {
14259  		var first = e.touches[0];
14260  		this._newPos = new Point(first.clientX, first.clientY);
14261  	},
14262
14263  	_isTapValid: function () {
14264  		return this._newPos.distanceTo(this._startPos) <= this._map.options.tapTolerance;
14265  	},
14266
14267  	_simulateEvent: function (type, e) {
14268  		var simulatedEvent = new MouseEvent(type, {
14269  			bubbles: true,
14270  			cancelable: true,
14271  			view: window,
14272  			// detail: 1,
14273  			screenX: e.screenX,
14274  			screenY: e.screenY,
14275  			clientX: e.clientX,
14276  			clientY: e.clientY,
14277  			// button: 2,
14278  			// buttons: 2
14279  		});
14280
14281  		simulatedEvent._simulated = true;
14282
14283  		e.target.dispatchEvent(simulatedEvent);
14284  	}
14285  });
14286
14287  // @section Handlers
14288  // @property tapHold: Handler
14289  // Long tap handler to simulate `contextmenu` event (useful in mobile Safari).
14290  Map.addInitHook('addHandler', 'tapHold', TapHold);
14291
14292  /*
14293   * L.Handler.TouchZoom is used by L.Map to add pinch zoom on supported mobile browsers.
14294   */
14295
14296  // @namespace Map
14297  // @section Interaction Options
14298  Map.mergeOptions({
14299  	// @section Touch interaction options
14300  	// @option touchZoom: Boolean|String = *
14301  	// Whether the map can be zoomed by touch-dragging with two fingers. If
14302  	// passed `'center'`, it will zoom to the center of the view regardless of
14303  	// where the touch events (fingers) were. Enabled for touch-capable web
14304  	// browsers.
14305  	touchZoom: Browser.touch,
14306
14307  	// @option bounceAtZoomLimits: Boolean = true
14308  	// Set it to false if you don't want the map to zoom beyond min/max zoom
14309  	// and then bounce back when pinch-zooming.
14310  	bounceAtZoomLimits: true
14311  });
14312
14313  var TouchZoom = Handler.extend({
14314  	addHooks: function () {
14315  		addClass(this._map._container, 'leaflet-touch-zoom');
14316  		on(this._map._container, 'touchstart', this._onTouchStart, this);
14317  	},
14318
14319  	removeHooks: function () {
14320  		removeClass(this._map._container, 'leaflet-touch-zoom');
14321  		off(this._map._container, 'touchstart', this._onTouchStart, this);
14322  	},
14323
14324  	_onTouchStart: function (e) {
14325  		var map = this._map;
14326  		if (!e.touches || e.touches.length !== 2 || map._animatingZoom || this._zooming) { return; }
14327
14328  		var p1 = map.mouseEventToContainerPoint(e.touches[0]),
14329  		    p2 = map.mouseEventToContainerPoint(e.touches[1]);
14330
14331  		this._centerPoint = map.getSize()._divideBy(2);
14332  		this._startLatLng = map.containerPointToLatLng(this._centerPoint);
14333  		if (map.options.touchZoom !== 'center') {
14334  			this._pinchStartLatLng = map.containerPointToLatLng(p1.add(p2)._divideBy(2));
14335  		}
14336
14337  		this._startDist = p1.distanceTo(p2);
14338  		this._startZoom = map.getZoom();
14339
14340  		this._moved = false;
14341  		this._zooming = true;
14342
14343  		map._stop();
14344
14345  		on(document, 'touchmove', this._onTouchMove, this);
14346  		on(document, 'touchend touchcancel', this._onTouchEnd, this);
14347
14348  		preventDefault(e);
14349  	},
14350
14351  	_onTouchMove: function (e) {
14352  		if (!e.touches || e.touches.length !== 2 || !this._zooming) { return; }
14353
14354  		var map = this._map,
14355  		    p1 = map.mouseEventToContainerPoint(e.touches[0]),
14356  		    p2 = map.mouseEventToContainerPoint(e.touches[1]),
14357  		    scale = p1.distanceTo(p2) / this._startDist;
14358
14359  		this._zoom = map.getScaleZoom(scale, this._startZoom);
14360
14361  		if (!map.options.bounceAtZoomLimits && (
14362  			(this._zoom < map.getMinZoom() && scale < 1) ||
14363  			(this._zoom > map.getMaxZoom() && scale > 1))) {
14364  			this._zoom = map._limitZoom(this._zoom);
14365  		}
14366
14367  		if (map.options.touchZoom === 'center') {
14368  			this._center = this._startLatLng;
14369  			if (scale === 1) { return; }
14370  		} else {
14371  			// Get delta from pinch to center, so centerLatLng is delta applied to initial pinchLatLng
14372  			var delta = p1._add(p2)._divideBy(2)._subtract(this._centerPoint);
14373  			if (scale === 1 && delta.x === 0 && delta.y === 0) { return; }
14374  			this._center = map.unproject(map.project(this._pinchStartLatLng, this._zoom).subtract(delta), this._zoom);
14375  		}
14376
14377  		if (!this._moved) {
14378  			map._moveStart(true, false);
14379  			this._moved = true;
14380  		}
14381
14382  		cancelAnimFrame(this._animRequest);
14383
14384  		var moveFn = bind(map._move, map, this._center, this._zoom, {pinch: true, round: false}, undefined);
14385  		this._animRequest = requestAnimFrame(moveFn, this, true);
14386
14387  		preventDefault(e);
14388  	},
14389
14390  	_onTouchEnd: function () {
14391  		if (!this._moved || !this._zooming) {
14392  			this._zooming = false;
14393  			return;
14394  		}
14395
14396  		this._zooming = false;
14397  		cancelAnimFrame(this._animRequest);
14398
14399  		off(document, 'touchmove', this._onTouchMove, this);
14400  		off(document, 'touchend touchcancel', this._onTouchEnd, this);
14401
14402  		// Pinch updates GridLayers' levels only when zoomSnap is off, so zoomSnap becomes noUpdate.
14403  		if (this._map.options.zoomAnimation) {
14404  			this._map._animateZoom(this._center, this._map._limitZoom(this._zoom), true, this._map.options.zoomSnap);
14405  		} else {
14406  			this._map._resetView(this._center, this._map._limitZoom(this._zoom));
14407  		}
14408  	}
14409  });
14410
14411  // @section Handlers
14412  // @property touchZoom: Handler
14413  // Touch zoom handler.
14414  Map.addInitHook('addHandler', 'touchZoom', TouchZoom);
14415
14416  Map.BoxZoom = BoxZoom;
14417  Map.DoubleClickZoom = DoubleClickZoom;
14418  Map.Drag = Drag;
14419  Map.Keyboard = Keyboard;
14420  Map.ScrollWheelZoom = ScrollWheelZoom;
14421  Map.TapHold = TapHold;
14422  Map.TouchZoom = TouchZoom;
14423
14424  exports.Bounds = Bounds;
14425  exports.Browser = Browser;
14426  exports.CRS = CRS;
14427  exports.Canvas = Canvas;
14428  exports.Circle = Circle;
14429  exports.CircleMarker = CircleMarker;
14430  exports.Class = Class;
14431  exports.Control = Control;
14432  exports.DivIcon = DivIcon;
14433  exports.DivOverlay = DivOverlay;
14434  exports.DomEvent = DomEvent;
14435  exports.DomUtil = DomUtil;
14436  exports.Draggable = Draggable;
14437  exports.Evented = Evented;
14438  exports.FeatureGroup = FeatureGroup;
14439  exports.GeoJSON = GeoJSON;
14440  exports.GridLayer = GridLayer;
14441  exports.Handler = Handler;
14442  exports.Icon = Icon;
14443  exports.ImageOverlay = ImageOverlay;
14444  exports.LatLng = LatLng;
14445  exports.LatLngBounds = LatLngBounds;
14446  exports.Layer = Layer;
14447  exports.LayerGroup = LayerGroup;
14448  exports.LineUtil = LineUtil;
14449  exports.Map = Map;
14450  exports.Marker = Marker;
14451  exports.Mixin = Mixin;
14452  exports.Path = Path;
14453  exports.Point = Point;
14454  exports.PolyUtil = PolyUtil;
14455  exports.Polygon = Polygon;
14456  exports.Polyline = Polyline;
14457  exports.Popup = Popup;
14458  exports.PosAnimation = PosAnimation;
14459  exports.Projection = index;
14460  exports.Rectangle = Rectangle;
14461  exports.Renderer = Renderer;
14462  exports.SVG = SVG;
14463  exports.SVGOverlay = SVGOverlay;
14464  exports.TileLayer = TileLayer;
14465  exports.Tooltip = Tooltip;
14466  exports.Transformation = Transformation;
14467  exports.Util = Util;
14468  exports.VideoOverlay = VideoOverlay;
14469  exports.bind = bind;
14470  exports.bounds = toBounds;
14471  exports.canvas = canvas;
14472  exports.circle = circle;
14473  exports.circleMarker = circleMarker;
14474  exports.control = control;
14475  exports.divIcon = divIcon;
14476  exports.extend = extend;
14477  exports.featureGroup = featureGroup;
14478  exports.geoJSON = geoJSON;
14479  exports.geoJson = geoJson;
14480  exports.gridLayer = gridLayer;
14481  exports.icon = icon;
14482  exports.imageOverlay = imageOverlay;
14483  exports.latLng = toLatLng;
14484  exports.latLngBounds = toLatLngBounds;
14485  exports.layerGroup = layerGroup;
14486  exports.map = createMap;
14487  exports.marker = marker;
14488  exports.point = toPoint;
14489  exports.polygon = polygon;
14490  exports.polyline = polyline;
14491  exports.popup = popup;
14492  exports.rectangle = rectangle;
14493  exports.setOptions = setOptions;
14494  exports.stamp = stamp;
14495  exports.svg = svg;
14496  exports.svgOverlay = svgOverlay;
14497  exports.tileLayer = tileLayer;
14498  exports.tooltip = tooltip;
14499  exports.transformation = toTransformation;
14500  exports.version = version;
14501  exports.videoOverlay = videoOverlay;
14502
14503  var oldL = window.L;
14504  exports.noConflict = function() {
14505  	window.L = oldL;
14506  	return this;
14507  }
14508  // Always export us to window global (see #2364)
14509  window.L = exports;
14510
14511}));
14512//# sourceMappingURL=leaflet-src.js.map

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.