PageSourceSearch

https://saii.kr/assets/leaflet-40e7db45.js

js saii.kr collected 2026-10-06 03:43:42 UTC 415,685 bytes, 14,420 lines download raw bytes

vendor: 45,705 bytes, lines 1-1578
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
6var version = "1.9.4";
7
8/*
9 * @namespace Util
10 *
11 * Various utility functions, used by Leaflet internally.
12 */
13
14// @function extend(dest: Object, src?: Object): Object
15// Merges the properties of the `src` object (or multiple objects) into `dest` object and returns the latter. Has an `L.extend` shortcut.
16function extend(dest) {
17	var i, j, len, src;
18
19	for (j = 1, len = arguments.length; j < len; j++) {
20		src = arguments[j];
21		for (i in src) {
22			dest[i] = src[i];
23		}
24	}
25	return dest;
26}
27
28// @function create(proto: Object, properties?: Object): Object
29// Compatibility polyfill for [Object.create](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Object/create)
30var create$2 = Object.create || (function () {
31	function F() {}
32	return function (proto) {
33		F.prototype = proto;
34		return new F();
35	};
36})();
37
38// @function bind(fn: Function, …): Function
39// 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).
40// Has a `L.bind()` shortcut.
41function bind(fn, obj) {
42	var slice = Array.prototype.slice;
43
44	if (fn.bind) {
45		return fn.bind.apply(fn, slice.call(arguments, 1));
46	}
47
48	var args = slice.call(arguments, 2);
49
50	return function () {
51		return fn.apply(obj, args.length ? args.concat(slice.call(arguments)) : arguments);
52	};
53}
54
55// @property lastId: Number
56// Last unique ID used by [`stamp()`](#util-stamp)
57var lastId = 0;
58
59// @function stamp(obj: Object): Number
60// Returns the unique ID of an object, assigning it one if it doesn't have it.
61function stamp(obj) {
62	if (!('_leaflet_id' in obj)) {
63		obj['_leaflet_id'] = ++lastId;
64	}
65	return obj._leaflet_id;
66}
67
68// @function throttle(fn: Function, time: Number, context: Object): Function
69// Returns a function which executes function `fn` with the given scope `context`
70// (so that the `this` keyword refers to `context` inside `fn`'s code). The function
71// `fn` will be called no more than one time per given amount of `time`. The arguments
72// received by the bound function will be any arguments passed when binding the
73// function, followed by any arguments passed when invoking the bound function.
74// Has an `L.throttle` shortcut.
75function throttle(fn, time, context) {
76	var lock, args, wrapperFn, later;
77
78	later = function () {
79		// reset lock and call if queued
80		lock = false;
81		if (args) {
82			wrapperFn.apply(context, args);
83			args = false;
84		}
85	};
86
87	wrapperFn = function () {
88		if (lock) {
89			// called too soon, queue to call later
90			args = arguments;
91
92		} else {
93			// call and lock until later
94			fn.apply(context, arguments);
95			setTimeout(later, time);
96			lock = true;
97		}
98	};
99
100	return wrapperFn;
101}
102
103// @function wrapNum(num: Number, range: Number[], includeMax?: Boolean): Number
104// Returns the number `num` modulo `range` in such a way so it lies within
105// `range[0]` and `range[1]`. The returned value will be always smaller than
106// `range[1]` unless `includeMax` is set to `true`.
107function wrapNum(x, range, includeMax) {
108	var max = range[1],
109	    min = range[0],
110	    d = max - min;
111	return x === max && includeMax ? x : ((x - min) % d + d) % d + min;
112}
113
114// @function falseFn(): Function
115// Returns a function which always returns `false`.
116function falseFn() { return false; }
117
118// @function formatNum(num: Number, precision?: Number|false): Number
119// Returns the number `num` rounded with specified `precision`.
120// The default `precision` value is 6 decimal places.
121// `false` can be passed to skip any processing (can be useful to avoid round-off errors).
122function formatNum(num, precision) {
123	if (precision === false) { return num; }
124	var pow = Math.pow(10, precision === undefined ? 6 : precision);
125	return Math.round(num * pow) / pow;
126}
127
128// @function trim(str: String): String
129// Compatibility polyfill for [String.prototype.trim](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String/Trim)
130function trim(str) {
131	return str.trim ? str.trim() : str.replace(/^\s+|\s+$/g, '');
132}
133
134// @function splitWords(str: String): String[]
135// Trims and splits the string on whitespace and returns the array of parts.
136function splitWords(str) {
137	return trim(str).split(/\s+/);
138}
139
140// @function setOptions(obj: Object, options: Object): Object
141// Merges the given properties to the `options` of the `obj` object, returning the resulting options. See `Class options`. Has an `L.setOptions` shortcut.
142function setOptions(obj, options) {
143	if (!Object.prototype.hasOwnProperty.call(obj, 'options')) {
144		obj.options = obj.options ? create$2(obj.options) : {};
145	}
146	for (var i in options) {
147		obj.options[i] = options[i];
148	}
149	return obj.options;
150}
151
152// @function getParamString(obj: Object, existingUrl?: String, uppercase?: Boolean): String
153// Converts an object into a parameter URL string, e.g. `{a: "foo", b: "bar"}`
154// translates to `'?a=foo&b=bar'`. If `existingUrl` is set, the parameters will
155// be appended at the end. If `uppercase` is `true`, the parameter names will
156// be uppercased (e.g. `'?A=foo&B=bar'`)
157function getParamString(obj, existingUrl, uppercase) {
158	var params = [];
159	for (var i in obj) {
160		params.push(encodeURIComponent(uppercase ? i.toUpperCase() : i) + '=' + encodeURIComponent(obj[i]));
161	}
162	return ((!existingUrl || existingUrl.indexOf('?') === -1) ? '?' : '&') + params.join('&');
163}
164
165var templateRe = /\{ *([\w_ -]+) *\}/g;
166
167// @function template(str: String, data: Object): String
168// Simple templating facility, accepts a template string of the form `'Hello {a}, {b}'`
169// and a data object like `{a: 'foo', b: 'bar'}`, returns evaluated string
170// `('Hello foo, bar')`. You can also specify functions instead of strings for
171// data values — they will be evaluated passing `data` as an argument.
172function template(str, data) {
173	return str.replace(templateRe, function (str, key) {
174		var value = data[key];
175
176		if (value === undefined) {
177			throw new Error('No value provided for variable ' + str);
178
179		} else if (typeof value === 'function') {
180			value = value(data);
181		}
182		return value;
183	});
184}
185
186// @function isArray(obj): Boolean
187// Compatibility polyfill for [Array.isArray](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Array/isArray)
188var isArray = Array.isArray || function (obj) {
189	return (Object.prototype.toString.call(obj) === '[object Array]');
190};
191
192// @function indexOf(array: Array, el: Object): Number
193// Compatibility polyfill for [Array.prototype.indexOf](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Array/indexOf)
194function indexOf(array, el) {
195	for (var i = 0; i < array.length; i++) {
196		if (array[i] === el) { return i; }
197	}
198	return -1;
199}
200
201// @property emptyImageUrl: String
202// Data URI string containing a base64-encoded empty GIF image.
203// Used as a hack to free memory from unused images on WebKit-powered
204// mobile devices (by setting image `src` to this string).
205var emptyImageUrl = 'data:image/gif;base64,R0lGODlhAQABAAD/ACwAAAAAAQABAAACADs=';
206
207// inspired by https://paulirish.com/2011/requestanimationframe-for-smart-animating/
208
209function getPrefixed(name) {
210	return window['webkit' + name] || window['moz' + name] || window['ms' + name];
211}
212
213var lastTime = 0;
214
215// fallback for IE 7-8
216function timeoutDefer(fn) {
217	var time = +new Date(),
218	    timeToCall = Math.max(0, 16 - (time - lastTime));
219
220	lastTime = time + timeToCall;
221	return window.setTimeout(fn, timeToCall);
222}
223
224var requestFn = window.requestAnimationFrame || getPrefixed('RequestAnimationFrame') || timeoutDefer;
225var cancelFn = window.cancelAnimationFrame || getPrefixed('CancelAnimationFrame') ||
226		getPrefixed('CancelRequestAnimationFrame') || function (id) { window.clearTimeout(id); };
227
228// @function requestAnimFrame(fn: Function, context?: Object, immediate?: Boolean): Number
229// Schedules `fn` to be executed when the browser repaints. `fn` is bound to
230// `context` if given. When `immediate` is set, `fn` is called immediately if
231// the browser doesn't have native support for
232// [`window.requestAnimationFrame`](https://developer.mozilla.org/docs/Web/API/window/requestAnimationFrame),
233// otherwise it's delayed. Returns a request ID that can be used to cancel the request.
234function requestAnimFrame(fn, context, immediate) {
235	if (immediate && requestFn === timeoutDefer) {
236		fn.call(context);
237	} else {
238		return requestFn.call(window, bind(fn, context));
239	}
240}
241
242// @function cancelAnimFrame(id: Number): undefined
243// Cancels a previous `requestAnimFrame`. See also [window.cancelAnimationFrame](https://developer.mozilla.org/docs/Web/API/window/cancelAnimationFrame).
244function cancelAnimFrame(id) {
245	if (id) {
246		cancelFn.call(window, id);
247	}
248}
249
250var Util = {
251  __proto__: null,
252  extend: extend,
253  create: create$2,
254  bind: bind,
255  get lastId () { return lastId; },
256  stamp: stamp,
257  throttle: throttle,
258  wrapNum: wrapNum,
259  falseFn: falseFn,
260  formatNum: formatNum,
261  trim: trim,
262  splitWords: splitWords,
263  setOptions: setOptions,
264  getParamString: getParamString,
265  template: template,
266  isArray: isArray,
267  indexOf: indexOf,
268  emptyImageUrl: emptyImageUrl,
269  requestFn: requestFn,
270  cancelFn: cancelFn,
271  requestAnimFrame: requestAnimFrame,
272  cancelAnimFrame: cancelAnimFrame
273};
274
275// @class Class
276// @aka L.Class
277
278// @section
279// @uninheritable
280
281// Thanks to John Resig and Dean Edwards for inspiration!
282
283function Class() {}
284
285Class.extend = function (props) {
286
287	// @function extend(props: Object): Function
288	// [Extends the current class](#class-inheritance) given the properties to be included.
289	// Returns a Javascript function that is a class constructor (to be called with `new`).
290	var NewClass = function () {
291
292		setOptions(this);
293
294		// call the constructor
295		if (this.initialize) {
296			this.initialize.apply(this, arguments);
297		}
298
299		// call all constructor hooks
300		this.callInitHooks();
301	};
302
303	var parentProto = NewClass.__super__ = this.prototype;
304
305	var proto = create$2(parentProto);
306	proto.constructor = NewClass;
307
308	NewClass.prototype = proto;
309
310	// inherit parent's statics
311	for (var i in this) {
312		if (Object.prototype.hasOwnProperty.call(this, i) && i !== 'prototype' && i !== '__super__') {
313			NewClass[i] = this[i];
314		}
315	}
316
317	// mix static properties into the class
318	if (props.statics) {
319		extend(NewClass, props.statics);
320	}
321
322	// mix includes into the prototype
323	if (props.includes) {
324		checkDeprecatedMixinEvents(props.includes);
325		extend.apply(null, [proto].concat(props.includes));
326	}
327
328	// mix given properties into the prototype
329	extend(proto, props);
330	delete proto.statics;
331	delete proto.includes;
332
333	// merge options
334	if (proto.options) {
335		proto.options = parentProto.options ? create$2(parentProto.options) : {};
336		extend(proto.options, props.options);
337	}
338
339	proto._initHooks = [];
340
341	// add method for calling all hooks
342	proto.callInitHooks = function () {
343
344		if (this._initHooksCalled) { return; }
345
346		if (parentProto.callInitHooks) {
347			parentProto.callInitHooks.call(this);
348		}
349
350		this._initHooksCalled = true;
351
352		for (var i = 0, len = proto._initHooks.length; i < len; i++) {
353			proto._initHooks[i].call(this);
354		}
355	};
356
357	return NewClass;
358};
359
360
361// @function include(properties: Object): this
362// [Includes a mixin](#class-includes) into the current class.
363Class.include = function (props) {
364	var parentOptions = this.prototype.options;
365	extend(this.prototype, props);
366	if (props.options) {
367		this.prototype.options = parentOptions;
368		this.mergeOptions(props.options);
369	}
370	return this;
371};
372
373// @function mergeOptions(options: Object): this
374// [Merges `options`](#class-options) into the defaults of the class.
375Class.mergeOptions = function (options) {
376	extend(this.prototype.options, options);
377	return this;
378};
379
380// @function addInitHook(fn: Function): this
381// Adds a [constructor hook](#class-constructor-hooks) to the class.
382Class.addInitHook = function (fn) { // (Function) || (String, args...)
383	var args = Array.prototype.slice.call(arguments, 1);
384
385	var init = typeof fn === 'function' ? fn : function () {
386		this[fn].apply(this, args);
387	};
388
389	this.prototype._initHooks = this.prototype._initHooks || [];
390	this.prototype._initHooks.push(init);
391	return this;
392};
393
394function checkDeprecatedMixinEvents(includes) {
395	/* global L: true */
396	if (typeof L === 'undefined' || !L || !L.Mixin) { return; }
397
398	includes = isArray(includes) ? includes : [includes];
399
400	for (var i = 0; i < includes.length; i++) {
401		if (includes[i] === L.Mixin.Events) {
402			console.warn('Deprecated include of L.Mixin.Events: ' +
403				'this property will be removed in future releases, ' +
404				'please inherit from L.Evented instead.', new Error().stack);
405		}
406	}
407}
408
409/*
410 * @class Evented
411 * @aka L.Evented
412 * @inherits Class
413 *
414 * 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).
415 *
416 * @example
417 *
418 * ```js
419 * map.on('click', function(e) {
420 * 	alert(e.latlng);
421 * } );
422 * ```
423 *
424 * Leaflet deals with event listeners by reference, so if you want to add a listener and then remove it, define it as a function:
425 *
426 * ```js
427 * function onClick(e) { ... }
428 *
429 * map.on('click', onClick);
430 * map.off('click', onClick);
431 * ```
432 */
433
434var Events = {
435	/* @method on(type: String, fn: Function, context?: Object): this
436	 * 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'`).
437	 *
438	 * @alternative
439	 * @method on(eventMap: Object): this
440	 * Adds a set of type/listener pairs, e.g. `{click: onClick, mousemove: onMouseMove}`
441	 */
442	on: function (types, fn, context) {
443
444		// types can be a map of types/handlers
445		if (typeof types === 'object') {
446			for (var type in types) {
447				// we don't process space-separated events here for performance;
448				// it's a hot path since Layer uses the on(obj) syntax
449				this._on(type, types[type], fn);
450			}
451
452		} else {
453			// types can be a string of space-separated words
454			types = splitWords(types);
455
456			for (var i = 0, len = types.length; i < len; i++) {
457				this._on(types[i], fn, context);
458			}
459		}
460
461		return this;
462	},
463
464	/* @method off(type: String, fn?: Function, context?: Object): this
465	 * 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.
466	 *
467	 * @alternative
468	 * @method off(eventMap: Object): this
469	 * Removes a set of type/listener pairs.
470	 *
471	 * @alternative
472	 * @method off: this
473	 * Removes all listeners to all events on the object. This includes implicitly attached events.
474	 */
475	off: function (types, fn, context) {
476
477		if (!arguments.length) {
478			// clear all listeners if called without arguments
479			delete this._events;
480
481		} else if (typeof types === 'object') {
482			for (var type in types) {
483				this._off(type, types[type], fn);
484			}
485
486		} else {
487			types = splitWords(types);
488
489			var removeAll = arguments.length === 1;
490			for (var i = 0, len = types.length; i < len; i++) {
491				if (removeAll) {
492					this._off(types[i]);
493				} else {
494					this._off(types[i], fn, context);
495				}
496			}
497		}
498
499		return this;
500	},
501
502	// attach listener (without syntactic sugar now)
503	_on: function (type, fn, context, _once) {
504		if (typeof fn !== 'function') {
505			console.warn('wrong listener type: ' + typeof fn);
506			return;
507		}
508
509		// check if fn already there
510		if (this._listens(type, fn, context) !== false) {
511			return;
512		}
513
514		if (context === this) {
515			// Less memory footprint.
516			context = undefined;
517		}
518
519		var newListener = {fn: fn, ctx: context};
520		if (_once) {
521			newListener.once = true;
522		}
523
524		this._events = this._events || {};
525		this._events[type] = this._events[type] || [];
526		this._events[type].push(newListener);
527	},
528
529	_off: function (type, fn, context) {
530		var listeners,
531		    i,
532		    len;
533
534		if (!this._events) {
535			return;
536		}
537
538		listeners = this._events[type];
539		if (!listeners) {
540			return;
541		}
542
543		if (arguments.length === 1) { // remove all
544			if (this._firingCount) {
545				// Set all removed listeners to noop
546				// so they are not called if remove happens in fire
547				for (i = 0, len = listeners.length; i < len; i++) {
548					listeners[i].fn = falseFn;
549				}
550			}
551			// clear all listeners for a type if function isn't specified
552			delete this._events[type];
553			return;
554		}
555
556		if (typeof fn !== 'function') {
557			console.warn('wrong listener type: ' + typeof fn);
558			return;
559		}
560
561		// find fn and remove it
562		var index = this._listens(type, fn, context);
563		if (index !== false) {
564			var listener = listeners[index];
565			if (this._firingCount) {
566				// set the removed listener to noop so that's not called if remove happens in fire
567				listener.fn = falseFn;
568
569				/* copy array in case events are being fired */
570				this._events[type] = listeners = listeners.slice();
571			}
572			listeners.splice(index, 1);
573		}
574	},
575
576	// @method fire(type: String, data?: Object, propagate?: Boolean): this
577	// Fires an event of the specified type. You can optionally provide a data
578	// object — the first argument of the listener function will contain its
579	// properties. The event can optionally be propagated to event parents.
580	fire: function (type, data, propagate) {
581		if (!this.listens(type, propagate)) { return this; }
582
583		var event = extend({}, data, {
584			type: type,
585			target: this,
586			sourceTarget: data && data.sourceTarget || this
587		});
588
589		if (this._events) {
590			var listeners = this._events[type];
591			if (listeners) {
592				this._firingCount = (this._firingCount + 1) || 1;
593				for (var i = 0, len = listeners.length; i < len; i++) {
594					var l = listeners[i];
595					// off overwrites l.fn, so we need to copy fn to a var
596					var fn = l.fn;
597					if (l.once) {
598						this.off(type, fn, l.ctx);
599					}
600					fn.call(l.ctx || this, event);
601				}
602
603				this._firingCount--;
604			}
605		}
606
607		if (propagate) {
608			// propagate the event to parents (set with addEventParent)
609			this._propagateEvent(event);
610		}
611
612		return this;
613	},
614
615	// @method listens(type: String, propagate?: Boolean): Boolean
616	// @method listens(type: String, fn: Function, context?: Object, propagate?: Boolean): Boolean
617	// Returns `true` if a particular event type has any listeners attached to it.
618	// The verification can optionally be propagated, it will return `true` if parents have the listener attached to it.
619	listens: function (type, fn, context, propagate) {
620		if (typeof type !== 'string') {
621			console.warn('"string" type argument expected');
622		}
623
624		// we don't overwrite the input `fn` value, because we need to use it for propagation
625		var _fn = fn;
626		if (typeof fn !== 'function') {
627			propagate = !!fn;
628			_fn = undefined;
629			context = undefined;
630		}
631
632		var listeners = this._events && this._events[type];
633		if (listeners && listeners.length) {
634			if (this._listens(type, _fn, context) !== false) {
635				return true;
636			}
637		}
638
639		if (propagate) {
640			// also check parents for listeners if event propagates
641			for (var id in this._eventParents) {
642				if (this._eventParents[id].listens(type, fn, context, propagate)) { return true; }
643			}
644		}
645		return false;
646	},
647
648	// returns the index (number) or false
649	_listens: function (type, fn, context) {
650		if (!this._events) {
651			return false;
652		}
653
654		var listeners = this._events[type] || [];
655		if (!fn) {
656			return !!listeners.length;
657		}
658
659		if (context === this) {
660			// Less memory footprint.
661			context = undefined;
662		}
663
664		for (var i = 0, len = listeners.length; i < len; i++) {
665			if (listeners[i].fn === fn && listeners[i].ctx === context) {
666				return i;
667			}
668		}
669		return false;
670
671	},
672
673	// @method once(…): this
674	// Behaves as [`on(…)`](#evented-on), except the listener will only get fired once and then removed.
675	once: function (types, fn, context) {
676
677		// types can be a map of types/handlers
678		if (typeof types === 'object') {
679			for (var type in types) {
680				// we don't process space-separated events here for performance;
681				// it's a hot path since Layer uses the on(obj) syntax
682				this._on(type, types[type], fn, true);
683			}
684
685		} else {
686			// types can be a string of space-separated words
687			types = splitWords(types);
688
689			for (var i = 0, len = types.length; i < len; i++) {
690				this._on(types[i], fn, context, true);
691			}
692		}
693
694		return this;
695	},
696
697	// @method addEventParent(obj: Evented): this
698	// Adds an event parent - an `Evented` that will receive propagated events
699	addEventParent: function (obj) {
700		this._eventParents = this._eventParents || {};
701		this._eventParents[stamp(obj)] = obj;
702		return this;
703	},
704
705	// @method removeEventParent(obj: Evented): this
706	// Removes an event parent, so it will stop receiving propagated events
707	removeEventParent: function (obj) {
708		if (this._eventParents) {
709			delete this._eventParents[stamp(obj)];
710		}
711		return this;
712	},
713
714	_propagateEvent: function (e) {
715		for (var id in this._eventParents) {
716			this._eventParents[id].fire(e.type, extend({
717				layer: e.target,
718				propagatedFrom: e.target
719			}, e), true);
720		}
721	}
722};
723
724// aliases; we should ditch those eventually
725
726// @method addEventListener(…): this
727// Alias to [`on(…)`](#evented-on)
728Events.addEventListener = Events.on;
729
730// @method removeEventListener(…): this
731// Alias to [`off(…)`](#evented-off)
732
733// @method clearAllEventListeners(…): this
734// Alias to [`off()`](#evented-off)
735Events.removeEventListener = Events.clearAllEventListeners = Events.off;
736
737// @method addOneTimeEventListener(…): this
738// Alias to [`once(…)`](#evented-once)
739Events.addOneTimeEventListener = Events.once;
740
741// @method fireEvent(…): this
742// Alias to [`fire(…)`](#evented-fire)
743Events.fireEvent = Events.fire;
744
745// @method hasEventListeners(…): Boolean
746// Alias to [`listens(…)`](#evented-listens)
747Events.hasEventListeners = Events.listens;
748
749var Evented = Class.extend(Events);
750
751/*
752 * @class Point
753 * @aka L.Point
754 *
755 * Represents a point with `x` and `y` coordinates in pixels.
756 *
757 * @example
758 *
759 * ```js
760 * var point = L.point(200, 300);
761 * ```
762 *
763 * 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:
764 *
765 * ```js
766 * map.panBy([200, 300]);
767 * map.panBy(L.point(200, 300));
768 * ```
769 *
770 * Note that `Point` does not inherit from Leaflet's `Class` object,
771 * which means new classes can't inherit from it, and new methods
772 * can't be added to it with the `include` function.
773 */
774
775function Point(x, y, round) {
776	// @property x: Number; The `x` coordinate of the point
777	this.x = (round ? Math.round(x) : x);
778	// @property y: Number; The `y` coordinate of the point
779	this.y = (round ? Math.round(y) : y);
780}
781
782var trunc = Math.trunc || function (v) {
783	return v > 0 ? Math.floor(v) : Math.ceil(v);
784};
785
786Point.prototype = {
787
788	// @method clone(): Point
789	// Returns a copy of the current point.
790	clone: function () {
791		return new Point(this.x, this.y);
792	},
793
794	// @method add(otherPoint: Point): Point
795	// Returns the result of addition of the current and the given points.
796	add: function (point) {
797		// non-destructive, returns a new point
798		return this.clone()._add(toPoint(point));
799	},
800
801	_add: function (point) {
802		// destructive, used directly for performance in situations where it's safe to modify existing point
803		this.x += point.x;
804		this.y += point.y;
805		return this;
806	},
807
808	// @method subtract(otherPoint: Point): Point
809	// Returns the result of subtraction of the given point from the current.
810	subtract: function (point) {
811		return this.clone()._subtract(toPoint(point));
812	},
813
814	_subtract: function (point) {
815		this.x -= point.x;
816		this.y -= point.y;
817		return this;
818	},
819
820	// @method divideBy(num: Number): Point
821	// Returns the result of division of the current point by the given number.
822	divideBy: function (num) {
823		return this.clone()._divideBy(num);
824	},
825
826	_divideBy: function (num) {
827		this.x /= num;
828		this.y /= num;
829		return this;
830	},
831
832	// @method multiplyBy(num: Number): Point
833	// Returns the result of multiplication of the current point by the given number.
834	multiplyBy: function (num) {
835		return this.clone()._multiplyBy(num);
836	},
837
838	_multiplyBy: function (num) {
839		this.x *= num;
840		this.y *= num;
841		return this;
842	},
843
844	// @method scaleBy(scale: Point): Point
845	// Multiply each coordinate of the current point by each coordinate of
846	// `scale`. In linear algebra terms, multiply the point by the
847	// [scaling matrix](https://en.wikipedia.org/wiki/Scaling_%28geometry%29#Matrix_representation)
848	// defined by `scale`.
849	scaleBy: function (point) {
850		return new Point(this.x * point.x, this.y * point.y);
851	},
852
853	// @method unscaleBy(scale: Point): Point
854	// Inverse of `scaleBy`. Divide each coordinate of the current point by
855	// each coordinate of `scale`.
856	unscaleBy: function (point) {
857		return new Point(this.x / point.x, this.y / point.y);
858	},
859
860	// @method round(): Point
861	// Returns a copy of the current point with rounded coordinates.
862	round: function () {
863		return this.clone()._round();
864	},
865
866	_round: function () {
867		this.x = Math.round(this.x);
868		this.y = Math.round(this.y);
869		return this;
870	},
871
872	// @method floor(): Point
873	// Returns a copy of the current point with floored coordinates (rounded down).
874	floor: function () {
875		return this.clone()._floor();
876	},
877
878	_floor: function () {
879		this.x = Math.floor(this.x);
880		this.y = Math.floor(this.y);
881		return this;
882	},
883
884	// @method ceil(): Point
885	// Returns a copy of the current point with ceiled coordinates (rounded up).
886	ceil: function () {
887		return this.clone()._ceil();
888	},
889
890	_ceil: function () {
891		this.x = Math.ceil(this.x);
892		this.y = Math.ceil(this.y);
893		return this;
894	},
895
896	// @method trunc(): Point
897	// Returns a copy of the current point with truncated coordinates (rounded towards zero).
898	trunc: function () {
899		return this.clone()._trunc();
900	},
901
902	_trunc: function () {
903		this.x = trunc(this.x);
904		this.y = trunc(this.y);
905		return this;
906	},
907
908	// @method distanceTo(otherPoint: Point): Number
909	// Returns the cartesian distance between the current and the given points.
910	distanceTo: function (point) {
911		point = toPoint(point);
912
913		var x = point.x - this.x,
914		    y = point.y - this.y;
915
916		return Math.sqrt(x * x + y * y);
917	},
918
919	// @method equals(otherPoint: Point): Boolean
920	// Returns `true` if the given point has the same coordinates.
921	equals: function (point) {
922		point = toPoint(point);
923
924		return point.x === this.x &&
925		       point.y === this.y;
926	},
927
928	// @method contains(otherPoint: Point): Boolean
929	// Returns `true` if both coordinates of the given point are less than the corresponding current point coordinates (in absolute values).
930	contains: function (point) {
931		point = toPoint(point);
932
933		return Math.abs(point.x) <= Math.abs(this.x) &&
934		       Math.abs(point.y) <= Math.abs(this.y);
935	},
936
937	// @method toString(): String
938	// Returns a string representation of the point for debugging purposes.
939	toString: function () {
940		return 'Point(' +
941		        formatNum(this.x) + ', ' +
942		        formatNum(this.y) + ')';
943	}
944};
945
946// @factory L.point(x: Number, y: Number, round?: Boolean)
947// Creates a Point object with the given `x` and `y` coordinates. If optional `round` is set to true, rounds the `x` and `y` values.
948
949// @alternative
950// @factory L.point(coords: Number[])
951// Expects an array of the form `[x, y]` instead.
952
953// @alternative
954// @factory L.point(coords: Object)
955// Expects a plain object of the form `{x: Number, y: Number}` instead.
956function toPoint(x, y, round) {
957	if (x instanceof Point) {
958		return x;
959	}
960	if (isArray(x)) {
961		return new Point(x[0], x[1]);
962	}
963	if (x === undefined || x === null) {
964		return x;
965	}
966	if (typeof x === 'object' && 'x' in x && 'y' in x) {
967		return new Point(x.x, x.y);
968	}
969	return new Point(x, y, round);
970}
971
972/*
973 * @class Bounds
974 * @aka L.Bounds
975 *
976 * Represents a rectangular area in pixel coordinates.
977 *
978 * @example
979 *
980 * ```js
981 * var p1 = L.point(10, 10),
982 * p2 = L.point(40, 60),
983 * bounds = L.bounds(p1, p2);
984 * ```
985 *
986 * 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:
987 *
988 * ```js
989 * otherBounds.intersects([[10, 10], [40, 60]]);
990 * ```
991 *
992 * Note that `Bounds` does not inherit from Leaflet's `Class` object,
993 * which means new classes can't inherit from it, and new methods
994 * can't be added to it with the `include` function.
995 */
996
997function Bounds(a, b) {
998	if (!a) { return; }
999
1000	var points = b ? [a, b] : a;
1001
1002	for (var i = 0, len = points.length; i < len; i++) {
1003		this.extend(points[i]);
1004	}
1005}
1006
1007Bounds.prototype = {
1008	// @method extend(point: Point): this
1009	// Extends the bounds to contain the given point.
1010
1011	// @alternative
1012	// @method extend(otherBounds: Bounds): this
1013	// Extend the bounds to contain the given bounds
1014	extend: function (obj) {
1015		var min2, max2;
1016		if (!obj) { return this; }
1017
1018		if (obj instanceof Point || typeof obj[0] === 'number' || 'x' in obj) {
1019			min2 = max2 = toPoint(obj);
1020		} else {
1021			obj = toBounds(obj);
1022			min2 = obj.min;
1023			max2 = obj.max;
1024
1025			if (!min2 || !max2) { return this; }
1026		}
1027
1028		// @property min: Point
1029		// The top left corner of the rectangle.
1030		// @property max: Point
1031		// The bottom right corner of the rectangle.
1032		if (!this.min && !this.max) {
1033			this.min = min2.clone();
1034			this.max = max2.clone();
1035		} else {
1036			this.min.x = Math.min(min2.x, this.min.x);
1037			this.max.x = Math.max(max2.x, this.max.x);
1038			this.min.y = Math.min(min2.y, this.min.y);
1039			this.max.y = Math.max(max2.y, this.max.y);
1040		}
1041		return this;
1042	},
1043
1044	// @method getCenter(round?: Boolean): Point
1045	// Returns the center point of the bounds.
1046	getCenter: function (round) {
1047		return toPoint(
1048		        (this.min.x + this.max.x) / 2,
1049		        (this.min.y + this.max.y) / 2, round);
1050	},
1051
1052	// @method getBottomLeft(): Point
1053	// Returns the bottom-left point of the bounds.
1054	getBottomLeft: function () {
1055		return toPoint(this.min.x, this.max.y);
1056	},
1057
1058	// @method getTopRight(): Point
1059	// Returns the top-right point of the bounds.
1060	getTopRight: function () { // -> Point
1061		return toPoint(this.max.x, this.min.y);
1062	},
1063
1064	// @method getTopLeft(): Point
1065	// Returns the top-left point of the bounds (i.e. [`this.min`](#bounds-min)).
1066	getTopLeft: function () {
1067		return this.min; // left, top
1068	},
1069
1070	// @method getBottomRight(): Point
1071	// Returns the bottom-right point of the bounds (i.e. [`this.max`](#bounds-max)).
1072	getBottomRight: function () {
1073		return this.max; // right, bottom
1074	},
1075
1076	// @method getSize(): Point
1077	// Returns the size of the given bounds
1078	getSize: function () {
1079		return this.max.subtract(this.min);
1080	},
1081
1082	// @method contains(otherBounds: Bounds): Boolean
1083	// Returns `true` if the rectangle contains the given one.
1084	// @alternative
1085	// @method contains(point: Point): Boolean
1086	// Returns `true` if the rectangle contains the given point.
1087	contains: function (obj) {
1088		var min, max;
1089
1090		if (typeof obj[0] === 'number' || obj instanceof Point) {
1091			obj = toPoint(obj);
1092		} else {
1093			obj = toBounds(obj);
1094		}
1095
1096		if (obj instanceof Bounds) {
1097			min = obj.min;
1098			max = obj.max;
1099		} else {
1100			min = max = obj;
1101		}
1102
1103		return (min.x >= this.min.x) &&
1104		       (max.x <= this.max.x) &&
1105		       (min.y >= this.min.y) &&
1106		       (max.y <= this.max.y);
1107	},
1108
1109	// @method intersects(otherBounds: Bounds): Boolean
1110	// Returns `true` if the rectangle intersects the given bounds. Two bounds
1111	// intersect if they have at least one point in common.
1112	intersects: function (bounds) { // (Bounds) -> Boolean
1113		bounds = toBounds(bounds);
1114
1115		var min = this.min,
1116		    max = this.max,
1117		    min2 = bounds.min,
1118		    max2 = bounds.max,
1119		    xIntersects = (max2.x >= min.x) && (min2.x <= max.x),
1120		    yIntersects = (max2.y >= min.y) && (min2.y <= max.y);
1121
1122		return xIntersects && yIntersects;
1123	},
1124
1125	// @method overlaps(otherBounds: Bounds): Boolean
1126	// Returns `true` if the rectangle overlaps the given bounds. Two bounds
1127	// overlap if their intersection is an area.
1128	overlaps: function (bounds) { // (Bounds) -> Boolean
1129		bounds = toBounds(bounds);
1130
1131		var min = this.min,
1132		    max = this.max,
1133		    min2 = bounds.min,
1134		    max2 = bounds.max,
1135		    xOverlaps = (max2.x > min.x) && (min2.x < max.x),
1136		    yOverlaps = (max2.y > min.y) && (min2.y < max.y);
1137
1138		return xOverlaps && yOverlaps;
1139	},
1140
1141	// @method isValid(): Boolean
1142	// Returns `true` if the bounds are properly initialized.
1143	isValid: function () {
1144		return !!(this.min && this.max);
1145	},
1146
1147
1148	// @method pad(bufferRatio: Number): Bounds
1149	// Returns bounds created by extending or retracting the current bounds by a given ratio in each direction.
1150	// For example, a ratio of 0.5 extends the bounds by 50% in each direction.
1151	// Negative values will retract the bounds.
1152	pad: function (bufferRatio) {
1153		var min = this.min,
1154		max = this.max,
1155		heightBuffer = Math.abs(min.x - max.x) * bufferRatio,
1156		widthBuffer = Math.abs(min.y - max.y) * bufferRatio;
1157
1158
1159		return toBounds(
1160			toPoint(min.x - heightBuffer, min.y - widthBuffer),
1161			toPoint(max.x + heightBuffer, max.y + widthBuffer));
1162	},
1163
1164
1165	// @method equals(otherBounds: Bounds): Boolean
1166	// Returns `true` if the rectangle is equivalent to the given bounds.
1167	equals: function (bounds) {
1168		if (!bounds) { return false; }
1169
1170		bounds = toBounds(bounds);
1171
1172		return this.min.equals(bounds.getTopLeft()) &&
1173			this.max.equals(bounds.getBottomRight());
1174	},
1175};
1176
1177
1178// @factory L.bounds(corner1: Point, corner2: Point)
1179// Creates a Bounds object from two corners coordinate pairs.
1180// @alternative
1181// @factory L.bounds(points: Point[])
1182// Creates a Bounds object from the given array of points.
1183function toBounds(a, b) {
1184	if (!a || a instanceof Bounds) {
1185		return a;
1186	}
1187	return new Bounds(a, b);
1188}
1189
1190/*
1191 * @class LatLngBounds
1192 * @aka L.LatLngBounds
1193 *
1194 * Represents a rectangular geographical area on a map.
1195 *
1196 * @example
1197 *
1198 * ```js
1199 * var corner1 = L.latLng(40.712, -74.227),
1200 * corner2 = L.latLng(40.774, -74.125),
1201 * bounds = L.latLngBounds(corner1, corner2);
1202 * ```
1203 *
1204 * 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:
1205 *
1206 * ```js
1207 * map.fitBounds([
1208 * 	[40.712, -74.227],
1209 * 	[40.774, -74.125]
1210 * ]);
1211 * ```
1212 *
1213 * 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.
1214 *
1215 * Note that `LatLngBounds` does not inherit from Leaflet's `Class` object,
1216 * which means new classes can't inherit from it, and new methods
1217 * can't be added to it with the `include` function.
1218 */
1219
1220function LatLngBounds(corner1, corner2) { // (LatLng, LatLng) or (LatLng[])
1221	if (!corner1) { return; }
1222
1223	var latlngs = corner2 ? [corner1, corner2] : corner1;
1224
1225	for (var i = 0, len = latlngs.length; i < len; i++) {
1226		this.extend(latlngs[i]);
1227	}
1228}
1229
1230LatLngBounds.prototype = {
1231
1232	// @method extend(latlng: LatLng): this
1233	// Extend the bounds to contain the given point
1234
1235	// @alternative
1236	// @method extend(otherBounds: LatLngBounds): this
1237	// Extend the bounds to contain the given bounds
1238	extend: function (obj) {
1239		var sw = this._southWest,
1240		    ne = this._northEast,
1241		    sw2, ne2;
1242
1243		if (obj instanceof LatLng) {
1244			sw2 = obj;
1245			ne2 = obj;
1246
1247		} else if (obj instanceof LatLngBounds) {
1248			sw2 = obj._southWest;
1249			ne2 = obj._northEast;
1250
1251			if (!sw2 || !ne2) { return this; }
1252
1253		} else {
1254			return obj ? this.extend(toLatLng(obj) || toLatLngBounds(obj)) : this;
1255		}
1256
1257		if (!sw && !ne) {
1258			this._southWest = new LatLng(sw2.lat, sw2.lng);
1259			this._northEast = new LatLng(ne2.lat, ne2.lng);
1260		} else {
1261			sw.lat = Math.min(sw2.lat, sw.lat);
1262			sw.lng = Math.min(sw2.lng, sw.lng);
1263			ne.lat = Math.max(ne2.lat, ne.lat);
1264			ne.lng = Math.max(ne2.lng, ne.lng);
1265		}
1266
1267		return this;
1268	},
1269
1270	// @method pad(bufferRatio: Number): LatLngBounds
1271	// Returns bounds created by extending or retracting the current bounds by a given ratio in each direction.
1272	// For example, a ratio of 0.5 extends the bounds by 50% in each direction.
1273	// Negative values will retract the bounds.
1274	pad: function (bufferRatio) {
1275		var sw = this._southWest,
1276		    ne = this._northEast,
1277		    heightBuffer = Math.abs(sw.lat - ne.lat) * bufferRatio,
1278		    widthBuffer = Math.abs(sw.lng - ne.lng) * bufferRatio;
1279
1280		return new LatLngBounds(
1281		        new LatLng(sw.lat - heightBuffer, sw.lng - widthBuffer),
1282		        new LatLng(ne.lat + heightBuffer, ne.lng + widthBuffer));
1283	},
1284
1285	// @method getCenter(): LatLng
1286	// Returns the center point of the bounds.
1287	getCenter: function () {
1288		return new LatLng(
1289		        (this._southWest.lat + this._northEast.lat) / 2,
1290		        (this._southWest.lng + this._northEast.lng) / 2);
1291	},
1292
1293	// @method getSouthWest(): LatLng
1294	// Returns the south-west point of the bounds.
1295	getSouthWest: function () {
1296		return this._southWest;
1297	},
1298
1299	// @method getNorthEast(): LatLng
1300	// Returns the north-east point of the bounds.
1301	getNorthEast: function () {
1302		return this._northEast;
1303	},
1304
1305	// @method getNorthWest(): LatLng
1306	// Returns the north-west point of the bounds.
1307	getNorthWest: function () {
1308		return new LatLng(this.getNorth(), this.getWest());
1309	},
1310
1311	// @method getSouthEast(): LatLng
1312	// Returns the south-east point of the bounds.
1313	getSouthEast: function () {
1314		return new LatLng(this.getSouth(), this.getEast());
1315	},
1316
1317	// @method getWest(): Number
1318	// Returns the west longitude of the bounds
1319	getWest: function () {
1320		return this._southWest.lng;
1321	},
1322
1323	// @method getSouth(): Number
1324	// Returns the south latitude of the bounds
1325	getSouth: function () {
1326		return this._southWest.lat;
1327	},
1328
1329	// @method getEast(): Number
1330	// Returns the east longitude of the bounds
1331	getEast: function () {
1332		return this._northEast.lng;
1333	},
1334
1335	// @method getNorth(): Number
1336	// Returns the north latitude of the bounds
1337	getNorth: function () {
1338		return this._northEast.lat;
1339	},
1340
1341	// @method contains(otherBounds: LatLngBounds): Boolean
1342	// Returns `true` if the rectangle contains the given one.
1343
1344	// @alternative
1345	// @method contains (latlng: LatLng): Boolean
1346	// Returns `true` if the rectangle contains the given point.
1347	contains: function (obj) { // (LatLngBounds) or (LatLng) -> Boolean
1348		if (typeof obj[0] === 'number' || obj instanceof LatLng || 'lat' in obj) {
1349			obj = toLatLng(obj);
1350		} else {
1351			obj = toLatLngBounds(obj);
1352		}
1353
1354		var sw = this._southWest,
1355		    ne = this._northEast,
1356		    sw2, ne2;
1357
1358		if (obj instanceof LatLngBounds) {
1359			sw2 = obj.getSouthWest();
1360			ne2 = obj.getNorthEast();
1361		} else {
1362			sw2 = ne2 = obj;
1363		}
1364
1365		return (sw2.lat >= sw.lat) && (ne2.lat <= ne.lat) &&
1366		       (sw2.lng >= sw.lng) && (ne2.lng <= ne.lng);
1367	},
1368
1369	// @method intersects(otherBounds: LatLngBounds): Boolean
1370	// Returns `true` if the rectangle intersects the given bounds. Two bounds intersect if they have at least one point in common.
1371	intersects: function (bounds) {
1372		bounds = toLatLngBounds(bounds);
1373
1374		var sw = this._southWest,
1375		    ne = this._northEast,
1376		    sw2 = bounds.getSouthWest(),
1377		    ne2 = bounds.getNorthEast(),
1378
1379		    latIntersects = (ne2.lat >= sw.lat) && (sw2.lat <= ne.lat),
1380		    lngIntersects = (ne2.lng >= sw.lng) && (sw2.lng <= ne.lng);
1381
1382		return latIntersects && lngIntersects;
1383	},
1384
1385	// @method overlaps(otherBounds: LatLngBounds): Boolean
1386	// Returns `true` if the rectangle overlaps the given bounds. Two bounds overlap if their intersection is an area.
1387	overlaps: function (bounds) {
1388		bounds = toLatLngBounds(bounds);
1389
1390		var sw = this._southWest,
1391		    ne = this._northEast,
1392		    sw2 = bounds.getSouthWest(),
1393		    ne2 = bounds.getNorthEast(),
1394
1395		    latOverlaps = (ne2.lat > sw.lat) && (sw2.lat < ne.lat),
1396		    lngOverlaps = (ne2.lng > sw.lng) && (sw2.lng < ne.lng);
1397
1398		return latOverlaps && lngOverlaps;
1399	},
1400
1401	// @method toBBoxString(): String
1402	// 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.
1403	toBBoxString: function () {
1404		return [this.getWest(), this.getSouth(), this.getEast(), this.getNorth()].join(',');
1405	},
1406
1407	// @method equals(otherBounds: LatLngBounds, maxMargin?: Number): Boolean
1408	// 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.
1409	equals: function (bounds, maxMargin) {
1410		if (!bounds) { return false; }
1411
1412		bounds = toLatLngBounds(bounds);
1413
1414		return this._southWest.equals(bounds.getSouthWest(), maxMargin) &&
1415		       this._northEast.equals(bounds.getNorthEast(), maxMargin);
1416	},
1417
1418	// @method isValid(): Boolean
1419	// Returns `true` if the bounds are properly initialized.
1420	isValid: function () {
1421		return !!(this._southWest && this._northEast);
1422	}
1423};
1424
1425// TODO International date line?
1426
1427// @factory L.latLngBounds(corner1: LatLng, corner2: LatLng)
1428// Creates a `LatLngBounds` object by defining two diagonally opposite corners of the rectangle.
1429
1430// @alternative
1431// @factory L.latLngBounds(latlngs: LatLng[])
1432// 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).
1433function toLatLngBounds(a, b) {
1434	if (a instanceof LatLngBounds) {
1435		return a;
1436	}
1437	return new LatLngBounds(a, b);
1438}
1439
1440/* @class LatLng
1441 * @aka L.LatLng
1442 *
1443 * Represents a geographical point with a certain latitude and longitude.
1444 *
1445 * @example
1446 *
1447 * ```
1448 * var latlng = L.latLng(50.5, 30.5);
1449 * ```
1450 *
1451 * 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:
1452 *
1453 * ```
1454 * map.panTo([50, 30]);
1455 * map.panTo({lon: 30, lat: 50});
1456 * map.panTo({lat: 50, lng: 30});
1457 * map.panTo(L.latLng(50, 30));
1458 * ```
1459 *
1460 * Note that `LatLng` does not inherit from Leaflet's `Class` object,
1461 * which means new classes can't inherit from it, and new methods
1462 * can't be added to it with the `include` function.
1463 */
1464
1465function LatLng(lat, lng, alt) {
1466	if (isNaN(lat) || isNaN(lng)) {
1467		throw new Error('Invalid LatLng object: (' + lat + ', ' + lng + ')');
1468	}
1469
1470	// @property lat: Number
1471	// Latitude in degrees
1472	this.lat = +lat;
1473
1474	// @property lng: Number
1475	// Longitude in degrees
1476	this.lng = +lng;
1477
1478	// @property alt: Number
1479	// Altitude in meters (optional)
1480	if (alt !== undefined) {
1481		this.alt = +alt;
1482	}
1483}
1484
1485LatLng.prototype = {
1486	// @method equals(otherLatLng: LatLng, maxMargin?: Number): Boolean
1487	// 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.
1488	equals: function (obj, maxMargin) {
1489		if (!obj) { return false; }
1490
1491		obj = toLatLng(obj);
1492
1493		var margin = Math.max(
1494		        Math.abs(this.lat - obj.lat),
1495		        Math.abs(this.lng - obj.lng));
1496
1497		return margin <= (maxMargin === undefined ? 1.0E-9 : maxMargin);
1498	},
1499
1500	// @method toString(): String
1501	// Returns a string representation of the point (for debugging purposes).
1502	toString: function (precision) {
1503		return 'LatLng(' +
1504		        formatNum(this.lat, precision) + ', ' +
1505		        formatNum(this.lng, precision) + ')';
1506	},
1507
1508	// @method distanceTo(otherLatLng: LatLng): Number
1509	// 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).
1510	distanceTo: function (other) {
1511		return Earth.distance(this, toLatLng(other));
1512	},
1513
1514	// @method wrap(): LatLng
1515	// Returns a new `LatLng` object with the longitude wrapped so it's always between -180 and +180 degrees.
1516	wrap: function () {
1517		return Earth.wrapLatLng(this);
1518	},
1519
1520	// @method toBounds(sizeInMeters: Number): LatLngBounds
1521	// Returns a new `LatLngBounds` object in which each boundary is `sizeInMeters/2` meters apart from the `LatLng`.
1522	toBounds: function (sizeInMeters) {
1523		var latAccuracy = 180 * sizeInMeters / 40075017,
1524		    lngAccuracy = latAccuracy / Math.cos((Math.PI / 180) * this.lat);
1525
1526		return toLatLngBounds(
1527		        [this.lat - latAccuracy, this.lng - lngAccuracy],
1528		        [this.lat + latAccuracy, this.lng + lngAccuracy]);
1529	},
1530
1531	clone: function () {
1532		return new LatLng(this.lat, this.lng, this.alt);
1533	}
1534};
1535
1536
1537
1538// @factory L.latLng(latitude: Number, longitude: Number, altitude?: Number): LatLng
1539// Creates an object representing a geographical point with the given latitude and longitude (and optionally altitude).
1540
1541// @alternative
1542// @factory L.latLng(coords: Array): LatLng
1543// Expects an array of the form `[Number, Number]` or `[Number, Number, Number]` instead.
1544
1545// @alternative
1546// @factory L.latLng(coords: Object): LatLng
1547// Expects an plain object of the form `{lat: Number, lng: Number}` or `{lat: Number, lng: Number, alt: Number}` instead.
1548
1549function toLatLng(a, b, c) {
1550	if (a instanceof LatLng) {
1551		return a;
1552	}
1553	if (isArray(a) && typeof a[0] !== 'object') {
1554		if (a.length === 3) {
1555			return new LatLng(a[0], a[1], a[2]);
1556		}
1557		if (a.length === 2) {
1558			return new LatLng(a[0], a[1]);
1559		}
1560		return null;
1561	}
1562	if (a === undefined || a === null) {
1563		return a;
1564	}
1565	if (typeof a === 'object' && 'lat' in a) {
1566		return new LatLng(a.lat, 'lng' in a ? a.lng : a.lon, a.alt);
1567	}
1568	if (b === undefined) {
1569		return null;
1570	}
1571	return new LatLng(a, b, c);
1572}
1573
1574/*
1575 * @namespace CRS
1576 * @crs L.CRS.Base
1577 * Object that defines coordinate reference systems for projecting
1578 * geographical points into pixel (screen) coordinates and back (and to
1579 * coordinates in other units for [WMS](https://en.wikipedia.org/wiki/Web_Map_Service) services). See
1580 * [spatial reference system](https://en.wikipedia.org/wiki/Spatial_reference_system).
1581 *
1582 * Leaflet defines the most usual CRSs by default. If you want to use a
1583 * CRS not defined by default, take a look at the
1584 * [Proj4Leaflet](https://github.com/kartena/Proj4Leaflet) plugin.
1585 *
1586 * Note that the CRS instances do not inherit from Leaflet's `Class` object,
1587 * and can't be instantiated. Also, new classes can't inherit from them,
1588 * and methods can't be added to them with the `include` function.
1589 */
1590
1591var CRS = {
1592	// @method latLngToPoint(latlng: LatLng, zoom: Number): Point
1593	// Projects geographical coordinates into pixel coordinates for a given zoom.
1594	latLngToPoint: function (latlng, zoom) {
1595		var projectedPoint = this.projection.project(latlng),
1596		    scale = this.scale(zoom);
1597
1598		return this.transformation._transform(projectedPoint, scale);
1599	},
1600
1601	// @method pointToLatLng(point: Point, zoom: Number): LatLng
1602	// The inverse of `latLngToPoint`. Projects pixel coordinates on a given
1603	// zoom into geographical coordinates.
1604	pointToLatLng: function (point, zoom) {
1605		var scale = this.scale(zoom),
1606		    untransformedPoint = this.transformation.untransform(point, scale);
1607
1608		return this.projection.unproject(untransformedPoint);
1609	},
1610
1611	// @method project(latlng: LatLng): Point
1612	// Projects geographical coordinates into coordinates in units accepted for
1613	// this CRS (e.g. meters for EPSG:3857, for passing it to WMS services).
1614	project: function (latlng) {
1615		return this.projection.project(latlng);
1616	},
1617
1618	// @method unproject(point: Point): LatLng
1619	// Given a projected coordinate returns the corresponding LatLng.
1620	// The inverse of `project`.
1621	unproject: function (point) {
1622		return this.projection.unproject(point);
1623	},
1624
1625	// @method scale(zoom: Number): Number
1626	// Returns the scale used when transforming projected coordinates into
1627	// pixel coordinates for a particular zoom. For example, it returns
1628	// `256 * 2^zoom` for Mercator-based CRS.
1629	scale: function (zoom) {
1630		return 256 * Math.pow(2, zoom);
1631	},
1632
1633	// @method zoom(scale: Number): Number
1634	// Inverse of `scale()`, returns the zoom level corresponding to a scale
1635	// factor of `scale`.
1636	zoom: function (scale) {
1637		return Math.log(scale / 256) / Math.LN2;
1638	},
1639
1640	// @method getProjectedBounds(zoom: Number): Bounds
1641	// Returns the projection's bounds scaled and transformed for the provided `zoom`.
1642	getProjectedBounds: function (zoom) {
1643		if (this.infinite) { return null; }
1644
1645		var b = this.projection.bounds,
1646		    s = this.scale(zoom),
1647		    min = this.transformation.transform(b.min, s),
1648		    max = this.transformation.transform(b.max, s);
1649
1650		return new Bounds(min, max);
1651	},
1652
1653	// @method distance(latlng1: LatLng, latlng2: LatLng): Number
1654	// Returns the distance between two geographical coordinates.
1655
1656	// @property code: String
1657	// Standard code name of the CRS passed into WMS services (e.g. `'EPSG:3857'`)
1658	//
1659	// @property wrapLng: Number[]
1660	// An array of two numbers defining whether the longitude (horizontal) coordinate
1661	// axis wraps around a given range and how. Defaults to `[-180, 180]` in most
1662	// geographical CRSs. If `undefined`, the longitude axis does not wrap around.
1663	//
1664	// @property wrapLat: Number[]
1665	// Like `wrapLng`, but for the latitude (vertical) axis.
1666
1667	// wrapLng: [min, max],
1668	// wrapLat: [min, max],
1669
1670	// @property infinite: Boolean
1671	// If true, the coordinate space will be unbounded (infinite in both axes)
1672	infinite: false,
1673
1674	// @method wrapLatLng(latlng: LatLng): LatLng
1675	// Returns a `LatLng` where lat and lng has been wrapped according to the
1676	// CRS's `wrapLat` and `wrapLng` properties, if they are outside the CRS's bounds.
1677	wrapLatLng: function (latlng) {
1678		var lng = this.wrapLng ? wrapNum(latlng.lng, this.wrapLng, true) : latlng.lng,
1679		    lat = this.wrapLat ? wrapNum(latlng.lat, this.wrapLat, true) : latlng.lat,
1680		    alt = latlng.alt;
1681
1682		return new LatLng(lat, lng, alt);
1683	},
1684
1685	// @method wrapLatLngBounds(bounds: LatLngBounds): LatLngBounds
1686	// Returns a `LatLngBounds` with the same size as the given one, ensuring
1687	// that its center is within the CRS's bounds.
1688	// Only accepts actual `L.LatLngBounds` instances, not arrays.
1689	wrapLatLngBounds: function (bounds) {
1690		var center = bounds.getCenter(),
1691		    newCenter = this.wrapLatLng(center),
1692		    latShift = center.lat - newCenter.lat,
1693		    lngShift = center.lng - newCenter.lng;
1694
1695		if (latShift === 0 && lngShift === 0) {
1696			return bounds;
1697		}
1698
vendor: 151,688 bytes, lines 1699-6733
1699		var sw = bounds.getSouthWest(),
1700		    ne = bounds.getNorthEast(),
1701		    newSw = new LatLng(sw.lat - latShift, sw.lng - lngShift),
1702		    newNe = new LatLng(ne.lat - latShift, ne.lng - lngShift);
1703
1704		return new LatLngBounds(newSw, newNe);
1705	}
1706};
1707
1708/*
1709 * @namespace CRS
1710 * @crs L.CRS.Earth
1711 *
1712 * Serves as the base for CRS that are global such that they cover the earth.
1713 * Can only be used as the base for other CRS and cannot be used directly,
1714 * since it does not have a `code`, `projection` or `transformation`. `distance()` returns
1715 * meters.
1716 */
1717
1718var Earth = extend({}, CRS, {
1719	wrapLng: [-180, 180],
1720
1721	// Mean Earth Radius, as recommended for use by
1722	// the International Union of Geodesy and Geophysics,
1723	// see https://rosettacode.org/wiki/Haversine_formula
1724	R: 6371000,
1725
1726	// distance between two geographical points using spherical law of cosines approximation
1727	distance: function (latlng1, latlng2) {
1728		var rad = Math.PI / 180,
1729		    lat1 = latlng1.lat * rad,
1730		    lat2 = latlng2.lat * rad,
1731		    sinDLat = Math.sin((latlng2.lat - latlng1.lat) * rad / 2),
1732		    sinDLon = Math.sin((latlng2.lng - latlng1.lng) * rad / 2),
1733		    a = sinDLat * sinDLat + Math.cos(lat1) * Math.cos(lat2) * sinDLon * sinDLon,
1734		    c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
1735		return this.R * c;
1736	}
1737});
1738
1739/*
1740 * @namespace Projection
1741 * @projection L.Projection.SphericalMercator
1742 *
1743 * Spherical Mercator projection — the most common projection for online maps,
1744 * used by almost all free and commercial tile providers. Assumes that Earth is
1745 * a sphere. Used by the `EPSG:3857` CRS.
1746 */
1747
1748var earthRadius = 6378137;
1749
1750var SphericalMercator = {
1751
1752	R: earthRadius,
1753	MAX_LATITUDE: 85.0511287798,
1754
1755	project: function (latlng) {
1756		var d = Math.PI / 180,
1757		    max = this.MAX_LATITUDE,
1758		    lat = Math.max(Math.min(max, latlng.lat), -max),
1759		    sin = Math.sin(lat * d);
1760
1761		return new Point(
1762			this.R * latlng.lng * d,
1763			this.R * Math.log((1 + sin) / (1 - sin)) / 2);
1764	},
1765
1766	unproject: function (point) {
1767		var d = 180 / Math.PI;
1768
1769		return new LatLng(
1770			(2 * Math.atan(Math.exp(point.y / this.R)) - (Math.PI / 2)) * d,
1771			point.x * d / this.R);
1772	},
1773
1774	bounds: (function () {
1775		var d = earthRadius * Math.PI;
1776		return new Bounds([-d, -d], [d, d]);
1777	})()
1778};
1779
1780/*
1781 * @class Transformation
1782 * @aka L.Transformation
1783 *
1784 * Represents an affine transformation: a set of coefficients `a`, `b`, `c`, `d`
1785 * for transforming a point of a form `(x, y)` into `(a*x + b, c*y + d)` and doing
1786 * the reverse. Used by Leaflet in its projections code.
1787 *
1788 * @example
1789 *
1790 * ```js
1791 * var transformation = L.transformation(2, 5, -1, 10),
1792 * 	p = L.point(1, 2),
1793 * 	p2 = transformation.transform(p), //  L.point(7, 8)
1794 * 	p3 = transformation.untransform(p2); //  L.point(1, 2)
1795 * ```
1796 */
1797
1798
1799// factory new L.Transformation(a: Number, b: Number, c: Number, d: Number)
1800// Creates a `Transformation` object with the given coefficients.
1801function Transformation(a, b, c, d) {
1802	if (isArray(a)) {
1803		// use array properties
1804		this._a = a[0];
1805		this._b = a[1];
1806		this._c = a[2];
1807		this._d = a[3];
1808		return;
1809	}
1810	this._a = a;
1811	this._b = b;
1812	this._c = c;
1813	this._d = d;
1814}
1815
1816Transformation.prototype = {
1817	// @method transform(point: Point, scale?: Number): Point
1818	// Returns a transformed point, optionally multiplied by the given scale.
1819	// Only accepts actual `L.Point` instances, not arrays.
1820	transform: function (point, scale) { // (Point, Number) -> Point
1821		return this._transform(point.clone(), scale);
1822	},
1823
1824	// destructive transform (faster)
1825	_transform: function (point, scale) {
1826		scale = scale || 1;
1827		point.x = scale * (this._a * point.x + this._b);
1828		point.y = scale * (this._c * point.y + this._d);
1829		return point;
1830	},
1831
1832	// @method untransform(point: Point, scale?: Number): Point
1833	// Returns the reverse transformation of the given point, optionally divided
1834	// by the given scale. Only accepts actual `L.Point` instances, not arrays.
1835	untransform: function (point, scale) {
1836		scale = scale || 1;
1837		return new Point(
1838		        (point.x / scale - this._b) / this._a,
1839		        (point.y / scale - this._d) / this._c);
1840	}
1841};
1842
1843// factory L.transformation(a: Number, b: Number, c: Number, d: Number)
1844
1845// @factory L.transformation(a: Number, b: Number, c: Number, d: Number)
1846// Instantiates a Transformation object with the given coefficients.
1847
1848// @alternative
1849// @factory L.transformation(coefficients: Array): Transformation
1850// Expects an coefficients array of the form
1851// `[a: Number, b: Number, c: Number, d: Number]`.
1852
1853function toTransformation(a, b, c, d) {
1854	return new Transformation(a, b, c, d);
1855}
1856
1857/*
1858 * @namespace CRS
1859 * @crs L.CRS.EPSG3857
1860 *
1861 * The most common CRS for online maps, used by almost all free and commercial
1862 * tile providers. Uses Spherical Mercator projection. Set in by default in
1863 * Map's `crs` option.
1864 */
1865
1866var EPSG3857 = extend({}, Earth, {
1867	code: 'EPSG:3857',
1868	projection: SphericalMercator,
1869
1870	transformation: (function () {
1871		var scale = 0.5 / (Math.PI * SphericalMercator.R);
1872		return toTransformation(scale, 0.5, -scale, 0.5);
1873	}())
1874});
1875
1876var EPSG900913 = extend({}, EPSG3857, {
1877	code: 'EPSG:900913'
1878});
1879
1880// @namespace SVG; @section
1881// There are several static functions which can be called without instantiating L.SVG:
1882
1883// @function create(name: String): SVGElement
1884// Returns a instance of [SVGElement](https://developer.mozilla.org/docs/Web/API/SVGElement),
1885// corresponding to the class name passed. For example, using 'line' will return
1886// an instance of [SVGLineElement](https://developer.mozilla.org/docs/Web/API/SVGLineElement).
1887function svgCreate(name) {
1888	return document.createElementNS('http://www.w3.org/2000/svg', name);
1889}
1890
1891// @function pointsToPath(rings: Point[], closed: Boolean): String
1892// Generates a SVG path string for multiple rings, with each ring turning
1893// into "M..L..L.." instructions
1894function pointsToPath(rings, closed) {
1895	var str = '',
1896	i, j, len, len2, points, p;
1897
1898	for (i = 0, len = rings.length; i < len; i++) {
1899		points = rings[i];
1900
1901		for (j = 0, len2 = points.length; j < len2; j++) {
1902			p = points[j];
1903			str += (j ? 'L' : 'M') + p.x + ' ' + p.y;
1904		}
1905
1906		// closes the ring for polygons; "x" is VML syntax
1907		str += closed ? (Browser.svg ? 'z' : 'x') : '';
1908	}
1909
1910	// SVG complains about empty path strings
1911	return str || 'M0 0';
1912}
1913
1914/*
1915 * @namespace Browser
1916 * @aka L.Browser
1917 *
1918 * A namespace with static properties for browser/feature detection used by Leaflet internally.
1919 *
1920 * @example
1921 *
1922 * ```js
1923 * if (L.Browser.ielt9) {
1924 *   alert('Upgrade your browser, dude!');
1925 * }
1926 * ```
1927 */
1928
1929var style = document.documentElement.style;
1930
1931// @property ie: Boolean; `true` for all Internet Explorer versions (not Edge).
1932var ie = 'ActiveXObject' in window;
1933
1934// @property ielt9: Boolean; `true` for Internet Explorer versions less than 9.
1935var ielt9 = ie && !document.addEventListener;
1936
1937// @property edge: Boolean; `true` for the Edge web browser.
1938var edge = 'msLaunchUri' in navigator && !('documentMode' in document);
1939
1940// @property webkit: Boolean;
1941// `true` for webkit-based browsers like Chrome and Safari (including mobile versions).
1942var webkit = userAgentContains('webkit');
1943
1944// @property android: Boolean
1945// **Deprecated.** `true` for any browser running on an Android platform.
1946var android = userAgentContains('android');
1947
1948// @property android23: Boolean; **Deprecated.** `true` for browsers running on Android 2 or Android 3.
1949var android23 = userAgentContains('android 2') || userAgentContains('android 3');
1950
1951/* See https://stackoverflow.com/a/17961266 for details on detecting stock Android */
1952var webkitVer = parseInt(/WebKit\/([0-9]+)|$/.exec(navigator.userAgent)[1], 10); // also matches AppleWebKit
1953// @property androidStock: Boolean; **Deprecated.** `true` for the Android stock browser (i.e. not Chrome)
1954var androidStock = android && userAgentContains('Google') && webkitVer < 537 && !('AudioNode' in window);
1955
1956// @property opera: Boolean; `true` for the Opera browser
1957var opera = !!window.opera;
1958
1959// @property chrome: Boolean; `true` for the Chrome browser.
1960var chrome = !edge && userAgentContains('chrome');
1961
1962// @property gecko: Boolean; `true` for gecko-based browsers like Firefox.
1963var gecko = userAgentContains('gecko') && !webkit && !opera && !ie;
1964
1965// @property safari: Boolean; `true` for the Safari browser.
1966var safari = !chrome && userAgentContains('safari');
1967
1968var phantom = userAgentContains('phantom');
1969
1970// @property opera12: Boolean
1971// `true` for the Opera browser supporting CSS transforms (version 12 or later).
1972var opera12 = 'OTransition' in style;
1973
1974// @property win: Boolean; `true` when the browser is running in a Windows platform
1975var win = navigator.platform.indexOf('Win') === 0;
1976
1977// @property ie3d: Boolean; `true` for all Internet Explorer versions supporting CSS transforms.
1978var ie3d = ie && ('transition' in style);
1979
1980// @property webkit3d: Boolean; `true` for webkit-based browsers supporting CSS transforms.
1981var webkit3d = ('WebKitCSSMatrix' in window) && ('m11' in new window.WebKitCSSMatrix()) && !android23;
1982
1983// @property gecko3d: Boolean; `true` for gecko-based browsers supporting CSS transforms.
1984var gecko3d = 'MozPerspective' in style;
1985
1986// @property any3d: Boolean
1987// `true` for all browsers supporting CSS transforms.
1988var any3d = !window.L_DISABLE_3D && (ie3d || webkit3d || gecko3d) && !opera12 && !phantom;
1989
1990// @property mobile: Boolean; `true` for all browsers running in a mobile device.
1991var mobile = typeof orientation !== 'undefined' || userAgentContains('mobile');
1992
1993// @property mobileWebkit: Boolean; `true` for all webkit-based browsers in a mobile device.
1994var mobileWebkit = mobile && webkit;
1995
1996// @property mobileWebkit3d: Boolean
1997// `true` for all webkit-based browsers in a mobile device supporting CSS transforms.
1998var mobileWebkit3d = mobile && webkit3d;
1999
2000// @property msPointer: Boolean
2001// `true` for browsers implementing the Microsoft touch events model (notably IE10).
2002var msPointer = !window.PointerEvent && window.MSPointerEvent;
2003
2004// @property pointer: Boolean
2005// `true` for all browsers supporting [pointer events](https://msdn.microsoft.com/en-us/library/dn433244%28v=vs.85%29.aspx).
2006var pointer = !!(window.PointerEvent || msPointer);
2007
2008// @property touchNative: Boolean
2009// `true` for all browsers supporting [touch events](https://developer.mozilla.org/docs/Web/API/Touch_events).
2010// **This does not necessarily mean** that the browser is running in a computer with
2011// a touchscreen, it only means that the browser is capable of understanding
2012// touch events.
2013var touchNative = 'ontouchstart' in window || !!window.TouchEvent;
2014
2015// @property touch: Boolean
2016// `true` for all browsers supporting either [touch](#browser-touch) or [pointer](#browser-pointer) events.
2017// Note: pointer events will be preferred (if available), and processed for all `touch*` listeners.
2018var touch = !window.L_NO_TOUCH && (touchNative || pointer);
2019
2020// @property mobileOpera: Boolean; `true` for the Opera browser in a mobile device.
2021var mobileOpera = mobile && opera;
2022
2023// @property mobileGecko: Boolean
2024// `true` for gecko-based browsers running in a mobile device.
2025var mobileGecko = mobile && gecko;
2026
2027// @property retina: Boolean
2028// `true` for browsers on a high-resolution "retina" screen or on any screen when browser's display zoom is more than 100%.
2029var retina = (window.devicePixelRatio || (window.screen.deviceXDPI / window.screen.logicalXDPI)) > 1;
2030
2031// @property passiveEvents: Boolean
2032// `true` for browsers that support passive events.
2033var passiveEvents = (function () {
2034	var supportsPassiveOption = false;
2035	try {
2036		var opts = Object.defineProperty({}, 'passive', {
2037			get: function () { // eslint-disable-line getter-return
2038				supportsPassiveOption = true;
2039			}
2040		});
2041		window.addEventListener('testPassiveEventSupport', falseFn, opts);
2042		window.removeEventListener('testPassiveEventSupport', falseFn, opts);
2043	} catch (e) {
2044		// Errors can safely be ignored since this is only a browser support test.
2045	}
2046	return supportsPassiveOption;
2047}());
2048
2049// @property canvas: Boolean
2050// `true` when the browser supports [`<canvas>`](https://developer.mozilla.org/docs/Web/API/Canvas_API).
2051var canvas$1 = (function () {
2052	return !!document.createElement('canvas').getContext;
2053}());
2054
2055// @property svg: Boolean
2056// `true` when the browser supports [SVG](https://developer.mozilla.org/docs/Web/SVG).
2057var svg$1 = !!(document.createElementNS && svgCreate('svg').createSVGRect);
2058
2059var inlineSvg = !!svg$1 && (function () {
2060	var div = document.createElement('div');
2061	div.innerHTML = '<svg/>';
2062	return (div.firstChild && div.firstChild.namespaceURI) === 'http://www.w3.org/2000/svg';
2063})();
2064
2065// @property vml: Boolean
2066// `true` if the browser supports [VML](https://en.wikipedia.org/wiki/Vector_Markup_Language).
2067var vml = !svg$1 && (function () {
2068	try {
2069		var div = document.createElement('div');
2070		div.innerHTML = '<v:shape adj="1"/>';
2071
2072		var shape = div.firstChild;
2073		shape.style.behavior = 'url(#default#VML)';
2074
2075		return shape && (typeof shape.adj === 'object');
2076
2077	} catch (e) {
2078		return false;
2079	}
2080}());
2081
2082
2083// @property mac: Boolean; `true` when the browser is running in a Mac platform
2084var mac = navigator.platform.indexOf('Mac') === 0;
2085
2086// @property mac: Boolean; `true` when the browser is running in a Linux platform
2087var linux = navigator.platform.indexOf('Linux') === 0;
2088
2089function userAgentContains(str) {
2090	return navigator.userAgent.toLowerCase().indexOf(str) >= 0;
2091}
2092
2093
2094var Browser = {
2095	ie: ie,
2096	ielt9: ielt9,
2097	edge: edge,
2098	webkit: webkit,
2099	android: android,
2100	android23: android23,
2101	androidStock: androidStock,
2102	opera: opera,
2103	chrome: chrome,
2104	gecko: gecko,
2105	safari: safari,
2106	phantom: phantom,
2107	opera12: opera12,
2108	win: win,
2109	ie3d: ie3d,
2110	webkit3d: webkit3d,
2111	gecko3d: gecko3d,
2112	any3d: any3d,
2113	mobile: mobile,
2114	mobileWebkit: mobileWebkit,
2115	mobileWebkit3d: mobileWebkit3d,
2116	msPointer: msPointer,
2117	pointer: pointer,
2118	touch: touch,
2119	touchNative: touchNative,
2120	mobileOpera: mobileOpera,
2121	mobileGecko: mobileGecko,
2122	retina: retina,
2123	passiveEvents: passiveEvents,
2124	canvas: canvas$1,
2125	svg: svg$1,
2126	vml: vml,
2127	inlineSvg: inlineSvg,
2128	mac: mac,
2129	linux: linux
2130};
2131
2132/*
2133 * Extends L.DomEvent to provide touch support for Internet Explorer and Windows-based devices.
2134 */
2135
2136var POINTER_DOWN =   Browser.msPointer ? 'MSPointerDown'   : 'pointerdown';
2137var POINTER_MOVE =   Browser.msPointer ? 'MSPointerMove'   : 'pointermove';
2138var POINTER_UP =     Browser.msPointer ? 'MSPointerUp'     : 'pointerup';
2139var POINTER_CANCEL = Browser.msPointer ? 'MSPointerCancel' : 'pointercancel';
2140var pEvent = {
2141	touchstart  : POINTER_DOWN,
2142	touchmove   : POINTER_MOVE,
2143	touchend    : POINTER_UP,
2144	touchcancel : POINTER_CANCEL
2145};
2146var handle = {
2147	touchstart  : _onPointerStart,
2148	touchmove   : _handlePointer,
2149	touchend    : _handlePointer,
2150	touchcancel : _handlePointer
2151};
2152var _pointers = {};
2153var _pointerDocListener = false;
2154
2155// Provides a touch events wrapper for (ms)pointer events.
2156// ref https://www.w3.org/TR/pointerevents/ https://www.w3.org/Bugs/Public/show_bug.cgi?id=22890
2157
2158function addPointerListener(obj, type, handler) {
2159	if (type === 'touchstart') {
2160		_addPointerDocListener();
2161	}
2162	if (!handle[type]) {
2163		console.warn('wrong event specified:', type);
2164		return falseFn;
2165	}
2166	handler = handle[type].bind(this, handler);
2167	obj.addEventListener(pEvent[type], handler, false);
2168	return handler;
2169}
2170
2171function removePointerListener(obj, type, handler) {
2172	if (!pEvent[type]) {
2173		console.warn('wrong event specified:', type);
2174		return;
2175	}
2176	obj.removeEventListener(pEvent[type], handler, false);
2177}
2178
2179function _globalPointerDown(e) {
2180	_pointers[e.pointerId] = e;
2181}
2182
2183function _globalPointerMove(e) {
2184	if (_pointers[e.pointerId]) {
2185		_pointers[e.pointerId] = e;
2186	}
2187}
2188
2189function _globalPointerUp(e) {
2190	delete _pointers[e.pointerId];
2191}
2192
2193function _addPointerDocListener() {
2194	// need to keep track of what pointers and how many are active to provide e.touches emulation
2195	if (!_pointerDocListener) {
2196		// we listen document as any drags that end by moving the touch off the screen get fired there
2197		document.addEventListener(POINTER_DOWN, _globalPointerDown, true);
2198		document.addEventListener(POINTER_MOVE, _globalPointerMove, true);
2199		document.addEventListener(POINTER_UP, _globalPointerUp, true);
2200		document.addEventListener(POINTER_CANCEL, _globalPointerUp, true);
2201
2202		_pointerDocListener = true;
2203	}
2204}
2205
2206function _handlePointer(handler, e) {
2207	if (e.pointerType === (e.MSPOINTER_TYPE_MOUSE || 'mouse')) { return; }
2208
2209	e.touches = [];
2210	for (var i in _pointers) {
2211		e.touches.push(_pointers[i]);
2212	}
2213	e.changedTouches = [e];
2214
2215	handler(e);
2216}
2217
2218function _onPointerStart(handler, e) {
2219	// IE10 specific: MsTouch needs preventDefault. See #2000
2220	if (e.MSPOINTER_TYPE_TOUCH && e.pointerType === e.MSPOINTER_TYPE_TOUCH) {
2221		preventDefault(e);
2222	}
2223	_handlePointer(handler, e);
2224}
2225
2226/*
2227 * Extends the event handling code with double tap support for mobile browsers.
2228 *
2229 * Note: currently most browsers fire native dblclick, with only a few exceptions
2230 * (see https://github.com/Leaflet/Leaflet/issues/7012#issuecomment-595087386)
2231 */
2232
2233function makeDblclick(event) {
2234	// in modern browsers `type` cannot be just overridden:
2235	// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Errors/Getter_only
2236	var newEvent = {},
2237	    prop, i;
2238	for (i in event) {
2239		prop = event[i];
2240		newEvent[i] = prop && prop.bind ? prop.bind(event) : prop;
2241	}
2242	event = newEvent;
2243	newEvent.type = 'dblclick';
2244	newEvent.detail = 2;
2245	newEvent.isTrusted = false;
2246	newEvent._simulated = true; // for debug purposes
2247	return newEvent;
2248}
2249
2250var delay = 200;
2251function addDoubleTapListener(obj, handler) {
2252	// Most browsers handle double tap natively
2253	obj.addEventListener('dblclick', handler);
2254
2255	// On some platforms the browser doesn't fire native dblclicks for touch events.
2256	// It seems that in all such cases `detail` property of `click` event is always `1`.
2257	// So here we rely on that fact to avoid excessive 'dblclick' simulation when not needed.
2258	var last = 0,
2259	    detail;
2260	function simDblclick(e) {
2261		if (e.detail !== 1) {
2262			detail = e.detail; // keep in sync to avoid false dblclick in some cases
2263			return;
2264		}
2265
2266		if (e.pointerType === 'mouse' ||
2267			(e.sourceCapabilities && !e.sourceCapabilities.firesTouchEvents)) {
2268
2269			return;
2270		}
2271
2272		// When clicking on an <input>, the browser generates a click on its
2273		// <label> (and vice versa) triggering two clicks in quick succession.
2274		// This ignores clicks on elements which are a label with a 'for'
2275		// attribute (or children of such a label), but not children of
2276		// a <input>.
2277		var path = getPropagationPath(e);
2278		if (path.some(function (el) {
2279			return el instanceof HTMLLabelElement && el.attributes.for;
2280		}) &&
2281			!path.some(function (el) {
2282				return (
2283					el instanceof HTMLInputElement ||
2284					el instanceof HTMLSelectElement
2285				);
2286			})
2287		) {
2288			return;
2289		}
2290
2291		var now = Date.now();
2292		if (now - last <= delay) {
2293			detail++;
2294			if (detail === 2) {
2295				handler(makeDblclick(e));
2296			}
2297		} else {
2298			detail = 1;
2299		}
2300		last = now;
2301	}
2302
2303	obj.addEventListener('click', simDblclick);
2304
2305	return {
2306		dblclick: handler,
2307		simDblclick: simDblclick
2308	};
2309}
2310
2311function removeDoubleTapListener(obj, handlers) {
2312	obj.removeEventListener('dblclick', handlers.dblclick);
2313	obj.removeEventListener('click', handlers.simDblclick);
2314}
2315
2316/*
2317 * @namespace DomUtil
2318 *
2319 * Utility functions to work with the [DOM](https://developer.mozilla.org/docs/Web/API/Document_Object_Model)
2320 * tree, used by Leaflet internally.
2321 *
2322 * Most functions expecting or returning a `HTMLElement` also work for
2323 * SVG elements. The only difference is that classes refer to CSS classes
2324 * in HTML and SVG classes in SVG.
2325 */
2326
2327
2328// @property TRANSFORM: String
2329// Vendor-prefixed transform style name (e.g. `'webkitTransform'` for WebKit).
2330var TRANSFORM = testProp(
2331	['transform', 'webkitTransform', 'OTransform', 'MozTransform', 'msTransform']);
2332
2333// webkitTransition comes first because some browser versions that drop vendor prefix don't do
2334// the same for the transitionend event, in particular the Android 4.1 stock browser
2335
2336// @property TRANSITION: String
2337// Vendor-prefixed transition style name.
2338var TRANSITION = testProp(
2339	['webkitTransition', 'transition', 'OTransition', 'MozTransition', 'msTransition']);
2340
2341// @property TRANSITION_END: String
2342// Vendor-prefixed transitionend event name.
2343var TRANSITION_END =
2344	TRANSITION === 'webkitTransition' || TRANSITION === 'OTransition' ? TRANSITION + 'End' : 'transitionend';
2345
2346
2347// @function get(id: String|HTMLElement): HTMLElement
2348// Returns an element given its DOM id, or returns the element itself
2349// if it was passed directly.
2350function get(id) {
2351	return typeof id === 'string' ? document.getElementById(id) : id;
2352}
2353
2354// @function getStyle(el: HTMLElement, styleAttrib: String): String
2355// Returns the value for a certain style attribute on an element,
2356// including computed values or values set through CSS.
2357function getStyle(el, style) {
2358	var value = el.style[style] || (el.currentStyle && el.currentStyle[style]);
2359
2360	if ((!value || value === 'auto') && document.defaultView) {
2361		var css = document.defaultView.getComputedStyle(el, null);
2362		value = css ? css[style] : null;
2363	}
2364	return value === 'auto' ? null : value;
2365}
2366
2367// @function create(tagName: String, className?: String, container?: HTMLElement): HTMLElement
2368// Creates an HTML element with `tagName`, sets its class to `className`, and optionally appends it to `container` element.
2369function create$1(tagName, className, container) {
2370	var el = document.createElement(tagName);
2371	el.className = className || '';
2372
2373	if (container) {
2374		container.appendChild(el);
2375	}
2376	return el;
2377}
2378
2379// @function remove(el: HTMLElement)
2380// Removes `el` from its parent element
2381function remove(el) {
2382	var parent = el.parentNode;
2383	if (parent) {
2384		parent.removeChild(el);
2385	}
2386}
2387
2388// @function empty(el: HTMLElement)
2389// Removes all of `el`'s children elements from `el`
2390function empty(el) {
2391	while (el.firstChild) {
2392		el.removeChild(el.firstChild);
2393	}
2394}
2395
2396// @function toFront(el: HTMLElement)
2397// Makes `el` the last child of its parent, so it renders in front of the other children.
2398function toFront(el) {
2399	var parent = el.parentNode;
2400	if (parent && parent.lastChild !== el) {
2401		parent.appendChild(el);
2402	}
2403}
2404
2405// @function toBack(el: HTMLElement)
2406// Makes `el` the first child of its parent, so it renders behind the other children.
2407function toBack(el) {
2408	var parent = el.parentNode;
2409	if (parent && parent.firstChild !== el) {
2410		parent.insertBefore(el, parent.firstChild);
2411	}
2412}
2413
2414// @function hasClass(el: HTMLElement, name: String): Boolean
2415// Returns `true` if the element's class attribute contains `name`.
2416function hasClass(el, name) {
2417	if (el.classList !== undefined) {
2418		return el.classList.contains(name);
2419	}
2420	var className = getClass(el);
2421	return className.length > 0 && new RegExp('(^|\\s)' + name + '(\\s|$)').test(className);
2422}
2423
2424// @function addClass(el: HTMLElement, name: String)
2425// Adds `name` to the element's class attribute.
2426function addClass(el, name) {
2427	if (el.classList !== undefined) {
2428		var classes = splitWords(name);
2429		for (var i = 0, len = classes.length; i < len; i++) {
2430			el.classList.add(classes[i]);
2431		}
2432	} else if (!hasClass(el, name)) {
2433		var className = getClass(el);
2434		setClass(el, (className ? className + ' ' : '') + name);
2435	}
2436}
2437
2438// @function removeClass(el: HTMLElement, name: String)
2439// Removes `name` from the element's class attribute.
2440function removeClass(el, name) {
2441	if (el.classList !== undefined) {
2442		el.classList.remove(name);
2443	} else {
2444		setClass(el, trim((' ' + getClass(el) + ' ').replace(' ' + name + ' ', ' ')));
2445	}
2446}
2447
2448// @function setClass(el: HTMLElement, name: String)
2449// Sets the element's class.
2450function setClass(el, name) {
2451	if (el.className.baseVal === undefined) {
2452		el.className = name;
2453	} else {
2454		// in case of SVG element
2455		el.className.baseVal = name;
2456	}
2457}
2458
2459// @function getClass(el: HTMLElement): String
2460// Returns the element's class.
2461function getClass(el) {
2462	// Check if the element is an SVGElementInstance and use the correspondingElement instead
2463	// (Required for linked SVG elements in IE11.)
2464	if (el.correspondingElement) {
2465		el = el.correspondingElement;
2466	}
2467	return el.className.baseVal === undefined ? el.className : el.className.baseVal;
2468}
2469
2470// @function setOpacity(el: HTMLElement, opacity: Number)
2471// Set the opacity of an element (including old IE support).
2472// `opacity` must be a number from `0` to `1`.
2473function setOpacity(el, value) {
2474	if ('opacity' in el.style) {
2475		el.style.opacity = value;
2476	} else if ('filter' in el.style) {
2477		_setOpacityIE(el, value);
2478	}
2479}
2480
2481function _setOpacityIE(el, value) {
2482	var filter = false,
2483	    filterName = 'DXImageTransform.Microsoft.Alpha';
2484
2485	// filters collection throws an error if we try to retrieve a filter that doesn't exist
2486	try {
2487		filter = el.filters.item(filterName);
2488	} catch (e) {
2489		// don't set opacity to 1 if we haven't already set an opacity,
2490		// it isn't needed and breaks transparent pngs.
2491		if (value === 1) { return; }
2492	}
2493
2494	value = Math.round(value * 100);
2495
2496	if (filter) {
2497		filter.Enabled = (value !== 100);
2498		filter.Opacity = value;
2499	} else {
2500		el.style.filter += ' progid:' + filterName + '(opacity=' + value + ')';
2501	}
2502}
2503
2504// @function testProp(props: String[]): String|false
2505// Goes through the array of style names and returns the first name
2506// that is a valid style name for an element. If no such name is found,
2507// it returns false. Useful for vendor-prefixed styles like `transform`.
2508function testProp(props) {
2509	var style = document.documentElement.style;
2510
2511	for (var i = 0; i < props.length; i++) {
2512		if (props[i] in style) {
2513			return props[i];
2514		}
2515	}
2516	return false;
2517}
2518
2519// @function setTransform(el: HTMLElement, offset: Point, scale?: Number)
2520// Resets the 3D CSS transform of `el` so it is translated by `offset` pixels
2521// and optionally scaled by `scale`. Does not have an effect if the
2522// browser doesn't support 3D CSS transforms.
2523function setTransform(el, offset, scale) {
2524	var pos = offset || new Point(0, 0);
2525
2526	el.style[TRANSFORM] =
2527		(Browser.ie3d ?
2528			'translate(' + pos.x + 'px,' + pos.y + 'px)' :
2529			'translate3d(' + pos.x + 'px,' + pos.y + 'px,0)') +
2530		(scale ? ' scale(' + scale + ')' : '');
2531}
2532
2533// @function setPosition(el: HTMLElement, position: Point)
2534// Sets the position of `el` to coordinates specified by `position`,
2535// using CSS translate or top/left positioning depending on the browser
2536// (used by Leaflet internally to position its layers).
2537function setPosition(el, point) {
2538
2539	/*eslint-disable */
2540	el._leaflet_pos = point;
2541	/* eslint-enable */
2542
2543	if (Browser.any3d) {
2544		setTransform(el, point);
2545	} else {
2546		el.style.left = point.x + 'px';
2547		el.style.top = point.y + 'px';
2548	}
2549}
2550
2551// @function getPosition(el: HTMLElement): Point
2552// Returns the coordinates of an element previously positioned with setPosition.
2553function getPosition(el) {
2554	// this method is only used for elements previously positioned using setPosition,
2555	// so it's safe to cache the position for performance
2556
2557	return el._leaflet_pos || new Point(0, 0);
2558}
2559
2560// @function disableTextSelection()
2561// Prevents the user from generating `selectstart` DOM events, usually generated
2562// when the user drags the mouse through a page with text. Used internally
2563// by Leaflet to override the behaviour of any click-and-drag interaction on
2564// the map. Affects drag interactions on the whole document.
2565
2566// @function enableTextSelection()
2567// Cancels the effects of a previous [`L.DomUtil.disableTextSelection`](#domutil-disabletextselection).
2568var disableTextSelection;
2569var enableTextSelection;
2570var _userSelect;
2571if ('onselectstart' in document) {
2572	disableTextSelection = function () {
2573		on(window, 'selectstart', preventDefault);
2574	};
2575	enableTextSelection = function () {
2576		off(window, 'selectstart', preventDefault);
2577	};
2578} else {
2579	var userSelectProperty = testProp(
2580		['userSelect', 'WebkitUserSelect', 'OUserSelect', 'MozUserSelect', 'msUserSelect']);
2581
2582	disableTextSelection = function () {
2583		if (userSelectProperty) {
2584			var style = document.documentElement.style;
2585			_userSelect = style[userSelectProperty];
2586			style[userSelectProperty] = 'none';
2587		}
2588	};
2589	enableTextSelection = function () {
2590		if (userSelectProperty) {
2591			document.documentElement.style[userSelectProperty] = _userSelect;
2592			_userSelect = undefined;
2593		}
2594	};
2595}
2596
2597// @function disableImageDrag()
2598// As [`L.DomUtil.disableTextSelection`](#domutil-disabletextselection), but
2599// for `dragstart` DOM events, usually generated when the user drags an image.
2600function disableImageDrag() {
2601	on(window, 'dragstart', preventDefault);
2602}
2603
2604// @function enableImageDrag()
2605// Cancels the effects of a previous [`L.DomUtil.disableImageDrag`](#domutil-disabletextselection).
2606function enableImageDrag() {
2607	off(window, 'dragstart', preventDefault);
2608}
2609
2610var _outlineElement, _outlineStyle;
2611// @function preventOutline(el: HTMLElement)
2612// Makes the [outline](https://developer.mozilla.org/docs/Web/CSS/outline)
2613// of the element `el` invisible. Used internally by Leaflet to prevent
2614// focusable elements from displaying an outline when the user performs a
2615// drag interaction on them.
2616function preventOutline(element) {
2617	while (element.tabIndex === -1) {
2618		element = element.parentNode;
2619	}
2620	if (!element.style) { return; }
2621	restoreOutline();
2622	_outlineElement = element;
2623	_outlineStyle = element.style.outlineStyle;
2624	element.style.outlineStyle = 'none';
2625	on(window, 'keydown', restoreOutline);
2626}
2627
2628// @function restoreOutline()
2629// Cancels the effects of a previous [`L.DomUtil.preventOutline`]().
2630function restoreOutline() {
2631	if (!_outlineElement) { return; }
2632	_outlineElement.style.outlineStyle = _outlineStyle;
2633	_outlineElement = undefined;
2634	_outlineStyle = undefined;
2635	off(window, 'keydown', restoreOutline);
2636}
2637
2638// @function getSizedParentNode(el: HTMLElement): HTMLElement
2639// Finds the closest parent node which size (width and height) is not null.
2640function getSizedParentNode(element) {
2641	do {
2642		element = element.parentNode;
2643	} while ((!element.offsetWidth || !element.offsetHeight) && element !== document.body);
2644	return element;
2645}
2646
2647// @function getScale(el: HTMLElement): Object
2648// Computes the CSS scale currently applied on the element.
2649// Returns an object with `x` and `y` members as horizontal and vertical scales respectively,
2650// and `boundingClientRect` as the result of [`getBoundingClientRect()`](https://developer.mozilla.org/en-US/docs/Web/API/Element/getBoundingClientRect).
2651function getScale(element) {
2652	var rect = element.getBoundingClientRect(); // Read-only in old browsers.
2653
2654	return {
2655		x: rect.width / element.offsetWidth || 1,
2656		y: rect.height / element.offsetHeight || 1,
2657		boundingClientRect: rect
2658	};
2659}
2660
2661var DomUtil = {
2662  __proto__: null,
2663  TRANSFORM: TRANSFORM,
2664  TRANSITION: TRANSITION,
2665  TRANSITION_END: TRANSITION_END,
2666  get: get,
2667  getStyle: getStyle,
2668  create: create$1,
2669  remove: remove,
2670  empty: empty,
2671  toFront: toFront,
2672  toBack: toBack,
2673  hasClass: hasClass,
2674  addClass: addClass,
2675  removeClass: removeClass,
2676  setClass: setClass,
2677  getClass: getClass,
2678  setOpacity: setOpacity,
2679  testProp: testProp,
2680  setTransform: setTransform,
2681  setPosition: setPosition,
2682  getPosition: getPosition,
2683  get disableTextSelection () { return disableTextSelection; },
2684  get enableTextSelection () { return enableTextSelection; },
2685  disableImageDrag: disableImageDrag,
2686  enableImageDrag: enableImageDrag,
2687  preventOutline: preventOutline,
2688  restoreOutline: restoreOutline,
2689  getSizedParentNode: getSizedParentNode,
2690  getScale: getScale
2691};
2692
2693/*
2694 * @namespace DomEvent
2695 * Utility functions to work with the [DOM events](https://developer.mozilla.org/docs/Web/API/Event), used by Leaflet internally.
2696 */
2697
2698// Inspired by John Resig, Dean Edwards and YUI addEvent implementations.
2699
2700// @function on(el: HTMLElement, types: String, fn: Function, context?: Object): this
2701// Adds a listener function (`fn`) to a particular DOM event type of the
2702// element `el`. You can optionally specify the context of the listener
2703// (object the `this` keyword will point to). You can also pass several
2704// space-separated types (e.g. `'click dblclick'`).
2705
2706// @alternative
2707// @function on(el: HTMLElement, eventMap: Object, context?: Object): this
2708// Adds a set of type/listener pairs, e.g. `{click: onClick, mousemove: onMouseMove}`
2709function on(obj, types, fn, context) {
2710
2711	if (types && typeof types === 'object') {
2712		for (var type in types) {
2713			addOne(obj, type, types[type], fn);
2714		}
2715	} else {
2716		types = splitWords(types);
2717
2718		for (var i = 0, len = types.length; i < len; i++) {
2719			addOne(obj, types[i], fn, context);
2720		}
2721	}
2722
2723	return this;
2724}
2725
2726var eventsKey = '_leaflet_events';
2727
2728// @function off(el: HTMLElement, types: String, fn: Function, context?: Object): this
2729// Removes a previously added listener function.
2730// Note that if you passed a custom context to on, you must pass the same
2731// context to `off` in order to remove the listener.
2732
2733// @alternative
2734// @function off(el: HTMLElement, eventMap: Object, context?: Object): this
2735// Removes a set of type/listener pairs, e.g. `{click: onClick, mousemove: onMouseMove}`
2736
2737// @alternative
2738// @function off(el: HTMLElement, types: String): this
2739// Removes all previously added listeners of given types.
2740
2741// @alternative
2742// @function off(el: HTMLElement): this
2743// Removes all previously added listeners from given HTMLElement
2744function off(obj, types, fn, context) {
2745
2746	if (arguments.length === 1) {
2747		batchRemove(obj);
2748		delete obj[eventsKey];
2749
2750	} else if (types && typeof types === 'object') {
2751		for (var type in types) {
2752			removeOne(obj, type, types[type], fn);
2753		}
2754
2755	} else {
2756		types = splitWords(types);
2757
2758		if (arguments.length === 2) {
2759			batchRemove(obj, function (type) {
2760				return indexOf(types, type) !== -1;
2761			});
2762		} else {
2763			for (var i = 0, len = types.length; i < len; i++) {
2764				removeOne(obj, types[i], fn, context);
2765			}
2766		}
2767	}
2768
2769	return this;
2770}
2771
2772function batchRemove(obj, filterFn) {
2773	for (var id in obj[eventsKey]) {
2774		var type = id.split(/\d/)[0];
2775		if (!filterFn || filterFn(type)) {
2776			removeOne(obj, type, null, null, id);
2777		}
2778	}
2779}
2780
2781var mouseSubst = {
2782	mouseenter: 'mouseover',
2783	mouseleave: 'mouseout',
2784	wheel: !('onwheel' in window) && 'mousewheel'
2785};
2786
2787function addOne(obj, type, fn, context) {
2788	var id = type + stamp(fn) + (context ? '_' + stamp(context) : '');
2789
2790	if (obj[eventsKey] && obj[eventsKey][id]) { return this; }
2791
2792	var handler = function (e) {
2793		return fn.call(context || obj, e || window.event);
2794	};
2795
2796	var originalHandler = handler;
2797
2798	if (!Browser.touchNative && Browser.pointer && type.indexOf('touch') === 0) {
2799		// Needs DomEvent.Pointer.js
2800		handler = addPointerListener(obj, type, handler);
2801
2802	} else if (Browser.touch && (type === 'dblclick')) {
2803		handler = addDoubleTapListener(obj, handler);
2804
2805	} else if ('addEventListener' in obj) {
2806
2807		if (type === 'touchstart' || type === 'touchmove' || type === 'wheel' ||  type === 'mousewheel') {
2808			obj.addEventListener(mouseSubst[type] || type, handler, Browser.passiveEvents ? {passive: false} : false);
2809
2810		} else if (type === 'mouseenter' || type === 'mouseleave') {
2811			handler = function (e) {
2812				e = e || window.event;
2813				if (isExternalTarget(obj, e)) {
2814					originalHandler(e);
2815				}
2816			};
2817			obj.addEventListener(mouseSubst[type], handler, false);
2818
2819		} else {
2820			obj.addEventListener(type, originalHandler, false);
2821		}
2822
2823	} else {
2824		obj.attachEvent('on' + type, handler);
2825	}
2826
2827	obj[eventsKey] = obj[eventsKey] || {};
2828	obj[eventsKey][id] = handler;
2829}
2830
2831function removeOne(obj, type, fn, context, id) {
2832	id = id || type + stamp(fn) + (context ? '_' + stamp(context) : '');
2833	var handler = obj[eventsKey] && obj[eventsKey][id];
2834
2835	if (!handler) { return this; }
2836
2837	if (!Browser.touchNative && Browser.pointer && type.indexOf('touch') === 0) {
2838		removePointerListener(obj, type, handler);
2839
2840	} else if (Browser.touch && (type === 'dblclick')) {
2841		removeDoubleTapListener(obj, handler);
2842
2843	} else if ('removeEventListener' in obj) {
2844
2845		obj.removeEventListener(mouseSubst[type] || type, handler, false);
2846
2847	} else {
2848		obj.detachEvent('on' + type, handler);
2849	}
2850
2851	obj[eventsKey][id] = null;
2852}
2853
2854// @function stopPropagation(ev: DOMEvent): this
2855// Stop the given event from propagation to parent elements. Used inside the listener functions:
2856// ```js
2857// L.DomEvent.on(div, 'click', function (ev) {
2858// 	L.DomEvent.stopPropagation(ev);
2859// });
2860// ```
2861function stopPropagation(e) {
2862
2863	if (e.stopPropagation) {
2864		e.stopPropagation();
2865	} else if (e.originalEvent) {  // In case of Leaflet event.
2866		e.originalEvent._stopped = true;
2867	} else {
2868		e.cancelBubble = true;
2869	}
2870
2871	return this;
2872}
2873
2874// @function disableScrollPropagation(el: HTMLElement): this
2875// Adds `stopPropagation` to the element's `'wheel'` events (plus browser variants).
2876function disableScrollPropagation(el) {
2877	addOne(el, 'wheel', stopPropagation);
2878	return this;
2879}
2880
2881// @function disableClickPropagation(el: HTMLElement): this
2882// Adds `stopPropagation` to the element's `'click'`, `'dblclick'`, `'contextmenu'`,
2883// `'mousedown'` and `'touchstart'` events (plus browser variants).
2884function disableClickPropagation(el) {
2885	on(el, 'mousedown touchstart dblclick contextmenu', stopPropagation);
2886	el['_leaflet_disable_click'] = true;
2887	return this;
2888}
2889
2890// @function preventDefault(ev: DOMEvent): this
2891// Prevents the default action of the DOM Event `ev` from happening (such as
2892// following a link in the href of the a element, or doing a POST request
2893// with page reload when a `<form>` is submitted).
2894// Use it inside listener functions.
2895function preventDefault(e) {
2896	if (e.preventDefault) {
2897		e.preventDefault();
2898	} else {
2899		e.returnValue = false;
2900	}
2901	return this;
2902}
2903
2904// @function stop(ev: DOMEvent): this
2905// Does `stopPropagation` and `preventDefault` at the same time.
2906function stop(e) {
2907	preventDefault(e);
2908	stopPropagation(e);
2909	return this;
2910}
2911
2912// @function getPropagationPath(ev: DOMEvent): Array
2913// Compatibility polyfill for [`Event.composedPath()`](https://developer.mozilla.org/en-US/docs/Web/API/Event/composedPath).
2914// Returns an array containing the `HTMLElement`s that the given DOM event
2915// should propagate to (if not stopped).
2916function getPropagationPath(ev) {
2917	if (ev.composedPath) {
2918		return ev.composedPath();
2919	}
2920
2921	var path = [];
2922	var el = ev.target;
2923
2924	while (el) {
2925		path.push(el);
2926		el = el.parentNode;
2927	}
2928	return path;
2929}
2930
2931
2932// @function getMousePosition(ev: DOMEvent, container?: HTMLElement): Point
2933// Gets normalized mouse position from a DOM event relative to the
2934// `container` (border excluded) or to the whole page if not specified.
2935function getMousePosition(e, container) {
2936	if (!container) {
2937		return new Point(e.clientX, e.clientY);
2938	}
2939
2940	var scale = getScale(container),
2941	    offset = scale.boundingClientRect; // left and top  values are in page scale (like the event clientX/Y)
2942
2943	return new Point(
2944		// offset.left/top values are in page scale (like clientX/Y),
2945		// whereas clientLeft/Top (border width) values are the original values (before CSS scale applies).
2946		(e.clientX - offset.left) / scale.x - container.clientLeft,
2947		(e.clientY - offset.top) / scale.y - container.clientTop
2948	);
2949}
2950
2951
2952//  except , Safari and
2953// We need double the scroll pixels (see #7403 and #4538) for all Browsers
2954// except OSX (Mac) -> 3x, Chrome running on Linux 1x
2955
2956var wheelPxFactor =
2957	(Browser.linux && Browser.chrome) ? window.devicePixelRatio :
2958	Browser.mac ? window.devicePixelRatio * 3 :
2959	window.devicePixelRatio > 0 ? 2 * window.devicePixelRatio : 1;
2960// @function getWheelDelta(ev: DOMEvent): Number
2961// Gets normalized wheel delta from a wheel DOM event, in vertical
2962// pixels scrolled (negative if scrolling down).
2963// Events from pointing devices without precise scrolling are mapped to
2964// a best guess of 60 pixels.
2965function getWheelDelta(e) {
2966	return (Browser.edge) ? e.wheelDeltaY / 2 : // Don't trust window-geometry-based delta
2967	       (e.deltaY && e.deltaMode === 0) ? -e.deltaY / wheelPxFactor : // Pixels
2968	       (e.deltaY && e.deltaMode === 1) ? -e.deltaY * 20 : // Lines
2969	       (e.deltaY && e.deltaMode === 2) ? -e.deltaY * 60 : // Pages
2970	       (e.deltaX || e.deltaZ) ? 0 :	// Skip horizontal/depth wheel events
2971	       e.wheelDelta ? (e.wheelDeltaY || e.wheelDelta) / 2 : // Legacy IE pixels
2972	       (e.detail && Math.abs(e.detail) < 32765) ? -e.detail * 20 : // Legacy Moz lines
2973	       e.detail ? e.detail / -32765 * 60 : // Legacy Moz pages
2974	       0;
2975}
2976
2977// check if element really left/entered the event target (for mouseenter/mouseleave)
2978function isExternalTarget(el, e) {
2979
2980	var related = e.relatedTarget;
2981
2982	if (!related) { return true; }
2983
2984	try {
2985		while (related && (related !== el)) {
2986			related = related.parentNode;
2987		}
2988	} catch (err) {
2989		return false;
2990	}
2991	return (related !== el);
2992}
2993
2994var DomEvent = {
2995  __proto__: null,
2996  on: on,
2997  off: off,
2998  stopPropagation: stopPropagation,
2999  disableScrollPropagation: disableScrollPropagation,
3000  disableClickPropagation: disableClickPropagation,
3001  preventDefault: preventDefault,
3002  stop: stop,
3003  getPropagationPath: getPropagationPath,
3004  getMousePosition: getMousePosition,
3005  getWheelDelta: getWheelDelta,
3006  isExternalTarget: isExternalTarget,
3007  addListener: on,
3008  removeListener: off
3009};
3010
3011/*
3012 * @class PosAnimation
3013 * @aka L.PosAnimation
3014 * @inherits Evented
3015 * Used internally for panning animations, utilizing CSS3 Transitions for modern browsers and a timer fallback for IE6-9.
3016 *
3017 * @example
3018 * ```js
3019 * var myPositionMarker = L.marker([48.864716, 2.294694]).addTo(map);
3020 *
3021 * myPositionMarker.on("click", function() {
3022 * 	var pos = map.latLngToLayerPoint(myPositionMarker.getLatLng());
3023 * 	pos.y -= 25;
3024 * 	var fx = new L.PosAnimation();
3025 *
3026 * 	fx.once('end',function() {
3027 * 		pos.y += 25;
3028 * 		fx.run(myPositionMarker._icon, pos, 0.8);
3029 * 	});
3030 *
3031 * 	fx.run(myPositionMarker._icon, pos, 0.3);
3032 * });
3033 *
3034 * ```
3035 *
3036 * @constructor L.PosAnimation()
3037 * Creates a `PosAnimation` object.
3038 *
3039 */
3040
3041var PosAnimation = Evented.extend({
3042
3043	// @method run(el: HTMLElement, newPos: Point, duration?: Number, easeLinearity?: Number)
3044	// Run an animation of a given element to a new position, optionally setting
3045	// duration in seconds (`0.25` by default) and easing linearity factor (3rd
3046	// argument of the [cubic bezier curve](https://cubic-bezier.com/#0,0,.5,1),
3047	// `0.5` by default).
3048	run: function (el, newPos, duration, easeLinearity) {
3049		this.stop();
3050
3051		this._el = el;
3052		this._inProgress = true;
3053		this._duration = duration || 0.25;
3054		this._easeOutPower = 1 / Math.max(easeLinearity || 0.5, 0.2);
3055
3056		this._startPos = getPosition(el);
3057		this._offset = newPos.subtract(this._startPos);
3058		this._startTime = +new Date();
3059
3060		// @event start: Event
3061		// Fired when the animation starts
3062		this.fire('start');
3063
3064		this._animate();
3065	},
3066
3067	// @method stop()
3068	// Stops the animation (if currently running).
3069	stop: function () {
3070		if (!this._inProgress) { return; }
3071
3072		this._step(true);
3073		this._complete();
3074	},
3075
3076	_animate: function () {
3077		// animation loop
3078		this._animId = requestAnimFrame(this._animate, this);
3079		this._step();
3080	},
3081
3082	_step: function (round) {
3083		var elapsed = (+new Date()) - this._startTime,
3084		    duration = this._duration * 1000;
3085
3086		if (elapsed < duration) {
3087			this._runFrame(this._easeOut(elapsed / duration), round);
3088		} else {
3089			this._runFrame(1);
3090			this._complete();
3091		}
3092	},
3093
3094	_runFrame: function (progress, round) {
3095		var pos = this._startPos.add(this._offset.multiplyBy(progress));
3096		if (round) {
3097			pos._round();
3098		}
3099		setPosition(this._el, pos);
3100
3101		// @event step: Event
3102		// Fired continuously during the animation.
3103		this.fire('step');
3104	},
3105
3106	_complete: function () {
3107		cancelAnimFrame(this._animId);
3108
3109		this._inProgress = false;
3110		// @event end: Event
3111		// Fired when the animation ends.
3112		this.fire('end');
3113	},
3114
3115	_easeOut: function (t) {
3116		return 1 - Math.pow(1 - t, this._easeOutPower);
3117	}
3118});
3119
3120/*
3121 * @class Map
3122 * @aka L.Map
3123 * @inherits Evented
3124 *
3125 * The central class of the API — it is used to create a map on a page and manipulate it.
3126 *
3127 * @example
3128 *
3129 * ```js
3130 * // initialize the map on the "map" div with a given center and zoom
3131 * var map = L.map('map', {
3132 * 	center: [51.505, -0.09],
3133 * 	zoom: 13
3134 * });
3135 * ```
3136 *
3137 */
3138
3139var Map = Evented.extend({
3140
3141	options: {
3142		// @section Map State Options
3143		// @option crs: CRS = L.CRS.EPSG3857
3144		// The [Coordinate Reference System](#crs) to use. Don't change this if you're not
3145		// sure what it means.
3146		crs: EPSG3857,
3147
3148		// @option center: LatLng = undefined
3149		// Initial geographic center of the map
3150		center: undefined,
3151
3152		// @option zoom: Number = undefined
3153		// Initial map zoom level
3154		zoom: undefined,
3155
3156		// @option minZoom: Number = *
3157		// Minimum zoom level of the map.
3158		// If not specified and at least one `GridLayer` or `TileLayer` is in the map,
3159		// the lowest of their `minZoom` options will be used instead.
3160		minZoom: undefined,
3161
3162		// @option maxZoom: Number = *
3163		// Maximum zoom level of the map.
3164		// If not specified and at least one `GridLayer` or `TileLayer` is in the map,
3165		// the highest of their `maxZoom` options will be used instead.
3166		maxZoom: undefined,
3167
3168		// @option layers: Layer[] = []
3169		// Array of layers that will be added to the map initially
3170		layers: [],
3171
3172		// @option maxBounds: LatLngBounds = null
3173		// When this option is set, the map restricts the view to the given
3174		// geographical bounds, bouncing the user back if the user tries to pan
3175		// outside the view. To set the restriction dynamically, use
3176		// [`setMaxBounds`](#map-setmaxbounds) method.
3177		maxBounds: undefined,
3178
3179		// @option renderer: Renderer = *
3180		// The default method for drawing vector layers on the map. `L.SVG`
3181		// or `L.Canvas` by default depending on browser support.
3182		renderer: undefined,
3183
3184
3185		// @section Animation Options
3186		// @option zoomAnimation: Boolean = true
3187		// Whether the map zoom animation is enabled. By default it's enabled
3188		// in all browsers that support CSS3 Transitions except Android.
3189		zoomAnimation: true,
3190
3191		// @option zoomAnimationThreshold: Number = 4
3192		// Won't animate zoom if the zoom difference exceeds this value.
3193		zoomAnimationThreshold: 4,
3194
3195		// @option fadeAnimation: Boolean = true
3196		// Whether the tile fade animation is enabled. By default it's enabled
3197		// in all browsers that support CSS3 Transitions except Android.
3198		fadeAnimation: true,
3199
3200		// @option markerZoomAnimation: Boolean = true
3201		// Whether markers animate their zoom with the zoom animation, if disabled
3202		// they will disappear for the length of the animation. By default it's
3203		// enabled in all browsers that support CSS3 Transitions except Android.
3204		markerZoomAnimation: true,
3205
3206		// @option transform3DLimit: Number = 2^23
3207		// Defines the maximum size of a CSS translation transform. The default
3208		// value should not be changed unless a web browser positions layers in
3209		// the wrong place after doing a large `panBy`.
3210		transform3DLimit: 8388608, // Precision limit of a 32-bit float
3211
3212		// @section Interaction Options
3213		// @option zoomSnap: Number = 1
3214		// Forces the map's zoom level to always be a multiple of this, particularly
3215		// right after a [`fitBounds()`](#map-fitbounds) or a pinch-zoom.
3216		// By default, the zoom level snaps to the nearest integer; lower values
3217		// (e.g. `0.5` or `0.1`) allow for greater granularity. A value of `0`
3218		// means the zoom level will not be snapped after `fitBounds` or a pinch-zoom.
3219		zoomSnap: 1,
3220
3221		// @option zoomDelta: Number = 1
3222		// Controls how much the map's zoom level will change after a
3223		// [`zoomIn()`](#map-zoomin), [`zoomOut()`](#map-zoomout), pressing `+`
3224		// or `-` on the keyboard, or using the [zoom controls](#control-zoom).
3225		// Values smaller than `1` (e.g. `0.5`) allow for greater granularity.
3226		zoomDelta: 1,
3227
3228		// @option trackResize: Boolean = true
3229		// Whether the map automatically handles browser window resize to update itself.
3230		trackResize: true
3231	},
3232
3233	initialize: function (id, options) { // (HTMLElement or String, Object)
3234		options = setOptions(this, options);
3235
3236		// Make sure to assign internal flags at the beginning,
3237		// to avoid inconsistent state in some edge cases.
3238		this._handlers = [];
3239		this._layers = {};
3240		this._zoomBoundLayers = {};
3241		this._sizeChanged = true;
3242
3243		this._initContainer(id);
3244		this._initLayout();
3245
3246		// hack for https://github.com/Leaflet/Leaflet/issues/1980
3247		this._onResize = bind(this._onResize, this);
3248
3249		this._initEvents();
3250
3251		if (options.maxBounds) {
3252			this.setMaxBounds(options.maxBounds);
3253		}
3254
3255		if (options.zoom !== undefined) {
3256			this._zoom = this._limitZoom(options.zoom);
3257		}
3258
3259		if (options.center && options.zoom !== undefined) {
3260			this.setView(toLatLng(options.center), options.zoom, {reset: true});
3261		}
3262
3263		this.callInitHooks();
3264
3265		// don't animate on browsers without hardware-accelerated transitions or old Android/Opera
3266		this._zoomAnimated = TRANSITION && Browser.any3d && !Browser.mobileOpera &&
3267				this.options.zoomAnimation;
3268
3269		// zoom transitions run with the same duration for all layers, so if one of transitionend events
3270		// happens after starting zoom animation (propagating to the map pane), we know that it ended globally
3271		if (this._zoomAnimated) {
3272			this._createAnimProxy();
3273			on(this._proxy, TRANSITION_END, this._catchTransitionEnd, this);
3274		}
3275
3276		this._addLayers(this.options.layers);
3277	},
3278
3279
3280	// @section Methods for modifying map state
3281
3282	// @method setView(center: LatLng, zoom: Number, options?: Zoom/pan options): this
3283	// Sets the view of the map (geographical center and zoom) with the given
3284	// animation options.
3285	setView: function (center, zoom, options) {
3286
3287		zoom = zoom === undefined ? this._zoom : this._limitZoom(zoom);
3288		center = this._limitCenter(toLatLng(center), zoom, this.options.maxBounds);
3289		options = options || {};
3290
3291		this._stop();
3292
3293		if (this._loaded && !options.reset && options !== true) {
3294
3295			if (options.animate !== undefined) {
3296				options.zoom = extend({animate: options.animate}, options.zoom);
3297				options.pan = extend({animate: options.animate, duration: options.duration}, options.pan);
3298			}
3299
3300			// try animating pan or zoom
3301			var moved = (this._zoom !== zoom) ?
3302				this._tryAnimatedZoom && this._tryAnimatedZoom(center, zoom, options.zoom) :
3303				this._tryAnimatedPan(center, options.pan);
3304
3305			if (moved) {
3306				// prevent resize handler call, the view will refresh after animation anyway
3307				clearTimeout(this._sizeTimer);
3308				return this;
3309			}
3310		}
3311
3312		// animation didn't start, just reset the map view
3313		this._resetView(center, zoom, options.pan && options.pan.noMoveStart);
3314
3315		return this;
3316	},
3317
3318	// @method setZoom(zoom: Number, options?: Zoom/pan options): this
3319	// Sets the zoom of the map.
3320	setZoom: function (zoom, options) {
3321		if (!this._loaded) {
3322			this._zoom = zoom;
3323			return this;
3324		}
3325		return this.setView(this.getCenter(), zoom, {zoom: options});
3326	},
3327
3328	// @method zoomIn(delta?: Number, options?: Zoom options): this
3329	// Increases the zoom of the map by `delta` ([`zoomDelta`](#map-zoomdelta) by default).
3330	zoomIn: function (delta, options) {
3331		delta = delta || (Browser.any3d ? this.options.zoomDelta : 1);
3332		return this.setZoom(this._zoom + delta, options);
3333	},
3334
3335	// @method zoomOut(delta?: Number, options?: Zoom options): this
3336	// Decreases the zoom of the map by `delta` ([`zoomDelta`](#map-zoomdelta) by default).
3337	zoomOut: function (delta, options) {
3338		delta = delta || (Browser.any3d ? this.options.zoomDelta : 1);
3339		return this.setZoom(this._zoom - delta, options);
3340	},
3341
3342	// @method setZoomAround(latlng: LatLng, zoom: Number, options: Zoom options): this
3343	// Zooms the map while keeping a specified geographical point on the map
3344	// stationary (e.g. used internally for scroll zoom and double-click zoom).
3345	// @alternative
3346	// @method setZoomAround(offset: Point, zoom: Number, options: Zoom options): this
3347	// Zooms the map while keeping a specified pixel on the map (relative to the top-left corner) stationary.
3348	setZoomAround: function (latlng, zoom, options) {
3349		var scale = this.getZoomScale(zoom),
3350		    viewHalf = this.getSize().divideBy(2),
3351		    containerPoint = latlng instanceof Point ? latlng : this.latLngToContainerPoint(latlng),
3352
3353		    centerOffset = containerPoint.subtract(viewHalf).multiplyBy(1 - 1 / scale),
3354		    newCenter = this.containerPointToLatLng(viewHalf.add(centerOffset));
3355
3356		return this.setView(newCenter, zoom, {zoom: options});
3357	},
3358
3359	_getBoundsCenterZoom: function (bounds, options) {
3360
3361		options = options || {};
3362		bounds = bounds.getBounds ? bounds.getBounds() : toLatLngBounds(bounds);
3363
3364		var paddingTL = toPoint(options.paddingTopLeft || options.padding || [0, 0]),
3365		    paddingBR = toPoint(options.paddingBottomRight || options.padding || [0, 0]),
3366
3367		    zoom = this.getBoundsZoom(bounds, false, paddingTL.add(paddingBR));
3368
3369		zoom = (typeof options.maxZoom === 'number') ? Math.min(options.maxZoom, zoom) : zoom;
3370
3371		if (zoom === Infinity) {
3372			return {
3373				center: bounds.getCenter(),
3374				zoom: zoom
3375			};
3376		}
3377
3378		var paddingOffset = paddingBR.subtract(paddingTL).divideBy(2),
3379
3380		    swPoint = this.project(bounds.getSouthWest(), zoom),
3381		    nePoint = this.project(bounds.getNorthEast(), zoom),
3382		    center = this.unproject(swPoint.add(nePoint).divideBy(2).add(paddingOffset), zoom);
3383
3384		return {
3385			center: center,
3386			zoom: zoom
3387		};
3388	},
3389
3390	// @method fitBounds(bounds: LatLngBounds, options?: fitBounds options): this
3391	// Sets a map view that contains the given geographical bounds with the
3392	// maximum zoom level possible.
3393	fitBounds: function (bounds, options) {
3394
3395		bounds = toLatLngBounds(bounds);
3396
3397		if (!bounds.isValid()) {
3398			throw new Error('Bounds are not valid.');
3399		}
3400
3401		var target = this._getBoundsCenterZoom(bounds, options);
3402		return this.setView(target.center, target.zoom, options);
3403	},
3404
3405	// @method fitWorld(options?: fitBounds options): this
3406	// Sets a map view that mostly contains the whole world with the maximum
3407	// zoom level possible.
3408	fitWorld: function (options) {
3409		return this.fitBounds([[-90, -180], [90, 180]], options);
3410	},
3411
3412	// @method panTo(latlng: LatLng, options?: Pan options): this
3413	// Pans the map to a given center.
3414	panTo: function (center, options) { // (LatLng)
3415		return this.setView(center, this._zoom, {pan: options});
3416	},
3417
3418	// @method panBy(offset: Point, options?: Pan options): this
3419	// Pans the map by a given number of pixels (animated).
3420	panBy: function (offset, options) {
3421		offset = toPoint(offset).round();
3422		options = options || {};
3423
3424		if (!offset.x && !offset.y) {
3425			return this.fire('moveend');
3426		}
3427		// If we pan too far, Chrome gets issues with tiles
3428		// and makes them disappear or appear in the wrong place (slightly offset) #2602
3429		if (options.animate !== true && !this.getSize().contains(offset)) {
3430			this._resetView(this.unproject(this.project(this.getCenter()).add(offset)), this.getZoom());
3431			return this;
3432		}
3433
3434		if (!this._panAnim) {
3435			this._panAnim = new PosAnimation();
3436
3437			this._panAnim.on({
3438				'step': this._onPanTransitionStep,
3439				'end': this._onPanTransitionEnd
3440			}, this);
3441		}
3442
3443		// don't fire movestart if animating inertia
3444		if (!options.noMoveStart) {
3445			this.fire('movestart');
3446		}
3447
3448		// animate pan unless animate: false specified
3449		if (options.animate !== false) {
3450			addClass(this._mapPane, 'leaflet-pan-anim');
3451
3452			var newPos = this._getMapPanePos().subtract(offset).round();
3453			this._panAnim.run(this._mapPane, newPos, options.duration || 0.25, options.easeLinearity);
3454		} else {
3455			this._rawPanBy(offset);
3456			this.fire('move').fire('moveend');
3457		}
3458
3459		return this;
3460	},
3461
3462	// @method flyTo(latlng: LatLng, zoom?: Number, options?: Zoom/pan options): this
3463	// Sets the view of the map (geographical center and zoom) performing a smooth
3464	// pan-zoom animation.
3465	flyTo: function (targetCenter, targetZoom, options) {
3466
3467		options = options || {};
3468		if (options.animate === false || !Browser.any3d) {
3469			return this.setView(targetCenter, targetZoom, options);
3470		}
3471
3472		this._stop();
3473
3474		var from = this.project(this.getCenter()),
3475		    to = this.project(targetCenter),
3476		    size = this.getSize(),
3477		    startZoom = this._zoom;
3478
3479		targetCenter = toLatLng(targetCenter);
3480		targetZoom = targetZoom === undefined ? startZoom : targetZoom;
3481
3482		var w0 = Math.max(size.x, size.y),
3483		    w1 = w0 * this.getZoomScale(startZoom, targetZoom),
3484		    u1 = (to.distanceTo(from)) || 1,
3485		    rho = 1.42,
3486		    rho2 = rho * rho;
3487
3488		function r(i) {
3489			var s1 = i ? -1 : 1,
3490			    s2 = i ? w1 : w0,
3491			    t1 = w1 * w1 - w0 * w0 + s1 * rho2 * rho2 * u1 * u1,
3492			    b1 = 2 * s2 * rho2 * u1,
3493			    b = t1 / b1,
3494			    sq = Math.sqrt(b * b + 1) - b;
3495
3496			    // workaround for floating point precision bug when sq = 0, log = -Infinite,
3497			    // thus triggering an infinite loop in flyTo
3498			    var log = sq < 0.000000001 ? -18 : Math.log(sq);
3499
3500			return log;
3501		}
3502
3503		function sinh(n) { return (Math.exp(n) - Math.exp(-n)) / 2; }
3504		function cosh(n) { return (Math.exp(n) + Math.exp(-n)) / 2; }
3505		function tanh(n) { return sinh(n) / cosh(n); }
3506
3507		var r0 = r(0);
3508
3509		function w(s) { return w0 * (cosh(r0) / cosh(r0 + rho * s)); }
3510		function u(s) { return w0 * (cosh(r0) * tanh(r0 + rho * s) - sinh(r0)) / rho2; }
3511
3512		function easeOut(t) { return 1 - Math.pow(1 - t, 1.5); }
3513
3514		var start = Date.now(),
3515		    S = (r(1) - r0) / rho,
3516		    duration = options.duration ? 1000 * options.duration : 1000 * S * 0.8;
3517
3518		function frame() {
3519			var t = (Date.now() - start) / duration,
3520			    s = easeOut(t) * S;
3521
3522			if (t <= 1) {
3523				this._flyToFrame = requestAnimFrame(frame, this);
3524
3525				this._move(
3526					this.unproject(from.add(to.subtract(from).multiplyBy(u(s) / u1)), startZoom),
3527					this.getScaleZoom(w0 / w(s), startZoom),
3528					{flyTo: true});
3529
3530			} else {
3531				this
3532					._move(targetCenter, targetZoom)
3533					._moveEnd(true);
3534			}
3535		}
3536
3537		this._moveStart(true, options.noMoveStart);
3538
3539		frame.call(this);
3540		return this;
3541	},
3542
3543	// @method flyToBounds(bounds: LatLngBounds, options?: fitBounds options): this
3544	// Sets the view of the map with a smooth animation like [`flyTo`](#map-flyto),
3545	// but takes a bounds parameter like [`fitBounds`](#map-fitbounds).
3546	flyToBounds: function (bounds, options) {
3547		var target = this._getBoundsCenterZoom(bounds, options);
3548		return this.flyTo(target.center, target.zoom, options);
3549	},
3550
3551	// @method setMaxBounds(bounds: LatLngBounds): this
3552	// Restricts the map view to the given bounds (see the [maxBounds](#map-maxbounds) option).
3553	setMaxBounds: function (bounds) {
3554		bounds = toLatLngBounds(bounds);
3555
3556		if (this.listens('moveend', this._panInsideMaxBounds)) {
3557			this.off('moveend', this._panInsideMaxBounds);
3558		}
3559
3560		if (!bounds.isValid()) {
3561			this.options.maxBounds = null;
3562			return this;
3563		}
3564
3565		this.options.maxBounds = bounds;
3566
3567		if (this._loaded) {
3568			this._panInsideMaxBounds();
3569		}
3570
3571		return this.on('moveend', this._panInsideMaxBounds);
3572	},
3573
3574	// @method setMinZoom(zoom: Number): this
3575	// Sets the lower limit for the available zoom levels (see the [minZoom](#map-minzoom) option).
3576	setMinZoom: function (zoom) {
3577		var oldZoom = this.options.minZoom;
3578		this.options.minZoom = zoom;
3579
3580		if (this._loaded && oldZoom !== zoom) {
3581			this.fire('zoomlevelschange');
3582
3583			if (this.getZoom() < this.options.minZoom) {
3584				return this.setZoom(zoom);
3585			}
3586		}
3587
3588		return this;
3589	},
3590
3591	// @method setMaxZoom(zoom: Number): this
3592	// Sets the upper limit for the available zoom levels (see the [maxZoom](#map-maxzoom) option).
3593	setMaxZoom: function (zoom) {
3594		var oldZoom = this.options.maxZoom;
3595		this.options.maxZoom = zoom;
3596
3597		if (this._loaded && oldZoom !== zoom) {
3598			this.fire('zoomlevelschange');
3599
3600			if (this.getZoom() > this.options.maxZoom) {
3601				return this.setZoom(zoom);
3602			}
3603		}
3604
3605		return this;
3606	},
3607
3608	// @method panInsideBounds(bounds: LatLngBounds, options?: Pan options): this
3609	// 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.
3610	panInsideBounds: function (bounds, options) {
3611		this._enforcingBounds = true;
3612		var center = this.getCenter(),
3613		    newCenter = this._limitCenter(center, this._zoom, toLatLngBounds(bounds));
3614
3615		if (!center.equals(newCenter)) {
3616			this.panTo(newCenter, options);
3617		}
3618
3619		this._enforcingBounds = false;
3620		return this;
3621	},
3622
3623	// @method panInside(latlng: LatLng, options?: padding options): this
3624	// Pans the map the minimum amount to make the `latlng` visible. Use
3625	// padding options to fit the display to more restricted bounds.
3626	// If `latlng` is already within the (optionally padded) display bounds,
3627	// the map will not be panned.
3628	panInside: function (latlng, options) {
3629		options = options || {};
3630
3631		var paddingTL = toPoint(options.paddingTopLeft || options.padding || [0, 0]),
3632		    paddingBR = toPoint(options.paddingBottomRight || options.padding || [0, 0]),
3633		    pixelCenter = this.project(this.getCenter()),
3634		    pixelPoint = this.project(latlng),
3635		    pixelBounds = this.getPixelBounds(),
3636		    paddedBounds = toBounds([pixelBounds.min.add(paddingTL), pixelBounds.max.subtract(paddingBR)]),
3637		    paddedSize = paddedBounds.getSize();
3638
3639		if (!paddedBounds.contains(pixelPoint)) {
3640			this._enforcingBounds = true;
3641			var centerOffset = pixelPoint.subtract(paddedBounds.getCenter());
3642			var offset = paddedBounds.extend(pixelPoint).getSize().subtract(paddedSize);
3643			pixelCenter.x += centerOffset.x < 0 ? -offset.x : offset.x;
3644			pixelCenter.y += centerOffset.y < 0 ? -offset.y : offset.y;
3645			this.panTo(this.unproject(pixelCenter), options);
3646			this._enforcingBounds = false;
3647		}
3648		return this;
3649	},
3650
3651	// @method invalidateSize(options: Zoom/pan options): this
3652	// Checks if the map container size changed and updates the map if so —
3653	// call it after you've changed the map size dynamically, also animating
3654	// pan by default. If `options.pan` is `false`, panning will not occur.
3655	// If `options.debounceMoveend` is `true`, it will delay `moveend` event so
3656	// that it doesn't happen often even if the method is called many
3657	// times in a row.
3658
3659	// @alternative
3660	// @method invalidateSize(animate: Boolean): this
3661	// Checks if the map container size changed and updates the map if so —
3662	// call it after you've changed the map size dynamically, also animating
3663	// pan by default.
3664	invalidateSize: function (options) {
3665		if (!this._loaded) { return this; }
3666
3667		options = extend({
3668			animate: false,
3669			pan: true
3670		}, options === true ? {animate: true} : options);
3671
3672		var oldSize = this.getSize();
3673		this._sizeChanged = true;
3674		this._lastCenter = null;
3675
3676		var newSize = this.getSize(),
3677		    oldCenter = oldSize.divideBy(2).round(),
3678		    newCenter = newSize.divideBy(2).round(),
3679		    offset = oldCenter.subtract(newCenter);
3680
3681		if (!offset.x && !offset.y) { return this; }
3682
3683		if (options.animate && options.pan) {
3684			this.panBy(offset);
3685
3686		} else {
3687			if (options.pan) {
3688				this._rawPanBy(offset);
3689			}
3690
3691			this.fire('move');
3692
3693			if (options.debounceMoveend) {
3694				clearTimeout(this._sizeTimer);
3695				this._sizeTimer = setTimeout(bind(this.fire, this, 'moveend'), 200);
3696			} else {
3697				this.fire('moveend');
3698			}
3699		}
3700
3701		// @section Map state change events
3702		// @event resize: ResizeEvent
3703		// Fired when the map is resized.
3704		return this.fire('resize', {
3705			oldSize: oldSize,
3706			newSize: newSize
3707		});
3708	},
3709
3710	// @section Methods for modifying map state
3711	// @method stop(): this
3712	// Stops the currently running `panTo` or `flyTo` animation, if any.
3713	stop: function () {
3714		this.setZoom(this._limitZoom(this._zoom));
3715		if (!this.options.zoomSnap) {
3716			this.fire('viewreset');
3717		}
3718		return this._stop();
3719	},
3720
3721	// @section Geolocation methods
3722	// @method locate(options?: Locate options): this
3723	// Tries to locate the user using the Geolocation API, firing a [`locationfound`](#map-locationfound)
3724	// event with location data on success or a [`locationerror`](#map-locationerror) event on failure,
3725	// and optionally sets the map view to the user's location with respect to
3726	// detection accuracy (or to the world view if geolocation failed).
3727	// Note that, if your page doesn't use HTTPS, this method will fail in
3728	// modern browsers ([Chrome 50 and newer](https://sites.google.com/a/chromium.org/dev/Home/chromium-security/deprecating-powerful-features-on-insecure-origins))
3729	// See `Locate options` for more details.
3730	locate: function (options) {
3731
3732		options = this._locateOptions = extend({
3733			timeout: 10000,
3734			watch: false
3735			// setView: false
3736			// maxZoom: <Number>
3737			// maximumAge: 0
3738			// enableHighAccuracy: false
3739		}, options);
3740
3741		if (!('geolocation' in navigator)) {
3742			this._handleGeolocationError({
3743				code: 0,
3744				message: 'Geolocation not supported.'
3745			});
3746			return this;
3747		}
3748
3749		var onResponse = bind(this._handleGeolocationResponse, this),
3750		    onError = bind(this._handleGeolocationError, this);
3751
3752		if (options.watch) {
3753			this._locationWatchId =
3754			        navigator.geolocation.watchPosition(onResponse, onError, options);
3755		} else {
3756			navigator.geolocation.getCurrentPosition(onResponse, onError, options);
3757		}
3758		return this;
3759	},
3760
3761	// @method stopLocate(): this
3762	// Stops watching location previously initiated by `map.locate({watch: true})`
3763	// and aborts resetting the map view if map.locate was called with
3764	// `{setView: true}`.
3765	stopLocate: function () {
3766		if (navigator.geolocation && navigator.geolocation.clearWatch) {
3767			navigator.geolocation.clearWatch(this._locationWatchId);
3768		}
3769		if (this._locateOptions) {
3770			this._locateOptions.setView = false;
3771		}
3772		return this;
3773	},
3774
3775	_handleGeolocationError: function (error) {
3776		if (!this._container._leaflet_id) { return; }
3777
3778		var c = error.code,
3779		    message = error.message ||
3780		            (c === 1 ? 'permission denied' :
3781		            (c === 2 ? 'position unavailable' : 'timeout'));
3782
3783		if (this._locateOptions.setView && !this._loaded) {
3784			this.fitWorld();
3785		}
3786
3787		// @section Location events
3788		// @event locationerror: ErrorEvent
3789		// Fired when geolocation (using the [`locate`](#map-locate) method) failed.
3790		this.fire('locationerror', {
3791			code: c,
3792			message: 'Geolocation error: ' + message + '.'
3793		});
3794	},
3795
3796	_handleGeolocationResponse: function (pos) {
3797		if (!this._container._leaflet_id) { return; }
3798
3799		var lat = pos.coords.latitude,
3800		    lng = pos.coords.longitude,
3801		    latlng = new LatLng(lat, lng),
3802		    bounds = latlng.toBounds(pos.coords.accuracy * 2),
3803		    options = this._locateOptions;
3804
3805		if (options.setView) {
3806			var zoom = this.getBoundsZoom(bounds);
3807			this.setView(latlng, options.maxZoom ? Math.min(zoom, options.maxZoom) : zoom);
3808		}
3809
3810		var data = {
3811			latlng: latlng,
3812			bounds: bounds,
3813			timestamp: pos.timestamp
3814		};
3815
3816		for (var i in pos.coords) {
3817			if (typeof pos.coords[i] === 'number') {
3818				data[i] = pos.coords[i];
3819			}
3820		}
3821
3822		// @event locationfound: LocationEvent
3823		// Fired when geolocation (using the [`locate`](#map-locate) method)
3824		// went successfully.
3825		this.fire('locationfound', data);
3826	},
3827
3828	// TODO Appropriate docs section?
3829	// @section Other Methods
3830	// @method addHandler(name: String, HandlerClass: Function): this
3831	// Adds a new `Handler` to the map, given its name and constructor function.
3832	addHandler: function (name, HandlerClass) {
3833		if (!HandlerClass) { return this; }
3834
3835		var handler = this[name] = new HandlerClass(this);
3836
3837		this._handlers.push(handler);
3838
3839		if (this.options[name]) {
3840			handler.enable();
3841		}
3842
3843		return this;
3844	},
3845
3846	// @method remove(): this
3847	// Destroys the map and clears all related event listeners.
3848	remove: function () {
3849
3850		this._initEvents(true);
3851		if (this.options.maxBounds) { this.off('moveend', this._panInsideMaxBounds); }
3852
3853		if (this._containerId !== this._container._leaflet_id) {
3854			throw new Error('Map container is being reused by another instance');
3855		}
3856
3857		try {
3858			// throws error in IE6-8
3859			delete this._container._leaflet_id;
3860			delete this._containerId;
3861		} catch (e) {
3862			/*eslint-disable */
3863			this._container._leaflet_id = undefined;
3864			/* eslint-enable */
3865			this._containerId = undefined;
3866		}
3867
3868		if (this._locationWatchId !== undefined) {
3869			this.stopLocate();
3870		}
3871
3872		this._stop();
3873
3874		remove(this._mapPane);
3875
3876		if (this._clearControlPos) {
3877			this._clearControlPos();
3878		}
3879		if (this._resizeRequest) {
3880			cancelAnimFrame(this._resizeRequest);
3881			this._resizeRequest = null;
3882		}
3883
3884		this._clearHandlers();
3885
3886		if (this._loaded) {
3887			// @section Map state change events
3888			// @event unload: Event
3889			// Fired when the map is destroyed with [remove](#map-remove) method.
3890			this.fire('unload');
3891		}
3892
3893		var i;
3894		for (i in this._layers) {
3895			this._layers[i].remove();
3896		}
3897		for (i in this._panes) {
3898			remove(this._panes[i]);
3899		}
3900
3901		this._layers = [];
3902		this._panes = [];
3903		delete this._mapPane;
3904		delete this._renderer;
3905
3906		return this;
3907	},
3908
3909	// @section Other Methods
3910	// @method createPane(name: String, container?: HTMLElement): HTMLElement
3911	// Creates a new [map pane](#map-pane) with the given name if it doesn't exist already,
3912	// then returns it. The pane is created as a child of `container`, or
3913	// as a child of the main map pane if not set.
3914	createPane: function (name, container) {
3915		var className = 'leaflet-pane' + (name ? ' leaflet-' + name.replace('Pane', '') + '-pane' : ''),
3916		    pane = create$1('div', className, container || this._mapPane);
3917
3918		if (name) {
3919			this._panes[name] = pane;
3920		}
3921		return pane;
3922	},
3923
3924	// @section Methods for Getting Map State
3925
3926	// @method getCenter(): LatLng
3927	// Returns the geographical center of the map view
3928	getCenter: function () {
3929		this._checkIfLoaded();
3930
3931		if (this._lastCenter && !this._moved()) {
3932			return this._lastCenter.clone();
3933		}
3934		return this.layerPointToLatLng(this._getCenterLayerPoint());
3935	},
3936
3937	// @method getZoom(): Number
3938	// Returns the current zoom level of the map view
3939	getZoom: function () {
3940		return this._zoom;
3941	},
3942
3943	// @method getBounds(): LatLngBounds
3944	// Returns the geographical bounds visible in the current map view
3945	getBounds: function () {
3946		var bounds = this.getPixelBounds(),
3947		    sw = this.unproject(bounds.getBottomLeft()),
3948		    ne = this.unproject(bounds.getTopRight());
3949
3950		return new LatLngBounds(sw, ne);
3951	},
3952
3953	// @method getMinZoom(): Number
3954	// 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.
3955	getMinZoom: function () {
3956		return this.options.minZoom === undefined ? this._layersMinZoom || 0 : this.options.minZoom;
3957	},
3958
3959	// @method getMaxZoom(): Number
3960	// Returns the maximum zoom level of the map (if set in the `maxZoom` option of the map or of any layers).
3961	getMaxZoom: function () {
3962		return this.options.maxZoom === undefined ?
3963			(this._layersMaxZoom === undefined ? Infinity : this._layersMaxZoom) :
3964			this.options.maxZoom;
3965	},
3966
3967	// @method getBoundsZoom(bounds: LatLngBounds, inside?: Boolean, padding?: Point): Number
3968	// Returns the maximum zoom level on which the given bounds fit to the map
3969	// view in its entirety. If `inside` (optional) is set to `true`, the method
3970	// instead returns the minimum zoom level on which the map view fits into
3971	// the given bounds in its entirety.
3972	getBoundsZoom: function (bounds, inside, padding) { // (LatLngBounds[, Boolean, Point]) -> Number
3973		bounds = toLatLngBounds(bounds);
3974		padding = toPoint(padding || [0, 0]);
3975
3976		var zoom = this.getZoom() || 0,
3977		    min = this.getMinZoom(),
3978		    max = this.getMaxZoom(),
3979		    nw = bounds.getNorthWest(),
3980		    se = bounds.getSouthEast(),
3981		    size = this.getSize().subtract(padding),
3982		    boundsSize = toBounds(this.project(se, zoom), this.project(nw, zoom)).getSize(),
3983		    snap = Browser.any3d ? this.options.zoomSnap : 1,
3984		    scalex = size.x / boundsSize.x,
3985		    scaley = size.y / boundsSize.y,
3986		    scale = inside ? Math.max(scalex, scaley) : Math.min(scalex, scaley);
3987
3988		zoom = this.getScaleZoom(scale, zoom);
3989
3990		if (snap) {
3991			zoom = Math.round(zoom / (snap / 100)) * (snap / 100); // don't jump if within 1% of a snap level
3992			zoom = inside ? Math.ceil(zoom / snap) * snap : Math.floor(zoom / snap) * snap;
3993		}
3994
3995		return Math.max(min, Math.min(max, zoom));
3996	},
3997
3998	// @method getSize(): Point
3999	// Returns the current size of the map container (in pixels).
4000	getSize: function () {
4001		if (!this._size || this._sizeChanged) {
4002			this._size = new Point(
4003				this._container.clientWidth || 0,
4004				this._container.clientHeight || 0);
4005
4006			this._sizeChanged = false;
4007		}
4008		return this._size.clone();
4009	},
4010
4011	// @method getPixelBounds(): Bounds
4012	// Returns the bounds of the current map view in projected pixel
4013	// coordinates (sometimes useful in layer and overlay implementations).
4014	getPixelBounds: function (center, zoom) {
4015		var topLeftPoint = this._getTopLeftPoint(center, zoom);
4016		return new Bounds(topLeftPoint, topLeftPoint.add(this.getSize()));
4017	},
4018
4019	// TODO: Check semantics - isn't the pixel origin the 0,0 coord relative to
4020	// the map pane? "left point of the map layer" can be confusing, specially
4021	// since there can be negative offsets.
4022	// @method getPixelOrigin(): Point
4023	// Returns the projected pixel coordinates of the top left point of
4024	// the map layer (useful in custom layer and overlay implementations).
4025	getPixelOrigin: function () {
4026		this._checkIfLoaded();
4027		return this._pixelOrigin;
4028	},
4029
4030	// @method getPixelWorldBounds(zoom?: Number): Bounds
4031	// Returns the world's bounds in pixel coordinates for zoom level `zoom`.
4032	// If `zoom` is omitted, the map's current zoom level is used.
4033	getPixelWorldBounds: function (zoom) {
4034		return this.options.crs.getProjectedBounds(zoom === undefined ? this.getZoom() : zoom);
4035	},
4036
4037	// @section Other Methods
4038
4039	// @method getPane(pane: String|HTMLElement): HTMLElement
4040	// Returns a [map pane](#map-pane), given its name or its HTML element (its identity).
4041	getPane: function (pane) {
4042		return typeof pane === 'string' ? this._panes[pane] : pane;
4043	},
4044
4045	// @method getPanes(): Object
4046	// Returns a plain object containing the names of all [panes](#map-pane) as keys and
4047	// the panes as values.
4048	getPanes: function () {
4049		return this._panes;
4050	},
4051
4052	// @method getContainer: HTMLElement
4053	// Returns the HTML element that contains the map.
4054	getContainer: function () {
4055		return this._container;
4056	},
4057
4058
4059	// @section Conversion Methods
4060
4061	// @method getZoomScale(toZoom: Number, fromZoom: Number): Number
4062	// Returns the scale factor to be applied to a map transition from zoom level
4063	// `fromZoom` to `toZoom`. Used internally to help with zoom animations.
4064	getZoomScale: function (toZoom, fromZoom) {
4065		// TODO replace with universal implementation after refactoring projections
4066		var crs = this.options.crs;
4067		fromZoom = fromZoom === undefined ? this._zoom : fromZoom;
4068		return crs.scale(toZoom) / crs.scale(fromZoom);
4069	},
4070
4071	// @method getScaleZoom(scale: Number, fromZoom: Number): Number
4072	// Returns the zoom level that the map would end up at, if it is at `fromZoom`
4073	// level and everything is scaled by a factor of `scale`. Inverse of
4074	// [`getZoomScale`](#map-getZoomScale).
4075	getScaleZoom: function (scale, fromZoom) {
4076		var crs = this.options.crs;
4077		fromZoom = fromZoom === undefined ? this._zoom : fromZoom;
4078		var zoom = crs.zoom(scale * crs.scale(fromZoom));
4079		return isNaN(zoom) ? Infinity : zoom;
4080	},
4081
4082	// @method project(latlng: LatLng, zoom: Number): Point
4083	// Projects a geographical coordinate `LatLng` according to the projection
4084	// of the map's CRS, then scales it according to `zoom` and the CRS's
4085	// `Transformation`. The result is pixel coordinate relative to
4086	// the CRS origin.
4087	project: function (latlng, zoom) {
4088		zoom = zoom === undefined ? this._zoom : zoom;
4089		return this.options.crs.latLngToPoint(toLatLng(latlng), zoom);
4090	},
4091
4092	// @method unproject(point: Point, zoom: Number): LatLng
4093	// Inverse of [`project`](#map-project).
4094	unproject: function (point, zoom) {
4095		zoom = zoom === undefined ? this._zoom : zoom;
4096		return this.options.crs.pointToLatLng(toPoint(point), zoom);
4097	},
4098
4099	// @method layerPointToLatLng(point: Point): LatLng
4100	// Given a pixel coordinate relative to the [origin pixel](#map-getpixelorigin),
4101	// returns the corresponding geographical coordinate (for the current zoom level).
4102	layerPointToLatLng: function (point) {
4103		var projectedPoint = toPoint(point).add(this.getPixelOrigin());
4104		return this.unproject(projectedPoint);
4105	},
4106
4107	// @method latLngToLayerPoint(latlng: LatLng): Point
4108	// Given a geographical coordinate, returns the corresponding pixel coordinate
4109	// relative to the [origin pixel](#map-getpixelorigin).
4110	latLngToLayerPoint: function (latlng) {
4111		var projectedPoint = this.project(toLatLng(latlng))._round();
4112		return projectedPoint._subtract(this.getPixelOrigin());
4113	},
4114
4115	// @method wrapLatLng(latlng: LatLng): LatLng
4116	// Returns a `LatLng` where `lat` and `lng` has been wrapped according to the
4117	// map's CRS's `wrapLat` and `wrapLng` properties, if they are outside the
4118	// CRS's bounds.
4119	// By default this means longitude is wrapped around the dateline so its
4120	// value is between -180 and +180 degrees.
4121	wrapLatLng: function (latlng) {
4122		return this.options.crs.wrapLatLng(toLatLng(latlng));
4123	},
4124
4125	// @method wrapLatLngBounds(bounds: LatLngBounds): LatLngBounds
4126	// Returns a `LatLngBounds` with the same size as the given one, ensuring that
4127	// its center is within the CRS's bounds.
4128	// By default this means the center longitude is wrapped around the dateline so its
4129	// value is between -180 and +180 degrees, and the majority of the bounds
4130	// overlaps the CRS's bounds.
4131	wrapLatLngBounds: function (latlng) {
4132		return this.options.crs.wrapLatLngBounds(toLatLngBounds(latlng));
4133	},
4134
4135	// @method distance(latlng1: LatLng, latlng2: LatLng): Number
4136	// Returns the distance between two geographical coordinates according to
4137	// the map's CRS. By default this measures distance in meters.
4138	distance: function (latlng1, latlng2) {
4139		return this.options.crs.distance(toLatLng(latlng1), toLatLng(latlng2));
4140	},
4141
4142	// @method containerPointToLayerPoint(point: Point): Point
4143	// Given a pixel coordinate relative to the map container, returns the corresponding
4144	// pixel coordinate relative to the [origin pixel](#map-getpixelorigin).
4145	containerPointToLayerPoint: function (point) { // (Point)
4146		return toPoint(point).subtract(this._getMapPanePos());
4147	},
4148
4149	// @method layerPointToContainerPoint(point: Point): Point
4150	// Given a pixel coordinate relative to the [origin pixel](#map-getpixelorigin),
4151	// returns the corresponding pixel coordinate relative to the map container.
4152	layerPointToContainerPoint: function (point) { // (Point)
4153		return toPoint(point).add(this._getMapPanePos());
4154	},
4155
4156	// @method containerPointToLatLng(point: Point): LatLng
4157	// Given a pixel coordinate relative to the map container, returns
4158	// the corresponding geographical coordinate (for the current zoom level).
4159	containerPointToLatLng: function (point) {
4160		var layerPoint = this.containerPointToLayerPoint(toPoint(point));
4161		return this.layerPointToLatLng(layerPoint);
4162	},
4163
4164	// @method latLngToContainerPoint(latlng: LatLng): Point
4165	// Given a geographical coordinate, returns the corresponding pixel coordinate
4166	// relative to the map container.
4167	latLngToContainerPoint: function (latlng) {
4168		return this.layerPointToContainerPoint(this.latLngToLayerPoint(toLatLng(latlng)));
4169	},
4170
4171	// @method mouseEventToContainerPoint(ev: MouseEvent): Point
4172	// Given a MouseEvent object, returns the pixel coordinate relative to the
4173	// map container where the event took place.
4174	mouseEventToContainerPoint: function (e) {
4175		return getMousePosition(e, this._container);
4176	},
4177
4178	// @method mouseEventToLayerPoint(ev: MouseEvent): Point
4179	// Given a MouseEvent object, returns the pixel coordinate relative to
4180	// the [origin pixel](#map-getpixelorigin) where the event took place.
4181	mouseEventToLayerPoint: function (e) {
4182		return this.containerPointToLayerPoint(this.mouseEventToContainerPoint(e));
4183	},
4184
4185	// @method mouseEventToLatLng(ev: MouseEvent): LatLng
4186	// Given a MouseEvent object, returns geographical coordinate where the
4187	// event took place.
4188	mouseEventToLatLng: function (e) { // (MouseEvent)
4189		return this.layerPointToLatLng(this.mouseEventToLayerPoint(e));
4190	},
4191
4192
4193	// map initialization methods
4194
4195	_initContainer: function (id) {
4196		var container = this._container = get(id);
4197
4198		if (!container) {
4199			throw new Error('Map container not found.');
4200		} else if (container._leaflet_id) {
4201			throw new Error('Map container is already initialized.');
4202		}
4203
4204		on(container, 'scroll', this._onScroll, this);
4205		this._containerId = stamp(container);
4206	},
4207
4208	_initLayout: function () {
4209		var container = this._container;
4210
4211		this._fadeAnimated = this.options.fadeAnimation && Browser.any3d;
4212
4213		addClass(container, 'leaflet-container' +
4214			(Browser.touch ? ' leaflet-touch' : '') +
4215			(Browser.retina ? ' leaflet-retina' : '') +
4216			(Browser.ielt9 ? ' leaflet-oldie' : '') +
4217			(Browser.safari ? ' leaflet-safari' : '') +
4218			(this._fadeAnimated ? ' leaflet-fade-anim' : ''));
4219
4220		var position = getStyle(container, 'position');
4221
4222		if (position !== 'absolute' && position !== 'relative' && position !== 'fixed' && position !== 'sticky') {
4223			container.style.position = 'relative';
4224		}
4225
4226		this._initPanes();
4227
4228		if (this._initControlPos) {
4229			this._initControlPos();
4230		}
4231	},
4232
4233	_initPanes: function () {
4234		var panes = this._panes = {};
4235		this._paneRenderers = {};
4236
4237		// @section
4238		//
4239		// Panes are DOM elements used to control the ordering of layers on the map. You
4240		// can access panes with [`map.getPane`](#map-getpane) or
4241		// [`map.getPanes`](#map-getpanes) methods. New panes can be created with the
4242		// [`map.createPane`](#map-createpane) method.
4243		//
4244		// Every map has the following default panes that differ only in zIndex.
4245		//
4246		// @pane mapPane: HTMLElement = 'auto'
4247		// Pane that contains all other map panes
4248
4249		this._mapPane = this.createPane('mapPane', this._container);
4250		setPosition(this._mapPane, new Point(0, 0));
4251
4252		// @pane tilePane: HTMLElement = 200
4253		// Pane for `GridLayer`s and `TileLayer`s
4254		this.createPane('tilePane');
4255		// @pane overlayPane: HTMLElement = 400
4256		// Pane for vectors (`Path`s, like `Polyline`s and `Polygon`s), `ImageOverlay`s and `VideoOverlay`s
4257		this.createPane('overlayPane');
4258		// @pane shadowPane: HTMLElement = 500
4259		// Pane for overlay shadows (e.g. `Marker` shadows)
4260		this.createPane('shadowPane');
4261		// @pane markerPane: HTMLElement = 600
4262		// Pane for `Icon`s of `Marker`s
4263		this.createPane('markerPane');
4264		// @pane tooltipPane: HTMLElement = 650
4265		// Pane for `Tooltip`s.
4266		this.createPane('tooltipPane');
4267		// @pane popupPane: HTMLElement = 700
4268		// Pane for `Popup`s.
4269		this.createPane('popupPane');
4270
4271		if (!this.options.markerZoomAnimation) {
4272			addClass(panes.markerPane, 'leaflet-zoom-hide');
4273			addClass(panes.shadowPane, 'leaflet-zoom-hide');
4274		}
4275	},
4276
4277
4278	// private methods that modify map state
4279
4280	// @section Map state change events
4281	_resetView: function (center, zoom, noMoveStart) {
4282		setPosition(this._mapPane, new Point(0, 0));
4283
4284		var loading = !this._loaded;
4285		this._loaded = true;
4286		zoom = this._limitZoom(zoom);
4287
4288		this.fire('viewprereset');
4289
4290		var zoomChanged = this._zoom !== zoom;
4291		this
4292			._moveStart(zoomChanged, noMoveStart)
4293			._move(center, zoom)
4294			._moveEnd(zoomChanged);
4295
4296		// @event viewreset: Event
4297		// Fired when the map needs to redraw its content (this usually happens
4298		// on map zoom or load). Very useful for creating custom overlays.
4299		this.fire('viewreset');
4300
4301		// @event load: Event
4302		// Fired when the map is initialized (when its center and zoom are set
4303		// for the first time).
4304		if (loading) {
4305			this.fire('load');
4306		}
4307	},
4308
4309	_moveStart: function (zoomChanged, noMoveStart) {
4310		// @event zoomstart: Event
4311		// Fired when the map zoom is about to change (e.g. before zoom animation).
4312		// @event movestart: Event
4313		// Fired when the view of the map starts changing (e.g. user starts dragging the map).
4314		if (zoomChanged) {
4315			this.fire('zoomstart');
4316		}
4317		if (!noMoveStart) {
4318			this.fire('movestart');
4319		}
4320		return this;
4321	},
4322
4323	_move: function (center, zoom, data, supressEvent) {
4324		if (zoom === undefined) {
4325			zoom = this._zoom;
4326		}
4327		var zoomChanged = this._zoom !== zoom;
4328
4329		this._zoom = zoom;
4330		this._lastCenter = center;
4331		this._pixelOrigin = this._getNewPixelOrigin(center);
4332
4333		if (!supressEvent) {
4334			// @event zoom: Event
4335			// Fired repeatedly during any change in zoom level,
4336			// including zoom and fly animations.
4337			if (zoomChanged || (data && data.pinch)) {	// Always fire 'zoom' if pinching because #3530
4338				this.fire('zoom', data);
4339			}
4340
4341			// @event move: Event
4342			// Fired repeatedly during any movement of the map,
4343			// including pan and fly animations.
4344			this.fire('move', data);
4345		} else if (data && data.pinch) {	// Always fire 'zoom' if pinching because #3530
4346			this.fire('zoom', data);
4347		}
4348		return this;
4349	},
4350
4351	_moveEnd: function (zoomChanged) {
4352		// @event zoomend: Event
4353		// Fired when the map zoom changed, after any animations.
4354		if (zoomChanged) {
4355			this.fire('zoomend');
4356		}
4357
4358		// @event moveend: Event
4359		// Fired when the center of the map stops changing
4360		// (e.g. user stopped dragging the map or after non-centered zoom).
4361		return this.fire('moveend');
4362	},
4363
4364	_stop: function () {
4365		cancelAnimFrame(this._flyToFrame);
4366		if (this._panAnim) {
4367			this._panAnim.stop();
4368		}
4369		return this;
4370	},
4371
4372	_rawPanBy: function (offset) {
4373		setPosition(this._mapPane, this._getMapPanePos().subtract(offset));
4374	},
4375
4376	_getZoomSpan: function () {
4377		return this.getMaxZoom() - this.getMinZoom();
4378	},
4379
4380	_panInsideMaxBounds: function () {
4381		if (!this._enforcingBounds) {
4382			this.panInsideBounds(this.options.maxBounds);
4383		}
4384	},
4385
4386	_checkIfLoaded: function () {
4387		if (!this._loaded) {
4388			throw new Error('Set map center and zoom first.');
4389		}
4390	},
4391
4392	// DOM event handling
4393
4394	// @section Interaction events
4395	_initEvents: function (remove) {
4396		this._targets = {};
4397		this._targets[stamp(this._container)] = this;
4398
4399		var onOff = remove ? off : on;
4400
4401		// @event click: MouseEvent
4402		// Fired when the user clicks (or taps) the map.
4403		// @event dblclick: MouseEvent
4404		// Fired when the user double-clicks (or double-taps) the map.
4405		// @event mousedown: MouseEvent
4406		// Fired when the user pushes the mouse button on the map.
4407		// @event mouseup: MouseEvent
4408		// Fired when the user releases the mouse button on the map.
4409		// @event mouseover: MouseEvent
4410		// Fired when the mouse enters the map.
4411		// @event mouseout: MouseEvent
4412		// Fired when the mouse leaves the map.
4413		// @event mousemove: MouseEvent
4414		// Fired while the mouse moves over the map.
4415		// @event contextmenu: MouseEvent
4416		// Fired when the user pushes the right mouse button on the map, prevents
4417		// default browser context menu from showing if there are listeners on
4418		// this event. Also fired on mobile when the user holds a single touch
4419		// for a second (also called long press).
4420		// @event keypress: KeyboardEvent
4421		// Fired when the user presses a key from the keyboard that produces a character value while the map is focused.
4422		// @event keydown: KeyboardEvent
4423		// Fired when the user presses a key from the keyboard while the map is focused. Unlike the `keypress` event,
4424		// the `keydown` event is fired for keys that produce a character value and for keys
4425		// that do not produce a character value.
4426		// @event keyup: KeyboardEvent
4427		// Fired when the user releases a key from the keyboard while the map is focused.
4428		onOff(this._container, 'click dblclick mousedown mouseup ' +
4429			'mouseover mouseout mousemove contextmenu keypress keydown keyup', this._handleDOMEvent, this);
4430
4431		if (this.options.trackResize) {
4432			onOff(window, 'resize', this._onResize, this);
4433		}
4434
4435		if (Browser.any3d && this.options.transform3DLimit) {
4436			(remove ? this.off : this.on).call(this, 'moveend', this._onMoveEnd);
4437		}
4438	},
4439
4440	_onResize: function () {
4441		cancelAnimFrame(this._resizeRequest);
4442		this._resizeRequest = requestAnimFrame(
4443		        function () { this.invalidateSize({debounceMoveend: true}); }, this);
4444	},
4445
4446	_onScroll: function () {
4447		this._container.scrollTop  = 0;
4448		this._container.scrollLeft = 0;
4449	},
4450
4451	_onMoveEnd: function () {
4452		var pos = this._getMapPanePos();
4453		if (Math.max(Math.abs(pos.x), Math.abs(pos.y)) >= this.options.transform3DLimit) {
4454			// https://bugzilla.mozilla.org/show_bug.cgi?id=1203873 but Webkit also have
4455			// a pixel offset on very high values, see: https://jsfiddle.net/dg6r5hhb/
4456			this._resetView(this.getCenter(), this.getZoom());
4457		}
4458	},
4459
4460	_findEventTargets: function (e, type) {
4461		var targets = [],
4462		    target,
4463		    isHover = type === 'mouseout' || type === 'mouseover',
4464		    src = e.target || e.srcElement,
4465		    dragging = false;
4466
4467		while (src) {
4468			target = this._targets[stamp(src)];
4469			if (target && (type === 'click' || type === 'preclick') && this._draggableMoved(target)) {
4470				// Prevent firing click after you just dragged an object.
4471				dragging = true;
4472				break;
4473			}
4474			if (target && target.listens(type, true)) {
4475				if (isHover && !isExternalTarget(src, e)) { break; }
4476				targets.push(target);
4477				if (isHover) { break; }
4478			}
4479			if (src === this._container) { break; }
4480			src = src.parentNode;
4481		}
4482		if (!targets.length && !dragging && !isHover && this.listens(type, true)) {
4483			targets = [this];
4484		}
4485		return targets;
4486	},
4487
4488	_isClickDisabled: function (el) {
4489		while (el && el !== this._container) {
4490			if (el['_leaflet_disable_click']) { return true; }
4491			el = el.parentNode;
4492		}
4493	},
4494
4495	_handleDOMEvent: function (e) {
4496		var el = (e.target || e.srcElement);
4497		if (!this._loaded || el['_leaflet_disable_events'] || e.type === 'click' && this._isClickDisabled(el)) {
4498			return;
4499		}
4500
4501		var type = e.type;
4502
4503		if (type === 'mousedown') {
4504			// prevents outline when clicking on keyboard-focusable element
4505			preventOutline(el);
4506		}
4507
4508		this._fireDOMEvent(e, type);
4509	},
4510
4511	_mouseEvents: ['click', 'dblclick', 'mouseover', 'mouseout', 'contextmenu'],
4512
4513	_fireDOMEvent: function (e, type, canvasTargets) {
4514
4515		if (e.type === 'click') {
4516			// Fire a synthetic 'preclick' event which propagates up (mainly for closing popups).
4517			// @event preclick: MouseEvent
4518			// Fired before mouse click on the map (sometimes useful when you
4519			// want something to happen on click before any existing click
4520			// handlers start running).
4521			var synth = extend({}, e);
4522			synth.type = 'preclick';
4523			this._fireDOMEvent(synth, synth.type, canvasTargets);
4524		}
4525
4526		// Find the layer the event is propagating from and its parents.
4527		var targets = this._findEventTargets(e, type);
4528
4529		if (canvasTargets) {
4530			var filtered = []; // pick only targets with listeners
4531			for (var i = 0; i < canvasTargets.length; i++) {
4532				if (canvasTargets[i].listens(type, true)) {
4533					filtered.push(canvasTargets[i]);
4534				}
4535			}
4536			targets = filtered.concat(targets);
4537		}
4538
4539		if (!targets.length) { return; }
4540
4541		if (type === 'contextmenu') {
4542			preventDefault(e);
4543		}
4544
4545		var target = targets[0];
4546		var data = {
4547			originalEvent: e
4548		};
4549
4550		if (e.type !== 'keypress' && e.type !== 'keydown' && e.type !== 'keyup') {
4551			var isMarker = target.getLatLng && (!target._radius || target._radius <= 10);
4552			data.containerPoint = isMarker ?
4553				this.latLngToContainerPoint(target.getLatLng()) : this.mouseEventToContainerPoint(e);
4554			data.layerPoint = this.containerPointToLayerPoint(data.containerPoint);
4555			data.latlng = isMarker ? target.getLatLng() : this.layerPointToLatLng(data.layerPoint);
4556		}
4557
4558		for (i = 0; i < targets.length; i++) {
4559			targets[i].fire(type, data, true);
4560			if (data.originalEvent._stopped ||
4561				(targets[i].options.bubblingMouseEvents === false && indexOf(this._mouseEvents, type) !== -1)) { return; }
4562		}
4563	},
4564
4565	_draggableMoved: function (obj) {
4566		obj = obj.dragging && obj.dragging.enabled() ? obj : this;
4567		return (obj.dragging && obj.dragging.moved()) || (this.boxZoom && this.boxZoom.moved());
4568	},
4569
4570	_clearHandlers: function () {
4571		for (var i = 0, len = this._handlers.length; i < len; i++) {
4572			this._handlers[i].disable();
4573		}
4574	},
4575
4576	// @section Other Methods
4577
4578	// @method whenReady(fn: Function, context?: Object): this
4579	// Runs the given function `fn` when the map gets initialized with
4580	// a view (center and zoom) and at least one layer, or immediately
4581	// if it's already initialized, optionally passing a function context.
4582	whenReady: function (callback, context) {
4583		if (this._loaded) {
4584			callback.call(context || this, {target: this});
4585		} else {
4586			this.on('load', callback, context);
4587		}
4588		return this;
4589	},
4590
4591
4592	// private methods for getting map state
4593
4594	_getMapPanePos: function () {
4595		return getPosition(this._mapPane) || new Point(0, 0);
4596	},
4597
4598	_moved: function () {
4599		var pos = this._getMapPanePos();
4600		return pos && !pos.equals([0, 0]);
4601	},
4602
4603	_getTopLeftPoint: function (center, zoom) {
4604		var pixelOrigin = center && zoom !== undefined ?
4605			this._getNewPixelOrigin(center, zoom) :
4606			this.getPixelOrigin();
4607		return pixelOrigin.subtract(this._getMapPanePos());
4608	},
4609
4610	_getNewPixelOrigin: function (center, zoom) {
4611		var viewHalf = this.getSize()._divideBy(2);
4612		return this.project(center, zoom)._subtract(viewHalf)._add(this._getMapPanePos())._round();
4613	},
4614
4615	_latLngToNewLayerPoint: function (latlng, zoom, center) {
4616		var topLeft = this._getNewPixelOrigin(center, zoom);
4617		return this.project(latlng, zoom)._subtract(topLeft);
4618	},
4619
4620	_latLngBoundsToNewLayerBounds: function (latLngBounds, zoom, center) {
4621		var topLeft = this._getNewPixelOrigin(center, zoom);
4622		return toBounds([
4623			this.project(latLngBounds.getSouthWest(), zoom)._subtract(topLeft),
4624			this.project(latLngBounds.getNorthWest(), zoom)._subtract(topLeft),
4625			this.project(latLngBounds.getSouthEast(), zoom)._subtract(topLeft),
4626			this.project(latLngBounds.getNorthEast(), zoom)._subtract(topLeft)
4627		]);
4628	},
4629
4630	// layer point of the current center
4631	_getCenterLayerPoint: function () {
4632		return this.containerPointToLayerPoint(this.getSize()._divideBy(2));
4633	},
4634
4635	// offset of the specified place to the current center in pixels
4636	_getCenterOffset: function (latlng) {
4637		return this.latLngToLayerPoint(latlng).subtract(this._getCenterLayerPoint());
4638	},
4639
4640	// adjust center for view to get inside bounds
4641	_limitCenter: function (center, zoom, bounds) {
4642
4643		if (!bounds) { return center; }
4644
4645		var centerPoint = this.project(center, zoom),
4646		    viewHalf = this.getSize().divideBy(2),
4647		    viewBounds = new Bounds(centerPoint.subtract(viewHalf), centerPoint.add(viewHalf)),
4648		    offset = this._getBoundsOffset(viewBounds, bounds, zoom);
4649
4650		// If offset is less than a pixel, ignore.
4651		// This prevents unstable projections from getting into
4652		// an infinite loop of tiny offsets.
4653		if (Math.abs(offset.x) <= 1 && Math.abs(offset.y) <= 1) {
4654			return center;
4655		}
4656
4657		return this.unproject(centerPoint.add(offset), zoom);
4658	},
4659
4660	// adjust offset for view to get inside bounds
4661	_limitOffset: function (offset, bounds) {
4662		if (!bounds) { return offset; }
4663
4664		var viewBounds = this.getPixelBounds(),
4665		    newBounds = new Bounds(viewBounds.min.add(offset), viewBounds.max.add(offset));
4666
4667		return offset.add(this._getBoundsOffset(newBounds, bounds));
4668	},
4669
4670	// returns offset needed for pxBounds to get inside maxBounds at a specified zoom
4671	_getBoundsOffset: function (pxBounds, maxBounds, zoom) {
4672		var projectedMaxBounds = toBounds(
4673		        this.project(maxBounds.getNorthEast(), zoom),
4674		        this.project(maxBounds.getSouthWest(), zoom)
4675		    ),
4676		    minOffset = projectedMaxBounds.min.subtract(pxBounds.min),
4677		    maxOffset = projectedMaxBounds.max.subtract(pxBounds.max),
4678
4679		    dx = this._rebound(minOffset.x, -maxOffset.x),
4680		    dy = this._rebound(minOffset.y, -maxOffset.y);
4681
4682		return new Point(dx, dy);
4683	},
4684
4685	_rebound: function (left, right) {
4686		return left + right > 0 ?
4687			Math.round(left - right) / 2 :
4688			Math.max(0, Math.ceil(left)) - Math.max(0, Math.floor(right));
4689	},
4690
4691	_limitZoom: function (zoom) {
4692		var min = this.getMinZoom(),
4693		    max = this.getMaxZoom(),
4694		    snap = Browser.any3d ? this.options.zoomSnap : 1;
4695		if (snap) {
4696			zoom = Math.round(zoom / snap) * snap;
4697		}
4698		return Math.max(min, Math.min(max, zoom));
4699	},
4700
4701	_onPanTransitionStep: function () {
4702		this.fire('move');
4703	},
4704
4705	_onPanTransitionEnd: function () {
4706		removeClass(this._mapPane, 'leaflet-pan-anim');
4707		this.fire('moveend');
4708	},
4709
4710	_tryAnimatedPan: function (center, options) {
4711		// difference between the new and current centers in pixels
4712		var offset = this._getCenterOffset(center)._trunc();
4713
4714		// don't animate too far unless animate: true specified in options
4715		if ((options && options.animate) !== true && !this.getSize().contains(offset)) { return false; }
4716
4717		this.panBy(offset, options);
4718
4719		return true;
4720	},
4721
4722	_createAnimProxy: function () {
4723
4724		var proxy = this._proxy = create$1('div', 'leaflet-proxy leaflet-zoom-animated');
4725		this._panes.mapPane.appendChild(proxy);
4726
4727		this.on('zoomanim', function (e) {
4728			var prop = TRANSFORM,
4729			    transform = this._proxy.style[prop];
4730
4731			setTransform(this._proxy, this.project(e.center, e.zoom), this.getZoomScale(e.zoom, 1));
4732
4733			// workaround for case when transform is the same and so transitionend event is not fired
4734			if (transform === this._proxy.style[prop] && this._animatingZoom) {
4735				this._onZoomTransitionEnd();
4736			}
4737		}, this);
4738
4739		this.on('load moveend', this._animMoveEnd, this);
4740
4741		this._on('unload', this._destroyAnimProxy, this);
4742	},
4743
4744	_destroyAnimProxy: function () {
4745		remove(this._proxy);
4746		this.off('load moveend', this._animMoveEnd, this);
4747		delete this._proxy;
4748	},
4749
4750	_animMoveEnd: function () {
4751		var c = this.getCenter(),
4752		    z = this.getZoom();
4753		setTransform(this._proxy, this.project(c, z), this.getZoomScale(z, 1));
4754	},
4755
4756	_catchTransitionEnd: function (e) {
4757		if (this._animatingZoom && e.propertyName.indexOf('transform') >= 0) {
4758			this._onZoomTransitionEnd();
4759		}
4760	},
4761
4762	_nothingToAnimate: function () {
4763		return !this._container.getElementsByClassName('leaflet-zoom-animated').length;
4764	},
4765
4766	_tryAnimatedZoom: function (center, zoom, options) {
4767
4768		if (this._animatingZoom) { return true; }
4769
4770		options = options || {};
4771
4772		// don't animate if disabled, not supported or zoom difference is too large
4773		if (!this._zoomAnimated || options.animate === false || this._nothingToAnimate() ||
4774		        Math.abs(zoom - this._zoom) > this.options.zoomAnimationThreshold) { return false; }
4775
4776		// offset is the pixel coords of the zoom origin relative to the current center
4777		var scale = this.getZoomScale(zoom),
4778		    offset = this._getCenterOffset(center)._divideBy(1 - 1 / scale);
4779
4780		// don't animate if the zoom origin isn't within one screen from the current center, unless forced
4781		if (options.animate !== true && !this.getSize().contains(offset)) { return false; }
4782
4783		requestAnimFrame(function () {
4784			this
4785			    ._moveStart(true, options.noMoveStart || false)
4786			    ._animateZoom(center, zoom, true);
4787		}, this);
4788
4789		return true;
4790	},
4791
4792	_animateZoom: function (center, zoom, startAnim, noUpdate) {
4793		if (!this._mapPane) { return; }
4794
4795		if (startAnim) {
4796			this._animatingZoom = true;
4797
4798			// remember what center/zoom to set after animation
4799			this._animateToCenter = center;
4800			this._animateToZoom = zoom;
4801
4802			addClass(this._mapPane, 'leaflet-zoom-anim');
4803		}
4804
4805		// @section Other Events
4806		// @event zoomanim: ZoomAnimEvent
4807		// Fired at least once per zoom animation. For continuous zoom, like pinch zooming, fired once per frame during zoom.
4808		this.fire('zoomanim', {
4809			center: center,
4810			zoom: zoom,
4811			noUpdate: noUpdate
4812		});
4813
4814		if (!this._tempFireZoomEvent) {
4815			this._tempFireZoomEvent = this._zoom !== this._animateToZoom;
4816		}
4817
4818		this._move(this._animateToCenter, this._animateToZoom, undefined, true);
4819
4820		// Work around webkit not firing 'transitionend', see https://github.com/Leaflet/Leaflet/issues/3689, 2693
4821		setTimeout(bind(this._onZoomTransitionEnd, this), 250);
4822	},
4823
4824	_onZoomTransitionEnd: function () {
4825		if (!this._animatingZoom) { return; }
4826
4827		if (this._mapPane) {
4828			removeClass(this._mapPane, 'leaflet-zoom-anim');
4829		}
4830
4831		this._animatingZoom = false;
4832
4833		this._move(this._animateToCenter, this._animateToZoom, undefined, true);
4834
4835		if (this._tempFireZoomEvent) {
4836			this.fire('zoom');
4837		}
4838		delete this._tempFireZoomEvent;
4839
4840		this.fire('move');
4841
4842		this._moveEnd(true);
4843	}
4844});
4845
4846// @section
4847
4848// @factory L.map(id: String, options?: Map options)
4849// Instantiates a map object given the DOM ID of a `<div>` element
4850// and optionally an object literal with `Map options`.
4851//
4852// @alternative
4853// @factory L.map(el: HTMLElement, options?: Map options)
4854// Instantiates a map object given an instance of a `<div>` HTML element
4855// and optionally an object literal with `Map options`.
4856function createMap(id, options) {
4857	return new Map(id, options);
4858}
4859
4860/*
4861 * @class Control
4862 * @aka L.Control
4863 * @inherits Class
4864 *
4865 * L.Control is a base class for implementing map controls. Handles positioning.
4866 * All other controls extend from this class.
4867 */
4868
4869var Control = Class.extend({
4870	// @section
4871	// @aka Control Options
4872	options: {
4873		// @option position: String = 'topright'
4874		// The position of the control (one of the map corners). Possible values are `'topleft'`,
4875		// `'topright'`, `'bottomleft'` or `'bottomright'`
4876		position: 'topright'
4877	},
4878
4879	initialize: function (options) {
4880		setOptions(this, options);
4881	},
4882
4883	/* @section
4884	 * Classes extending L.Control will inherit the following methods:
4885	 *
4886	 * @method getPosition: string
4887	 * Returns the position of the control.
4888	 */
4889	getPosition: function () {
4890		return this.options.position;
4891	},
4892
4893	// @method setPosition(position: string): this
4894	// Sets the position of the control.
4895	setPosition: function (position) {
4896		var map = this._map;
4897
4898		if (map) {
4899			map.removeControl(this);
4900		}
4901
4902		this.options.position = position;
4903
4904		if (map) {
4905			map.addControl(this);
4906		}
4907
4908		return this;
4909	},
4910
4911	// @method getContainer: HTMLElement
4912	// Returns the HTMLElement that contains the control.
4913	getContainer: function () {
4914		return this._container;
4915	},
4916
4917	// @method addTo(map: Map): this
4918	// Adds the control to the given map.
4919	addTo: function (map) {
4920		this.remove();
4921		this._map = map;
4922
4923		var container = this._container = this.onAdd(map),
4924		    pos = this.getPosition(),
4925		    corner = map._controlCorners[pos];
4926
4927		addClass(container, 'leaflet-control');
4928
4929		if (pos.indexOf('bottom') !== -1) {
4930			corner.insertBefore(container, corner.firstChild);
4931		} else {
4932			corner.appendChild(container);
4933		}
4934
4935		this._map.on('unload', this.remove, this);
4936
4937		return this;
4938	},
4939
4940	// @method remove: this
4941	// Removes the control from the map it is currently active on.
4942	remove: function () {
4943		if (!this._map) {
4944			return this;
4945		}
4946
4947		remove(this._container);
4948
4949		if (this.onRemove) {
4950			this.onRemove(this._map);
4951		}
4952
4953		this._map.off('unload', this.remove, this);
4954		this._map = null;
4955
4956		return this;
4957	},
4958
4959	_refocusOnMap: function (e) {
4960		// if map exists and event is not a keyboard event
4961		if (this._map && e && e.screenX > 0 && e.screenY > 0) {
4962			this._map.getContainer().focus();
4963		}
4964	}
4965});
4966
4967var control = function (options) {
4968	return new Control(options);
4969};
4970
4971/* @section Extension methods
4972 * @uninheritable
4973 *
4974 * Every control should extend from `L.Control` and (re-)implement the following methods.
4975 *
4976 * @method onAdd(map: Map): HTMLElement
4977 * Should return the container DOM element for the control and add listeners on relevant map events. Called on [`control.addTo(map)`](#control-addTo).
4978 *
4979 * @method onRemove(map: Map)
4980 * Optional method. Should contain all clean up code that removes the listeners previously added in [`onAdd`](#control-onadd). Called on [`control.remove()`](#control-remove).
4981 */
4982
4983/* @namespace Map
4984 * @section Methods for Layers and Controls
4985 */
4986Map.include({
4987	// @method addControl(control: Control): this
4988	// Adds the given control to the map
4989	addControl: function (control) {
4990		control.addTo(this);
4991		return this;
4992	},
4993
4994	// @method removeControl(control: Control): this
4995	// Removes the given control from the map
4996	removeControl: function (control) {
4997		control.remove();
4998		return this;
4999	},
5000
5001	_initControlPos: function () {
5002		var corners = this._controlCorners = {},
5003		    l = 'leaflet-',
5004		    container = this._controlContainer =
5005		            create$1('div', l + 'control-container', this._container);
5006
5007		function createCorner(vSide, hSide) {
5008			var className = l + vSide + ' ' + l + hSide;
5009
5010			corners[vSide + hSide] = create$1('div', className, container);
5011		}
5012
5013		createCorner('top', 'left');
5014		createCorner('top', 'right');
5015		createCorner('bottom', 'left');
5016		createCorner('bottom', 'right');
5017	},
5018
5019	_clearControlPos: function () {
5020		for (var i in this._controlCorners) {
5021			remove(this._controlCorners[i]);
5022		}
5023		remove(this._controlContainer);
5024		delete this._controlCorners;
5025		delete this._controlContainer;
5026	}
5027});
5028
5029/*
5030 * @class Control.Layers
5031 * @aka L.Control.Layers
5032 * @inherits Control
5033 *
5034 * 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`.
5035 *
5036 * @example
5037 *
5038 * ```js
5039 * var baseLayers = {
5040 * 	"Mapbox": mapbox,
5041 * 	"OpenStreetMap": osm
5042 * };
5043 *
5044 * var overlays = {
5045 * 	"Marker": marker,
5046 * 	"Roads": roadsLayer
5047 * };
5048 *
5049 * L.control.layers(baseLayers, overlays).addTo(map);
5050 * ```
5051 *
5052 * The `baseLayers` and `overlays` parameters are object literals with layer names as keys and `Layer` objects as values:
5053 *
5054 * ```js
5055 * {
5056 *     "<someName1>": layer1,
5057 *     "<someName2>": layer2
5058 * }
5059 * ```
5060 *
5061 * The layer names can contain HTML, which allows you to add additional styling to the items:
5062 *
5063 * ```js
5064 * {"<img src='my-layer-icon' /> <span class='my-layer-item'>My Layer</span>": myLayer}
5065 * ```
5066 */
5067
5068var Layers = Control.extend({
5069	// @section
5070	// @aka Control.Layers options
5071	options: {
5072		// @option collapsed: Boolean = true
5073		// If `true`, the control will be collapsed into an icon and expanded on mouse hover, touch, or keyboard activation.
5074		collapsed: true,
5075		position: 'topright',
5076
5077		// @option autoZIndex: Boolean = true
5078		// 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.
5079		autoZIndex: true,
5080
5081		// @option hideSingleBase: Boolean = false
5082		// If `true`, the base layers in the control will be hidden when there is only one.
5083		hideSingleBase: false,
5084
5085		// @option sortLayers: Boolean = false
5086		// Whether to sort the layers. When `false`, layers will keep the order
5087		// in which they were added to the control.
5088		sortLayers: false,
5089
5090		// @option sortFunction: Function = *
5091		// A [compare function](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Array/sort)
5092		// that will be used for sorting the layers, when `sortLayers` is `true`.
5093		// The function receives both the `L.Layer` instances and their names, as in
5094		// `sortFunction(layerA, layerB, nameA, nameB)`.
5095		// By default, it sorts layers alphabetically by their name.
5096		sortFunction: function (layerA, layerB, nameA, nameB) {
5097			return nameA < nameB ? -1 : (nameB < nameA ? 1 : 0);
5098		}
5099	},
5100
5101	initialize: function (baseLayers, overlays, options) {
5102		setOptions(this, options);
5103
5104		this._layerControlInputs = [];
5105		this._layers = [];
5106		this._lastZIndex = 0;
5107		this._handlingClick = false;
5108		this._preventClick = false;
5109
5110		for (var i in baseLayers) {
5111			this._addLayer(baseLayers[i], i);
5112		}
5113
5114		for (i in overlays) {
5115			this._addLayer(overlays[i], i, true);
5116		}
5117	},
5118
5119	onAdd: function (map) {
5120		this._initLayout();
5121		this._update();
5122
5123		this._map = map;
5124		map.on('zoomend', this._checkDisabledLayers, this);
5125
5126		for (var i = 0; i < this._layers.length; i++) {
5127			this._layers[i].layer.on('add remove', this._onLayerChange, this);
5128		}
5129
5130		return this._container;
5131	},
5132
5133	addTo: function (map) {
5134		Control.prototype.addTo.call(this, map);
5135		// Trigger expand after Layers Control has been inserted into DOM so that is now has an actual height.
5136		return this._expandIfNotCollapsed();
5137	},
5138
5139	onRemove: function () {
5140		this._map.off('zoomend', this._checkDisabledLayers, this);
5141
5142		for (var i = 0; i < this._layers.length; i++) {
5143			this._layers[i].layer.off('add remove', this._onLayerChange, this);
5144		}
5145	},
5146
5147	// @method addBaseLayer(layer: Layer, name: String): this
5148	// Adds a base layer (radio button entry) with the given name to the control.
5149	addBaseLayer: function (layer, name) {
5150		this._addLayer(layer, name);
5151		return (this._map) ? this._update() : this;
5152	},
5153
5154	// @method addOverlay(layer: Layer, name: String): this
5155	// Adds an overlay (checkbox entry) with the given name to the control.
5156	addOverlay: function (layer, name) {
5157		this._addLayer(layer, name, true);
5158		return (this._map) ? this._update() : this;
5159	},
5160
5161	// @method removeLayer(layer: Layer): this
5162	// Remove the given layer from the control.
5163	removeLayer: function (layer) {
5164		layer.off('add remove', this._onLayerChange, this);
5165
5166		var obj = this._getLayer(stamp(layer));
5167		if (obj) {
5168			this._layers.splice(this._layers.indexOf(obj), 1);
5169		}
5170		return (this._map) ? this._update() : this;
5171	},
5172
5173	// @method expand(): this
5174	// Expand the control container if collapsed.
5175	expand: function () {
5176		addClass(this._container, 'leaflet-control-layers-expanded');
5177		this._section.style.height = null;
5178		var acceptableHeight = this._map.getSize().y - (this._container.offsetTop + 50);
5179		if (acceptableHeight < this._section.clientHeight) {
5180			addClass(this._section, 'leaflet-control-layers-scrollbar');
5181			this._section.style.height = acceptableHeight + 'px';
5182		} else {
5183			removeClass(this._section, 'leaflet-control-layers-scrollbar');
5184		}
5185		this._checkDisabledLayers();
5186		return this;
5187	},
5188
5189	// @method collapse(): this
5190	// Collapse the control container if expanded.
5191	collapse: function () {
5192		removeClass(this._container, 'leaflet-control-layers-expanded');
5193		return this;
5194	},
5195
5196	_initLayout: function () {
5197		var className = 'leaflet-control-layers',
5198		    container = this._container = create$1('div', className),
5199		    collapsed = this.options.collapsed;
5200
5201		// makes this work on IE touch devices by stopping it from firing a mouseout event when the touch is released
5202		container.setAttribute('aria-haspopup', true);
5203
5204		disableClickPropagation(container);
5205		disableScrollPropagation(container);
5206
5207		var section = this._section = create$1('section', className + '-list');
5208
5209		if (collapsed) {
5210			this._map.on('click', this.collapse, this);
5211
5212			on(container, {
5213				mouseenter: this._expandSafely,
5214				mouseleave: this.collapse
5215			}, this);
5216		}
5217
5218		var link = this._layersLink = create$1('a', className + '-toggle', container);
5219		link.href = '#';
5220		link.title = 'Layers';
5221		link.setAttribute('role', 'button');
5222
5223		on(link, {
5224			keydown: function (e) {
5225				if (e.keyCode === 13) {
5226					this._expandSafely();
5227				}
5228			},
5229			// Certain screen readers intercept the key event and instead send a click event
5230			click: function (e) {
5231				preventDefault(e);
5232				this._expandSafely();
5233			}
5234		}, this);
5235
5236		if (!collapsed) {
5237			this.expand();
5238		}
5239
5240		this._baseLayersList = create$1('div', className + '-base', section);
5241		this._separator = create$1('div', className + '-separator', section);
5242		this._overlaysList = create$1('div', className + '-overlays', section);
5243
5244		container.appendChild(section);
5245	},
5246
5247	_getLayer: function (id) {
5248		for (var i = 0; i < this._layers.length; i++) {
5249
5250			if (this._layers[i] && stamp(this._layers[i].layer) === id) {
5251				return this._layers[i];
5252			}
5253		}
5254	},
5255
5256	_addLayer: function (layer, name, overlay) {
5257		if (this._map) {
5258			layer.on('add remove', this._onLayerChange, this);
5259		}
5260
5261		this._layers.push({
5262			layer: layer,
5263			name: name,
5264			overlay: overlay
5265		});
5266
5267		if (this.options.sortLayers) {
5268			this._layers.sort(bind(function (a, b) {
5269				return this.options.sortFunction(a.layer, b.layer, a.name, b.name);
5270			}, this));
5271		}
5272
5273		if (this.options.autoZIndex && layer.setZIndex) {
5274			this._lastZIndex++;
5275			layer.setZIndex(this._lastZIndex);
5276		}
5277
5278		this._expandIfNotCollapsed();
5279	},
5280
5281	_update: function () {
5282		if (!this._container) { return this; }
5283
5284		empty(this._baseLayersList);
5285		empty(this._overlaysList);
5286
5287		this._layerControlInputs = [];
5288		var baseLayersPresent, overlaysPresent, i, obj, baseLayersCount = 0;
5289
5290		for (i = 0; i < this._layers.length; i++) {
5291			obj = this._layers[i];
5292			this._addItem(obj);
5293			overlaysPresent = overlaysPresent || obj.overlay;
5294			baseLayersPresent = baseLayersPresent || !obj.overlay;
5295			baseLayersCount += !obj.overlay ? 1 : 0;
5296		}
5297
5298		// Hide base layers section if there's only one layer.
5299		if (this.options.hideSingleBase) {
5300			baseLayersPresent = baseLayersPresent && baseLayersCount > 1;
5301			this._baseLayersList.style.display = baseLayersPresent ? '' : 'none';
5302		}
5303
5304		this._separator.style.display = overlaysPresent && baseLayersPresent ? '' : 'none';
5305
5306		return this;
5307	},
5308
5309	_onLayerChange: function (e) {
5310		if (!this._handlingClick) {
5311			this._update();
5312		}
5313
5314		var obj = this._getLayer(stamp(e.target));
5315
5316		// @namespace Map
5317		// @section Layer events
5318		// @event baselayerchange: LayersControlEvent
5319		// Fired when the base layer is changed through the [layers control](#control-layers).
5320		// @event overlayadd: LayersControlEvent
5321		// Fired when an overlay is selected through the [layers control](#control-layers).
5322		// @event overlayremove: LayersControlEvent
5323		// Fired when an overlay is deselected through the [layers control](#control-layers).
5324		// @namespace Control.Layers
5325		var type = obj.overlay ?
5326			(e.type === 'add' ? 'overlayadd' : 'overlayremove') :
5327			(e.type === 'add' ? 'baselayerchange' : null);
5328
5329		if (type) {
5330			this._map.fire(type, obj);
5331		}
5332	},
5333
5334	// IE7 bugs out if you create a radio dynamically, so you have to do it this hacky way (see https://stackoverflow.com/a/119079)
5335	_createRadioElement: function (name, checked) {
5336
5337		var radioHtml = '<input type="radio" class="leaflet-control-layers-selector" name="' +
5338				name + '"' + (checked ? ' checked="checked"' : '') + '/>';
5339
5340		var radioFragment = document.createElement('div');
5341		radioFragment.innerHTML = radioHtml;
5342
5343		return radioFragment.firstChild;
5344	},
5345
5346	_addItem: function (obj) {
5347		var label = document.createElement('label'),
5348		    checked = this._map.hasLayer(obj.layer),
5349		    input;
5350
5351		if (obj.overlay) {
5352			input = document.createElement('input');
5353			input.type = 'checkbox';
5354			input.className = 'leaflet-control-layers-selector';
5355			input.defaultChecked = checked;
5356		} else {
5357			input = this._createRadioElement('leaflet-base-layers_' + stamp(this), checked);
5358		}
5359
5360		this._layerControlInputs.push(input);
5361		input.layerId = stamp(obj.layer);
5362
5363		on(input, 'click', this._onInputClick, this);
5364
5365		var name = document.createElement('span');
5366		name.innerHTML = ' ' + obj.name;
5367
5368		// Helps from preventing layer control flicker when checkboxes are disabled
5369		// https://github.com/Leaflet/Leaflet/issues/2771
5370		var holder = document.createElement('span');
5371
5372		label.appendChild(holder);
5373		holder.appendChild(input);
5374		holder.appendChild(name);
5375
5376		var container = obj.overlay ? this._overlaysList : this._baseLayersList;
5377		container.appendChild(label);
5378
5379		this._checkDisabledLayers();
5380		return label;
5381	},
5382
5383	_onInputClick: function () {
5384		// expanding the control on mobile with a click can cause adding a layer - we don't want this
5385		if (this._preventClick) {
5386			return;
5387		}
5388
5389		var inputs = this._layerControlInputs,
5390		    input, layer;
5391		var addedLayers = [],
5392		    removedLayers = [];
5393
5394		this._handlingClick = true;
5395
5396		for (var i = inputs.length - 1; i >= 0; i--) {
5397			input = inputs[i];
5398			layer = this._getLayer(input.layerId).layer;
5399
5400			if (input.checked) {
5401				addedLayers.push(layer);
5402			} else if (!input.checked) {
5403				removedLayers.push(layer);
5404			}
5405		}
5406
5407		// Bugfix issue 2318: Should remove all old layers before readding new ones
5408		for (i = 0; i < removedLayers.length; i++) {
5409			if (this._map.hasLayer(removedLayers[i])) {
5410				this._map.removeLayer(removedLayers[i]);
5411			}
5412		}
5413		for (i = 0; i < addedLayers.length; i++) {
5414			if (!this._map.hasLayer(addedLayers[i])) {
5415				this._map.addLayer(addedLayers[i]);
5416			}
5417		}
5418
5419		this._handlingClick = false;
5420
5421		this._refocusOnMap();
5422	},
5423
5424	_checkDisabledLayers: function () {
5425		var inputs = this._layerControlInputs,
5426		    input,
5427		    layer,
5428		    zoom = this._map.getZoom();
5429
5430		for (var i = inputs.length - 1; i >= 0; i--) {
5431			input = inputs[i];
5432			layer = this._getLayer(input.layerId).layer;
5433			input.disabled = (layer.options.minZoom !== undefined && zoom < layer.options.minZoom) ||
5434			                 (layer.options.maxZoom !== undefined && zoom > layer.options.maxZoom);
5435
5436		}
5437	},
5438
5439	_expandIfNotCollapsed: function () {
5440		if (this._map && !this.options.collapsed) {
5441			this.expand();
5442		}
5443		return this;
5444	},
5445
5446	_expandSafely: function () {
5447		var section = this._section;
5448		this._preventClick = true;
5449		on(section, 'click', preventDefault);
5450		this.expand();
5451		var that = this;
5452		setTimeout(function () {
5453			off(section, 'click', preventDefault);
5454			that._preventClick = false;
5455		});
5456	}
5457
5458});
5459
5460
5461// @factory L.control.layers(baselayers?: Object, overlays?: Object, options?: Control.Layers options)
5462// 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.
5463var layers = function (baseLayers, overlays, options) {
5464	return new Layers(baseLayers, overlays, options);
5465};
5466
5467/*
5468 * @class Control.Zoom
5469 * @aka L.Control.Zoom
5470 * @inherits Control
5471 *
5472 * 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`.
5473 */
5474
5475var Zoom = Control.extend({
5476	// @section
5477	// @aka Control.Zoom options
5478	options: {
5479		position: 'topleft',
5480
5481		// @option zoomInText: String = '<span aria-hidden="true">+</span>'
5482		// The text set on the 'zoom in' button.
5483		zoomInText: '<span aria-hidden="true">+</span>',
5484
5485		// @option zoomInTitle: String = 'Zoom in'
5486		// The title set on the 'zoom in' button.
5487		zoomInTitle: 'Zoom in',
5488
5489		// @option zoomOutText: String = '<span aria-hidden="true">&#x2212;</span>'
5490		// The text set on the 'zoom out' button.
5491		zoomOutText: '<span aria-hidden="true">&#x2212;</span>',
5492
5493		// @option zoomOutTitle: String = 'Zoom out'
5494		// The title set on the 'zoom out' button.
5495		zoomOutTitle: 'Zoom out'
5496	},
5497
5498	onAdd: function (map) {
5499		var zoomName = 'leaflet-control-zoom',
5500		    container = create$1('div', zoomName + ' leaflet-bar'),
5501		    options = this.options;
5502
5503		this._zoomInButton  = this._createButton(options.zoomInText, options.zoomInTitle,
5504		        zoomName + '-in',  container, this._zoomIn);
5505		this._zoomOutButton = this._createButton(options.zoomOutText, options.zoomOutTitle,
5506		        zoomName + '-out', container, this._zoomOut);
5507
5508		this._updateDisabled();
5509		map.on('zoomend zoomlevelschange', this._updateDisabled, this);
5510
5511		return container;
5512	},
5513
5514	onRemove: function (map) {
5515		map.off('zoomend zoomlevelschange', this._updateDisabled, this);
5516	},
5517
5518	disable: function () {
5519		this._disabled = true;
5520		this._updateDisabled();
5521		return this;
5522	},
5523
5524	enable: function () {
5525		this._disabled = false;
5526		this._updateDisabled();
5527		return this;
5528	},
5529
5530	_zoomIn: function (e) {
5531		if (!this._disabled && this._map._zoom < this._map.getMaxZoom()) {
5532			this._map.zoomIn(this._map.options.zoomDelta * (e.shiftKey ? 3 : 1));
5533		}
5534	},
5535
5536	_zoomOut: function (e) {
5537		if (!this._disabled && this._map._zoom > this._map.getMinZoom()) {
5538			this._map.zoomOut(this._map.options.zoomDelta * (e.shiftKey ? 3 : 1));
5539		}
5540	},
5541
5542	_createButton: function (html, title, className, container, fn) {
5543		var link = create$1('a', className, container);
5544		link.innerHTML = html;
5545		link.href = '#';
5546		link.title = title;
5547
5548		/*
5549		 * Will force screen readers like VoiceOver to read this as "Zoom in - button"
5550		 */
5551		link.setAttribute('role', 'button');
5552		link.setAttribute('aria-label', title);
5553
5554		disableClickPropagation(link);
5555		on(link, 'click', stop);
5556		on(link, 'click', fn, this);
5557		on(link, 'click', this._refocusOnMap, this);
5558
5559		return link;
5560	},
5561
5562	_updateDisabled: function () {
5563		var map = this._map,
5564		    className = 'leaflet-disabled';
5565
5566		removeClass(this._zoomInButton, className);
5567		removeClass(this._zoomOutButton, className);
5568		this._zoomInButton.setAttribute('aria-disabled', 'false');
5569		this._zoomOutButton.setAttribute('aria-disabled', 'false');
5570
5571		if (this._disabled || map._zoom === map.getMinZoom()) {
5572			addClass(this._zoomOutButton, className);
5573			this._zoomOutButton.setAttribute('aria-disabled', 'true');
5574		}
5575		if (this._disabled || map._zoom === map.getMaxZoom()) {
5576			addClass(this._zoomInButton, className);
5577			this._zoomInButton.setAttribute('aria-disabled', 'true');
5578		}
5579	}
5580});
5581
5582// @namespace Map
5583// @section Control options
5584// @option zoomControl: Boolean = true
5585// Whether a [zoom control](#control-zoom) is added to the map by default.
5586Map.mergeOptions({
5587	zoomControl: true
5588});
5589
5590Map.addInitHook(function () {
5591	if (this.options.zoomControl) {
5592		// @section Controls
5593		// @property zoomControl: Control.Zoom
5594		// The default zoom control (only available if the
5595		// [`zoomControl` option](#map-zoomcontrol) was `true` when creating the map).
5596		this.zoomControl = new Zoom();
5597		this.addControl(this.zoomControl);
5598	}
5599});
5600
5601// @namespace Control.Zoom
5602// @factory L.control.zoom(options: Control.Zoom options)
5603// Creates a zoom control
5604var zoom = function (options) {
5605	return new Zoom(options);
5606};
5607
5608/*
5609 * @class Control.Scale
5610 * @aka L.Control.Scale
5611 * @inherits Control
5612 *
5613 * 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`.
5614 *
5615 * @example
5616 *
5617 * ```js
5618 * L.control.scale().addTo(map);
5619 * ```
5620 */
5621
5622var Scale = Control.extend({
5623	// @section
5624	// @aka Control.Scale options
5625	options: {
5626		position: 'bottomleft',
5627
5628		// @option maxWidth: Number = 100
5629		// Maximum width of the control in pixels. The width is set dynamically to show round values (e.g. 100, 200, 500).
5630		maxWidth: 100,
5631
5632		// @option metric: Boolean = True
5633		// Whether to show the metric scale line (m/km).
5634		metric: true,
5635
5636		// @option imperial: Boolean = True
5637		// Whether to show the imperial scale line (mi/ft).
5638		imperial: true
5639
5640		// @option updateWhenIdle: Boolean = false
5641		// If `true`, the control is updated on [`moveend`](#map-moveend), otherwise it's always up-to-date (updated on [`move`](#map-move)).
5642	},
5643
5644	onAdd: function (map) {
5645		var className = 'leaflet-control-scale',
5646		    container = create$1('div', className),
5647		    options = this.options;
5648
5649		this._addScales(options, className + '-line', container);
5650
5651		map.on(options.updateWhenIdle ? 'moveend' : 'move', this._update, this);
5652		map.whenReady(this._update, this);
5653
5654		return container;
5655	},
5656
5657	onRemove: function (map) {
5658		map.off(this.options.updateWhenIdle ? 'moveend' : 'move', this._update, this);
5659	},
5660
5661	_addScales: function (options, className, container) {
5662		if (options.metric) {
5663			this._mScale = create$1('div', className, container);
5664		}
5665		if (options.imperial) {
5666			this._iScale = create$1('div', className, container);
5667		}
5668	},
5669
5670	_update: function () {
5671		var map = this._map,
5672		    y = map.getSize().y / 2;
5673
5674		var maxMeters = map.distance(
5675			map.containerPointToLatLng([0, y]),
5676			map.containerPointToLatLng([this.options.maxWidth, y]));
5677
5678		this._updateScales(maxMeters);
5679	},
5680
5681	_updateScales: function (maxMeters) {
5682		if (this.options.metric && maxMeters) {
5683			this._updateMetric(maxMeters);
5684		}
5685		if (this.options.imperial && maxMeters) {
5686			this._updateImperial(maxMeters);
5687		}
5688	},
5689
5690	_updateMetric: function (maxMeters) {
5691		var meters = this._getRoundNum(maxMeters),
5692		    label = meters < 1000 ? meters + ' m' : (meters / 1000) + ' km';
5693
5694		this._updateScale(this._mScale, label, meters / maxMeters);
5695	},
5696
5697	_updateImperial: function (maxMeters) {
5698		var maxFeet = maxMeters * 3.2808399,
5699		    maxMiles, miles, feet;
5700
5701		if (maxFeet > 5280) {
5702			maxMiles = maxFeet / 5280;
5703			miles = this._getRoundNum(maxMiles);
5704			this._updateScale(this._iScale, miles + ' mi', miles / maxMiles);
5705
5706		} else {
5707			feet = this._getRoundNum(maxFeet);
5708			this._updateScale(this._iScale, feet + ' ft', feet / maxFeet);
5709		}
5710	},
5711
5712	_updateScale: function (scale, text, ratio) {
5713		scale.style.width = Math.round(this.options.maxWidth * ratio) + 'px';
5714		scale.innerHTML = text;
5715	},
5716
5717	_getRoundNum: function (num) {
5718		var pow10 = Math.pow(10, (Math.floor(num) + '').length - 1),
5719		    d = num / pow10;
5720
5721		d = d >= 10 ? 10 :
5722		    d >= 5 ? 5 :
5723		    d >= 3 ? 3 :
5724		    d >= 2 ? 2 : 1;
5725
5726		return pow10 * d;
5727	}
5728});
5729
5730
5731// @factory L.control.scale(options?: Control.Scale options)
5732// Creates an scale control with the given options.
5733var scale = function (options) {
5734	return new Scale(options);
5735};
5736
5737var 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>';
5738
5739
5740/*
5741 * @class Control.Attribution
5742 * @aka L.Control.Attribution
5743 * @inherits Control
5744 *
5745 * 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.
5746 */
5747
5748var Attribution = Control.extend({
5749	// @section
5750	// @aka Control.Attribution options
5751	options: {
5752		position: 'bottomright',
5753
5754		// @option prefix: String|false = 'Leaflet'
5755		// The HTML text shown before the attributions. Pass `false` to disable.
5756		prefix: '<a href="https://leafletjs.com" title="A JavaScript library for interactive maps">' + (Browser.inlineSvg ? ukrainianFlag + ' ' : '') + 'Leaflet</a>'
5757	},
5758
5759	initialize: function (options) {
5760		setOptions(this, options);
5761
5762		this._attributions = {};
5763	},
5764
5765	onAdd: function (map) {
5766		map.attributionControl = this;
5767		this._container = create$1('div', 'leaflet-control-attribution');
5768		disableClickPropagation(this._container);
5769
5770		// TODO ugly, refactor
5771		for (var i in map._layers) {
5772			if (map._layers[i].getAttribution) {
5773				this.addAttribution(map._layers[i].getAttribution());
5774			}
5775		}
5776
5777		this._update();
5778
5779		map.on('layeradd', this._addAttribution, this);
5780
5781		return this._container;
5782	},
5783
5784	onRemove: function (map) {
5785		map.off('layeradd', this._addAttribution, this);
5786	},
5787
5788	_addAttribution: function (ev) {
5789		if (ev.layer.getAttribution) {
5790			this.addAttribution(ev.layer.getAttribution());
5791			ev.layer.once('remove', function () {
5792				this.removeAttribution(ev.layer.getAttribution());
5793			}, this);
5794		}
5795	},
5796
5797	// @method setPrefix(prefix: String|false): this
5798	// The HTML text shown before the attributions. Pass `false` to disable.
5799	setPrefix: function (prefix) {
5800		this.options.prefix = prefix;
5801		this._update();
5802		return this;
5803	},
5804
5805	// @method addAttribution(text: String): this
5806	// Adds an attribution text (e.g. `'&copy; OpenStreetMap contributors'`).
5807	addAttribution: function (text) {
5808		if (!text) { return this; }
5809
5810		if (!this._attributions[text]) {
5811			this._attributions[text] = 0;
5812		}
5813		this._attributions[text]++;
5814
5815		this._update();
5816
5817		return this;
5818	},
5819
5820	// @method removeAttribution(text: String): this
5821	// Removes an attribution text.
5822	removeAttribution: function (text) {
5823		if (!text) { return this; }
5824
5825		if (this._attributions[text]) {
5826			this._attributions[text]--;
5827			this._update();
5828		}
5829
5830		return this;
5831	},
5832
5833	_update: function () {
5834		if (!this._map) { return; }
5835
5836		var attribs = [];
5837
5838		for (var i in this._attributions) {
5839			if (this._attributions[i]) {
5840				attribs.push(i);
5841			}
5842		}
5843
5844		var prefixAndAttribs = [];
5845
5846		if (this.options.prefix) {
5847			prefixAndAttribs.push(this.options.prefix);
5848		}
5849		if (attribs.length) {
5850			prefixAndAttribs.push(attribs.join(', '));
5851		}
5852
5853		this._container.innerHTML = prefixAndAttribs.join(' <span aria-hidden="true">|</span> ');
5854	}
5855});
5856
5857// @namespace Map
5858// @section Control options
5859// @option attributionControl: Boolean = true
5860// Whether a [attribution control](#control-attribution) is added to the map by default.
5861Map.mergeOptions({
5862	attributionControl: true
5863});
5864
5865Map.addInitHook(function () {
5866	if (this.options.attributionControl) {
5867		new Attribution().addTo(this);
5868	}
5869});
5870
5871// @namespace Control.Attribution
5872// @factory L.control.attribution(options: Control.Attribution options)
5873// Creates an attribution control.
5874var attribution = function (options) {
5875	return new Attribution(options);
5876};
5877
5878Control.Layers = Layers;
5879Control.Zoom = Zoom;
5880Control.Scale = Scale;
5881Control.Attribution = Attribution;
5882
5883control.layers = layers;
5884control.zoom = zoom;
5885control.scale = scale;
5886control.attribution = attribution;
5887
5888/*
5889	L.Handler is a base class for handler classes that are used internally to inject
5890	interaction features like dragging to classes like Map and Marker.
5891*/
5892
5893// @class Handler
5894// @aka L.Handler
5895// Abstract class for map interaction handlers
5896
5897var Handler = Class.extend({
5898	initialize: function (map) {
5899		this._map = map;
5900	},
5901
5902	// @method enable(): this
5903	// Enables the handler
5904	enable: function () {
5905		if (this._enabled) { return this; }
5906
5907		this._enabled = true;
5908		this.addHooks();
5909		return this;
5910	},
5911
5912	// @method disable(): this
5913	// Disables the handler
5914	disable: function () {
5915		if (!this._enabled) { return this; }
5916
5917		this._enabled = false;
5918		this.removeHooks();
5919		return this;
5920	},
5921
5922	// @method enabled(): Boolean
5923	// Returns `true` if the handler is enabled
5924	enabled: function () {
5925		return !!this._enabled;
5926	}
5927
5928	// @section Extension methods
5929	// Classes inheriting from `Handler` must implement the two following methods:
5930	// @method addHooks()
5931	// Called when the handler is enabled, should add event hooks.
5932	// @method removeHooks()
5933	// Called when the handler is disabled, should remove the event hooks added previously.
5934});
5935
5936// @section There is static function which can be called without instantiating L.Handler:
5937// @function addTo(map: Map, name: String): this
5938// Adds a new Handler to the given map with the given name.
5939Handler.addTo = function (map, name) {
5940	map.addHandler(name, this);
5941	return this;
5942};
5943
5944var Mixin = {Events: Events};
5945
5946/*
5947 * @class Draggable
5948 * @aka L.Draggable
5949 * @inherits Evented
5950 *
5951 * A class for making DOM elements draggable (including touch support).
5952 * Used internally for map and marker dragging. Only works for elements
5953 * that were positioned with [`L.DomUtil.setPosition`](#domutil-setposition).
5954 *
5955 * @example
5956 * ```js
5957 * var draggable = new L.Draggable(elementToDrag);
5958 * draggable.enable();
5959 * ```
5960 */
5961
5962var START = Browser.touch ? 'touchstart mousedown' : 'mousedown';
5963
5964var Draggable = Evented.extend({
5965
5966	options: {
5967		// @section
5968		// @aka Draggable options
5969		// @option clickTolerance: Number = 3
5970		// The max number of pixels a user can shift the mouse pointer during a click
5971		// for it to be considered a valid click (as opposed to a mouse drag).
5972		clickTolerance: 3
5973	},
5974
5975	// @constructor L.Draggable(el: HTMLElement, dragHandle?: HTMLElement, preventOutline?: Boolean, options?: Draggable options)
5976	// Creates a `Draggable` object for moving `el` when you start dragging the `dragHandle` element (equals `el` itself by default).
5977	initialize: function (element, dragStartTarget, preventOutline, options) {
5978		setOptions(this, options);
5979
5980		this._element = element;
5981		this._dragStartTarget = dragStartTarget || element;
5982		this._preventOutline = preventOutline;
5983	},
5984
5985	// @method enable()
5986	// Enables the dragging ability
5987	enable: function () {
5988		if (this._enabled) { return; }
5989
5990		on(this._dragStartTarget, START, this._onDown, this);
5991
5992		this._enabled = true;
5993	},
5994
5995	// @method disable()
5996	// Disables the dragging ability
5997	disable: function () {
5998		if (!this._enabled) { return; }
5999
6000		// If we're currently dragging this draggable,
6001		// disabling it counts as first ending the drag.
6002		if (Draggable._dragging === this) {
6003			this.finishDrag(true);
6004		}
6005
6006		off(this._dragStartTarget, START, this._onDown, this);
6007
6008		this._enabled = false;
6009		this._moved = false;
6010	},
6011
6012	_onDown: function (e) {
6013		// Ignore the event if disabled; this happens in IE11
6014		// under some circumstances, see #3666.
6015		if (!this._enabled) { return; }
6016
6017		this._moved = false;
6018
6019		if (hasClass(this._element, 'leaflet-zoom-anim')) { return; }
6020
6021		if (e.touches && e.touches.length !== 1) {
6022			// Finish dragging to avoid conflict with touchZoom
6023			if (Draggable._dragging === this) {
6024				this.finishDrag();
6025			}
6026			return;
6027		}
6028
6029		if (Draggable._dragging || e.shiftKey || ((e.which !== 1) && (e.button !== 1) && !e.touches)) { return; }
6030		Draggable._dragging = this;  // Prevent dragging multiple objects at once.
6031
6032		if (this._preventOutline) {
6033			preventOutline(this._element);
6034		}
6035
6036		disableImageDrag();
6037		disableTextSelection();
6038
6039		if (this._moving) { return; }
6040
6041		// @event down: Event
6042		// Fired when a drag is about to start.
6043		this.fire('down');
6044
6045		var first = e.touches ? e.touches[0] : e,
6046		    sizedParent = getSizedParentNode(this._element);
6047
6048		this._startPoint = new Point(first.clientX, first.clientY);
6049		this._startPos = getPosition(this._element);
6050
6051		// Cache the scale, so that we can continuously compensate for it during drag (_onMove).
6052		this._parentScale = getScale(sizedParent);
6053
6054		var mouseevent = e.type === 'mousedown';
6055		on(document, mouseevent ? 'mousemove' : 'touchmove', this._onMove, this);
6056		on(document, mouseevent ? 'mouseup' : 'touchend touchcancel', this._onUp, this);
6057	},
6058
6059	_onMove: function (e) {
6060		// Ignore the event if disabled; this happens in IE11
6061		// under some circumstances, see #3666.
6062		if (!this._enabled) { return; }
6063
6064		if (e.touches && e.touches.length > 1) {
6065			this._moved = true;
6066			return;
6067		}
6068
6069		var first = (e.touches && e.touches.length === 1 ? e.touches[0] : e),
6070		    offset = new Point(first.clientX, first.clientY)._subtract(this._startPoint);
6071
6072		if (!offset.x && !offset.y) { return; }
6073		if (Math.abs(offset.x) + Math.abs(offset.y) < this.options.clickTolerance) { return; }
6074
6075		// We assume that the parent container's position, border and scale do not change for the duration of the drag.
6076		// Therefore there is no need to account for the position and border (they are eliminated by the subtraction)
6077		// and we can use the cached value for the scale.
6078		offset.x /= this._parentScale.x;
6079		offset.y /= this._parentScale.y;
6080
6081		preventDefault(e);
6082
6083		if (!this._moved) {
6084			// @event dragstart: Event
6085			// Fired when a drag starts
6086			this.fire('dragstart');
6087
6088			this._moved = true;
6089
6090			addClass(document.body, 'leaflet-dragging');
6091
6092			this._lastTarget = e.target || e.srcElement;
6093			// IE and Edge do not give the <use> element, so fetch it
6094			// if necessary
6095			if (window.SVGElementInstance && this._lastTarget instanceof window.SVGElementInstance) {
6096				this._lastTarget = this._lastTarget.correspondingUseElement;
6097			}
6098			addClass(this._lastTarget, 'leaflet-drag-target');
6099		}
6100
6101		this._newPos = this._startPos.add(offset);
6102		this._moving = true;
6103
6104		this._lastEvent = e;
6105		this._updatePosition();
6106	},
6107
6108	_updatePosition: function () {
6109		var e = {originalEvent: this._lastEvent};
6110
6111		// @event predrag: Event
6112		// Fired continuously during dragging *before* each corresponding
6113		// update of the element's position.
6114		this.fire('predrag', e);
6115		setPosition(this._element, this._newPos);
6116
6117		// @event drag: Event
6118		// Fired continuously during dragging.
6119		this.fire('drag', e);
6120	},
6121
6122	_onUp: function () {
6123		// Ignore the event if disabled; this happens in IE11
6124		// under some circumstances, see #3666.
6125		if (!this._enabled) { return; }
6126		this.finishDrag();
6127	},
6128
6129	finishDrag: function (noInertia) {
6130		removeClass(document.body, 'leaflet-dragging');
6131
6132		if (this._lastTarget) {
6133			removeClass(this._lastTarget, 'leaflet-drag-target');
6134			this._lastTarget = null;
6135		}
6136
6137		off(document, 'mousemove touchmove', this._onMove, this);
6138		off(document, 'mouseup touchend touchcancel', this._onUp, this);
6139
6140		enableImageDrag();
6141		enableTextSelection();
6142
6143		var fireDragend = this._moved && this._moving;
6144
6145		this._moving = false;
6146		Draggable._dragging = false;
6147
6148		if (fireDragend) {
6149			// @event dragend: DragEndEvent
6150			// Fired when the drag ends.
6151			this.fire('dragend', {
6152				noInertia: noInertia,
6153				distance: this._newPos.distanceTo(this._startPos)
6154			});
6155		}
6156	}
6157
6158});
6159
6160/*
6161 * @namespace PolyUtil
6162 * Various utility functions for polygon geometries.
6163 */
6164
6165/* @function clipPolygon(points: Point[], bounds: Bounds, round?: Boolean): Point[]
6166 * 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)).
6167 * Used by Leaflet to only show polygon points that are on the screen or near, increasing
6168 * performance. Note that polygon points needs different algorithm for clipping
6169 * than polyline, so there's a separate method for it.
6170 */
6171function clipPolygon(points, bounds, round) {
6172	var clippedPoints,
6173	    edges = [1, 4, 2, 8],
6174	    i, j, k,
6175	    a, b,
6176	    len, edge, p;
6177
6178	for (i = 0, len = points.length; i < len; i++) {
6179		points[i]._code = _getBitCode(points[i], bounds);
6180	}
6181
6182	// for each edge (left, bottom, right, top)
6183	for (k = 0; k < 4; k++) {
6184		edge = edges[k];
6185		clippedPoints = [];
6186
6187		for (i = 0, len = points.length, j = len - 1; i < len; j = i++) {
6188			a = points[i];
6189			b = points[j];
6190
6191			// if a is inside the clip window
6192			if (!(a._code & edge)) {
6193				// if b is outside the clip window (a->b goes out of screen)
6194				if (b._code & edge) {
6195					p = _getEdgeIntersection(b, a, edge, bounds, round);
6196					p._code = _getBitCode(p, bounds);
6197					clippedPoints.push(p);
6198				}
6199				clippedPoints.push(a);
6200
6201			// else if b is inside the clip window (a->b enters the screen)
6202			} else if (!(b._code & edge)) {
6203				p = _getEdgeIntersection(b, a, edge, bounds, round);
6204				p._code = _getBitCode(p, bounds);
6205				clippedPoints.push(p);
6206			}
6207		}
6208		points = clippedPoints;
6209	}
6210
6211	return points;
6212}
6213
6214/* @function polygonCenter(latlngs: LatLng[], crs: CRS): LatLng
6215 * Returns the center ([centroid](http://en.wikipedia.org/wiki/Centroid)) of the passed LatLngs (first ring) from a polygon.
6216 */
6217function polygonCenter(latlngs, crs) {
6218	var i, j, p1, p2, f, area, x, y, center;
6219
6220	if (!latlngs || latlngs.length === 0) {
6221		throw new Error('latlngs not passed');
6222	}
6223
6224	if (!isFlat(latlngs)) {
6225		console.warn('latlngs are not flat! Only the first ring will be used');
6226		latlngs = latlngs[0];
6227	}
6228
6229	var centroidLatLng = toLatLng([0, 0]);
6230
6231	var bounds = toLatLngBounds(latlngs);
6232	var areaBounds = bounds.getNorthWest().distanceTo(bounds.getSouthWest()) * bounds.getNorthEast().distanceTo(bounds.getNorthWest());
6233	// tests showed that below 1700 rounding errors are happening
6234	if (areaBounds < 1700) {
6235		// getting a inexact center, to move the latlngs near to [0, 0] to prevent rounding errors
6236		centroidLatLng = centroid(latlngs);
6237	}
6238
6239	var len = latlngs.length;
6240	var points = [];
6241	for (i = 0; i < len; i++) {
6242		var latlng = toLatLng(latlngs[i]);
6243		points.push(crs.project(toLatLng([latlng.lat - centroidLatLng.lat, latlng.lng - centroidLatLng.lng])));
6244	}
6245
6246	area = x = y = 0;
6247
6248	// polygon centroid algorithm;
6249	for (i = 0, j = len - 1; i < len; j = i++) {
6250		p1 = points[i];
6251		p2 = points[j];
6252
6253		f = p1.y * p2.x - p2.y * p1.x;
6254		x += (p1.x + p2.x) * f;
6255		y += (p1.y + p2.y) * f;
6256		area += f * 3;
6257	}
6258
6259	if (area === 0) {
6260		// Polygon is so small that all points are on same pixel.
6261		center = points[0];
6262	} else {
6263		center = [x / area, y / area];
6264	}
6265
6266	var latlngCenter = crs.unproject(toPoint(center));
6267	return toLatLng([latlngCenter.lat + centroidLatLng.lat, latlngCenter.lng + centroidLatLng.lng]);
6268}
6269
6270/* @function centroid(latlngs: LatLng[]): LatLng
6271 * Returns the 'center of mass' of the passed LatLngs.
6272 */
6273function centroid(coords) {
6274	var latSum = 0;
6275	var lngSum = 0;
6276	var len = 0;
6277	for (var i = 0; i < coords.length; i++) {
6278		var latlng = toLatLng(coords[i]);
6279		latSum += latlng.lat;
6280		lngSum += latlng.lng;
6281		len++;
6282	}
6283	return toLatLng([latSum / len, lngSum / len]);
6284}
6285
6286var PolyUtil = {
6287  __proto__: null,
6288  clipPolygon: clipPolygon,
6289  polygonCenter: polygonCenter,
6290  centroid: centroid
6291};
6292
6293/*
6294 * @namespace LineUtil
6295 *
6296 * Various utility functions for polyline points processing, used by Leaflet internally to make polylines lightning-fast.
6297 */
6298
6299// Simplify polyline with vertex reduction and Douglas-Peucker simplification.
6300// Improves rendering performance dramatically by lessening the number of points to draw.
6301
6302// @function simplify(points: Point[], tolerance: Number): Point[]
6303// Dramatically reduces the number of points in a polyline while retaining
6304// its shape and returns a new array of simplified points, using the
6305// [Ramer-Douglas-Peucker algorithm](https://en.wikipedia.org/wiki/Ramer-Douglas-Peucker_algorithm).
6306// Used for a huge performance boost when processing/displaying Leaflet polylines for
6307// each zoom level and also reducing visual noise. tolerance affects the amount of
6308// simplification (lesser value means higher quality but slower and with more points).
6309// Also released as a separated micro-library [Simplify.js](https://mourner.github.io/simplify-js/).
6310function simplify(points, tolerance) {
6311	if (!tolerance || !points.length) {
6312		return points.slice();
6313	}
6314
6315	var sqTolerance = tolerance * tolerance;
6316
6317	    // stage 1: vertex reduction
6318	    points = _reducePoints(points, sqTolerance);
6319
6320	    // stage 2: Douglas-Peucker simplification
6321	    points = _simplifyDP(points, sqTolerance);
6322
6323	return points;
6324}
6325
6326// @function pointToSegmentDistance(p: Point, p1: Point, p2: Point): Number
6327// Returns the distance between point `p` and segment `p1` to `p2`.
6328function pointToSegmentDistance(p, p1, p2) {
6329	return Math.sqrt(_sqClosestPointOnSegment(p, p1, p2, true));
6330}
6331
6332// @function closestPointOnSegment(p: Point, p1: Point, p2: Point): Number
6333// Returns the closest point from a point `p` on a segment `p1` to `p2`.
6334function closestPointOnSegment(p, p1, p2) {
6335	return _sqClosestPointOnSegment(p, p1, p2);
6336}
6337
6338// Ramer-Douglas-Peucker simplification, see https://en.wikipedia.org/wiki/Ramer-Douglas-Peucker_algorithm
6339function _simplifyDP(points, sqTolerance) {
6340
6341	var len = points.length,
6342	    ArrayConstructor = typeof Uint8Array !== undefined + '' ? Uint8Array : Array,
6343	    markers = new ArrayConstructor(len);
6344
6345	    markers[0] = markers[len - 1] = 1;
6346
6347	_simplifyDPStep(points, markers, sqTolerance, 0, len - 1);
6348
6349	var i,
6350	    newPoints = [];
6351
6352	for (i = 0; i < len; i++) {
6353		if (markers[i]) {
6354			newPoints.push(points[i]);
6355		}
6356	}
6357
6358	return newPoints;
6359}
6360
6361function _simplifyDPStep(points, markers, sqTolerance, first, last) {
6362
6363	var maxSqDist = 0,
6364	index, i, sqDist;
6365
6366	for (i = first + 1; i <= last - 1; i++) {
6367		sqDist = _sqClosestPointOnSegment(points[i], points[first], points[last], true);
6368
6369		if (sqDist > maxSqDist) {
6370			index = i;
6371			maxSqDist = sqDist;
6372		}
6373	}
6374
6375	if (maxSqDist > sqTolerance) {
6376		markers[index] = 1;
6377
6378		_simplifyDPStep(points, markers, sqTolerance, first, index);
6379		_simplifyDPStep(points, markers, sqTolerance, index, last);
6380	}
6381}
6382
6383// reduce points that are too close to each other to a single point
6384function _reducePoints(points, sqTolerance) {
6385	var reducedPoints = [points[0]];
6386
6387	for (var i = 1, prev = 0, len = points.length; i < len; i++) {
6388		if (_sqDist(points[i], points[prev]) > sqTolerance) {
6389			reducedPoints.push(points[i]);
6390			prev = i;
6391		}
6392	}
6393	if (prev < len - 1) {
6394		reducedPoints.push(points[len - 1]);
6395	}
6396	return reducedPoints;
6397}
6398
6399var _lastCode;
6400
6401// @function clipSegment(a: Point, b: Point, bounds: Bounds, useLastCode?: Boolean, round?: Boolean): Point[]|Boolean
6402// Clips the segment a to b by rectangular bounds with the
6403// [Cohen-Sutherland algorithm](https://en.wikipedia.org/wiki/Cohen%E2%80%93Sutherland_algorithm)
6404// (modifying the segment points directly!). Used by Leaflet to only show polyline
6405// points that are on the screen or near, increasing performance.
6406function clipSegment(a, b, bounds, useLastCode, round) {
6407	var codeA = useLastCode ? _lastCode : _getBitCode(a, bounds),
6408	    codeB = _getBitCode(b, bounds),
6409
6410	    codeOut, p, newCode;
6411
6412	    // save 2nd code to avoid calculating it on the next segment
6413	    _lastCode = codeB;
6414
6415	while (true) {
6416		// if a,b is inside the clip window (trivial accept)
6417		if (!(codeA | codeB)) {
6418			return [a, b];
6419		}
6420
6421		// if a,b is outside the clip window (trivial reject)
6422		if (codeA & codeB) {
6423			return false;
6424		}
6425
6426		// other cases
6427		codeOut = codeA || codeB;
6428		p = _getEdgeIntersection(a, b, codeOut, bounds, round);
6429		newCode = _getBitCode(p, bounds);
6430
6431		if (codeOut === codeA) {
6432			a = p;
6433			codeA = newCode;
6434		} else {
6435			b = p;
6436			codeB = newCode;
6437		}
6438	}
6439}
6440
6441function _getEdgeIntersection(a, b, code, bounds, round) {
6442	var dx = b.x - a.x,
6443	    dy = b.y - a.y,
6444	    min = bounds.min,
6445	    max = bounds.max,
6446	    x, y;
6447
6448	if (code & 8) { // top
6449		x = a.x + dx * (max.y - a.y) / dy;
6450		y = max.y;
6451
6452	} else if (code & 4) { // bottom
6453		x = a.x + dx * (min.y - a.y) / dy;
6454		y = min.y;
6455
6456	} else if (code & 2) { // right
6457		x = max.x;
6458		y = a.y + dy * (max.x - a.x) / dx;
6459
6460	} else if (code & 1) { // left
6461		x = min.x;
6462		y = a.y + dy * (min.x - a.x) / dx;
6463	}
6464
6465	return new Point(x, y, round);
6466}
6467
6468function _getBitCode(p, bounds) {
6469	var code = 0;
6470
6471	if (p.x < bounds.min.x) { // left
6472		code |= 1;
6473	} else if (p.x > bounds.max.x) { // right
6474		code |= 2;
6475	}
6476
6477	if (p.y < bounds.min.y) { // bottom
6478		code |= 4;
6479	} else if (p.y > bounds.max.y) { // top
6480		code |= 8;
6481	}
6482
6483	return code;
6484}
6485
6486// square distance (to avoid unnecessary Math.sqrt calls)
6487function _sqDist(p1, p2) {
6488	var dx = p2.x - p1.x,
6489	    dy = p2.y - p1.y;
6490	return dx * dx + dy * dy;
6491}
6492
6493// return closest point on segment or distance to that point
6494function _sqClosestPointOnSegment(p, p1, p2, sqDist) {
6495	var x = p1.x,
6496	    y = p1.y,
6497	    dx = p2.x - x,
6498	    dy = p2.y - y,
6499	    dot = dx * dx + dy * dy,
6500	    t;
6501
6502	if (dot > 0) {
6503		t = ((p.x - x) * dx + (p.y - y) * dy) / dot;
6504
6505		if (t > 1) {
6506			x = p2.x;
6507			y = p2.y;
6508		} else if (t > 0) {
6509			x += dx * t;
6510			y += dy * t;
6511		}
6512	}
6513
6514	dx = p.x - x;
6515	dy = p.y - y;
6516
6517	return sqDist ? dx * dx + dy * dy : new Point(x, y);
6518}
6519
6520
6521// @function isFlat(latlngs: LatLng[]): Boolean
6522// Returns true if `latlngs` is a flat array, false is nested.
6523function isFlat(latlngs) {
6524	return !isArray(latlngs[0]) || (typeof latlngs[0][0] !== 'object' && typeof latlngs[0][0] !== 'undefined');
6525}
6526
6527function _flat(latlngs) {
6528	console.warn('Deprecated use of _flat, please use L.LineUtil.isFlat instead.');
6529	return isFlat(latlngs);
6530}
6531
6532/* @function polylineCenter(latlngs: LatLng[], crs: CRS): LatLng
6533 * Returns the center ([centroid](http://en.wikipedia.org/wiki/Centroid)) of the passed LatLngs (first ring) from a polyline.
6534 */
6535function polylineCenter(latlngs, crs) {
6536	var i, halfDist, segDist, dist, p1, p2, ratio, center;
6537
6538	if (!latlngs || latlngs.length === 0) {
6539		throw new Error('latlngs not passed');
6540	}
6541
6542	if (!isFlat(latlngs)) {
6543		console.warn('latlngs are not flat! Only the first ring will be used');
6544		latlngs = latlngs[0];
6545	}
6546
6547	var centroidLatLng = toLatLng([0, 0]);
6548
6549	var bounds = toLatLngBounds(latlngs);
6550	var areaBounds = bounds.getNorthWest().distanceTo(bounds.getSouthWest()) * bounds.getNorthEast().distanceTo(bounds.getNorthWest());
6551	// tests showed that below 1700 rounding errors are happening
6552	if (areaBounds < 1700) {
6553		// getting a inexact center, to move the latlngs near to [0, 0] to prevent rounding errors
6554		centroidLatLng = centroid(latlngs);
6555	}
6556
6557	var len = latlngs.length;
6558	var points = [];
6559	for (i = 0; i < len; i++) {
6560		var latlng = toLatLng(latlngs[i]);
6561		points.push(crs.project(toLatLng([latlng.lat - centroidLatLng.lat, latlng.lng - centroidLatLng.lng])));
6562	}
6563
6564	for (i = 0, halfDist = 0; i < len - 1; i++) {
6565		halfDist += points[i].distanceTo(points[i + 1]) / 2;
6566	}
6567
6568	// The line is so small in the current view that all points are on the same pixel.
6569	if (halfDist === 0) {
6570		center = points[0];
6571	} else {
6572		for (i = 0, dist = 0; i < len - 1; i++) {
6573			p1 = points[i];
6574			p2 = points[i + 1];
6575			segDist = p1.distanceTo(p2);
6576			dist += segDist;
6577
6578			if (dist > halfDist) {
6579				ratio = (dist - halfDist) / segDist;
6580				center = [
6581					p2.x - ratio * (p2.x - p1.x),
6582					p2.y - ratio * (p2.y - p1.y)
6583				];
6584				break;
6585			}
6586		}
6587	}
6588
6589	var latlngCenter = crs.unproject(toPoint(center));
6590	return toLatLng([latlngCenter.lat + centroidLatLng.lat, latlngCenter.lng + centroidLatLng.lng]);
6591}
6592
6593var LineUtil = {
6594  __proto__: null,
6595  simplify: simplify,
6596  pointToSegmentDistance: pointToSegmentDistance,
6597  closestPointOnSegment: closestPointOnSegment,
6598  clipSegment: clipSegment,
6599  _getEdgeIntersection: _getEdgeIntersection,
6600  _getBitCode: _getBitCode,
6601  _sqClosestPointOnSegment: _sqClosestPointOnSegment,
6602  isFlat: isFlat,
6603  _flat: _flat,
6604  polylineCenter: polylineCenter
6605};
6606
6607/*
6608 * @namespace Projection
6609 * @section
6610 * Leaflet comes with a set of already defined Projections out of the box:
6611 *
6612 * @projection L.Projection.LonLat
6613 *
6614 * Equirectangular, or Plate Carree projection — the most simple projection,
6615 * mostly used by GIS enthusiasts. Directly maps `x` as longitude, and `y` as
6616 * latitude. Also suitable for flat worlds, e.g. game maps. Used by the
6617 * `EPSG:4326` and `Simple` CRS.
6618 */
6619
6620var LonLat = {
6621	project: function (latlng) {
6622		return new Point(latlng.lng, latlng.lat);
6623	},
6624
6625	unproject: function (point) {
6626		return new LatLng(point.y, point.x);
6627	},
6628
6629	bounds: new Bounds([-180, -90], [180, 90])
6630};
6631
6632/*
6633 * @namespace Projection
6634 * @projection L.Projection.Mercator
6635 *
6636 * Elliptical Mercator projection — more complex than Spherical Mercator. Assumes that Earth is an ellipsoid. Used by the EPSG:3395 CRS.
6637 */
6638
6639var Mercator = {
6640	R: 6378137,
6641	R_MINOR: 6356752.314245179,
6642
6643	bounds: new Bounds([-20037508.34279, -15496570.73972], [20037508.34279, 18764656.23138]),
6644
6645	project: function (latlng) {
6646		var d = Math.PI / 180,
6647		    r = this.R,
6648		    y = latlng.lat * d,
6649		    tmp = this.R_MINOR / r,
6650		    e = Math.sqrt(1 - tmp * tmp),
6651		    con = e * Math.sin(y);
6652
6653		var ts = Math.tan(Math.PI / 4 - y / 2) / Math.pow((1 - con) / (1 + con), e / 2);
6654		y = -r * Math.log(Math.max(ts, 1E-10));
6655
6656		return new Point(latlng.lng * d * r, y);
6657	},
6658
6659	unproject: function (point) {
6660		var d = 180 / Math.PI,
6661		    r = this.R,
6662		    tmp = this.R_MINOR / r,
6663		    e = Math.sqrt(1 - tmp * tmp),
6664		    ts = Math.exp(-point.y / r),
6665		    phi = Math.PI / 2 - 2 * Math.atan(ts);
6666
6667		for (var i = 0, dphi = 0.1, con; i < 15 && Math.abs(dphi) > 1e-7; i++) {
6668			con = e * Math.sin(phi);
6669			con = Math.pow((1 - con) / (1 + con), e / 2);
6670			dphi = Math.PI / 2 - 2 * Math.atan(ts * con) - phi;
6671			phi += dphi;
6672		}
6673
6674		return new LatLng(phi * d, point.x * d / r);
6675	}
6676};
6677
6678/*
6679 * @class Projection
6680
6681 * An object with methods for projecting geographical coordinates of the world onto
6682 * a flat surface (and back). See [Map projection](https://en.wikipedia.org/wiki/Map_projection).
6683
6684 * @property bounds: Bounds
6685 * The bounds (specified in CRS units) where the projection is valid
6686
6687 * @method project(latlng: LatLng): Point
6688 * Projects geographical coordinates into a 2D point.
6689 * Only accepts actual `L.LatLng` instances, not arrays.
6690
6691 * @method unproject(point: Point): LatLng
6692 * The inverse of `project`. Projects a 2D point into a geographical location.
6693 * Only accepts actual `L.Point` instances, not arrays.
6694
6695 * Note that the projection instances do not inherit from Leaflet's `Class` object,
6696 * and can't be instantiated. Also, new classes can't inherit from them,
6697 * and methods can't be added to them with the `include` function.
6698
6699 */
6700
6701var index = {
6702  __proto__: null,
6703  LonLat: LonLat,
6704  Mercator: Mercator,
6705  SphericalMercator: SphericalMercator
6706};
6707
6708/*
6709 * @namespace CRS
6710 * @crs L.CRS.EPSG3395
6711 *
6712 * Rarely used by some commercial tile providers. Uses Elliptical Mercator projection.
6713 */
6714var EPSG3395 = extend({}, Earth, {
6715	code: 'EPSG:3395',
6716	projection: Mercator,
6717
6718	transformation: (function () {
6719		var scale = 0.5 / (Math.PI * Mercator.R);
6720		return toTransformation(scale, 0.5, -scale, 0.5);
6721	}())
6722});
6723
6724/*
6725 * @namespace CRS
6726 * @crs L.CRS.EPSG4326
6727 *
6728 * A common CRS among GIS enthusiasts. Uses simple Equirectangular projection.
6729 *
6730 * Leaflet 1.0.x complies with the [TMS coordinate scheme for EPSG:4326](https://wiki.osgeo.org/wiki/Tile_Map_Service_Specification#global-geodetic),
6731 * which is a breaking change from 0.7.x behaviour.  If you are using a `TileLayer`
6732 * with this CRS, ensure that there are two 256x256 pixel tiles covering the
6733 * whole earth at zoom level zero, and that the tile coordinate origin 
6733is (-180,+90),
6734 * or (-180,-90) for `TileLayer`s with [the `tms` option](#tilelayer-tms) set.
6735 */
6736
6737var EPSG4326 = extend({}, Earth, {
6738	code: 'EPSG:4326',
6739	projection: LonLat,
6740	transformation: toTransformation(1 / 180, 1, -1 / 180, 0.5)
vendor: 8,645 bytes, lines 6740-7054
6740
6741});
6742
6743/*
6744 * @namespace CRS
6745 * @crs L.CRS.Simple
6746 *
6747 * A simple CRS that maps longitude and latitude into `x` and `y` directly.
6748 * May be used for maps of flat surfaces (e.g. game maps). Note that the `y`
6749 * axis should still be inverted (going from bottom to top). `distance()` returns
6750 * simple euclidean distance.
6751 */
6752
6753var Simple = extend({}, CRS, {
6754	projection: LonLat,
6755	transformation: toTransformation(1, 0, -1, 0),
6756
6757	scale: function (zoom) {
6758		return Math.pow(2, zoom);
6759	},
6760
6761	zoom: function (scale) {
6762		return Math.log(scale) / Math.LN2;
6763	},
6764
6765	distance: function (latlng1, latlng2) {
6766		var dx = latlng2.lng - latlng1.lng,
6767		    dy = latlng2.lat - latlng1.lat;
6768
6769		return Math.sqrt(dx * dx + dy * dy);
6770	},
6771
6772	infinite: true
6773});
6774
6775CRS.Earth = Earth;
6776CRS.EPSG3395 = EPSG3395;
6777CRS.EPSG3857 = EPSG3857;
6778CRS.EPSG900913 = EPSG900913;
6779CRS.EPSG4326 = EPSG4326;
6780CRS.Simple = Simple;
6781
6782/*
6783 * @class Layer
6784 * @inherits Evented
6785 * @aka L.Layer
6786 * @aka ILayer
6787 *
6788 * A set of methods from the Layer base class that all Leaflet layers use.
6789 * Inherits all methods, options and events from `L.Evented`.
6790 *
6791 * @example
6792 *
6793 * ```js
6794 * var layer = L.marker(latlng).addTo(map);
6795 * layer.addTo(map);
6796 * layer.remove();
6797 * ```
6798 *
6799 * @event add: Event
6800 * Fired after the layer is added to a map
6801 *
6802 * @event remove: Event
6803 * Fired after the layer is removed from a map
6804 */
6805
6806
6807var Layer = Evented.extend({
6808
6809	// Classes extending `L.Layer` will inherit the following options:
6810	options: {
6811		// @option pane: String = 'overlayPane'
6812		// 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.
6813		pane: 'overlayPane',
6814
6815		// @option attribution: String = null
6816		// 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.
6817		attribution: null,
6818
6819		bubblingMouseEvents: true
6820	},
6821
6822	/* @section
6823	 * Classes extending `L.Layer` will inherit the following methods:
6824	 *
6825	 * @method addTo(map: Map|LayerGroup): this
6826	 * Adds the layer to the given map or layer group.
6827	 */
6828	addTo: function (map) {
6829		map.addLayer(this);
6830		return this;
6831	},
6832
6833	// @method remove: this
6834	// Removes the layer from the map it is currently active on.
6835	remove: function () {
6836		return this.removeFrom(this._map || this._mapToAdd);
6837	},
6838
6839	// @method removeFrom(map: Map): this
6840	// Removes the layer from the given map
6841	//
6842	// @alternative
6843	// @method removeFrom(group: LayerGroup): this
6844	// Removes the layer from the given `LayerGroup`
6845	removeFrom: function (obj) {
6846		if (obj) {
6847			obj.removeLayer(this);
6848		}
6849		return this;
6850	},
6851
6852	// @method getPane(name? : String): HTMLElement
6853	// Returns the `HTMLElement` representing the named pane on the map. If `name` is omitted, returns the pane for this layer.
6854	getPane: function (name) {
6855		return this._map.getPane(name ? (this.options[name] || name) : this.options.pane);
6856	},
6857
6858	addInteractiveTarget: function (targetEl) {
6859		this._map._targets[stamp(targetEl)] = this;
6860		return this;
6861	},
6862
6863	removeInteractiveTarget: function (targetEl) {
6864		delete this._map._targets[stamp(targetEl)];
6865		return this;
6866	},
6867
6868	// @method getAttribution: String
6869	// Used by the `attribution control`, returns the [attribution option](#gridlayer-attribution).
6870	getAttribution: function () {
6871		return this.options.attribution;
6872	},
6873
6874	_layerAdd: function (e) {
6875		var map = e.target;
6876
6877		// check in case layer gets added and then removed before the map is ready
6878		if (!map.hasLayer(this)) { return; }
6879
6880		this._map = map;
6881		this._zoomAnimated = map._zoomAnimated;
6882
6883		if (this.getEvents) {
6884			var events = this.getEvents();
6885			map.on(events, this);
6886			this.once('remove', function () {
6887				map.off(events, this);
6888			}, this);
6889		}
6890
6891		this.onAdd(map);
6892
6893		this.fire('add');
6894		map.fire('layeradd', {layer: this});
6895	}
6896});
6897
6898/* @section Extension methods
6899 * @uninheritable
6900 *
6901 * Every layer should extend from `L.Layer` and (re-)implement the following methods.
6902 *
6903 * @method onAdd(map: Map): this
6904 * 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).
6905 *
6906 * @method onRemove(map: Map): this
6907 * 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).
6908 *
6909 * @method getEvents(): Object
6910 * 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.
6911 *
6912 * @method getAttribution(): String
6913 * This optional method should return a string containing HTML to be shown on the `Attribution control` whenever the layer is visible.
6914 *
6915 * @method beforeAdd(map: Map): this
6916 * 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.
6917 */
6918
6919
6920/* @namespace Map
6921 * @section Layer events
6922 *
6923 * @event layeradd: LayerEvent
6924 * Fired when a new layer is added to the map.
6925 *
6926 * @event layerremove: LayerEvent
6927 * Fired when some layer is removed from the map
6928 *
6929 * @section Methods for Layers and Controls
6930 */
6931Map.include({
6932	// @method addLayer(layer: Layer): this
6933	// Adds the given layer to the map
6934	addLayer: function (layer) {
6935		if (!layer._layerAdd) {
6936			throw new Error('The provided object is not a Layer.');
6937		}
6938
6939		var id = stamp(layer);
6940		if (this._layers[id]) { return this; }
6941		this._layers[id] = layer;
6942
6943		layer._mapToAdd = this;
6944
6945		if (layer.beforeAdd) {
6946			layer.beforeAdd(this);
6947		}
6948
6949		this.whenReady(layer._layerAdd, layer);
6950
6951		return this;
6952	},
6953
6954	// @method removeLayer(layer: Layer): this
6955	// Removes the given layer from the map.
6956	removeLayer: function (layer) {
6957		var id = stamp(layer);
6958
6959		if (!this._layers[id]) { return this; }
6960
6961		if (this._loaded) {
6962			layer.onRemove(this);
6963		}
6964
6965		delete this._layers[id];
6966
6967		if (this._loaded) {
6968			this.fire('layerremove', {layer: layer});
6969			layer.fire('remove');
6970		}
6971
6972		layer._map = layer._mapToAdd = null;
6973
6974		return this;
6975	},
6976
6977	// @method hasLayer(layer: Layer): Boolean
6978	// Returns `true` if the given layer is currently added to the map
6979	hasLayer: function (layer) {
6980		return stamp(layer) in this._layers;
6981	},
6982
6983	/* @method eachLayer(fn: Function, context?: Object): this
6984	 * Iterates over the layers of the map, optionally specifying context of the iterator function.
6985	 * ```
6986	 * map.eachLayer(function(layer){
6987	 *     layer.bindPopup('Hello');
6988	 * });
6989	 * ```
6990	 */
6991	eachLayer: function (method, context) {
6992		for (var i in this._layers) {
6993			method.call(context, this._layers[i]);
6994		}
6995		return this;
6996	},
6997
6998	_addLayers: function (layers) {
6999		layers = layers ? (isArray(layers) ? layers : [layers]) : [];
7000
7001		for (var i = 0, len = layers.length; i < len; i++) {
7002			this.addLayer(layers[i]);
7003		}
7004	},
7005
7006	_addZoomLimit: function (layer) {
7007		if (!isNaN(layer.options.maxZoom) || !isNaN(layer.options.minZoom)) {
7008			this._zoomBoundLayers[stamp(layer)] = layer;
7009			this._updateZoomLevels();
7010		}
7011	},
7012
7013	_removeZoomLimit: function (layer) {
7014		var id = stamp(layer);
7015
7016		if (this._zoomBoundLayers[id]) {
7017			delete this._zoomBoundLayers[id];
7018			this._updateZoomLevels();
7019		}
7020	},
7021
7022	_updateZoomLevels: function () {
7023		var minZoom = Infinity,
7024		    maxZoom = -Infinity,
7025		    oldZoomSpan = this._getZoomSpan();
7026
7027		for (var i in this._zoomBoundLayers) {
7028			var options = this._zoomBoundLayers[i].options;
7029
7030			minZoom = options.minZoom === undefined ? minZoom : Math.min(minZoom, options.minZoom);
7031			maxZoom = options.maxZoom === undefined ? maxZoom : Math.max(maxZoom, options.maxZoom);
7032		}
7033
7034		this._layersMaxZoom = maxZoom === -Infinity ? undefined : maxZoom;
7035		this._layersMinZoom = minZoom === Infinity ? undefined : minZoom;
7036
7037		// @section Map state change events
7038		// @event zoomlevelschange: Event
7039		// Fired when the number of zoomlevels on the map is changed due
7040		// to adding or removing a layer.
7041		if (oldZoomSpan !== this._getZoomSpan()) {
7042			this.fire('zoomlevelschange');
7043		}
7044
7045		if (this.options.maxZoom === undefined && this._layersMaxZoom && this.getZoom() > this._layersMaxZoom) {
7046			this.setZoom(this._layersMaxZoom);
7047		}
7048		if (this.options.minZoom === undefined && this._layersMinZoom && this.getZoom() < this._layersMinZoom) {
7049			this.setZoom(this._layersMinZoom);
7050		}
7051	}
7052});
7053
7054/*
vendor: 27,523 bytes, lines 7054-8090
7054
7055 * @class LayerGroup
7056 * @aka L.LayerGroup
7057 * @inherits Interactive layer
7058 *
7059 * Used to group several layers and handle them as one. If you add it to the map,
7060 * any layers added or removed from the group will be added/removed on the map as
7061 * well. Extends `Layer`.
7062 *
7063 * @example
7064 *
7065 * ```js
7066 * L.layerGroup([marker1, marker2])
7067 * 	.addLayer(polyline)
7068 * 	.addTo(map);
7069 * ```
7070 */
7071
7072var LayerGroup = Layer.extend({
7073
7074	initialize: function (layers, options) {
7075		setOptions(this, options);
7076
7077		this._layers = {};
7078
7079		var i, len;
7080
7081		if (layers) {
7082			for (i = 0, len = layers.length; i < len; i++) {
7083				this.addLayer(layers[i]);
7084			}
7085		}
7086	},
7087
7088	// @method addLayer(layer: Layer): this
7089	// Adds the given layer to the group.
7090	addLayer: function (layer) {
7091		var id = this.getLayerId(layer);
7092
7093		this._layers[id] = layer;
7094
7095		if (this._map) {
7096			this._map.addLayer(layer);
7097		}
7098
7099		return this;
7100	},
7101
7102	// @method removeLayer(layer: Layer): this
7103	// Removes the given layer from the group.
7104	// @alternative
7105	// @method removeLayer(id: Number): this
7106	// Removes the layer with the given internal ID from the group.
7107	removeLayer: function (layer) {
7108		var id = layer in this._layers ? layer : this.getLayerId(layer);
7109
7110		if (this._map && this._layers[id]) {
7111			this._map.removeLayer(this._layers[id]);
7112		}
7113
7114		delete this._layers[id];
7115
7116		return this;
7117	},
7118
7119	// @method hasLayer(layer: Layer): Boolean
7120	// Returns `true` if the given layer is currently added to the group.
7121	// @alternative
7122	// @method hasLayer(id: Number): Boolean
7123	// Returns `true` if the given internal ID is currently added to the group.
7124	hasLayer: function (layer) {
7125		var layerId = typeof layer === 'number' ? layer : this.getLayerId(layer);
7126		return layerId in this._layers;
7127	},
7128
7129	// @method clearLayers(): this
7130	// Removes all the layers from the group.
7131	clearLayers: function () {
7132		return this.eachLayer(this.removeLayer, this);
7133	},
7134
7135	// @method invoke(methodName: String, …): this
7136	// Calls `methodName` on every layer contained in this group, passing any
7137	// additional parameters. Has no effect if the layers contained do not
7138	// implement `methodName`.
7139	invoke: function (methodName) {
7140		var args = Array.prototype.slice.call(arguments, 1),
7141		    i, layer;
7142
7143		for (i in this._layers) {
7144			layer = this._layers[i];
7145
7146			if (layer[methodName]) {
7147				layer[methodName].apply(layer, args);
7148			}
7149		}
7150
7151		return this;
7152	},
7153
7154	onAdd: function (map) {
7155		this.eachLayer(map.addLayer, map);
7156	},
7157
7158	onRemove: function (map) {
7159		this.eachLayer(map.removeLayer, map);
7160	},
7161
7162	// @method eachLayer(fn: Function, context?: Object): this
7163	// Iterates over the layers of the group, optionally specifying context of the iterator function.
7164	// ```js
7165	// group.eachLayer(function (layer) {
7166	// 	layer.bindPopup('Hello');
7167	// });
7168	// ```
7169	eachLayer: function (method, context) {
7170		for (var i in this._layers) {
7171			method.call(context, this._layers[i]);
7172		}
7173		return this;
7174	},
7175
7176	// @method getLayer(id: Number): Layer
7177	// Returns the layer with the given internal ID.
7178	getLayer: function (id) {
7179		return this._layers[id];
7180	},
7181
7182	// @method getLayers(): Layer[]
7183	// Returns an array of all the layers added to the group.
7184	getLayers: function () {
7185		var layers = [];
7186		this.eachLayer(layers.push, layers);
7187		return layers;
7188	},
7189
7190	// @method setZIndex(zIndex: Number): this
7191	// Calls `setZIndex` on every layer contained in this group, passing the z-index.
7192	setZIndex: function (zIndex) {
7193		return this.invoke('setZIndex', zIndex);
7194	},
7195
7196	// @method getLayerId(layer: Layer): Number
7197	// Returns the internal ID for a layer
7198	getLayerId: function (layer) {
7199		return stamp(layer);
7200	}
7201});
7202
7203
7204// @factory L.layerGroup(layers?: Layer[], options?: Object)
7205// Create a layer group, optionally given an initial set of layers and an `options` object.
7206var layerGroup = function (layers, options) {
7207	return new LayerGroup(layers, options);
7208};
7209
7210/*
7211 * @class FeatureGroup
7212 * @aka L.FeatureGroup
7213 * @inherits LayerGroup
7214 *
7215 * Extended `LayerGroup` that makes it easier to do the same thing to all its member layers:
7216 *  * [`bindPopup`](#layer-bindpopup) binds a popup to all of the layers at once (likewise with [`bindTooltip`](#layer-bindtooltip))
7217 *  * Events are propagated to the `FeatureGroup`, so if the group has an event
7218 * handler, it will handle events from any of the layers. This includes mouse events
7219 * and custom events.
7220 *  * Has `layeradd` and `layerremove` events
7221 *
7222 * @example
7223 *
7224 * ```js
7225 * L.featureGroup([marker1, marker2, polyline])
7226 * 	.bindPopup('Hello world!')
7227 * 	.on('click', function() { alert('Clicked on a member of the group!'); })
7228 * 	.addTo(map);
7229 * ```
7230 */
7231
7232var FeatureGroup = LayerGroup.extend({
7233
7234	addLayer: function (layer) {
7235		if (this.hasLayer(layer)) {
7236			return this;
7237		}
7238
7239		layer.addEventParent(this);
7240
7241		LayerGroup.prototype.addLayer.call(this, layer);
7242
7243		// @event layeradd: LayerEvent
7244		// Fired when a layer is added to this `FeatureGroup`
7245		return this.fire('layeradd', {layer: layer});
7246	},
7247
7248	removeLayer: function (layer) {
7249		if (!this.hasLayer(layer)) {
7250			return this;
7251		}
7252		if (layer in this._layers) {
7253			layer = this._layers[layer];
7254		}
7255
7256		layer.removeEventParent(this);
7257
7258		LayerGroup.prototype.removeLayer.call(this, layer);
7259
7260		// @event layerremove: LayerEvent
7261		// Fired when a layer is removed from this `FeatureGroup`
7262		return this.fire('layerremove', {layer: layer});
7263	},
7264
7265	// @method setStyle(style: Path options): this
7266	// Sets the given path options to each layer of the group that has a `setStyle` method.
7267	setStyle: function (style) {
7268		return this.invoke('setStyle', style);
7269	},
7270
7271	// @method bringToFront(): this
7272	// Brings the layer group to the top of all other layers
7273	bringToFront: function () {
7274		return this.invoke('bringToFront');
7275	},
7276
7277	// @method bringToBack(): this
7278	// Brings the layer group to the back of all other layers
7279	bringToBack: function () {
7280		return this.invoke('bringToBack');
7281	},
7282
7283	// @method getBounds(): LatLngBounds
7284	// Returns the LatLngBounds of the Feature Group (created from bounds and coordinates of its children).
7285	getBounds: function () {
7286		var bounds = new LatLngBounds();
7287
7288		for (var id in this._layers) {
7289			var layer = this._layers[id];
7290			bounds.extend(layer.getBounds ? layer.getBounds() : layer.getLatLng());
7291		}
7292		return bounds;
7293	}
7294});
7295
7296// @factory L.featureGroup(layers?: Layer[], options?: Object)
7297// Create a feature group, optionally given an initial set of layers and an `options` object.
7298var featureGroup = function (layers, options) {
7299	return new FeatureGroup(layers, options);
7300};
7301
7302/*
7303 * @class Icon
7304 * @aka L.Icon
7305 *
7306 * Represents an icon to provide when creating a marker.
7307 *
7308 * @example
7309 *
7310 * ```js
7311 * var myIcon = L.icon({
7312 *     iconUrl: 'my-icon.png',
7313 *     iconRetinaUrl: '[email protected]',
7314 *     iconSize: [38, 95],
7315 *     iconAnchor: [22, 94],
7316 *     popupAnchor: [-3, -76],
7317 *     shadowUrl: 'my-icon-shadow.png',
7318 *     shadowRetinaUrl: '[email protected]',
7319 *     shadowSize: [68, 95],
7320 *     shadowAnchor: [22, 94]
7321 * });
7322 *
7323 * L.marker([50.505, 30.57], {icon: myIcon}).addTo(map);
7324 * ```
7325 *
7326 * `L.Icon.Default` extends `L.Icon` and is the blue icon Leaflet uses for markers by default.
7327 *
7328 */
7329
7330var Icon = Class.extend({
7331
7332	/* @section
7333	 * @aka Icon options
7334	 *
7335	 * @option iconUrl: String = null
7336	 * **(required)** The URL to the icon image (absolute or relative to your script path).
7337	 *
7338	 * @option iconRetinaUrl: String = null
7339	 * The URL to a retina sized version of the icon image (absolute or relative to your
7340	 * script path). Used for Retina screen devices.
7341	 *
7342	 * @option iconSize: Point = null
7343	 * Size of the icon image in pixels.
7344	 *
7345	 * @option iconAnchor: Point = null
7346	 * The coordinates of the "tip" of the icon (relative to its top left corner). The icon
7347	 * will be aligned so that this point is at the marker's geographical location. Centered
7348	 * by default if size is specified, also can be set in CSS with negative margins.
7349	 *
7350	 * @option popupAnchor: Point = [0, 0]
7351	 * The coordinates of the point from which popups will "open", relative to the icon anchor.
7352	 *
7353	 * @option tooltipAnchor: Point = [0, 0]
7354	 * The coordinates of the point from which tooltips will "open", relative to the icon anchor.
7355	 *
7356	 * @option shadowUrl: String = null
7357	 * The URL to the icon shadow image. If not specified, no shadow image will be created.
7358	 *
7359	 * @option shadowRetinaUrl: String = null
7360	 *
7361	 * @option shadowSize: Point = null
7362	 * Size of the shadow image in pixels.
7363	 *
7364	 * @option shadowAnchor: Point = null
7365	 * The coordinates of the "tip" of the shadow (relative to its top left corner) (the same
7366	 * as iconAnchor if not specified).
7367	 *
7368	 * @option className: String = ''
7369	 * A custom class name to assign to both icon and shadow images. Empty by default.
7370	 */
7371
7372	options: {
7373		popupAnchor: [0, 0],
7374		tooltipAnchor: [0, 0],
7375
7376		// @option crossOrigin: Boolean|String = false
7377		// Whether the crossOrigin attribute will be added to the tiles.
7378		// 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.
7379		// Refer to [CORS Settings](https://developer.mozilla.org/en-US/docs/Web/HTML/CORS_settings_attributes) for valid String values.
7380		crossOrigin: false
7381	},
7382
7383	initialize: function (options) {
7384		setOptions(this, options);
7385	},
7386
7387	// @method createIcon(oldIcon?: HTMLElement): HTMLElement
7388	// Called internally when the icon has to be shown, returns a `<img>` HTML element
7389	// styled according to the options.
7390	createIcon: function (oldIcon) {
7391		return this._createIcon('icon', oldIcon);
7392	},
7393
7394	// @method createShadow(oldIcon?: HTMLElement): HTMLElement
7395	// As `createIcon`, but for the shadow beneath it.
7396	createShadow: function (oldIcon) {
7397		return this._createIcon('shadow', oldIcon);
7398	},
7399
7400	_createIcon: function (name, oldIcon) {
7401		var src = this._getIconUrl(name);
7402
7403		if (!src) {
7404			if (name === 'icon') {
7405				throw new Error('iconUrl not set in Icon options (see the docs).');
7406			}
7407			return null;
7408		}
7409
7410		var img = this._createImg(src, oldIcon && oldIcon.tagName === 'IMG' ? oldIcon : null);
7411		this._setIconStyles(img, name);
7412
7413		if (this.options.crossOrigin || this.options.crossOrigin === '') {
7414			img.crossOrigin = this.options.crossOrigin === true ? '' : this.options.crossOrigin;
7415		}
7416
7417		return img;
7418	},
7419
7420	_setIconStyles: function (img, name) {
7421		var options = this.options;
7422		var sizeOption = options[name + 'Size'];
7423
7424		if (typeof sizeOption === 'number') {
7425			sizeOption = [sizeOption, sizeOption];
7426		}
7427
7428		var size = toPoint(sizeOption),
7429		    anchor = toPoint(name === 'shadow' && options.shadowAnchor || options.iconAnchor ||
7430		            size && size.divideBy(2, true));
7431
7432		img.className = 'leaflet-marker-' + name + ' ' + (options.className || '');
7433
7434		if (anchor) {
7435			img.style.marginLeft = (-anchor.x) + 'px';
7436			img.style.marginTop  = (-anchor.y) + 'px';
7437		}
7438
7439		if (size) {
7440			img.style.width  = size.x + 'px';
7441			img.style.height = size.y + 'px';
7442		}
7443	},
7444
7445	_createImg: function (src, el) {
7446		el = el || document.createElement('img');
7447		el.src = src;
7448		return el;
7449	},
7450
7451	_getIconUrl: function (name) {
7452		return Browser.retina && this.options[name + 'RetinaUrl'] || this.options[name + 'Url'];
7453	}
7454});
7455
7456
7457// @factory L.icon(options: Icon options)
7458// Creates an icon instance with the given options.
7459function icon(options) {
7460	return new Icon(options);
7461}
7462
7463/*
7464 * @miniclass Icon.Default (Icon)
7465 * @aka L.Icon.Default
7466 * @section
7467 *
7468 * A trivial subclass of `Icon`, represents the icon to use in `Marker`s when
7469 * no icon is specified. Points to the blue marker image distributed with Leaflet
7470 * releases.
7471 *
7472 * In order to customize the default icon, just change the properties of `L.Icon.Default.prototype.options`
7473 * (which is a set of `Icon options`).
7474 *
7475 * If you want to _completely_ replace the default icon, override the
7476 * `L.Marker.prototype.options.icon` with your own icon instead.
7477 */
7478
7479var IconDefault = Icon.extend({
7480
7481	options: {
7482		iconUrl:       'marker-icon.png',
7483		iconRetinaUrl: 'marker-icon-2x.png',
7484		shadowUrl:     'marker-shadow.png',
7485		iconSize:    [25, 41],
7486		iconAnchor:  [12, 41],
7487		popupAnchor: [1, -34],
7488		tooltipAnchor: [16, -28],
7489		shadowSize:  [41, 41]
7490	},
7491
7492	_getIconUrl: function (name) {
7493		if (typeof IconDefault.imagePath !== 'string') {	// Deprecated, backwards-compatibility only
7494			IconDefault.imagePath = this._detectIconPath();
7495		}
7496
7497		// @option imagePath: String
7498		// `Icon.Default` will try to auto-detect the location of the
7499		// blue icon images. If you are placing these images in a non-standard
7500		// way, set this option to point to the right path.
7501		return (this.options.imagePath || IconDefault.imagePath) + Icon.prototype._getIconUrl.call(this, name);
7502	},
7503
7504	_stripUrl: function (path) {	// separate function to use in tests
7505		var strip = function (str, re, idx) {
7506			var match = re.exec(str);
7507			return match && match[idx];
7508		};
7509		path = strip(path, /^url\((['"])?(.+)\1\)$/, 2);
7510		return path && strip(path, /^(.*)marker-icon\.png$/, 1);
7511	},
7512
7513	_detectIconPath: function () {
7514		var el = create$1('div',  'leaflet-default-icon-path', document.body);
7515		var path = getStyle(el, 'background-image') ||
7516		           getStyle(el, 'backgroundImage');	// IE8
7517
7518		document.body.removeChild(el);
7519		path = this._stripUrl(path);
7520		if (path) { return path; }
7521		var link = document.querySelector('link[href$="leaflet.css"]');
7522		if (!link) { return ''; }
7523		return link.href.substring(0, link.href.length - 'leaflet.css'.length - 1);
7524	}
7525});
7526
7527/*
7528 * L.Handler.MarkerDrag is used internally by L.Marker to make the markers draggable.
7529 */
7530
7531
7532/* @namespace Marker
7533 * @section Interaction handlers
7534 *
7535 * 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:
7536 *
7537 * ```js
7538 * marker.dragging.disable();
7539 * ```
7540 *
7541 * @property dragging: Handler
7542 * Marker dragging handler (by both mouse and touch). Only valid when the marker is on the map (Otherwise set [`marker.options.draggable`](#marker-draggable)).
7543 */
7544
7545var MarkerDrag = Handler.extend({
7546	initialize: function (marker) {
7547		this._marker = marker;
7548	},
7549
7550	addHooks: function () {
7551		var icon = this._marker._icon;
7552
7553		if (!this._draggable) {
7554			this._draggable = new Draggable(icon, icon, true);
7555		}
7556
7557		this._draggable.on({
7558			dragstart: this._onDragStart,
7559			predrag: this._onPreDrag,
7560			drag: this._onDrag,
7561			dragend: this._onDragEnd
7562		}, this).enable();
7563
7564		addClass(icon, 'leaflet-marker-draggable');
7565	},
7566
7567	removeHooks: function () {
7568		this._draggable.off({
7569			dragstart: this._onDragStart,
7570			predrag: this._onPreDrag,
7571			drag: this._onDrag,
7572			dragend: this._onDragEnd
7573		}, this).disable();
7574
7575		if (this._marker._icon) {
7576			removeClass(this._marker._icon, 'leaflet-marker-draggable');
7577		}
7578	},
7579
7580	moved: function () {
7581		return this._draggable && this._draggable._moved;
7582	},
7583
7584	_adjustPan: function (e) {
7585		var marker = this._marker,
7586		    map = marker._map,
7587		    speed = this._marker.options.autoPanSpeed,
7588		    padding = this._marker.options.autoPanPadding,
7589		    iconPos = getPosition(marker._icon),
7590		    bounds = map.getPixelBounds(),
7591		    origin = map.getPixelOrigin();
7592
7593		var panBounds = toBounds(
7594			bounds.min._subtract(origin).add(padding),
7595			bounds.max._subtract(origin).subtract(padding)
7596		);
7597
7598		if (!panBounds.contains(iconPos)) {
7599			// Compute incremental movement
7600			var movement = toPoint(
7601				(Math.max(panBounds.max.x, iconPos.x) - panBounds.max.x) / (bounds.max.x - panBounds.max.x) -
7602				(Math.min(panBounds.min.x, iconPos.x) - panBounds.min.x) / (bounds.min.x - panBounds.min.x),
7603
7604				(Math.max(panBounds.max.y, iconPos.y) - panBounds.max.y) / (bounds.max.y - panBounds.max.y) -
7605				(Math.min(panBounds.min.y, iconPos.y) - panBounds.min.y) / (bounds.min.y - panBounds.min.y)
7606			).multiplyBy(speed);
7607
7608			map.panBy(movement, {animate: false});
7609
7610			this._draggable._newPos._add(movement);
7611			this._draggable._startPos._add(movement);
7612
7613			setPosition(marker._icon, this._draggable._newPos);
7614			this._onDrag(e);
7615
7616			this._panRequest = requestAnimFrame(this._adjustPan.bind(this, e));
7617		}
7618	},
7619
7620	_onDragStart: function () {
7621		// @section Dragging events
7622		// @event dragstart: Event
7623		// Fired when the user starts dragging the marker.
7624
7625		// @event movestart: Event
7626		// Fired when the marker starts moving (because of dragging).
7627
7628		this._oldLatLng = this._marker.getLatLng();
7629
7630		// When using ES6 imports it could not be set when `Popup` was not imported as well
7631		this._marker.closePopup && this._marker.closePopup();
7632
7633		this._marker
7634			.fire('movestart')
7635			.fire('dragstart');
7636	},
7637
7638	_onPreDrag: function (e) {
7639		if (this._marker.options.autoPan) {
7640			cancelAnimFrame(this._panRequest);
7641			this._panRequest = requestAnimFrame(this._adjustPan.bind(this, e));
7642		}
7643	},
7644
7645	_onDrag: function (e) {
7646		var marker = this._marker,
7647		    shadow = marker._shadow,
7648		    iconPos = getPosition(marker._icon),
7649		    latlng = marker._map.layerPointToLatLng(iconPos);
7650
7651		// update shadow position
7652		if (shadow) {
7653			setPosition(shadow, iconPos);
7654		}
7655
7656		marker._latlng = latlng;
7657		e.latlng = latlng;
7658		e.oldLatLng = this._oldLatLng;
7659
7660		// @event drag: Event
7661		// Fired repeatedly while the user drags the marker.
7662		marker
7663		    .fire('move', e)
7664		    .fire('drag', e);
7665	},
7666
7667	_onDragEnd: function (e) {
7668		// @event dragend: DragEndEvent
7669		// Fired when the user stops dragging the marker.
7670
7671		 cancelAnimFrame(this._panRequest);
7672
7673		// @event moveend: Event
7674		// Fired when the marker stops moving (because of dragging).
7675		delete this._oldLatLng;
7676		this._marker
7677		    .fire('moveend')
7678		    .fire('dragend', e);
7679	}
7680});
7681
7682/*
7683 * @class Marker
7684 * @inherits Interactive layer
7685 * @aka L.Marker
7686 * L.Marker is used to display clickable/draggable icons on the map. Extends `Layer`.
7687 *
7688 * @example
7689 *
7690 * ```js
7691 * L.marker([50.5, 30.5]).addTo(map);
7692 * ```
7693 */
7694
7695var Marker = Layer.extend({
7696
7697	// @section
7698	// @aka Marker options
7699	options: {
7700		// @option icon: Icon = *
7701		// Icon instance to use for rendering the marker.
7702		// See [Icon documentation](#L.Icon) for details on how to customize the marker icon.
7703		// If not specified, a common instance of `L.Icon.Default` is used.
7704		icon: new IconDefault(),
7705
7706		// Option inherited from "Interactive layer" abstract class
7707		interactive: true,
7708
7709		// @option keyboard: Boolean = true
7710		// Whether the marker can be tabbed to with a keyboard and clicked by pressing enter.
7711		keyboard: true,
7712
7713		// @option title: String = ''
7714		// Text for the browser tooltip that appear on marker hover (no tooltip by default).
7715		// [Useful for accessibility](https://leafletjs.com/examples/accessibility/#markers-must-be-labelled).
7716		title: '',
7717
7718		// @option alt: String = 'Marker'
7719		// Text for the `alt` attribute of the icon image.
7720		// [Useful for accessibility](https://leafletjs.com/examples/accessibility/#markers-must-be-labelled).
7721		alt: 'Marker',
7722
7723		// @option zIndexOffset: Number = 0
7724		// 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).
7725		zIndexOffset: 0,
7726
7727		// @option opacity: Number = 1.0
7728		// The opacity of the marker.
7729		opacity: 1,
7730
7731		// @option riseOnHover: Boolean = false
7732		// If `true`, the marker will get on top of others when you hover the mouse over it.
7733		riseOnHover: false,
7734
7735		// @option riseOffset: Number = 250
7736		// The z-index offset used for the `riseOnHover` feature.
7737		riseOffset: 250,
7738
7739		// @option pane: String = 'markerPane'
7740		// `Map pane` where the markers icon will be added.
7741		pane: 'markerPane',
7742
7743		// @option shadowPane: String = 'shadowPane'
7744		// `Map pane` where the markers shadow will be added.
7745		shadowPane: 'shadowPane',
7746
7747		// @option bubblingMouseEvents: Boolean = false
7748		// When `true`, a mouse event on this marker will trigger the same event on the map
7749		// (unless [`L.DomEvent.stopPropagation`](#domevent-stoppropagation) is used).
7750		bubblingMouseEvents: false,
7751
7752		// @option autoPanOnFocus: Boolean = true
7753		// When `true`, the map will pan whenever the marker is focused (via
7754		// e.g. pressing `tab` on the keyboard) to ensure the marker is
7755		// visible within the map's bounds
7756		autoPanOnFocus: true,
7757
7758		// @section Draggable marker options
7759		// @option draggable: Boolean = false
7760		// Whether the marker is draggable with mouse/touch or not.
7761		draggable: false,
7762
7763		// @option autoPan: Boolean = false
7764		// Whether to pan the map when dragging this marker near its edge or not.
7765		autoPan: false,
7766
7767		// @option autoPanPadding: Point = Point(50, 50)
7768		// Distance (in pixels to the left/right and to the top/bottom) of the
7769		// map edge to start panning the map.
7770		autoPanPadding: [50, 50],
7771
7772		// @option autoPanSpeed: Number = 10
7773		// Number of pixels the map should pan by.
7774		autoPanSpeed: 10
7775	},
7776
7777	/* @section
7778	 *
7779	 * In addition to [shared layer methods](#Layer) like `addTo()` and `remove()` and [popup methods](#Popup) like bindPopup() you can also use the following methods:
7780	 */
7781
7782	initialize: function (latlng, options) {
7783		setOptions(this, options);
7784		this._latlng = toLatLng(latlng);
7785	},
7786
7787	onAdd: function (map) {
7788		this._zoomAnimated = this._zoomAnimated && map.options.markerZoomAnimation;
7789
7790		if (this._zoomAnimated) {
7791			map.on('zoomanim', this._animateZoom, this);
7792		}
7793
7794		this._initIcon();
7795		this.update();
7796	},
7797
7798	onRemove: function (map) {
7799		if (this.dragging && this.dragging.enabled()) {
7800			this.options.draggable = true;
7801			this.dragging.removeHooks();
7802		}
7803		delete this.dragging;
7804
7805		if (this._zoomAnimated) {
7806			map.off('zoomanim', this._animateZoom, this);
7807		}
7808
7809		this._removeIcon();
7810		this._removeShadow();
7811	},
7812
7813	getEvents: function () {
7814		return {
7815			zoom: this.update,
7816			viewreset: this.update
7817		};
7818	},
7819
7820	// @method getLatLng: LatLng
7821	// Returns the current geographical position of the marker.
7822	getLatLng: function () {
7823		return this._latlng;
7824	},
7825
7826	// @method setLatLng(latlng: LatLng): this
7827	// Changes the marker position to the given point.
7828	setLatLng: function (latlng) {
7829		var oldLatLng = this._latlng;
7830		this._latlng = toLatLng(latlng);
7831		this.update();
7832
7833		// @event move: Event
7834		// 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`.
7835		return this.fire('move', {oldLatLng: oldLatLng, latlng: this._latlng});
7836	},
7837
7838	// @method setZIndexOffset(offset: Number): this
7839	// Changes the [zIndex offset](#marker-zindexoffset) of the marker.
7840	setZIndexOffset: function (offset) {
7841		this.options.zIndexOffset = offset;
7842		return this.update();
7843	},
7844
7845	// @method getIcon: Icon
7846	// Returns the current icon used by the marker
7847	getIcon: function () {
7848		return this.options.icon;
7849	},
7850
7851	// @method setIcon(icon: Icon): this
7852	// Changes the marker icon.
7853	setIcon: function (icon) {
7854
7855		this.options.icon = icon;
7856
7857		if (this._map) {
7858			this._initIcon();
7859			this.update();
7860		}
7861
7862		if (this._popup) {
7863			this.bindPopup(this._popup, this._popup.options);
7864		}
7865
7866		return this;
7867	},
7868
7869	getElement: function () {
7870		return this._icon;
7871	},
7872
7873	update: function () {
7874
7875		if (this._icon && this._map) {
7876			var pos = this._map.latLngToLayerPoint(this._latlng).round();
7877			this._setPos(pos);
7878		}
7879
7880		return this;
7881	},
7882
7883	_initIcon: function () {
7884		var options = this.options,
7885		    classToAdd = 'leaflet-zoom-' + (this._zoomAnimated ? 'animated' : 'hide');
7886
7887		var icon = options.icon.createIcon(this._icon),
7888		    addIcon = false;
7889
7890		// if we're not reusing the icon, remove the old one and init new one
7891		if (icon !== this._icon) {
7892			if (this._icon) {
7893				this._removeIcon();
7894			}
7895			addIcon = true;
7896
7897			if (options.title) {
7898				icon.title = options.title;
7899			}
7900
7901			if (icon.tagName === 'IMG') {
7902				icon.alt = options.alt || '';
7903			}
7904		}
7905
7906		addClass(icon, classToAdd);
7907
7908		if (options.keyboard) {
7909			icon.tabIndex = '0';
7910			icon.setAttribute('role', 'button');
7911		}
7912
7913		this._icon = icon;
7914
7915		if (options.riseOnHover) {
7916			this.on({
7917				mouseover: this._bringToFront,
7918				mouseout: this._resetZIndex
7919			});
7920		}
7921
7922		if (this.options.autoPanOnFocus) {
7923			on(icon, 'focus', this._panOnFocus, this);
7924		}
7925
7926		var newShadow = options.icon.createShadow(this._shadow),
7927		    addShadow = false;
7928
7929		if (newShadow !== this._shadow) {
7930			this._removeShadow();
7931			addShadow = true;
7932		}
7933
7934		if (newShadow) {
7935			addClass(newShadow, classToAdd);
7936			newShadow.alt = '';
7937		}
7938		this._shadow = newShadow;
7939
7940
7941		if (options.opacity < 1) {
7942			this._updateOpacity();
7943		}
7944
7945
7946		if (addIcon) {
7947			this.getPane().appendChild(this._icon);
7948		}
7949		this._initInteraction();
7950		if (newShadow && addShadow) {
7951			this.getPane(options.shadowPane).appendChild(this._shadow);
7952		}
7953	},
7954
7955	_removeIcon: function () {
7956		if (this.options.riseOnHover) {
7957			this.off({
7958				mouseover: this._bringToFront,
7959				mouseout: this._resetZIndex
7960			});
7961		}
7962
7963		if (this.options.autoPanOnFocus) {
7964			off(this._icon, 'focus', this._panOnFocus, this);
7965		}
7966
7967		remove(this._icon);
7968		this.removeInteractiveTarget(this._icon);
7969
7970		this._icon = null;
7971	},
7972
7973	_removeShadow: function () {
7974		if (this._shadow) {
7975			remove(this._shadow);
7976		}
7977		this._shadow = null;
7978	},
7979
7980	_setPos: function (pos) {
7981
7982		if (this._icon) {
7983			setPosition(this._icon, pos);
7984		}
7985
7986		if (this._shadow) {
7987			setPosition(this._shadow, pos);
7988		}
7989
7990		this._zIndex = pos.y + this.options.zIndexOffset;
7991
7992		this._resetZIndex();
7993	},
7994
7995	_updateZIndex: function (offset) {
7996		if (this._icon) {
7997			this._icon.style.zIndex = this._zIndex + offset;
7998		}
7999	},
8000
8001	_animateZoom: function (opt) {
8002		var pos = this._map._latLngToNewLayerPoint(this._latlng, opt.zoom, opt.center).round();
8003
8004		this._setPos(pos);
8005	},
8006
8007	_initInteraction: function () {
8008
8009		if (!this.options.interactive) { return; }
8010
8011		addClass(this._icon, 'leaflet-interactive');
8012
8013		this.addInteractiveTarget(this._icon);
8014
8015		if (MarkerDrag) {
8016			var draggable = this.options.draggable;
8017			if (this.dragging) {
8018				draggable = this.dragging.enabled();
8019				this.dragging.disable();
8020			}
8021
8022			this.dragging = new MarkerDrag(this);
8023
8024			if (draggable) {
8025				this.dragging.enable();
8026			}
8027		}
8028	},
8029
8030	// @method setOpacity(opacity: Number): this
8031	// Changes the opacity of the marker.
8032	setOpacity: function (opacity) {
8033		this.options.opacity = opacity;
8034		if (this._map) {
8035			this._updateOpacity();
8036		}
8037
8038		return this;
8039	},
8040
8041	_updateOpacity: function () {
8042		var opacity = this.options.opacity;
8043
8044		if (this._icon) {
8045			setOpacity(this._icon, opacity);
8046		}
8047
8048		if (this._shadow) {
8049			setOpacity(this._shadow, opacity);
8050		}
8051	},
8052
8053	_bringToFront: function () {
8054		this._updateZIndex(this.options.riseOffset);
8055	},
8056
8057	_resetZIndex: function () {
8058		this._updateZIndex(0);
8059	},
8060
8061	_panOnFocus: function () {
8062		var map = this._map;
8063		if (!map) { return; }
8064
8065		var iconOpts = this.options.icon.options;
8066		var size = iconOpts.iconSize ? toPoint(iconOpts.iconSize) : toPoint(0, 0);
8067		var anchor = iconOpts.iconAnchor ? toPoint(iconOpts.iconAnchor) : toPoint(0, 0);
8068
8069		map.panInside(this._latlng, {
8070			paddingTopLeft: anchor,
8071			paddingBottomRight: size.subtract(anchor)
8072		});
8073	},
8074
8075	_getPopupAnchor: function () {
8076		return this.options.icon.options.popupAnchor;
8077	},
8078
8079	_getTooltipAnchor: function () {
8080		return this.options.icon.options.tooltipAnchor;
8081	}
8082});
8083
8084
8085// factory L.marker(latlng: LatLng, options? : Marker options)
8086
8087// @factory L.marker(latlng: LatLng, options? : Marker options)
8088// Instantiates a Marker object given a geographical point and optionally an options object.
8089function marker(latlng, options) {
8090	return new Marker(latlng, options);
vendor: 22,477 bytes, lines 8090-8902
8090
8091}
8092
8093/*
8094 * @class Path
8095 * @aka L.Path
8096 * @inherits Interactive layer
8097 *
8098 * An abstract class that contains options and constants shared between vector
8099 * overlays (Polygon, Polyline, Circle). Do not use it directly. Extends `Layer`.
8100 */
8101
8102var Path = Layer.extend({
8103
8104	// @section
8105	// @aka Path options
8106	options: {
8107		// @option stroke: Boolean = true
8108		// Whether to draw stroke along the path. Set it to `false` to disable borders on polygons or circles.
8109		stroke: true,
8110
8111		// @option color: String = '#3388ff'
8112		// Stroke color
8113		color: '#3388ff',
8114
8115		// @option weight: Number = 3
8116		// Stroke width in pixels
8117		weight: 3,
8118
8119		// @option opacity: Number = 1.0
8120		// Stroke opacity
8121		opacity: 1,
8122
8123		// @option lineCap: String= 'round'
8124		// A string that defines [shape to be used at the end](https://developer.mozilla.org/docs/Web/SVG/Attribute/stroke-linecap) of the stroke.
8125		lineCap: 'round',
8126
8127		// @option lineJoin: String = 'round'
8128		// A string that defines [shape to be used at the corners](https://developer.mozilla.org/docs/Web/SVG/Attribute/stroke-linejoin) of the stroke.
8129		lineJoin: 'round',
8130
8131		// @option dashArray: String = null
8132		// 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).
8133		dashArray: null,
8134
8135		// @option dashOffset: String = null
8136		// 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).
8137		dashOffset: null,
8138
8139		// @option fill: Boolean = depends
8140		// Whether to fill the path with color. Set it to `false` to disable filling on polygons or circles.
8141		fill: false,
8142
8143		// @option fillColor: String = *
8144		// Fill color. Defaults to the value of the [`color`](#path-color) option
8145		fillColor: null,
8146
8147		// @option fillOpacity: Number = 0.2
8148		// Fill opacity.
8149		fillOpacity: 0.2,
8150
8151		// @option fillRule: String = 'evenodd'
8152		// A string that defines [how the inside of a shape](https://developer.mozilla.org/docs/Web/SVG/Attribute/fill-rule) is determined.
8153		fillRule: 'evenodd',
8154
8155		// className: '',
8156
8157		// Option inherited from "Interactive layer" abstract class
8158		interactive: true,
8159
8160		// @option bubblingMouseEvents: Boolean = true
8161		// When `true`, a mouse event on this path will trigger the same event on the map
8162		// (unless [`L.DomEvent.stopPropagation`](#domevent-stoppropagation) is used).
8163		bubblingMouseEvents: true
8164	},
8165
8166	beforeAdd: function (map) {
8167		// Renderer is set here because we need to call renderer.getEvents
8168		// before this.getEvents.
8169		this._renderer = map.getRenderer(this);
8170	},
8171
8172	onAdd: function () {
8173		this._renderer._initPath(this);
8174		this._reset();
8175		this._renderer._addPath(this);
8176	},
8177
8178	onRemove: function () {
8179		this._renderer._removePath(this);
8180	},
8181
8182	// @method redraw(): this
8183	// Redraws the layer. Sometimes useful after you changed the coordinates that the path uses.
8184	redraw: function () {
8185		if (this._map) {
8186			this._renderer._updatePath(this);
8187		}
8188		return this;
8189	},
8190
8191	// @method setStyle(style: Path options): this
8192	// Changes the appearance of a Path based on the options in the `Path options` object.
8193	setStyle: function (style) {
8194		setOptions(this, style);
8195		if (this._renderer) {
8196			this._renderer._updateStyle(this);
8197			if (this.options.stroke && style && Object.prototype.hasOwnProperty.call(style, 'weight')) {
8198				this._updateBounds();
8199			}
8200		}
8201		return this;
8202	},
8203
8204	// @method bringToFront(): this
8205	// Brings the layer to the top of all path layers.
8206	bringToFront: function () {
8207		if (this._renderer) {
8208			this._renderer._bringToFront(this);
8209		}
8210		return this;
8211	},
8212
8213	// @method bringToBack(): this
8214	// Brings the layer to the bottom of all path layers.
8215	bringToBack: function () {
8216		if (this._renderer) {
8217			this._renderer._bringToBack(this);
8218		}
8219		return this;
8220	},
8221
8222	getElement: function () {
8223		return this._path;
8224	},
8225
8226	_reset: function () {
8227		// defined in child classes
8228		this._project();
8229		this._update();
8230	},
8231
8232	_clickTolerance: function () {
8233		// used when doing hit detection for Canvas layers
8234		return (this.options.stroke ? this.options.weight / 2 : 0) +
8235		  (this._renderer.options.tolerance || 0);
8236	}
8237});
8238
8239/*
8240 * @class CircleMarker
8241 * @aka L.CircleMarker
8242 * @inherits Path
8243 *
8244 * A circle of a fixed size with radius specified in pixels. Extends `Path`.
8245 */
8246
8247var CircleMarker = Path.extend({
8248
8249	// @section
8250	// @aka CircleMarker options
8251	options: {
8252		fill: true,
8253
8254		// @option radius: Number = 10
8255		// Radius of the circle marker, in pixels
8256		radius: 10
8257	},
8258
8259	initialize: function (latlng, options) {
8260		setOptions(this, options);
8261		this._latlng = toLatLng(latlng);
8262		this._radius = this.options.radius;
8263	},
8264
8265	// @method setLatLng(latLng: LatLng): this
8266	// Sets the position of a circle marker to a new location.
8267	setLatLng: function (latlng) {
8268		var oldLatLng = this._latlng;
8269		this._latlng = toLatLng(latlng);
8270		this.redraw();
8271
8272		// @event move: Event
8273		// Fired when the marker is moved via [`setLatLng`](#circlemarker-setlatlng). Old and new coordinates are included in event arguments as `oldLatLng`, `latlng`.
8274		return this.fire('move', {oldLatLng: oldLatLng, latlng: this._latlng});
8275	},
8276
8277	// @method getLatLng(): LatLng
8278	// Returns the current geographical position of the circle marker
8279	getLatLng: function () {
8280		return this._latlng;
8281	},
8282
8283	// @method setRadius(radius: Number): this
8284	// Sets the radius of a circle marker. Units are in pixels.
8285	setRadius: function (radius) {
8286		this.options.radius = this._radius = radius;
8287		return this.redraw();
8288	},
8289
8290	// @method getRadius(): Number
8291	// Returns the current radius of the circle
8292	getRadius: function () {
8293		return this._radius;
8294	},
8295
8296	setStyle : function (options) {
8297		var radius = options && options.radius || this._radius;
8298		Path.prototype.setStyle.call(this, options);
8299		this.setRadius(radius);
8300		return this;
8301	},
8302
8303	_project: function () {
8304		this._point = this._map.latLngToLayerPoint(this._latlng);
8305		this._updateBounds();
8306	},
8307
8308	_updateBounds: function () {
8309		var r = this._radius,
8310		    r2 = this._radiusY || r,
8311		    w = this._clickTolerance(),
8312		    p = [r + w, r2 + w];
8313		this._pxBounds = new Bounds(this._point.subtract(p), this._point.add(p));
8314	},
8315
8316	_update: function () {
8317		if (this._map) {
8318			this._updatePath();
8319		}
8320	},
8321
8322	_updatePath: function () {
8323		this._renderer._updateCircle(this);
8324	},
8325
8326	_empty: function () {
8327		return this._radius && !this._renderer._bounds.intersects(this._pxBounds);
8328	},
8329
8330	// Needed by the `Canvas` renderer for interactivity
8331	_containsPoint: function (p) {
8332		return p.distanceTo(this._point) <= this._radius + this._clickTolerance();
8333	}
8334});
8335
8336
8337// @factory L.circleMarker(latlng: LatLng, options?: CircleMarker options)
8338// Instantiates a circle marker object given a geographical point, and an optional options object.
8339function circleMarker(latlng, options) {
8340	return new CircleMarker(latlng, options);
8341}
8342
8343/*
8344 * @class Circle
8345 * @aka L.Circle
8346 * @inherits CircleMarker
8347 *
8348 * A class for drawing circle overlays on a map. Extends `CircleMarker`.
8349 *
8350 * It's an approximation and starts to diverge from a real circle closer to poles (due to projection distortion).
8351 *
8352 * @example
8353 *
8354 * ```js
8355 * L.circle([50.5, 30.5], {radius: 200}).addTo(map);
8356 * ```
8357 */
8358
8359var Circle = CircleMarker.extend({
8360
8361	initialize: function (latlng, options, legacyOptions) {
8362		if (typeof options === 'number') {
8363			// Backwards compatibility with 0.7.x factory (latlng, radius, options?)
8364			options = extend({}, legacyOptions, {radius: options});
8365		}
8366		setOptions(this, options);
8367		this._latlng = toLatLng(latlng);
8368
8369		if (isNaN(this.options.radius)) { throw new Error('Circle radius cannot be NaN'); }
8370
8371		// @section
8372		// @aka Circle options
8373		// @option radius: Number; Radius of the circle, in meters.
8374		this._mRadius = this.options.radius;
8375	},
8376
8377	// @method setRadius(radius: Number): this
8378	// Sets the radius of a circle. Units are in meters.
8379	setRadius: function (radius) {
8380		this._mRadius = radius;
8381		return this.redraw();
8382	},
8383
8384	// @method getRadius(): Number
8385	// Returns the current radius of a circle. Units are in meters.
8386	getRadius: function () {
8387		return this._mRadius;
8388	},
8389
8390	// @method getBounds(): LatLngBounds
8391	// Returns the `LatLngBounds` of the path.
8392	getBounds: function () {
8393		var half = [this._radius, this._radiusY || this._radius];
8394
8395		return new LatLngBounds(
8396			this._map.layerPointToLatLng(this._point.subtract(half)),
8397			this._map.layerPointToLatLng(this._point.add(half)));
8398	},
8399
8400	setStyle: Path.prototype.setStyle,
8401
8402	_project: function () {
8403
8404		var lng = this._latlng.lng,
8405		    lat = this._latlng.lat,
8406		    map = this._map,
8407		    crs = map.options.crs;
8408
8409		if (crs.distance === Earth.distance) {
8410			var d = Math.PI / 180,
8411			    latR = (this._mRadius / Earth.R) / d,
8412			    top = map.project([lat + latR, lng]),
8413			    bottom = map.project([lat - latR, lng]),
8414			    p = top.add(bottom).divideBy(2),
8415			    lat2 = map.unproject(p).lat,
8416			    lngR = Math.acos((Math.cos(latR * d) - Math.sin(lat * d) * Math.sin(lat2 * d)) /
8417			            (Math.cos(lat * d) * Math.cos(lat2 * d))) / d;
8418
8419			if (isNaN(lngR) || lngR === 0) {
8420				lngR = latR / Math.cos(Math.PI / 180 * lat); // Fallback for edge case, #2425
8421			}
8422
8423			this._point = p.subtract(map.getPixelOrigin());
8424			this._radius = isNaN(lngR) ? 0 : p.x - map.project([lat2, lng - lngR]).x;
8425			this._radiusY = p.y - top.y;
8426
8427		} else {
8428			var latlng2 = crs.unproject(crs.project(this._latlng).subtract([this._mRadius, 0]));
8429
8430			this._point = map.latLngToLayerPoint(this._latlng);
8431			this._radius = this._point.x - map.latLngToLayerPoint(latlng2).x;
8432		}
8433
8434		this._updateBounds();
8435	}
8436});
8437
8438// @factory L.circle(latlng: LatLng, options?: Circle options)
8439// Instantiates a circle object given a geographical point, and an options object
8440// which contains the circle radius.
8441// @alternative
8442// @factory L.circle(latlng: LatLng, radius: Number, options?: Circle options)
8443// Obsolete way of instantiating a circle, for compatibility with 0.7.x code.
8444// Do not use in new applications or plugins.
8445function circle(latlng, options, legacyOptions) {
8446	return new Circle(latlng, options, legacyOptions);
8447}
8448
8449/*
8450 * @class Polyline
8451 * @aka L.Polyline
8452 * @inherits Path
8453 *
8454 * A class for drawing polyline overlays on a map. Extends `Path`.
8455 *
8456 * @example
8457 *
8458 * ```js
8459 * // create a red polyline from an array of LatLng points
8460 * var latlngs = [
8461 * 	[45.51, -122.68],
8462 * 	[37.77, -122.43],
8463 * 	[34.04, -118.2]
8464 * ];
8465 *
8466 * var polyline = L.polyline(latlngs, {color: 'red'}).addTo(map);
8467 *
8468 * // zoom the map to the polyline
8469 * map.fitBounds(polyline.getBounds());
8470 * ```
8471 *
8472 * You can also pass a multi-dimensional array to represent a `MultiPolyline` shape:
8473 *
8474 * ```js
8475 * // create a red polyline from an array of arrays of LatLng points
8476 * var latlngs = [
8477 * 	[[45.51, -122.68],
8478 * 	 [37.77, -122.43],
8479 * 	 [34.04, -118.2]],
8480 * 	[[40.78, -73.91],
8481 * 	 [41.83, -87.62],
8482 * 	 [32.76, -96.72]]
8483 * ];
8484 * ```
8485 */
8486
8487
8488var Polyline = Path.extend({
8489
8490	// @section
8491	// @aka Polyline options
8492	options: {
8493		// @option smoothFactor: Number = 1.0
8494		// How much to simplify the polyline on each zoom level. More means
8495		// better performance and smoother look, and less means more accurate representation.
8496		smoothFactor: 1.0,
8497
8498		// @option noClip: Boolean = false
8499		// Disable polyline clipping.
8500		noClip: false
8501	},
8502
8503	initialize: function (latlngs, options) {
8504		setOptions(this, options);
8505		this._setLatLngs(latlngs);
8506	},
8507
8508	// @method getLatLngs(): LatLng[]
8509	// Returns an array of the points in the path, or nested arrays of points in case of multi-polyline.
8510	getLatLngs: function () {
8511		return this._latlngs;
8512	},
8513
8514	// @method setLatLngs(latlngs: LatLng[]): this
8515	// Replaces all the points in the polyline with the given array of geographical points.
8516	setLatLngs: function (latlngs) {
8517		this._setLatLngs(latlngs);
8518		return this.redraw();
8519	},
8520
8521	// @method isEmpty(): Boolean
8522	// Returns `true` if the Polyline has no LatLngs.
8523	isEmpty: function () {
8524		return !this._latlngs.length;
8525	},
8526
8527	// @method closestLayerPoint(p: Point): Point
8528	// Returns the point closest to `p` on the Polyline.
8529	closestLayerPoint: function (p) {
8530		var minDistance = Infinity,
8531		    minPoint = null,
8532		    closest = _sqClosestPointOnSegment,
8533		    p1, p2;
8534
8535		for (var j = 0, jLen = this._parts.length; j < jLen; j++) {
8536			var points = this._parts[j];
8537
8538			for (var i = 1, len = points.length; i < len; i++) {
8539				p1 = points[i - 1];
8540				p2 = points[i];
8541
8542				var sqDist = closest(p, p1, p2, true);
8543
8544				if (sqDist < minDistance) {
8545					minDistance = sqDist;
8546					minPoint = closest(p, p1, p2);
8547				}
8548			}
8549		}
8550		if (minPoint) {
8551			minPoint.distance = Math.sqrt(minDistance);
8552		}
8553		return minPoint;
8554	},
8555
8556	// @method getCenter(): LatLng
8557	// Returns the center ([centroid](https://en.wikipedia.org/wiki/Centroid)) of the polyline.
8558	getCenter: function () {
8559		// throws error when not yet added to map as this center calculation requires projected coordinates
8560		if (!this._map) {
8561			throw new Error('Must add layer to map before using getCenter()');
8562		}
8563		return polylineCenter(this._defaultShape(), this._map.options.crs);
8564	},
8565
8566	// @method getBounds(): LatLngBounds
8567	// Returns the `LatLngBounds` of the path.
8568	getBounds: function () {
8569		return this._bounds;
8570	},
8571
8572	// @method addLatLng(latlng: LatLng, latlngs?: LatLng[]): this
8573	// Adds a given point to the polyline. By default, adds to the first ring of
8574	// the polyline in case of a multi-polyline, but can be overridden by passing
8575	// a specific ring as a LatLng array (that you can earlier access with [`getLatLngs`](#polyline-getlatlngs)).
8576	addLatLng: function (latlng, latlngs) {
8577		latlngs = latlngs || this._defaultShape();
8578		latlng = toLatLng(latlng);
8579		latlngs.push(latlng);
8580		this._bounds.extend(latlng);
8581		return this.redraw();
8582	},
8583
8584	_setLatLngs: function (latlngs) {
8585		this._bounds = new LatLngBounds();
8586		this._latlngs = this._convertLatLngs(latlngs);
8587	},
8588
8589	_defaultShape: function () {
8590		return isFlat(this._latlngs) ? this._latlngs : this._latlngs[0];
8591	},
8592
8593	// recursively convert latlngs input into actual LatLng instances; calculate bounds along the way
8594	_convertLatLngs: function (latlngs) {
8595		var result = [],
8596		    flat = isFlat(latlngs);
8597
8598		for (var i = 0, len = latlngs.length; i < len; i++) {
8599			if (flat) {
8600				result[i] = toLatLng(latlngs[i]);
8601				this._bounds.extend(result[i]);
8602			} else {
8603				result[i] = this._convertLatLngs(latlngs[i]);
8604			}
8605		}
8606
8607		return result;
8608	},
8609
8610	_project: function () {
8611		var pxBounds = new Bounds();
8612		this._rings = [];
8613		this._projectLatlngs(this._latlngs, this._rings, pxBounds);
8614
8615		if (this._bounds.isValid() && pxBounds.isValid()) {
8616			this._rawPxBounds = pxBounds;
8617			this._updateBounds();
8618		}
8619	},
8620
8621	_updateBounds: function () {
8622		var w = this._clickTolerance(),
8623		    p = new Point(w, w);
8624
8625		if (!this._rawPxBounds) {
8626			return;
8627		}
8628
8629		this._pxBounds = new Bounds([
8630			this._rawPxBounds.min.subtract(p),
8631			this._rawPxBounds.max.add(p)
8632		]);
8633	},
8634
8635	// recursively turns latlngs into a set of rings with projected coordinates
8636	_projectLatlngs: function (latlngs, result, projectedBounds) {
8637		var flat = latlngs[0] instanceof LatLng,
8638		    len = latlngs.length,
8639		    i, ring;
8640
8641		if (flat) {
8642			ring = [];
8643			for (i = 0; i < len; i++) {
8644				ring[i] = this._map.latLngToLayerPoint(latlngs[i]);
8645				projectedBounds.extend(ring[i]);
8646			}
8647			result.push(ring);
8648		} else {
8649			for (i = 0; i < len; i++) {
8650				this._projectLatlngs(latlngs[i], result, projectedBounds);
8651			}
8652		}
8653	},
8654
8655	// clip polyline by renderer bounds so that we have less to render for performance
8656	_clipPoints: function () {
8657		var bounds = this._renderer._bounds;
8658
8659		this._parts = [];
8660		if (!this._pxBounds || !this._pxBounds.intersects(bounds)) {
8661			return;
8662		}
8663
8664		if (this.options.noClip) {
8665			this._parts = this._rings;
8666			return;
8667		}
8668
8669		var parts = this._parts,
8670		    i, j, k, len, len2, segment, points;
8671
8672		for (i = 0, k = 0, len = this._rings.length; i < len; i++) {
8673			points = this._rings[i];
8674
8675			for (j = 0, len2 = points.length; j < len2 - 1; j++) {
8676				segment = clipSegment(points[j], points[j + 1], bounds, j, true);
8677
8678				if (!segment) { continue; }
8679
8680				parts[k] = parts[k] || [];
8681				parts[k].push(segment[0]);
8682
8683				// if segment goes out of screen, or it's the last one, it's the end of the line part
8684				if ((segment[1] !== points[j + 1]) || (j === len2 - 2)) {
8685					parts[k].push(segment[1]);
8686					k++;
8687				}
8688			}
8689		}
8690	},
8691
8692	// simplify each clipped part of the polyline for performance
8693	_simplifyPoints: function () {
8694		var parts = this._parts,
8695		    tolerance = this.options.smoothFactor;
8696
8697		for (var i = 0, len = parts.length; i < len; i++) {
8698			parts[i] = simplify(parts[i], tolerance);
8699		}
8700	},
8701
8702	_update: function () {
8703		if (!this._map) { return; }
8704
8705		this._clipPoints();
8706		this._simplifyPoints();
8707		this._updatePath();
8708	},
8709
8710	_updatePath: function () {
8711		this._renderer._updatePoly(this);
8712	},
8713
8714	// Needed by the `Canvas` renderer for interactivity
8715	_containsPoint: function (p, closed) {
8716		var i, j, k, len, len2, part,
8717		    w = this._clickTolerance();
8718
8719		if (!this._pxBounds || !this._pxBounds.contains(p)) { return false; }
8720
8721		// hit detection for polylines
8722		for (i = 0, len = this._parts.length; i < len; i++) {
8723			part = this._parts[i];
8724
8725			for (j = 0, len2 = part.length, k = len2 - 1; j < len2; k = j++) {
8726				if (!closed && (j === 0)) { continue; }
8727
8728				if (pointToSegmentDistance(p, part[k], part[j]) <= w) {
8729					return true;
8730				}
8731			}
8732		}
8733		return false;
8734	}
8735});
8736
8737// @factory L.polyline(latlngs: LatLng[], options?: Polyline options)
8738// Instantiates a polyline object given an array of geographical points and
8739// optionally an options object. You can create a `Polyline` object with
8740// multiple separate lines (`MultiPolyline`) by passing an array of arrays
8741// of geographic points.
8742function polyline(latlngs, options) {
8743	return new Polyline(latlngs, options);
8744}
8745
8746// Retrocompat. Allow plugins to support Leaflet versions before and after 1.1.
8747Polyline._flat = _flat;
8748
8749/*
8750 * @class Polygon
8751 * @aka L.Polygon
8752 * @inherits Polyline
8753 *
8754 * A class for drawing polygon overlays on a map. Extends `Polyline`.
8755 *
8756 * 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.
8757 *
8758 *
8759 * @example
8760 *
8761 * ```js
8762 * // create a red polygon from an array of LatLng points
8763 * var latlngs = [[37, -109.05],[41, -109.03],[41, -102.05],[37, -102.04]];
8764 *
8765 * var polygon = L.polygon(latlngs, {color: 'red'}).addTo(map);
8766 *
8767 * // zoom the map to the polygon
8768 * map.fitBounds(polygon.getBounds());
8769 * ```
8770 *
8771 * 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:
8772 *
8773 * ```js
8774 * var latlngs = [
8775 *   [[37, -109.05],[41, -109.03],[41, -102.05],[37, -102.04]], // outer ring
8776 *   [[37.29, -108.58],[40.71, -108.58],[40.71, -102.50],[37.29, -102.50]] // hole
8777 * ];
8778 * ```
8779 *
8780 * Additionally, you can pass a multi-dimensional array to represent a MultiPolygon shape.
8781 *
8782 * ```js
8783 * var latlngs = [
8784 *   [ // first polygon
8785 *     [[37, -109.05],[41, -109.03],[41, -102.05],[37, -102.04]], // outer ring
8786 *     [[37.29, -108.58],[40.71, -108.58],[40.71, -102.50],[37.29, -102.50]] // hole
8787 *   ],
8788 *   [ // second polygon
8789 *     [[41, -111.03],[45, -111.04],[45, -104.05],[41, -104.05]]
8790 *   ]
8791 * ];
8792 * ```
8793 */
8794
8795var Polygon = Polyline.extend({
8796
8797	options: {
8798		fill: true
8799	},
8800
8801	isEmpty: function () {
8802		return !this._latlngs.length || !this._latlngs[0].length;
8803	},
8804
8805	// @method getCenter(): LatLng
8806	// Returns the center ([centroid](http://en.wikipedia.org/wiki/Centroid)) of the Polygon.
8807	getCenter: function () {
8808		// throws error when not yet added to map as this center calculation requires projected coordinates
8809		if (!this._map) {
8810			throw new Error('Must add layer to map before using getCenter()');
8811		}
8812		return polygonCenter(this._defaultShape(), this._map.options.crs);
8813	},
8814
8815	_convertLatLngs: function (latlngs) {
8816		var result = Polyline.prototype._convertLatLngs.call(this, latlngs),
8817		    len = result.length;
8818
8819		// remove last point if it equals first one
8820		if (len >= 2 && result[0] instanceof LatLng && result[0].equals(result[len - 1])) {
8821			result.pop();
8822		}
8823		return result;
8824	},
8825
8826	_setLatLngs: function (latlngs) {
8827		Polyline.prototype._setLatLngs.call(this, latlngs);
8828		if (isFlat(this._latlngs)) {
8829			this._latlngs = [this._latlngs];
8830		}
8831	},
8832
8833	_defaultShape: function () {
8834		return isFlat(this._latlngs[0]) ? this._latlngs[0] : this._latlngs[0][0];
8835	},
8836
8837	_clipPoints: function () {
8838		// polygons need a different clipping algorithm so we redefine that
8839
8840		var bounds = this._renderer._bounds,
8841		    w = this.options.weight,
8842		    p = new Point(w, w);
8843
8844		// increase clip padding by stroke width to avoid stroke on clip edges
8845		bounds = new Bounds(bounds.min.subtract(p), bounds.max.add(p));
8846
8847		this._parts = [];
8848		if (!this._pxBounds || !this._pxBounds.intersects(bounds)) {
8849			return;
8850		}
8851
8852		if (this.options.noClip) {
8853			this._parts = this._rings;
8854			return;
8855		}
8856
8857		for (var i = 0, len = this._rings.length, clipped; i < len; i++) {
8858			clipped = clipPolygon(this._rings[i], bounds, true);
8859			if (clipped.length) {
8860				this._parts.push(clipped);
8861			}
8862		}
8863	},
8864
8865	_updatePath: function () {
8866		this._renderer._updatePoly(this, true);
8867	},
8868
8869	// Needed by the `Canvas` renderer for interactivity
8870	_containsPoint: function (p) {
8871		var inside = false,
8872		    part, p1, p2, i, j, k, len, len2;
8873
8874		if (!this._pxBounds || !this._pxBounds.contains(p)) { return false; }
8875
8876		// ray casting algorithm for detecting if point is in polygon
8877		for (i = 0, len = this._parts.length; i < len; i++) {
8878			part = this._parts[i];
8879
8880			for (j = 0, len2 = part.length, k = len2 - 1; j < len2; k = j++) {
8881				p1 = part[j];
8882				p2 = part[k];
8883
8884				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)) {
8885					inside = !inside;
8886				}
8887			}
8888		}
8889
8890		// also check if it's on polygon stroke
8891		return inside || Polyline.prototype._containsPoint.call(this, p, true);
8892	}
8893
8894});
8895
8896
8897// @factory L.polygon(latlngs: LatLng[], options?: Polyline options)
8898function polygon(latlngs, options) {
8899	return new Polygon(latlngs, options);
8900}
8901
8902/*
8902
8903 * @class GeoJSON
8904 * @aka L.GeoJSON
8905 * @inherits FeatureGroup
8906 *
8907 * Represents a GeoJSON object or an array of GeoJSON objects. Allows you to parse
8908 * GeoJSON data and display it on the map. Extends `FeatureGroup`.
8909 *
8910 * @example
8911 *
8912 * ```js
8913 * L.geoJSON(data, {
8914 * 	style: function (feature) {
8915 * 		return {color: feature.properties.color};
8916 * 	}
8917 * }).bindPopup(function (layer) {
8918 * 	return layer.feature.properties.description;
8919 * }).addTo(map);
8920 * ```
8921 */
8922
8923var GeoJSON = FeatureGroup.extend({
8924
8925	/* @section
8926	 * @aka GeoJSON options
8927	 *
8928	 * @option pointToLayer: Function = *
8929	 * A `Function` defining how GeoJSON points spawn Leaflet layers. It is internally
8930	 * called when data is added, passing the GeoJSON point feature and its `LatLng`.
8931	 * The default is to spawn a default `Marker`:
8932	 * ```js
8933	 * function(geoJsonPoint, latlng) {
8934	 * 	return L.marker(latlng);
8935	 * }
8936	 * ```
8937	 *
8938	 * @option style: Function = *
8939	 * A `Function` defining the `Path options` for styling GeoJSON lines and polygons,
8940	 * called internally when data is added.
8941	 * The default value is to not override any defaults:
8942	 * ```js
8943	 * function (geoJsonFeature) {
8944	 * 	return {}
8945	 * }
8946	 * ```
8947	 *
8948	 * @option onEachFeature: Function = *
8949	 * A `Function` that will be called once for each created `Feature`, after it has
8950	 * been created and styled. Useful for attaching events and popups to features.
8951	 * The default is to do nothing with the newly created layers:
8952	 * ```js
8953	 * function (feature, layer) {}
8954	 * ```
8955	 *
8956	 * @option filter: Function = *
8957	 * A `Function` that will be used to decide whether to include a feature or not.
8958	 * The default is to include all features:
8959	 * ```js
8960	 * function (geoJsonFeature) {
8961	 * 	return true;
8962	 * }
8963	 * ```
8964	 * Note: dynamically changing the `filter` option will have effect only on newly
8965	 * added data. It will _not_ re-evaluate already included features.
8966	 *
8967	 * @option coordsToLatLng: Function = *
8968	 * A `Function` that will be used for converting GeoJSON coordinates to `LatLng`s.
8969	 * The default is the `coordsToLatLng` static method.
8970	 *
8971	 * @option markersInheritOptions: Boolean = false
8972	 * Whether default Markers for "Point" type Features inherit from group options.
8973	 */
8974
8975	initialize: function (geojson, options) {
8976		setOptions(this, options);
8977
8978		this._layers = {};
8979
8980		if (geojson) {
8981			this.addData(geojson);
8982		}
8983	},
8984
8985	// @method addData( <GeoJSON> data ): this
8986	// Adds a GeoJSON object to the layer.
8987	addData: function (geojson) {
8988		var features = isArray(geojson) ? geojson : geojson.features,
8989		    i, len, feature;
8990
8991		if (features) {
8992			for (i = 0, len = features.length; i < len; i++) {
8993				// only add this if geometry or geometries are set and not null
8994				feature = features[i];
8995				if (feature.geometries || feature.geometry || feature.features || feature.coordinates) {
8996					this.addData(feature);
8997				}
8998			}
8999			return this;
9000		}
9001
9002		var options = this.options;
9003
9004		if (options.filter && !options.filter(geojson)) { return this; }
9005
9006		var layer = geometryToLayer(geojson, options);
9007		if (!layer) {
9008			return this;
9009		}
9010		layer.feature = asFeature(geojson);
9011
9012		layer.defaultOptions = layer.options;
9013		this.resetStyle(layer);
9014
9015		if (options.onEachFeature) {
9016			options.onEachFeature(geojson, layer);
9017		}
9018
9019		return this.addLayer(layer);
9020	},
9021
9022	// @method resetStyle( <Path> layer? ): this
9023	// Resets the given vector layer's style to the original GeoJSON style, useful for resetting style after hover events.
9024	// If `layer` is omitted, the style of all features in the current layer is reset.
9025	resetStyle: function (layer) {
9026		if (layer === undefined) {
9027			return this.eachLayer(this.resetStyle, this);
9028		}
9029		// reset any custom styles
9030		layer.options = extend({}, layer.defaultOptions);
9031		this._setLayerStyle(layer, this.options.style);
9032		return this;
9033	},
9034
9035	// @method setStyle( <Function> style ): this
9036	// Changes styles of GeoJSON vector layers with the given style function.
9037	setStyle: function (style) {
9038		return this.eachLayer(function (layer) {
9039			this._setLayerStyle(layer, style);
9040		}, this);
9041	},
9042
9043	_setLayerStyle: function (layer, style) {
9044		if (layer.setStyle) {
9045			if (typeof style === 'function') {
9046				style = style(layer.feature);
9047			}
9048			layer.setStyle(style);
9049		}
9050	}
9051});
9052
9053// @section
9054// There are several static functions which can be called without instantiating L.GeoJSON:
9055
9056// @function geometryToLayer(featureData: Object, options?: GeoJSON options): Layer
9057// Creates a `Layer` from a given GeoJSON feature. Can use a custom
9058// [`pointToLayer`](#geojson-pointtolayer) and/or [`coordsToLatLng`](#geojson-coordstolatlng)
9059// functions if provided as options.
9060function geometryToLayer(geojson, options) {
9061
9062	var geometry = geojson.type === 'Feature' ? geojson.geometry : geojson,
9063	    coords = geometry ? geometry.coordinates : null,
9064	    layers = [],
9065	    pointToLayer = options && options.pointToLayer,
9066	    _coordsToLatLng = options && options.coordsToLatLng || coordsToLatLng,
9067	    latlng, latlngs, i, len;
9068
9069	if (!coords && !geometry) {
9070		return null;
9071	}
9072
9073	switch (geometry.type) {
9074	case 'Point':
9075		latlng = _coordsToLatLng(coords);
9076		return _pointToLayer(pointToLayer, geojson, latlng, options);
9077
9078	case 'MultiPoint':
vendor: 29,837 bytes, lines 9079-10120
9079		for (i = 0, len = coords.length; i < len; i++) {
9080			latlng = _coordsToLatLng(coords[i]);
9081			layers.push(_pointToLayer(pointToLayer, geojson, latlng, options));
9082		}
9083		return new FeatureGroup(layers);
9084
9085	case 'LineString':
9086	case 'MultiLineString':
9087		latlngs = coordsToLatLngs(coords, geometry.type === 'LineString' ? 0 : 1, _coordsToLatLng);
9088		return new Polyline(latlngs, options);
9089
9090	case 'Polygon':
9091	case 'MultiPolygon':
9092		latlngs = coordsToLatLngs(coords, geometry.type === 'Polygon' ? 1 : 2, _coordsToLatLng);
9093		return new Polygon(latlngs, options);
9094
9095	case 'GeometryCollection':
9096		for (i = 0, len = geometry.geometries.length; i < len; i++) {
9097			var geoLayer = geometryToLayer({
9098				geometry: geometry.geometries[i],
9099				type: 'Feature',
9100				properties: geojson.properties
9101			}, options);
9102
9103			if (geoLayer) {
9104				layers.push(geoLayer);
9105			}
9106		}
9107		return new FeatureGroup(layers);
9108
9109	case 'FeatureCollection':
9110		for (i = 0, len = geometry.features.length; i < len; i++) {
9111			var featureLayer = geometryToLayer(geometry.features[i], options);
9112
9113			if (featureLayer) {
9114				layers.push(featureLayer);
9115			}
9116		}
9117		return new FeatureGroup(layers);
9118
9119	default:
9120		throw new Error('Invalid GeoJSON object.');
9121	}
9122}
9123
9124function _pointToLayer(pointToLayerFn, geojson, latlng, options) {
9125	return pointToLayerFn ?
9126		pointToLayerFn(geojson, latlng) :
9127		new Marker(latlng, options && options.markersInheritOptions && options);
9128}
9129
9130// @function coordsToLatLng(coords: Array): LatLng
9131// Creates a `LatLng` object from an array of 2 numbers (longitude, latitude)
9132// or 3 numbers (longitude, latitude, altitude) used in GeoJSON for points.
9133function coordsToLatLng(coords) {
9134	return new LatLng(coords[1], coords[0], coords[2]);
9135}
9136
9137// @function coordsToLatLngs(coords: Array, levelsDeep?: Number, coordsToLatLng?: Function): Array
9138// Creates a multidimensional array of `LatLng`s from a GeoJSON coordinates array.
9139// `levelsDeep` specifies the nesting level (0 is for an array of points, 1 for an array of arrays of points, etc., 0 by default).
9140// Can use a custom [`coordsToLatLng`](#geojson-coordstolatlng) function.
9141function coordsToLatLngs(coords, levelsDeep, _coordsToLatLng) {
9142	var latlngs = [];
9143
9144	for (var i = 0, len = coords.length, latlng; i < len; i++) {
9145		latlng = levelsDeep ?
9146			coordsToLatLngs(coords[i], levelsDeep - 1, _coordsToLatLng) :
9147			(_coordsToLatLng || coordsToLatLng)(coords[i]);
9148
9149		latlngs.push(latlng);
9150	}
9151
9152	return latlngs;
9153}
9154
9155// @function latLngToCoords(latlng: LatLng, precision?: Number|false): Array
9156// Reverse of [`coordsToLatLng`](#geojson-coordstolatlng)
9157// Coordinates values are rounded with [`formatNum`](#util-formatnum) function.
9158function latLngToCoords(latlng, precision) {
9159	latlng = toLatLng(latlng);
9160	return latlng.alt !== undefined ?
9161		[formatNum(latlng.lng, precision), formatNum(latlng.lat, precision), formatNum(latlng.alt, precision)] :
9162		[formatNum(latlng.lng, precision), formatNum(latlng.lat, precision)];
9163}
9164
9165// @function latLngsToCoords(latlngs: Array, levelsDeep?: Number, closed?: Boolean, precision?: Number|false): Array
9166// Reverse of [`coordsToLatLngs`](#geojson-coordstolatlngs)
9167// `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.
9168// Coordinates values are rounded with [`formatNum`](#util-formatnum) function.
9169function latLngsToCoords(latlngs, levelsDeep, closed, precision) {
9170	var coords = [];
9171
9172	for (var i = 0, len = latlngs.length; i < len; i++) {
9173		// Check for flat arrays required to ensure unbalanced arrays are correctly converted in recursion
9174		coords.push(levelsDeep ?
9175			latLngsToCoords(latlngs[i], isFlat(latlngs[i]) ? 0 : levelsDeep - 1, closed, precision) :
9176			latLngToCoords(latlngs[i], precision));
9177	}
9178
9179	if (!levelsDeep && closed && coords.length > 0) {
9180		coords.push(coords[0].slice());
9181	}
9182
9183	return coords;
9184}
9185
9186function getFeature(layer, newGeometry) {
9187	return layer.feature ?
9188		extend({}, layer.feature, {geometry: newGeometry}) :
9189		asFeature(newGeometry);
9190}
9191
9192// @function asFeature(geojson: Object): Object
9193// Normalize GeoJSON geometries/features into GeoJSON features.
9194function asFeature(geojson) {
9195	if (geojson.type === 'Feature' || geojson.type === 'FeatureCollection') {
9196		return geojson;
9197	}
9198
9199	return {
9200		type: 'Feature',
9201		properties: {},
9202		geometry: geojson
9203	};
9204}
9205
9206var PointToGeoJSON = {
9207	toGeoJSON: function (precision) {
9208		return getFeature(this, {
9209			type: 'Point',
9210			coordinates: latLngToCoords(this.getLatLng(), precision)
9211		});
9212	}
9213};
9214
9215// @namespace Marker
9216// @section Other methods
9217// @method toGeoJSON(precision?: Number|false): Object
9218// Coordinates values are rounded with [`formatNum`](#util-formatnum) function with given `precision`.
9219// Returns a [`GeoJSON`](https://en.wikipedia.org/wiki/GeoJSON) representation of the marker (as a GeoJSON `Point` Feature).
9220Marker.include(PointToGeoJSON);
9221
9222// @namespace CircleMarker
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 circle marker (as a GeoJSON `Point` Feature).
9226Circle.include(PointToGeoJSON);
9227CircleMarker.include(PointToGeoJSON);
9228
9229
9230// @namespace Polyline
9231// @method toGeoJSON(precision?: Number|false): Object
9232// Coordinates values are rounded with [`formatNum`](#util-formatnum) function with given `precision`.
9233// Returns a [`GeoJSON`](https://en.wikipedia.org/wiki/GeoJSON) representation of the polyline (as a GeoJSON `LineString` or `MultiLineString` Feature).
9234Polyline.include({
9235	toGeoJSON: function (precision) {
9236		var multi = !isFlat(this._latlngs);
9237
9238		var coords = latLngsToCoords(this._latlngs, multi ? 1 : 0, false, precision);
9239
9240		return getFeature(this, {
9241			type: (multi ? 'Multi' : '') + 'LineString',
9242			coordinates: coords
9243		});
9244	}
9245});
9246
9247// @namespace Polygon
9248// @method toGeoJSON(precision?: Number|false): Object
9249// Coordinates values are rounded with [`formatNum`](#util-formatnum) function with given `precision`.
9250// Returns a [`GeoJSON`](https://en.wikipedia.org/wiki/GeoJSON) representation of the polygon (as a GeoJSON `Polygon` or `MultiPolygon` Feature).
9251Polygon.include({
9252	toGeoJSON: function (precision) {
9253		var holes = !isFlat(this._latlngs),
9254		    multi = holes && !isFlat(this._latlngs[0]);
9255
9256		var coords = latLngsToCoords(this._latlngs, multi ? 2 : holes ? 1 : 0, true, precision);
9257
9258		if (!holes) {
9259			coords = [coords];
9260		}
9261
9262		return getFeature(this, {
9263			type: (multi ? 'Multi' : '') + 'Polygon',
9264			coordinates: coords
9265		});
9266	}
9267});
9268
9269
9270// @namespace LayerGroup
9271LayerGroup.include({
9272	toMultiPoint: function (precision) {
9273		var coords = [];
9274
9275		this.eachLayer(function (layer) {
9276			coords.push(layer.toGeoJSON(precision).geometry.coordinates);
9277		});
9278
9279		return getFeature(this, {
9280			type: 'MultiPoint',
9281			coordinates: coords
9282		});
9283	},
9284
9285	// @method toGeoJSON(precision?: Number|false): Object
9286	// Coordinates values are rounded with [`formatNum`](#util-formatnum) function with given `precision`.
9287	// Returns a [`GeoJSON`](https://en.wikipedia.org/wiki/GeoJSON) representation of the layer group (as a GeoJSON `FeatureCollection`, `GeometryCollection`, or `MultiPoint`).
9288	toGeoJSON: function (precision) {
9289
9290		var type = this.feature && this.feature.geometry && this.feature.geometry.type;
9291
9292		if (type === 'MultiPoint') {
9293			return this.toMultiPoint(precision);
9294		}
9295
9296		var isGeometryCollection = type === 'GeometryCollection',
9297		    jsons = [];
9298
9299		this.eachLayer(function (layer) {
9300			if (layer.toGeoJSON) {
9301				var json = layer.toGeoJSON(precision);
9302				if (isGeometryCollection) {
9303					jsons.push(json.geometry);
9304				} else {
9305					var feature = asFeature(json);
9306					// Squash nested feature collections
9307					if (feature.type === 'FeatureCollection') {
9308						jsons.push.apply(jsons, feature.features);
9309					} else {
9310						jsons.push(feature);
9311					}
9312				}
9313			}
9314		});
9315
9316		if (isGeometryCollection) {
9317			return getFeature(this, {
9318				geometries: jsons,
9319				type: 'GeometryCollection'
9320			});
9321		}
9322
9323		return {
9324			type: 'FeatureCollection',
9325			features: jsons
9326		};
9327	}
9328});
9329
9330// @namespace GeoJSON
9331// @factory L.geoJSON(geojson?: Object, options?: GeoJSON options)
9332// Creates a GeoJSON layer. Optionally accepts an object in
9333// [GeoJSON format](https://tools.ietf.org/html/rfc7946) to display on the map
9334// (you can alternatively add it later with `addData` method) and an `options` object.
9335function geoJSON(geojson, options) {
9336	return new GeoJSON(geojson, options);
9337}
9338
9339// Backward compatibility.
9340var geoJson = geoJSON;
9341
9342/*
9343 * @class ImageOverlay
9344 * @aka L.ImageOverlay
9345 * @inherits Interactive layer
9346 *
9347 * Used to load and display a single image over specific bounds of the map. Extends `Layer`.
9348 *
9349 * @example
9350 *
9351 * ```js
9352 * var imageUrl = 'https://maps.lib.utexas.edu/maps/historical/newark_nj_1922.jpg',
9353 * 	imageBounds = [[40.712216, -74.22655], [40.773941, -74.12544]];
9354 * L.imageOverlay(imageUrl, imageBounds).addTo(map);
9355 * ```
9356 */
9357
9358var ImageOverlay = Layer.extend({
9359
9360	// @section
9361	// @aka ImageOverlay options
9362	options: {
9363		// @option opacity: Number = 1.0
9364		// The opacity of the image overlay.
9365		opacity: 1,
9366
9367		// @option alt: String = ''
9368		// Text for the `alt` attribute of the image (useful for accessibility).
9369		alt: '',
9370
9371		// @option interactive: Boolean = false
9372		// If `true`, the image overlay will emit [mouse events](#interactive-layer) when clicked or hovered.
9373		interactive: false,
9374
9375		// @option crossOrigin: Boolean|String = false
9376		// Whether the crossOrigin attribute will be added to the image.
9377		// 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.
9378		// Refer to [CORS Settings](https://developer.mozilla.org/en-US/docs/Web/HTML/CORS_settings_attributes) for valid String values.
9379		crossOrigin: false,
9380
9381		// @option errorOverlayUrl: String = ''
9382		// URL to the overlay image to show in place of the overlay that failed to load.
9383		errorOverlayUrl: '',
9384
9385		// @option zIndex: Number = 1
9386		// The explicit [zIndex](https://developer.mozilla.org/docs/Web/CSS/CSS_Positioning/Understanding_z_index) of the overlay layer.
9387		zIndex: 1,
9388
9389		// @option className: String = ''
9390		// A custom class name to assign to the image. Empty by default.
9391		className: ''
9392	},
9393
9394	initialize: function (url, bounds, options) { // (String, LatLngBounds, Object)
9395		this._url = url;
9396		this._bounds = toLatLngBounds(bounds);
9397
9398		setOptions(this, options);
9399	},
9400
9401	onAdd: function () {
9402		if (!this._image) {
9403			this._initImage();
9404
9405			if (this.options.opacity < 1) {
9406				this._updateOpacity();
9407			}
9408		}
9409
9410		if (this.options.interactive) {
9411			addClass(this._image, 'leaflet-interactive');
9412			this.addInteractiveTarget(this._image);
9413		}
9414
9415		this.getPane().appendChild(this._image);
9416		this._reset();
9417	},
9418
9419	onRemove: function () {
9420		remove(this._image);
9421		if (this.options.interactive) {
9422			this.removeInteractiveTarget(this._image);
9423		}
9424	},
9425
9426	// @method setOpacity(opacity: Number): this
9427	// Sets the opacity of the overlay.
9428	setOpacity: function (opacity) {
9429		this.options.opacity = opacity;
9430
9431		if (this._image) {
9432			this._updateOpacity();
9433		}
9434		return this;
9435	},
9436
9437	setStyle: function (styleOpts) {
9438		if (styleOpts.opacity) {
9439			this.setOpacity(styleOpts.opacity);
9440		}
9441		return this;
9442	},
9443
9444	// @method bringToFront(): this
9445	// Brings the layer to the top of all overlays.
9446	bringToFront: function () {
9447		if (this._map) {
9448			toFront(this._image);
9449		}
9450		return this;
9451	},
9452
9453	// @method bringToBack(): this
9454	// Brings the layer to the bottom of all overlays.
9455	bringToBack: function () {
9456		if (this._map) {
9457			toBack(this._image);
9458		}
9459		return this;
9460	},
9461
9462	// @method setUrl(url: String): this
9463	// Changes the URL of the image.
9464	setUrl: function (url) {
9465		this._url = url;
9466
9467		if (this._image) {
9468			this._image.src = url;
9469		}
9470		return this;
9471	},
9472
9473	// @method setBounds(bounds: LatLngBounds): this
9474	// Update the bounds that this ImageOverlay covers
9475	setBounds: function (bounds) {
9476		this._bounds = toLatLngBounds(bounds);
9477
9478		if (this._map) {
9479			this._reset();
9480		}
9481		return this;
9482	},
9483
9484	getEvents: function () {
9485		var events = {
9486			zoom: this._reset,
9487			viewreset: this._reset
9488		};
9489
9490		if (this._zoomAnimated) {
9491			events.zoomanim = this._animateZoom;
9492		}
9493
9494		return events;
9495	},
9496
9497	// @method setZIndex(value: Number): this
9498	// Changes the [zIndex](#imageoverlay-zindex) of the image overlay.
9499	setZIndex: function (value) {
9500		this.options.zIndex = value;
9501		this._updateZIndex();
9502		return this;
9503	},
9504
9505	// @method getBounds(): LatLngBounds
9506	// Get the bounds that this ImageOverlay covers
9507	getBounds: function () {
9508		return this._bounds;
9509	},
9510
9511	// @method getElement(): HTMLElement
9512	// Returns the instance of [`HTMLImageElement`](https://developer.mozilla.org/docs/Web/API/HTMLImageElement)
9513	// used by this overlay.
9514	getElement: function () {
9515		return this._image;
9516	},
9517
9518	_initImage: function () {
9519		var wasElementSupplied = this._url.tagName === 'IMG';
9520		var img = this._image = wasElementSupplied ? this._url : create$1('img');
9521
9522		addClass(img, 'leaflet-image-layer');
9523		if (this._zoomAnimated) { addClass(img, 'leaflet-zoom-animated'); }
9524		if (this.options.className) { addClass(img, this.options.className); }
9525
9526		img.onselectstart = falseFn;
9527		img.onmousemove = falseFn;
9528
9529		// @event load: Event
9530		// Fired when the ImageOverlay layer has loaded its image
9531		img.onload = bind(this.fire, this, 'load');
9532		img.onerror = bind(this._overlayOnError, this, 'error');
9533
9534		if (this.options.crossOrigin || this.options.crossOrigin === '') {
9535			img.crossOrigin = this.options.crossOrigin === true ? '' : this.options.crossOrigin;
9536		}
9537
9538		if (this.options.zIndex) {
9539			this._updateZIndex();
9540		}
9541
9542		if (wasElementSupplied) {
9543			this._url = img.src;
9544			return;
9545		}
9546
9547		img.src = this._url;
9548		img.alt = this.options.alt;
9549	},
9550
9551	_animateZoom: function (e) {
9552		var scale = this._map.getZoomScale(e.zoom),
9553		    offset = this._map._latLngBoundsToNewLayerBounds(this._bounds, e.zoom, e.center).min;
9554
9555		setTransform(this._image, offset, scale);
9556	},
9557
9558	_reset: function () {
9559		var image = this._image,
9560		    bounds = new Bounds(
9561		        this._map.latLngToLayerPoint(this._bounds.getNorthWest()),
9562		        this._map.latLngToLayerPoint(this._bounds.getSouthEast())),
9563		    size = bounds.getSize();
9564
9565		setPosition(image, bounds.min);
9566
9567		image.style.width  = size.x + 'px';
9568		image.style.height = size.y + 'px';
9569	},
9570
9571	_updateOpacity: function () {
9572		setOpacity(this._image, this.options.opacity);
9573	},
9574
9575	_updateZIndex: function () {
9576		if (this._image && this.options.zIndex !== undefined && this.options.zIndex !== null) {
9577			this._image.style.zIndex = this.options.zIndex;
9578		}
9579	},
9580
9581	_overlayOnError: function () {
9582		// @event error: Event
9583		// Fired when the ImageOverlay layer fails to load its image
9584		this.fire('error');
9585
9586		var errorUrl = this.options.errorOverlayUrl;
9587		if (errorUrl && this._url !== errorUrl) {
9588			this._url = errorUrl;
9589			this._image.src = errorUrl;
9590		}
9591	},
9592
9593	// @method getCenter(): LatLng
9594	// Returns the center of the ImageOverlay.
9595	getCenter: function () {
9596		return this._bounds.getCenter();
9597	}
9598});
9599
9600// @factory L.imageOverlay(imageUrl: String, bounds: LatLngBounds, options?: ImageOverlay options)
9601// Instantiates an image overlay object given the URL of the image and the
9602// geographical bounds it is tied to.
9603var imageOverlay = function (url, bounds, options) {
9604	return new ImageOverlay(url, bounds, options);
9605};
9606
9607/*
9608 * @class VideoOverlay
9609 * @aka L.VideoOverlay
9610 * @inherits ImageOverlay
9611 *
9612 * Used to load and display a video player over specific bounds of the map. Extends `ImageOverlay`.
9613 *
9614 * A video overlay uses the [`<video>`](https://developer.mozilla.org/docs/Web/HTML/Element/video)
9615 * HTML5 element.
9616 *
9617 * @example
9618 *
9619 * ```js
9620 * var videoUrl = 'https://www.mapbox.com/bites/00188/patricia_nasa.webm',
9621 * 	videoBounds = [[ 32, -130], [ 13, -100]];
9622 * L.videoOverlay(videoUrl, videoBounds ).addTo(map);
9623 * ```
9624 */
9625
9626var VideoOverlay = ImageOverlay.extend({
9627
9628	// @section
9629	// @aka VideoOverlay options
9630	options: {
9631		// @option autoplay: Boolean = true
9632		// Whether the video starts playing automatically when loaded.
9633		// On some browsers autoplay will only work with `muted: true`
9634		autoplay: true,
9635
9636		// @option loop: Boolean = true
9637		// Whether the video will loop back to the beginning when played.
9638		loop: true,
9639
9640		// @option keepAspectRatio: Boolean = true
9641		// Whether the video will save aspect ratio after the projection.
9642		// Relevant for supported browsers. See [browser compatibility](https://developer.mozilla.org/en-US/docs/Web/CSS/object-fit)
9643		keepAspectRatio: true,
9644
9645		// @option muted: Boolean = false
9646		// Whether the video starts on mute when loaded.
9647		muted: false,
9648
9649		// @option playsInline: Boolean = true
9650		// Mobile browsers will play the video right where it is instead of open it up in fullscreen mode.
9651		playsInline: true
9652	},
9653
9654	_initImage: function () {
9655		var wasElementSupplied = this._url.tagName === 'VIDEO';
9656		var vid = this._image = wasElementSupplied ? this._url : create$1('video');
9657
9658		addClass(vid, 'leaflet-image-layer');
9659		if (this._zoomAnimated) { addClass(vid, 'leaflet-zoom-animated'); }
9660		if (this.options.className) { addClass(vid, this.options.className); }
9661
9662		vid.onselectstart = falseFn;
9663		vid.onmousemove = falseFn;
9664
9665		// @event load: Event
9666		// Fired when the video has finished loading the first frame
9667		vid.onloadeddata = bind(this.fire, this, 'load');
9668
9669		if (wasElementSupplied) {
9670			var sourceElements = vid.getElementsByTagName('source');
9671			var sources = [];
9672			for (var j = 0; j < sourceElements.length; j++) {
9673				sources.push(sourceElements[j].src);
9674			}
9675
9676			this._url = (sourceElements.length > 0) ? sources : [vid.src];
9677			return;
9678		}
9679
9680		if (!isArray(this._url)) { this._url = [this._url]; }
9681
9682		if (!this.options.keepAspectRatio && Object.prototype.hasOwnProperty.call(vid.style, 'objectFit')) {
9683			vid.style['objectFit'] = 'fill';
9684		}
9685		vid.autoplay = !!this.options.autoplay;
9686		vid.loop = !!this.options.loop;
9687		vid.muted = !!this.options.muted;
9688		vid.playsInline = !!this.options.playsInline;
9689		for (var i = 0; i < this._url.length; i++) {
9690			var source = create$1('source');
9691			source.src = this._url[i];
9692			vid.appendChild(source);
9693		}
9694	}
9695
9696	// @method getElement(): HTMLVideoElement
9697	// Returns the instance of [`HTMLVideoElement`](https://developer.mozilla.org/docs/Web/API/HTMLVideoElement)
9698	// used by this overlay.
9699});
9700
9701
9702// @factory L.videoOverlay(video: String|Array|HTMLVideoElement, bounds: LatLngBounds, options?: VideoOverlay options)
9703// Instantiates an image overlay object given the URL of the video (or array of URLs, or even a video element) and the
9704// geographical bounds it is tied to.
9705
9706function videoOverlay(video, bounds, options) {
9707	return new VideoOverlay(video, bounds, options);
9708}
9709
9710/*
9711 * @class SVGOverlay
9712 * @aka L.SVGOverlay
9713 * @inherits ImageOverlay
9714 *
9715 * Used to load, display and provide DOM access to an SVG file over specific bounds of the map. Extends `ImageOverlay`.
9716 *
9717 * An SVG overlay uses the [`<svg>`](https://developer.mozilla.org/docs/Web/SVG/Element/svg) element.
9718 *
9719 * @example
9720 *
9721 * ```js
9722 * var svgElement = document.createElementNS("http://www.w3.org/2000/svg", "svg");
9723 * svgElement.setAttribute('xmlns', "http://www.w3.org/2000/svg");
9724 * svgElement.setAttribute('viewBox', "0 0 200 200");
9725 * 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"/>';
9726 * var svgElementBounds = [ [ 32, -130 ], [ 13, -100 ] ];
9727 * L.svgOverlay(svgElement, svgElementBounds).addTo(map);
9728 * ```
9729 */
9730
9731var SVGOverlay = ImageOverlay.extend({
9732	_initImage: function () {
9733		var el = this._image = this._url;
9734
9735		addClass(el, 'leaflet-image-layer');
9736		if (this._zoomAnimated) { addClass(el, 'leaflet-zoom-animated'); }
9737		if (this.options.className) { addClass(el, this.options.className); }
9738
9739		el.onselectstart = falseFn;
9740		el.onmousemove = falseFn;
9741	}
9742
9743	// @method getElement(): SVGElement
9744	// Returns the instance of [`SVGElement`](https://developer.mozilla.org/docs/Web/API/SVGElement)
9745	// used by this overlay.
9746});
9747
9748
9749// @factory L.svgOverlay(svg: String|SVGElement, bounds: LatLngBounds, options?: SVGOverlay options)
9750// Instantiates an image overlay object given an SVG element and the geographical bounds it is tied to.
9751// A viewBox attribute is required on the SVG element to zoom in and out properly.
9752
9753function svgOverlay(el, bounds, options) {
9754	return new SVGOverlay(el, bounds, options);
9755}
9756
9757/*
9758 * @class DivOverlay
9759 * @inherits Interactive layer
9760 * @aka L.DivOverlay
9761 * Base model for L.Popup and L.Tooltip. Inherit from it for custom overlays like plugins.
9762 */
9763
9764// @namespace DivOverlay
9765var DivOverlay = Layer.extend({
9766
9767	// @section
9768	// @aka DivOverlay options
9769	options: {
9770		// @option interactive: Boolean = false
9771		// If true, the popup/tooltip will listen to the mouse events.
9772		interactive: false,
9773
9774		// @option offset: Point = Point(0, 0)
9775		// The offset of the overlay position.
9776		offset: [0, 0],
9777
9778		// @option className: String = ''
9779		// A custom CSS class name to assign to the overlay.
9780		className: '',
9781
9782		// @option pane: String = undefined
9783		// `Map pane` where the overlay will be added.
9784		pane: undefined,
9785
9786		// @option content: String|HTMLElement|Function = ''
9787		// Sets the HTML content of the overlay while initializing. If a function is passed the source layer will be
9788		// passed to the function. The function should return a `String` or `HTMLElement` to be used in the overlay.
9789		content: ''
9790	},
9791
9792	initialize: function (options, source) {
9793		if (options && (options instanceof LatLng || isArray(options))) {
9794			this._latlng = toLatLng(options);
9795			setOptions(this, source);
9796		} else {
9797			setOptions(this, options);
9798			this._source = source;
9799		}
9800		if (this.options.content) {
9801			this._content = this.options.content;
9802		}
9803	},
9804
9805	// @method openOn(map: Map): this
9806	// Adds the overlay to the map.
9807	// Alternative to `map.openPopup(popup)`/`.openTooltip(tooltip)`.
9808	openOn: function (map) {
9809		map = arguments.length ? map : this._source._map; // experimental, not the part of public api
9810		if (!map.hasLayer(this)) {
9811			map.addLayer(this);
9812		}
9813		return this;
9814	},
9815
9816	// @method close(): this
9817	// Closes the overlay.
9818	// Alternative to `map.closePopup(popup)`/`.closeTooltip(tooltip)`
9819	// and `layer.closePopup()`/`.closeTooltip()`.
9820	close: function () {
9821		if (this._map) {
9822			this._map.removeLayer(this);
9823		}
9824		return this;
9825	},
9826
9827	// @method toggle(layer?: Layer): this
9828	// Opens or closes the overlay bound to layer depending on its current state.
9829	// Argument may be omitted only for overlay bound to layer.
9830	// Alternative to `layer.togglePopup()`/`.toggleTooltip()`.
9831	toggle: function (layer) {
9832		if (this._map) {
9833			this.close();
9834		} else {
9835			if (arguments.length) {
9836				this._source = layer;
9837			} else {
9838				layer = this._source;
9839			}
9840			this._prepareOpen();
9841
9842			// open the overlay on the map
9843			this.openOn(layer._map);
9844		}
9845		return this;
9846	},
9847
9848	onAdd: function (map) {
9849		this._zoomAnimated = map._zoomAnimated;
9850
9851		if (!this._container) {
9852			this._initLayout();
9853		}
9854
9855		if (map._fadeAnimated) {
9856			setOpacity(this._container, 0);
9857		}
9858
9859		clearTimeout(this._removeTimeout);
9860		this.getPane().appendChild(this._container);
9861		this.update();
9862
9863		if (map._fadeAnimated) {
9864			setOpacity(this._container, 1);
9865		}
9866
9867		this.bringToFront();
9868
9869		if (this.options.interactive) {
9870			addClass(this._container, 'leaflet-interactive');
9871			this.addInteractiveTarget(this._container);
9872		}
9873	},
9874
9875	onRemove: function (map) {
9876		if (map._fadeAnimated) {
9877			setOpacity(this._container, 0);
9878			this._removeTimeout = setTimeout(bind(remove, undefined, this._container), 200);
9879		} else {
9880			remove(this._container);
9881		}
9882
9883		if (this.options.interactive) {
9884			removeClass(this._container, 'leaflet-interactive');
9885			this.removeInteractiveTarget(this._container);
9886		}
9887	},
9888
9889	// @namespace DivOverlay
9890	// @method getLatLng: LatLng
9891	// Returns the geographical point of the overlay.
9892	getLatLng: function () {
9893		return this._latlng;
9894	},
9895
9896	// @method setLatLng(latlng: LatLng): this
9897	// Sets the geographical point where the overlay will open.
9898	setLatLng: function (latlng) {
9899		this._latlng = toLatLng(latlng);
9900		if (this._map) {
9901			this._updatePosition();
9902			this._adjustPan();
9903		}
9904		return this;
9905	},
9906
9907	// @method getContent: String|HTMLElement
9908	// Returns the content of the overlay.
9909	getContent: function () {
9910		return this._content;
9911	},
9912
9913	// @method setContent(htmlContent: String|HTMLElement|Function): this
9914	// Sets the HTML content of the overlay. If a function is passed the source layer will be passed to the function.
9915	// The function should return a `String` or `HTMLElement` to be used in the overlay.
9916	setContent: function (content) {
9917		this._content = content;
9918		this.update();
9919		return this;
9920	},
9921
9922	// @method getElement: String|HTMLElement
9923	// Returns the HTML container of the overlay.
9924	getElement: function () {
9925		return this._container;
9926	},
9927
9928	// @method update: null
9929	// Updates the overlay content, layout and position. Useful for updating the overlay after something inside changed, e.g. image loaded.
9930	update: function () {
9931		if (!this._map) { return; }
9932
9933		this._container.style.visibility = 'hidden';
9934
9935		this._updateContent();
9936		this._updateLayout();
9937		this._updatePosition();
9938
9939		this._container.style.visibility = '';
9940
9941		this._adjustPan();
9942	},
9943
9944	getEvents: function () {
9945		var events = {
9946			zoom: this._updatePosition,
9947			viewreset: this._updatePosition
9948		};
9949
9950		if (this._zoomAnimated) {
9951			events.zoomanim = this._animateZoom;
9952		}
9953		return events;
9954	},
9955
9956	// @method isOpen: Boolean
9957	// Returns `true` when the overlay is visible on the map.
9958	isOpen: function () {
9959		return !!this._map && this._map.hasLayer(this);
9960	},
9961
9962	// @method bringToFront: this
9963	// Brings this overlay in front of other overlays (in the same map pane).
9964	bringToFront: function () {
9965		if (this._map) {
9966			toFront(this._container);
9967		}
9968		return this;
9969	},
9970
9971	// @method bringToBack: this
9972	// Brings this overlay to the back of other overlays (in the same map pane).
9973	bringToBack: function () {
9974		if (this._map) {
9975			toBack(this._container);
9976		}
9977		return this;
9978	},
9979
9980	// prepare bound overlay to open: update latlng pos / content source (for FeatureGroup)
9981	_prepareOpen: function (latlng) {
9982		var source = this._source;
9983		if (!source._map) { return false; }
9984
9985		if (source instanceof FeatureGroup) {
9986			source = null;
9987			var layers = this._source._layers;
9988			for (var id in layers) {
9989				if (layers[id]._map) {
9990					source = layers[id];
9991					break;
9992				}
9993			}
9994			if (!source) { return false; } // Unable to get source layer.
9995
9996			// set overlay source to this layer
9997			this._source = source;
9998		}
9999
10000		if (!latlng) {
10001			if (source.getCenter) {
10002				latlng = source.getCenter();
10003			} else if (source.getLatLng) {
10004				latlng = source.getLatLng();
10005			} else if (source.getBounds) {
10006				latlng = source.getBounds().getCenter();
10007			} else {
10008				throw new Error('Unable to get source layer LatLng.');
10009			}
10010		}
10011		this.setLatLng(latlng);
10012
10013		if (this._map) {
10014			// update the overlay (content, layout, etc...)
10015			this.update();
10016		}
10017
10018		return true;
10019	},
10020
10021	_updateContent: function () {
10022		if (!this._content) { return; }
10023
10024		var node = this._contentNode;
10025		var content = (typeof this._content === 'function') ? this._content(this._source || this) : this._content;
10026
10027		if (typeof content === 'string') {
10028			// CVE-2025-69993: strings are text; callers needing markup must pass a DOM node.
10029			node.textContent = content;
10030		} else {
10031			while (node.hasChildNodes()) {
10032				node.removeChild(node.firstChild);
10033			}
10034			node.appendChild(content);
10035		}
10036
10037		// @namespace DivOverlay
10038		// @section DivOverlay events
10039		// @event contentupdate: Event
10040		// Fired when the content of the overlay is updated
10041		this.fire('contentupdate');
10042	},
10043
10044	_updatePosition: function () {
10045		if (!this._map) { return; }
10046
10047		var pos = this._map.latLngToLayerPoint(this._latlng),
10048		    offset = toPoint(this.options.offset),
10049		    anchor = this._getAnchor();
10050
10051		if (this._zoomAnimated) {
10052			setPosition(this._container, pos.add(anchor));
10053		} else {
10054			offset = offset.add(pos).add(anchor);
10055		}
10056
10057		var bottom = this._containerBottom = -offset.y,
10058		    left = this._containerLeft = -Math.round(this._containerWidth / 2) + offset.x;
10059
10060		// bottom position the overlay in case the height of the overlay changes (images loading etc)
10061		this._container.style.bottom = bottom + 'px';
10062		this._container.style.left = left + 'px';
10063	},
10064
10065	_getAnchor: function () {
10066		return [0, 0];
10067	}
10068
10069});
10070
10071Map.include({
10072	_initOverlay: function (OverlayClass, content, latlng, options) {
10073		var overlay = content;
10074		if (!(overlay instanceof OverlayClass)) {
10075			overlay = new OverlayClass(options).setContent(content);
10076		}
10077		if (latlng) {
10078			overlay.setLatLng(latlng);
10079		}
10080		return overlay;
10081	}
10082});
10083
10084
10085Layer.include({
10086	_initOverlay: function (OverlayClass, old, content, options) {
10087		var overlay = content;
10088		if (overlay instanceof OverlayClass) {
10089			setOptions(overlay, options);
10090			overlay._source = this;
10091		} else {
10092			overlay = (old && !options) ? old : new OverlayClass(options, this);
10093			overlay.setContent(content);
10094		}
10095		return overlay;
10096	}
10097});
10098
10099/*
10100 * @class Popup
10101 * @inherits DivOverlay
10102 * @aka L.Popup
10103 * Used to open popups in certain places of the map. Use [Map.openPopup](#map-openpopup) to
10104 * open popups while making sure that only one popup is open at one time
10105 * (recommended for usability), or use [Map.addLayer](#map-addlayer) to open as many as you want.
10106 *
10107 * @example
10108 *
10109 * If you want to just bind a popup to marker click and then open it, it's really easy:
10110 *
10111 * ```js
10112 * marker.bindPopup(popupContent).openPopup();
10113 * ```
10114 * Path overlays like polylines also have a `bindPopup` method.
10115 *
10116 * A popup can be also standalone:
10117 *
10118 * ```js
10119 * var popup = L.popup()
10120 * 	.setLatLng(latlng)
10121 * 	.setContent('<p>Hello world!<br />This is a nice popup.</p>')
10122 * 	.openOn(map);
10123 * ```
10124 * or
10125 * ```js
10126 * var popup = L.popup(latlng, {content: '<p>Hello world!<br />This is a nice popup.</p>')
10127 * 	.openOn(map);
10128 * ```
10129 */
10130
10131
10132// @namespace Popup
10133var Popup = DivOverlay.extend({
10134
10135	// @section
10136	// @aka Popup options
10137	options: {
10138		// @option pane: String = 'popupPane'
10139		// `Map pane` where the popup will be added.
10140		pane: 'popupPane',
10141
10142		// @option offset: Point = Point(0, 7)
10143		// The offset of the popup position.
10144		offset: [0, 7],
10145
10146		// @option maxWidth: Number = 300
10147		// Max width of the popup, in pixels.
10148		maxWidth: 300,
10149
10150		// @option minWidth: Number = 50
10151		// Min width of the popup, in pixels.
10152		minWidth: 50,
10153
10154		// @option maxHeight: Number = null
10155		// If set, creates a scrollable container of the given height
10156		// inside a popup if its content exceeds it.
10157		// The scrollable container can be styled using the
10158		// `leaflet-popup-scrolled` CSS class selector.
10159		maxHeight: null,
10160
10161		// @option autoPan: Boolean = true
10162		// Set it to `false` if you don't want the map to do panning animation
10163		// to fit the opened popup.
10164		autoPan: true,
10165
10166		// @option autoPanPaddingTopLeft: Point = null
10167		// The margin between the popup and the top left corner of the map
10168		// view after autopanning was performed.
10169		autoPanPaddingTopLeft: null,
10170
10171		// @option autoPanPaddingBottomRight: Point = null
10172		// The margin between the popup and the bottom right corner of the map
10173		// view after autopanning was performed.
10174		autoPanPaddingBottomRight: null,
10175
10176		// @option autoPanPadding: Point = Point(5, 5)
10177		// Equivalent of setting both top left and bottom right autopan padding to the same value.
10178		autoPanPadding: [5, 5],
10179
10180		// @option keepInView: Boolean = false
10181		// Set it to `true` if you want to prevent users from panning the popup
10182		// off of the screen while it is open.
10183		keepInView: false,
10184
10185		// @option closeButton: Boolean = true
10186		// Controls the presence of a close button in the popup.
10187		closeButton: true,
10188
10189		// @option autoClose: Boolean = true
10190		// Set it to `false` if you want to override the default behavior of
10191		// the popup closing when another popup is opened.
10192		autoClose: true,
10193
10194		// @option closeOnEscapeKey: Boolean = true
10195		// Set it to `false` if you want to override the default behavior of
10196		// the ESC key for closing of the popup.
10197		closeOnEscapeKey: true,
10198
10199		// @option closeOnClick: Boolean = *
10200		// Set it if you want to override the default behavior of the popup closing when user clicks
10201		// on the map. Defaults to the map's [`closePopupOnClick`](#map-closepopuponclick) option.
10202
10203		// @option className: String = ''
10204		// A custom CSS class name to assign to the popup.
10205		className: ''
10206	},
10207
10208	// @namespace Popup
10209	// @method openOn(map: Map): this
10210	// Alternative to `map.openPopup(popup)`.
10211	// Adds the popup to the map and closes the previous one.
10212	openOn: function (map) {
10213		map = arguments.length ? map : this._source._map; // experimental, not the part of public api
10214
10215		if (!map.hasLayer(this) && map._popup && map._popup.options.autoClose) {
10216			map.removeLayer(map._popup);
10217		}
10218		map._popup = this;
10219
10220		return DivOverlay.prototype.openOn.call(this, map);
10221	},
10222
10223	onAdd: function (map) {
10224		DivOverlay.prototype.onAdd.call(this, map);
10225
10226		// @namespace Map
10227		// @section Popup events
10228		// @event popupopen: PopupEvent
10229		// Fired when a popup is opened in the map
10230		map.fire('popupopen', {popup: this});
10231
10232		if (this._source) {
10233			// @namespace Layer
10234			// @section Popup events
10235			// @event popupopen: PopupEvent
10236			// Fired when a popup bound to this layer is opened
10237			this._source.fire('popupopen', {popup: this}, true);
10238			// For non-path layers, we toggle the popup when clicking
10239			// again the layer, so prevent the map to reopen it.
10240			if (!(this._source instanceof Path)) {
10241				this._source.on('preclick', stopPropagation);
10242			}
10243		}
10244	},
10245
10246	onRemove: function (map) {
10247		DivOverlay.prototype.onRemove.call(this, map);
10248
10249		// @namespace Map
10250		// @section Popup events
10251		// @event popupclose: PopupEvent
10252		// Fired when a popup in the map is closed
10253		map.fire('popupclose', {popup: this});
10254
10255		if (this._source) {
10256			// @namespace Layer
10257			// @section Popup events
10258			// @event popupclose: PopupEvent
10259			// Fired when a popup bound to this layer is closed
10260			this._source.fire('popupclose', {popup: this}, true);
10261			if (!(this._source instanceof Path)) {
10262				this._source.off('preclick', stopPropagation);
10263			}
10264		}
10265	},
10266
10267	getEvents: function () {
10268		var events = DivOverlay.prototype.getEvents.call(this);
10269
10270		if (this.options.closeOnClick !== undefined ? this.options.closeOnClick : this._map.options.closePopupOnClick) {
10271			events.preclick = this.close;
10272		}
10273
10274		if (this.options.keepInView) {
10275			events.moveend = this._adjustPan;
10276		}
10277
10278		return events;
10279	},
10280
10281	_initLayout: function () {
10282		var prefix = 'leaflet-popup',
10283		    container = this._container = create$1('div',
10284			prefix + ' ' + (this.options.className || '') +
10285			' leaflet-zoom-animated');
10286
10287		var wrapper = this._wrapper = create$1('div', prefix + '-content-wrapper', container);
10288		this._contentNode = create$1('div', prefix + '-content', wrapper);
10289
10290		disableClickPropagation(container);
10291		disableScrollPropagation(this._contentNode);
10292		on(container, 'contextmenu', stopPropagation);
10293
10294		this._tipContainer = create$1('div', prefix + '-tip-container', container);
10295		this._tip = create$1('div', prefix + '-tip', this._tipContainer);
10296
10297		if (this.options.closeButton) {
10298			var closeButton = this._closeButton = create$1('a', prefix + '-close-button', container);
10299			closeButton.setAttribute('role', 'button'); // overrides the implicit role=link of <a> elements #7399
10300			closeButton.setAttribute('aria-label', 'Close popup');
10301			closeButton.href = '#close';
10302			closeButton.innerHTML = '<span aria-hidden="true">&#215;</span>';
10303
10304			on(closeButton, 'click', function (ev) {
10305				preventDefault(ev);
10306				this.close();
10307			}, this);
10308		}
10309	},
10310
10311	_updateLayout: function () {
10312		var container = this._contentNode,
10313		    style = container.style;
10314
10315		style.width = '';
10316		style.whiteSpace = 'nowrap';
10317
10318		var width = container.offsetWidth;
10319		width = Math.min(width, this.options.maxWidth);
10320		width = Math.max(width, this.options.minWidth);
10321
10322		style.width = (width + 1) + 'px';
10323		style.whiteSpace = '';
10324
10325		style.height = '';
10326
10327		var height = container.offsetHeight,
10328		    maxHeight = this.options.maxHeight,
10329		    scrolledClass = 'leaflet-popup-scrolled';
10330
10331		if (maxHeight && height > maxHeight) {
10332			style.height = maxHeight + 'px';
10333			addClass(container, scrolledClass);
10334		} else {
10335			removeClass(container, scrolledClass);
10336		}
10337
10338		this._containerWidth = this._container.offsetWidth;
10339	},
10340
10341	_animateZoom: function (e) {
10342		var pos = this._map._latLngToNewLayerPoint(this._latlng, e.zoom, e.center),
10343		    anchor = this._getAnchor();
10344		setPosition(this._container, pos.add(anchor));
10345	},
10346
10347	_adjustPan: function () {
10348		if (!this.options.autoPan) { return; }
10349		if (this._map._panAnim) { this._map._panAnim.stop(); }
10350
10351		// We can endlessly recurse if keepInView is set and the view resets.
10352		// Let's guard against that by exiting early if we're responding to our own autopan.
10353		if (this._autopanning) {
10354			this._autopanning = false;
vendor: 7,050 bytes, lines 10355-10594
10355			return;
10356		}
10357
10358		var map = this._map,
10359		    marginBottom = parseInt(getStyle(this._container, 'marginBottom'), 10) || 0,
10360		    containerHeight = this._container.offsetHeight + marginBottom,
10361		    containerWidth = this._containerWidth,
10362		    layerPos = new Point(this._containerLeft, -containerHeight - this._containerBottom);
10363
10364		layerPos._add(getPosition(this._container));
10365
10366		var containerPos = map.layerPointToContainerPoint(layerPos),
10367		    padding = toPoint(this.options.autoPanPadding),
10368		    paddingTL = toPoint(this.options.autoPanPaddingTopLeft || padding),
10369		    paddingBR = toPoint(this.options.autoPanPaddingBottomRight || padding),
10370		    size = map.getSize(),
10371		    dx = 0,
10372		    dy = 0;
10373
10374		if (containerPos.x + containerWidth + paddingBR.x > size.x) { // right
10375			dx = containerPos.x + containerWidth - size.x + paddingBR.x;
10376		}
10377		if (containerPos.x - dx - paddingTL.x < 0) { // left
10378			dx = containerPos.x - paddingTL.x;
10379		}
10380		if (containerPos.y + containerHeight + paddingBR.y > size.y) { // bottom
10381			dy = containerPos.y + containerHeight - size.y + paddingBR.y;
10382		}
10383		if (containerPos.y - dy - paddingTL.y < 0) { // top
10384			dy = containerPos.y - paddingTL.y;
10385		}
10386
10387		// @namespace Map
10388		// @section Popup events
10389		// @event autopanstart: Event
10390		// Fired when the map starts autopanning when opening a popup.
10391		if (dx || dy) {
10392			// Track that we're autopanning, as this function will be re-ran on moveend
10393			if (this.options.keepInView) {
10394				this._autopanning = true;
10395			}
10396
10397			map
10398			    .fire('autopanstart')
10399			    .panBy([dx, dy]);
10400		}
10401	},
10402
10403	_getAnchor: function () {
10404		// Where should we anchor the popup on the source layer?
10405		return toPoint(this._source && this._source._getPopupAnchor ? this._source._getPopupAnchor() : [0, 0]);
10406	}
10407
10408});
10409
10410// @namespace Popup
10411// @factory L.popup(options?: Popup options, source?: Layer)
10412// 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.
10413// @alternative
10414// @factory L.popup(latlng: LatLng, options?: Popup options)
10415// Instantiates a `Popup` object given `latlng` where the popup will open and an optional `options` object that describes its appearance and location.
10416var popup = function (options, source) {
10417	return new Popup(options, source);
10418};
10419
10420
10421/* @namespace Map
10422 * @section Interaction Options
10423 * @option closePopupOnClick: Boolean = true
10424 * Set it to `false` if you don't want popups to close when user clicks the map.
10425 */
10426Map.mergeOptions({
10427	closePopupOnClick: true
10428});
10429
10430
10431// @namespace Map
10432// @section Methods for Layers and Controls
10433Map.include({
10434	// @method openPopup(popup: Popup): this
10435	// Opens the specified popup while closing the previously opened (to make sure only one is opened at one time for usability).
10436	// @alternative
10437	// @method openPopup(content: String|HTMLElement, latlng: LatLng, options?: Popup options): this
10438	// Creates a popup with the specified content and options and opens it in the given point on a map.
10439	openPopup: function (popup, latlng, options) {
10440		this._initOverlay(Popup, popup, latlng, options)
10441		  .openOn(this);
10442
10443		return this;
10444	},
10445
10446	// @method closePopup(popup?: Popup): this
10447	// Closes the popup previously opened with [openPopup](#map-openpopup) (or the given one).
10448	closePopup: function (popup) {
10449		popup = arguments.length ? popup : this._popup;
10450		if (popup) {
10451			popup.close();
10452		}
10453		return this;
10454	}
10455});
10456
10457/*
10458 * @namespace Layer
10459 * @section Popup methods example
10460 *
10461 * All layers share a set of methods convenient for binding popups to it.
10462 *
10463 * ```js
10464 * var layer = L.Polygon(latlngs).bindPopup('Hi There!').addTo(map);
10465 * layer.openPopup();
10466 * layer.closePopup();
10467 * ```
10468 *
10469 * 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.
10470 */
10471
10472// @section Popup methods
10473Layer.include({
10474
10475	// @method bindPopup(content: String|HTMLElement|Function|Popup, options?: Popup options): this
10476	// Binds a popup to the layer with the passed `content` and sets up the
10477	// necessary event listeners. If a `Function` is passed it will receive
10478	// the layer as the first argument and should return a `String` or `HTMLElement`.
10479	bindPopup: function (content, options) {
10480		this._popup = this._initOverlay(Popup, this._popup, content, options);
10481		if (!this._popupHandlersAdded) {
10482			this.on({
10483				click: this._openPopup,
10484				keypress: this._onKeyPress,
10485				remove: this.closePopup,
10486				move: this._movePopup
10487			});
10488			this._popupHandlersAdded = true;
10489		}
10490
10491		return this;
10492	},
10493
10494	// @method unbindPopup(): this
10495	// Removes the popup previously bound with `bindPopup`.
10496	unbindPopup: function () {
10497		if (this._popup) {
10498			this.off({
10499				click: this._openPopup,
10500				keypress: this._onKeyPress,
10501				remove: this.closePopup,
10502				move: this._movePopup
10503			});
10504			this._popupHandlersAdded = false;
10505			this._popup = null;
10506		}
10507		return this;
10508	},
10509
10510	// @method openPopup(latlng?: LatLng): this
10511	// Opens the bound popup at the specified `latlng` or at the default popup anchor if no `latlng` is passed.
10512	openPopup: function (latlng) {
10513		if (this._popup) {
10514			if (!(this instanceof FeatureGroup)) {
10515				this._popup._source = this;
10516			}
10517			if (this._popup._prepareOpen(latlng || this._latlng)) {
10518				// open the popup on the map
10519				this._popup.openOn(this._map);
10520			}
10521		}
10522		return this;
10523	},
10524
10525	// @method closePopup(): this
10526	// Closes the popup bound to this layer if it is open.
10527	closePopup: function () {
10528		if (this._popup) {
10529			this._popup.close();
10530		}
10531		return this;
10532	},
10533
10534	// @method togglePopup(): this
10535	// Opens or closes the popup bound to this layer depending on its current state.
10536	togglePopup: function () {
10537		if (this._popup) {
10538			this._popup.toggle(this);
10539		}
10540		return this;
10541	},
10542
10543	// @method isPopupOpen(): boolean
10544	// Returns `true` if the popup bound to this layer is currently open.
10545	isPopupOpen: function () {
10546		return (this._popup ? this._popup.isOpen() : false);
10547	},
10548
10549	// @method setPopupContent(content: String|HTMLElement|Popup): this
10550	// Sets the content of the popup bound to this layer.
10551	setPopupContent: function (content) {
10552		if (this._popup) {
10553			this._popup.setContent(content);
10554		}
10555		return this;
10556	},
10557
10558	// @method getPopup(): Popup
10559	// Returns the popup bound to this layer.
10560	getPopup: function () {
10561		return this._popup;
10562	},
10563
10564	_openPopup: function (e) {
10565		if (!this._popup || !this._map) {
10566			return;
10567		}
10568		// prevent map click
10569		stop(e);
10570
10571		var target = e.layer || e.target;
10572		if (this._popup._source === target && !(target instanceof Path)) {
10573			// treat it like a marker and figure out
10574			// if we should toggle it open/closed
10575			if (this._map.hasLayer(this._popup)) {
10576				this.closePopup();
10577			} else {
10578				this.openPopup(e.latlng);
10579			}
10580			return;
10581		}
10582		this._popup._source = target;
10583		this.openPopup(e.latlng);
10584	},
10585
10586	_movePopup: function (e) {
10587		this._popup.setLatLng(e.latlng);
10588	},
10589
10590	_onKeyPress: function (e) {
10591		if (e.originalEvent.keyCode === 13) {
10592			this._openPopup(e);
10593		}
10594	}
vendor: 39,520 bytes, lines 10594-12022
10594
10595});
10596
10597/*
10598 * @class Tooltip
10599 * @inherits DivOverlay
10600 * @aka L.Tooltip
10601 * Used to display small texts on top of map layers.
10602 *
10603 * @example
10604 * If you want to just bind a tooltip to marker:
10605 *
10606 * ```js
10607 * marker.bindTooltip("my tooltip text").openTooltip();
10608 * ```
10609 * Path overlays like polylines also have a `bindTooltip` method.
10610 *
10611 * A tooltip can be also standalone:
10612 *
10613 * ```js
10614 * var tooltip = L.tooltip()
10615 * 	.setLatLng(latlng)
10616 * 	.setContent('Hello world!<br />This is a nice tooltip.')
10617 * 	.addTo(map);
10618 * ```
10619 * or
10620 * ```js
10621 * var tooltip = L.tooltip(latlng, {content: 'Hello world!<br />This is a nice tooltip.'})
10622 * 	.addTo(map);
10623 * ```
10624 *
10625 *
10626 * Note about tooltip offset. Leaflet takes two options in consideration
10627 * for computing tooltip offsetting:
10628 * - the `offset` Tooltip option: it defaults to [0, 0], and it's specific to one tooltip.
10629 *   Add a positive x offset to move the tooltip to the right, and a positive y offset to
10630 *   move it to the bottom. Negatives will move to the left and top.
10631 * - the `tooltipAnchor` Icon option: this will only be considered for Marker. You
10632 *   should adapt this value if you use a custom icon.
10633 */
10634
10635
10636// @namespace Tooltip
10637var Tooltip = DivOverlay.extend({
10638
10639	// @section
10640	// @aka Tooltip options
10641	options: {
10642		// @option pane: String = 'tooltipPane'
10643		// `Map pane` where the tooltip will be added.
10644		pane: 'tooltipPane',
10645
10646		// @option offset: Point = Point(0, 0)
10647		// Optional offset of the tooltip position.
10648		offset: [0, 0],
10649
10650		// @option direction: String = 'auto'
10651		// Direction where to open the tooltip. Possible values are: `right`, `left`,
10652		// `top`, `bottom`, `center`, `auto`.
10653		// `auto` will dynamically switch between `right` and `left` according to the tooltip
10654		// position on the map.
10655		direction: 'auto',
10656
10657		// @option permanent: Boolean = false
10658		// Whether to open the tooltip permanently or only on mouseover.
10659		permanent: false,
10660
10661		// @option sticky: Boolean = false
10662		// If true, the tooltip will follow the mouse instead of being fixed at the feature center.
10663		sticky: false,
10664
10665		// @option opacity: Number = 0.9
10666		// Tooltip container opacity.
10667		opacity: 0.9
10668	},
10669
10670	onAdd: function (map) {
10671		DivOverlay.prototype.onAdd.call(this, map);
10672		this.setOpacity(this.options.opacity);
10673
10674		// @namespace Map
10675		// @section Tooltip events
10676		// @event tooltipopen: TooltipEvent
10677		// Fired when a tooltip is opened in the map.
10678		map.fire('tooltipopen', {tooltip: this});
10679
10680		if (this._source) {
10681			this.addEventParent(this._source);
10682
10683			// @namespace Layer
10684			// @section Tooltip events
10685			// @event tooltipopen: TooltipEvent
10686			// Fired when a tooltip bound to this layer is opened.
10687			this._source.fire('tooltipopen', {tooltip: this}, true);
10688		}
10689	},
10690
10691	onRemove: function (map) {
10692		DivOverlay.prototype.onRemove.call(this, map);
10693
10694		// @namespace Map
10695		// @section Tooltip events
10696		// @event tooltipclose: TooltipEvent
10697		// Fired when a tooltip in the map is closed.
10698		map.fire('tooltipclose', {tooltip: this});
10699
10700		if (this._source) {
10701			this.removeEventParent(this._source);
10702
10703			// @namespace Layer
10704			// @section Tooltip events
10705			// @event tooltipclose: TooltipEvent
10706			// Fired when a tooltip bound to this layer is closed.
10707			this._source.fire('tooltipclose', {tooltip: this}, true);
10708		}
10709	},
10710
10711	getEvents: function () {
10712		var events = DivOverlay.prototype.getEvents.call(this);
10713
10714		if (!this.options.permanent) {
10715			events.preclick = this.close;
10716		}
10717
10718		return events;
10719	},
10720
10721	_initLayout: function () {
10722		var prefix = 'leaflet-tooltip',
10723		    className = prefix + ' ' + (this.options.className || '') + ' leaflet-zoom-' + (this._zoomAnimated ? 'animated' : 'hide');
10724
10725		this._contentNode = this._container = create$1('div', className);
10726
10727		this._container.setAttribute('role', 'tooltip');
10728		this._container.setAttribute('id', 'leaflet-tooltip-' + stamp(this));
10729	},
10730
10731	_updateLayout: function () {},
10732
10733	_adjustPan: function () {},
10734
10735	_setPosition: function (pos) {
10736		var subX, subY,
10737		    map = this._map,
10738		    container = this._container,
10739		    centerPoint = map.latLngToContainerPoint(map.getCenter()),
10740		    tooltipPoint = map.layerPointToContainerPoint(pos),
10741		    direction = this.options.direction,
10742		    tooltipWidth = container.offsetWidth,
10743		    tooltipHeight = container.offsetHeight,
10744		    offset = toPoint(this.options.offset),
10745		    anchor = this._getAnchor();
10746
10747		if (direction === 'top') {
10748			subX = tooltipWidth / 2;
10749			subY = tooltipHeight;
10750		} else if (direction === 'bottom') {
10751			subX = tooltipWidth / 2;
10752			subY = 0;
10753		} else if (direction === 'center') {
10754			subX = tooltipWidth / 2;
10755			subY = tooltipHeight / 2;
10756		} else if (direction === 'right') {
10757			subX = 0;
10758			subY = tooltipHeight / 2;
10759		} else if (direction === 'left') {
10760			subX = tooltipWidth;
10761			subY = tooltipHeight / 2;
10762		} else if (tooltipPoint.x < centerPoint.x) {
10763			direction = 'right';
10764			subX = 0;
10765			subY = tooltipHeight / 2;
10766		} else {
10767			direction = 'left';
10768			subX = tooltipWidth + (offset.x + anchor.x) * 2;
10769			subY = tooltipHeight / 2;
10770		}
10771
10772		pos = pos.subtract(toPoint(subX, subY, true)).add(offset).add(anchor);
10773
10774		removeClass(container, 'leaflet-tooltip-right');
10775		removeClass(container, 'leaflet-tooltip-left');
10776		removeClass(container, 'leaflet-tooltip-top');
10777		removeClass(container, 'leaflet-tooltip-bottom');
10778		addClass(container, 'leaflet-tooltip-' + direction);
10779		setPosition(container, pos);
10780	},
10781
10782	_updatePosition: function () {
10783		var pos = this._map.latLngToLayerPoint(this._latlng);
10784		this._setPosition(pos);
10785	},
10786
10787	setOpacity: function (opacity) {
10788		this.options.opacity = opacity;
10789
10790		if (this._container) {
10791			setOpacity(this._container, opacity);
10792		}
10793	},
10794
10795	_animateZoom: function (e) {
10796		var pos = this._map._latLngToNewLayerPoint(this._latlng, e.zoom, e.center);
10797		this._setPosition(pos);
10798	},
10799
10800	_getAnchor: function () {
10801		// Where should we anchor the tooltip on the source layer?
10802		return toPoint(this._source && this._source._getTooltipAnchor && !this.options.sticky ? this._source._getTooltipAnchor() : [0, 0]);
10803	}
10804
10805});
10806
10807// @namespace Tooltip
10808// @factory L.tooltip(options?: Tooltip options, source?: Layer)
10809// 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.
10810// @alternative
10811// @factory L.tooltip(latlng: LatLng, options?: Tooltip options)
10812// Instantiates a `Tooltip` object given `latlng` where the tooltip will open and an optional `options` object that describes its appearance and location.
10813var tooltip = function (options, source) {
10814	return new Tooltip(options, source);
10815};
10816
10817// @namespace Map
10818// @section Methods for Layers and Controls
10819Map.include({
10820
10821	// @method openTooltip(tooltip: Tooltip): this
10822	// Opens the specified tooltip.
10823	// @alternative
10824	// @method openTooltip(content: String|HTMLElement, latlng: LatLng, options?: Tooltip options): this
10825	// Creates a tooltip with the specified content and options and open it.
10826	openTooltip: function (tooltip, latlng, options) {
10827		this._initOverlay(Tooltip, tooltip, latlng, options)
10828		  .openOn(this);
10829
10830		return this;
10831	},
10832
10833	// @method closeTooltip(tooltip: Tooltip): this
10834	// Closes the tooltip given as parameter.
10835	closeTooltip: function (tooltip) {
10836		tooltip.close();
10837		return this;
10838	}
10839
10840});
10841
10842/*
10843 * @namespace Layer
10844 * @section Tooltip methods example
10845 *
10846 * All layers share a set of methods convenient for binding tooltips to it.
10847 *
10848 * ```js
10849 * var layer = L.Polygon(latlngs).bindTooltip('Hi There!').addTo(map);
10850 * layer.openTooltip();
10851 * layer.closeTooltip();
10852 * ```
10853 */
10854
10855// @section Tooltip methods
10856Layer.include({
10857
10858	// @method bindTooltip(content: String|HTMLElement|Function|Tooltip, options?: Tooltip options): this
10859	// Binds a tooltip to the layer with the passed `content` and sets up the
10860	// necessary event listeners. If a `Function` is passed it will receive
10861	// the layer as the first argument and should return a `String` or `HTMLElement`.
10862	bindTooltip: function (content, options) {
10863
10864		if (this._tooltip && this.isTooltipOpen()) {
10865			this.unbindTooltip();
10866		}
10867
10868		this._tooltip = this._initOverlay(Tooltip, this._tooltip, content, options);
10869		this._initTooltipInteractions();
10870
10871		if (this._tooltip.options.permanent && this._map && this._map.hasLayer(this)) {
10872			this.openTooltip();
10873		}
10874
10875		return this;
10876	},
10877
10878	// @method unbindTooltip(): this
10879	// Removes the tooltip previously bound with `bindTooltip`.
10880	unbindTooltip: function () {
10881		if (this._tooltip) {
10882			this._initTooltipInteractions(true);
10883			this.closeTooltip();
10884			this._tooltip = null;
10885		}
10886		return this;
10887	},
10888
10889	_initTooltipInteractions: function (remove) {
10890		if (!remove && this._tooltipHandlersAdded) { return; }
10891		var onOff = remove ? 'off' : 'on',
10892		    events = {
10893			remove: this.closeTooltip,
10894			move: this._moveTooltip
10895		    };
10896		if (!this._tooltip.options.permanent) {
10897			events.mouseover = this._openTooltip;
10898			events.mouseout = this.closeTooltip;
10899			events.click = this._openTooltip;
10900			if (this._map) {
10901				this._addFocusListeners();
10902			} else {
10903				events.add = this._addFocusListeners;
10904			}
10905		} else {
10906			events.add = this._openTooltip;
10907		}
10908		if (this._tooltip.options.sticky) {
10909			events.mousemove = this._moveTooltip;
10910		}
10911		this[onOff](events);
10912		this._tooltipHandlersAdded = !remove;
10913	},
10914
10915	// @method openTooltip(latlng?: LatLng): this
10916	// Opens the bound tooltip at the specified `latlng` or at the default tooltip anchor if no `latlng` is passed.
10917	openTooltip: function (latlng) {
10918		if (this._tooltip) {
10919			if (!(this instanceof FeatureGroup)) {
10920				this._tooltip._source = this;
10921			}
10922			if (this._tooltip._prepareOpen(latlng)) {
10923				// open the tooltip on the map
10924				this._tooltip.openOn(this._map);
10925
10926				if (this.getElement) {
10927					this._setAriaDescribedByOnLayer(this);
10928				} else if (this.eachLayer) {
10929					this.eachLayer(this._setAriaDescribedByOnLayer, this);
10930				}
10931			}
10932		}
10933		return this;
10934	},
10935
10936	// @method closeTooltip(): this
10937	// Closes the tooltip bound to this layer if it is open.
10938	closeTooltip: function () {
10939		if (this._tooltip) {
10940			return this._tooltip.close();
10941		}
10942	},
10943
10944	// @method toggleTooltip(): this
10945	// Opens or closes the tooltip bound to this layer depending on its current state.
10946	toggleTooltip: function () {
10947		if (this._tooltip) {
10948			this._tooltip.toggle(this);
10949		}
10950		return this;
10951	},
10952
10953	// @method isTooltipOpen(): boolean
10954	// Returns `true` if the tooltip bound to this layer is currently open.
10955	isTooltipOpen: function () {
10956		return this._tooltip.isOpen();
10957	},
10958
10959	// @method setTooltipContent(content: String|HTMLElement|Tooltip): this
10960	// Sets the content of the tooltip bound to this layer.
10961	setTooltipContent: function (content) {
10962		if (this._tooltip) {
10963			this._tooltip.setContent(content);
10964		}
10965		return this;
10966	},
10967
10968	// @method getTooltip(): Tooltip
10969	// Returns the tooltip bound to this layer.
10970	getTooltip: function () {
10971		return this._tooltip;
10972	},
10973
10974	_addFocusListeners: function () {
10975		if (this.getElement) {
10976			this._addFocusListenersOnLayer(this);
10977		} else if (this.eachLayer) {
10978			this.eachLayer(this._addFocusListenersOnLayer, this);
10979		}
10980	},
10981
10982	_addFocusListenersOnLayer: function (layer) {
10983		var el = typeof layer.getElement === 'function' && layer.getElement();
10984		if (el) {
10985			on(el, 'focus', function () {
10986				this._tooltip._source = layer;
10987				this.openTooltip();
10988			}, this);
10989			on(el, 'blur', this.closeTooltip, this);
10990		}
10991	},
10992
10993	_setAriaDescribedByOnLayer: function (layer) {
10994		var el = typeof layer.getElement === 'function' && layer.getElement();
10995		if (el) {
10996			el.setAttribute('aria-describedby', this._tooltip._container.id);
10997		}
10998	},
10999
11000
11001	_openTooltip: function (e) {
11002		if (!this._tooltip || !this._map) {
11003			return;
11004		}
11005
11006		// If the map is moving, we will show the tooltip after it's done.
11007		if (this._map.dragging && this._map.dragging.moving() && !this._openOnceFlag) {
11008			this._openOnceFlag = true;
11009			var that = this;
11010			this._map.once('moveend', function () {
11011				that._openOnceFlag = false;
11012				that._openTooltip(e);
11013			});
11014			return;
11015		}
11016
11017		this._tooltip._source = e.layer || e.target;
11018
11019		this.openTooltip(this._tooltip.options.sticky ? e.latlng : undefined);
11020	},
11021
11022	_moveTooltip: function (e) {
11023		var latlng = e.latlng, containerPoint, layerPoint;
11024		if (this._tooltip.options.sticky && e.originalEvent) {
11025			containerPoint = this._map.mouseEventToContainerPoint(e.originalEvent);
11026			layerPoint = this._map.containerPointToLayerPoint(containerPoint);
11027			latlng = this._map.layerPointToLatLng(layerPoint);
11028		}
11029		this._tooltip.setLatLng(latlng);
11030	}
11031});
11032
11033/*
11034 * @class DivIcon
11035 * @aka L.DivIcon
11036 * @inherits Icon
11037 *
11038 * Represents a lightweight icon for markers that uses a simple `<div>`
11039 * element instead of an image. Inherits from `Icon` but ignores the `iconUrl` and shadow options.
11040 *
11041 * @example
11042 * ```js
11043 * var myIcon = L.divIcon({className: 'my-div-icon'});
11044 * // you can set .my-div-icon styles in CSS
11045 *
11046 * L.marker([50.505, 30.57], {icon: myIcon}).addTo(map);
11047 * ```
11048 *
11049 * By default, it has a 'leaflet-div-icon' CSS class and is styled as a little white square with a shadow.
11050 */
11051
11052var DivIcon = Icon.extend({
11053	options: {
11054		// @section
11055		// @aka DivIcon options
11056		iconSize: [12, 12], // also can be set through CSS
11057
11058		// iconAnchor: (Point),
11059		// popupAnchor: (Point),
11060
11061		// @option html: String|HTMLElement = ''
11062		// Custom HTML code to put inside the div element, empty by default. Alternatively,
11063		// an instance of `HTMLElement`.
11064		html: false,
11065
11066		// @option bgPos: Point = [0, 0]
11067		// Optional relative position of the background, in pixels
11068		bgPos: null,
11069
11070		className: 'leaflet-div-icon'
11071	},
11072
11073	createIcon: function (oldIcon) {
11074		var div = (oldIcon && oldIcon.tagName === 'DIV') ? oldIcon : document.createElement('div'),
11075		    options = this.options;
11076
11077		if (options.html instanceof Element) {
11078			empty(div);
11079			div.appendChild(options.html);
11080		} else {
11081			div.innerHTML = options.html !== false ? options.html : '';
11082		}
11083
11084		if (options.bgPos) {
11085			var bgPos = toPoint(options.bgPos);
11086			div.style.backgroundPosition = (-bgPos.x) + 'px ' + (-bgPos.y) + 'px';
11087		}
11088		this._setIconStyles(div, 'icon');
11089
11090		return div;
11091	},
11092
11093	createShadow: function () {
11094		return null;
11095	}
11096});
11097
11098// @factory L.divIcon(options: DivIcon options)
11099// Creates a `DivIcon` instance with the given options.
11100function divIcon(options) {
11101	return new DivIcon(options);
11102}
11103
11104Icon.Default = IconDefault;
11105
11106/*
11107 * @class GridLayer
11108 * @inherits Layer
11109 * @aka L.GridLayer
11110 *
11111 * Generic class for handling a tiled grid of HTML elements. This is the base class for all tile layers and replaces `TileLayer.Canvas`.
11112 * 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.
11113 *
11114 *
11115 * @section Synchronous usage
11116 * @example
11117 *
11118 * 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.
11119 *
11120 * ```js
11121 * var CanvasLayer = L.GridLayer.extend({
11122 *     createTile: function(coords){
11123 *         // create a <canvas> element for drawing
11124 *         var tile = L.DomUtil.create('canvas', 'leaflet-tile');
11125 *
11126 *         // setup tile width and height according to the options
11127 *         var size = this.getTileSize();
11128 *         tile.width = size.x;
11129 *         tile.height = size.y;
11130 *
11131 *         // get a canvas context and draw something on it using coords.x, coords.y and coords.z
11132 *         var ctx = tile.getContext('2d');
11133 *
11134 *         // return the tile so it can be rendered on screen
11135 *         return tile;
11136 *     }
11137 * });
11138 * ```
11139 *
11140 * @section Asynchronous usage
11141 * @example
11142 *
11143 * 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.
11144 *
11145 * ```js
11146 * var CanvasLayer = L.GridLayer.extend({
11147 *     createTile: function(coords, done){
11148 *         var error;
11149 *
11150 *         // create a <canvas> element for drawing
11151 *         var tile = L.DomUtil.create('canvas', 'leaflet-tile');
11152 *
11153 *         // setup tile width and height according to the options
11154 *         var size = this.getTileSize();
11155 *         tile.width = size.x;
11156 *         tile.height = size.y;
11157 *
11158 *         // draw something asynchronously and pass the tile to the done() callback
11159 *         setTimeout(function() {
11160 *             done(error, tile);
11161 *         }, 1000);
11162 *
11163 *         return tile;
11164 *     }
11165 * });
11166 * ```
11167 *
11168 * @section
11169 */
11170
11171
11172var GridLayer = Layer.extend({
11173
11174	// @section
11175	// @aka GridLayer options
11176	options: {
11177		// @option tileSize: Number|Point = 256
11178		// Width and height of tiles in the grid. Use a number if width and height are equal, or `L.point(width, height)` otherwise.
11179		tileSize: 256,
11180
11181		// @option opacity: Number = 1.0
11182		// Opacity of the tiles. Can be used in the `createTile()` function.
11183		opacity: 1,
11184
11185		// @option updateWhenIdle: Boolean = (depends)
11186		// Load new tiles only when panning ends.
11187		// `true` by default on mobile browsers, in order to avoid too many requests and keep smooth navigation.
11188		// `false` otherwise in order to display new tiles _during_ panning, since it is easy to pan outside the
11189		// [`keepBuffer`](#gridlayer-keepbuffer) option in desktop browsers.
11190		updateWhenIdle: Browser.mobile,
11191
11192		// @option updateWhenZooming: Boolean = true
11193		// 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.
11194		updateWhenZooming: true,
11195
11196		// @option updateInterval: Number = 200
11197		// Tiles will not update more than once every `updateInterval` milliseconds when panning.
11198		updateInterval: 200,
11199
11200		// @option zIndex: Number = 1
11201		// The explicit zIndex of the tile layer.
11202		zIndex: 1,
11203
11204		// @option bounds: LatLngBounds = undefined
11205		// If set, tiles will only be loaded inside the set `LatLngBounds`.
11206		bounds: null,
11207
11208		// @option minZoom: Number = 0
11209		// The minimum zoom level down to which this layer will be displayed (inclusive).
11210		minZoom: 0,
11211
11212		// @option maxZoom: Number = undefined
11213		// The maximum zoom level up to which this layer will be displayed (inclusive).
11214		maxZoom: undefined,
11215
11216		// @option maxNativeZoom: Number = undefined
11217		// Maximum zoom number the tile source has available. If it is specified,
11218		// the tiles on all zoom levels higher than `maxNativeZoom` will be loaded
11219		// from `maxNativeZoom` level and auto-scaled.
11220		maxNativeZoom: undefined,
11221
11222		// @option minNativeZoom: Number = undefined
11223		// Minimum zoom number the tile source has available. If it is specified,
11224		// the tiles on all zoom levels lower than `minNativeZoom` will be loaded
11225		// from `minNativeZoom` level and auto-scaled.
11226		minNativeZoom: undefined,
11227
11228		// @option noWrap: Boolean = false
11229		// Whether the layer is wrapped around the antimeridian. If `true`, the
11230		// GridLayer will only be displayed once at low zoom levels. Has no
11231		// effect when the [map CRS](#map-crs) doesn't wrap around. Can be used
11232		// in combination with [`bounds`](#gridlayer-bounds) to prevent requesting
11233		// tiles outside the CRS limits.
11234		noWrap: false,
11235
11236		// @option pane: String = 'tilePane'
11237		// `Map pane` where the grid layer will be added.
11238		pane: 'tilePane',
11239
11240		// @option className: String = ''
11241		// A custom class name to assign to the tile layer. Empty by default.
11242		className: '',
11243
11244		// @option keepBuffer: Number = 2
11245		// When panning the map, keep this many rows and columns of tiles before unloading them.
11246		keepBuffer: 2
11247	},
11248
11249	initialize: function (options) {
11250		setOptions(this, options);
11251	},
11252
11253	onAdd: function () {
11254		this._initContainer();
11255
11256		this._levels = {};
11257		this._tiles = {};
11258
11259		this._resetView(); // implicit _update() call
11260	},
11261
11262	beforeAdd: function (map) {
11263		map._addZoomLimit(this);
11264	},
11265
11266	onRemove: function (map) {
11267		this._removeAllTiles();
11268		remove(this._container);
11269		map._removeZoomLimit(this);
11270		this._container = null;
11271		this._tileZoom = undefined;
11272	},
11273
11274	// @method bringToFront: this
11275	// Brings the tile layer to the top of all tile layers.
11276	bringToFront: function () {
11277		if (this._map) {
11278			toFront(this._container);
11279			this._setAutoZIndex(Math.max);
11280		}
11281		return this;
11282	},
11283
11284	// @method bringToBack: this
11285	// Brings the tile layer to the bottom of all tile layers.
11286	bringToBack: function () {
11287		if (this._map) {
11288			toBack(this._container);
11289			this._setAutoZIndex(Math.min);
11290		}
11291		return this;
11292	},
11293
11294	// @method getContainer: HTMLElement
11295	// Returns the HTML element that contains the tiles for this layer.
11296	getContainer: function () {
11297		return this._container;
11298	},
11299
11300	// @method setOpacity(opacity: Number): this
11301	// Changes the [opacity](#gridlayer-opacity) of the grid layer.
11302	setOpacity: function (opacity) {
11303		this.options.opacity = opacity;
11304		this._updateOpacity();
11305		return this;
11306	},
11307
11308	// @method setZIndex(zIndex: Number): this
11309	// Changes the [zIndex](#gridlayer-zindex) of the grid layer.
11310	setZIndex: function (zIndex) {
11311		this.options.zIndex = zIndex;
11312		this._updateZIndex();
11313
11314		return this;
11315	},
11316
11317	// @method isLoading: Boolean
11318	// Returns `true` if any tile in the grid layer has not finished loading.
11319	isLoading: function () {
11320		return this._loading;
11321	},
11322
11323	// @method redraw: this
11324	// Causes the layer to clear all the tiles and request them again.
11325	redraw: function () {
11326		if (this._map) {
11327			this._removeAllTiles();
11328			var tileZoom = this._clampZoom(this._map.getZoom());
11329			if (tileZoom !== this._tileZoom) {
11330				this._tileZoom = tileZoom;
11331				this._updateLevels();
11332			}
11333			this._update();
11334		}
11335		return this;
11336	},
11337
11338	getEvents: function () {
11339		var events = {
11340			viewprereset: this._invalidateAll,
11341			viewreset: this._resetView,
11342			zoom: this._resetView,
11343			moveend: this._onMoveEnd
11344		};
11345
11346		if (!this.options.updateWhenIdle) {
11347			// update tiles on move, but not more often than once per given interval
11348			if (!this._onMove) {
11349				this._onMove = throttle(this._onMoveEnd, this.options.updateInterval, this);
11350			}
11351
11352			events.move = this._onMove;
11353		}
11354
11355		if (this._zoomAnimated) {
11356			events.zoomanim = this._animateZoom;
11357		}
11358
11359		return events;
11360	},
11361
11362	// @section Extension methods
11363	// Layers extending `GridLayer` shall reimplement the following method.
11364	// @method createTile(coords: Object, done?: Function): HTMLElement
11365	// Called only internally, must be overridden by classes extending `GridLayer`.
11366	// Returns the `HTMLElement` corresponding to the given `coords`. If the `done` callback
11367	// is specified, it must be called when the tile has finished loading and drawing.
11368	createTile: function () {
11369		return document.createElement('div');
11370	},
11371
11372	// @section
11373	// @method getTileSize: Point
11374	// Normalizes the [tileSize option](#gridlayer-tilesize) into a point. Used by the `createTile()` method.
11375	getTileSize: function () {
11376		var s = this.options.tileSize;
11377		return s instanceof Point ? s : new Point(s, s);
11378	},
11379
11380	_updateZIndex: function () {
11381		if (this._container && this.options.zIndex !== undefined && this.options.zIndex !== null) {
11382			this._container.style.zIndex = this.options.zIndex;
11383		}
11384	},
11385
11386	_setAutoZIndex: function (compare) {
11387		// go through all other layers of the same pane, set zIndex to max + 1 (front) or min - 1 (back)
11388
11389		var layers = this.getPane().children,
11390		    edgeZIndex = -compare(-Infinity, Infinity); // -Infinity for max, Infinity for min
11391
11392		for (var i = 0, len = layers.length, zIndex; i < len; i++) {
11393
11394			zIndex = layers[i].style.zIndex;
11395
11396			if (layers[i] !== this._container && zIndex) {
11397				edgeZIndex = compare(edgeZIndex, +zIndex);
11398			}
11399		}
11400
11401		if (isFinite(edgeZIndex)) {
11402			this.options.zIndex = edgeZIndex + compare(-1, 1);
11403			this._updateZIndex();
11404		}
11405	},
11406
11407	_updateOpacity: function () {
11408		if (!this._map) { return; }
11409
11410		// IE doesn't inherit filter opacity properly, so we're forced to set it on tiles
11411		if (Browser.ielt9) { return; }
11412
11413		setOpacity(this._container, this.options.opacity);
11414
11415		var now = +new Date(),
11416		    nextFrame = false,
11417		    willPrune = false;
11418
11419		for (var key in this._tiles) {
11420			var tile = this._tiles[key];
11421			if (!tile.current || !tile.loaded) { continue; }
11422
11423			var fade = Math.min(1, (now - tile.loaded) / 200);
11424
11425			setOpacity(tile.el, fade);
11426			if (fade < 1) {
11427				nextFrame = true;
11428			} else {
11429				if (tile.active) {
11430					willPrune = true;
11431				} else {
11432					this._onOpaqueTile(tile);
11433				}
11434				tile.active = true;
11435			}
11436		}
11437
11438		if (willPrune && !this._noPrune) { this._pruneTiles(); }
11439
11440		if (nextFrame) {
11441			cancelAnimFrame(this._fadeFrame);
11442			this._fadeFrame = requestAnimFrame(this._updateOpacity, this);
11443		}
11444	},
11445
11446	_onOpaqueTile: falseFn,
11447
11448	_initContainer: function () {
11449		if (this._container) { return; }
11450
11451		this._container = create$1('div', 'leaflet-layer ' + (this.options.className || ''));
11452		this._updateZIndex();
11453
11454		if (this.options.opacity < 1) {
11455			this._updateOpacity();
11456		}
11457
11458		this.getPane().appendChild(this._container);
11459	},
11460
11461	_updateLevels: function () {
11462
11463		var zoom = this._tileZoom,
11464		    maxZoom = this.options.maxZoom;
11465
11466		if (zoom === undefined) { return undefined; }
11467
11468		for (var z in this._levels) {
11469			z = Number(z);
11470			if (this._levels[z].el.children.length || z === zoom) {
11471				this._levels[z].el.style.zIndex = maxZoom - Math.abs(zoom - z);
11472				this._onUpdateLevel(z);
11473			} else {
11474				remove(this._levels[z].el);
11475				this._removeTilesAtZoom(z);
11476				this._onRemoveLevel(z);
11477				delete this._levels[z];
11478			}
11479		}
11480
11481		var level = this._levels[zoom],
11482		    map = this._map;
11483
11484		if (!level) {
11485			level = this._levels[zoom] = {};
11486
11487			level.el = create$1('div', 'leaflet-tile-container leaflet-zoom-animated', this._container);
11488			level.el.style.zIndex = maxZoom;
11489
11490			level.origin = map.project(map.unproject(map.getPixelOrigin()), zoom).round();
11491			level.zoom = zoom;
11492
11493			this._setZoomTransform(level, map.getCenter(), map.getZoom());
11494
11495			// force the browser to consider the newly added element for transition
11496			falseFn(level.el.offsetWidth);
11497
11498			this._onCreateLevel(level);
11499		}
11500
11501		this._level = level;
11502
11503		return level;
11504	},
11505
11506	_onUpdateLevel: falseFn,
11507
11508	_onRemoveLevel: falseFn,
11509
11510	_onCreateLevel: falseFn,
11511
11512	_pruneTiles: function () {
11513		if (!this._map) {
11514			return;
11515		}
11516
11517		var key, tile;
11518
11519		var zoom = this._map.getZoom();
11520		if (zoom > this.options.maxZoom ||
11521			zoom < this.options.minZoom) {
11522			this._removeAllTiles();
11523			return;
11524		}
11525
11526		for (key in this._tiles) {
11527			tile = this._tiles[key];
11528			tile.retain = tile.current;
11529		}
11530
11531		for (key in this._tiles) {
11532			tile = this._tiles[key];
11533			if (tile.current && !tile.active) {
11534				var coords = tile.coords;
11535				if (!this._retainParent(coords.x, coords.y, coords.z, coords.z - 5)) {
11536					this._retainChildren(coords.x, coords.y, coords.z, coords.z + 2);
11537				}
11538			}
11539		}
11540
11541		for (key in this._tiles) {
11542			if (!this._tiles[key].retain) {
11543				this._removeTile(key);
11544			}
11545		}
11546	},
11547
11548	_removeTilesAtZoom: function (zoom) {
11549		for (var key in this._tiles) {
11550			if (this._tiles[key].coords.z !== zoom) {
11551				continue;
11552			}
11553			this._removeTile(key);
11554		}
11555	},
11556
11557	_removeAllTiles: function () {
11558		for (var key in this._tiles) {
11559			this._removeTile(key);
11560		}
11561	},
11562
11563	_invalidateAll: function () {
11564		for (var z in this._levels) {
11565			remove(this._levels[z].el);
11566			this._onRemoveLevel(Number(z));
11567			delete this._levels[z];
11568		}
11569		this._removeAllTiles();
11570
11571		this._tileZoom = undefined;
11572	},
11573
11574	_retainParent: function (x, y, z, minZoom) {
11575		var x2 = Math.floor(x / 2),
11576		    y2 = Math.floor(y / 2),
11577		    z2 = z - 1,
11578		    coords2 = new Point(+x2, +y2);
11579		coords2.z = +z2;
11580
11581		var key = this._tileCoordsToKey(coords2),
11582		    tile = this._tiles[key];
11583
11584		if (tile && tile.active) {
11585			tile.retain = true;
11586			return true;
11587
11588		} else if (tile && tile.loaded) {
11589			tile.retain = true;
11590		}
11591
11592		if (z2 > minZoom) {
11593			return this._retainParent(x2, y2, z2, minZoom);
11594		}
11595
11596		return false;
11597	},
11598
11599	_retainChildren: function (x, y, z, maxZoom) {
11600
11601		for (var i = 2 * x; i < 2 * x + 2; i++) {
11602			for (var j = 2 * y; j < 2 * y + 2; j++) {
11603
11604				var coords = new Point(i, j);
11605				coords.z = z + 1;
11606
11607				var key = this._tileCoordsToKey(coords),
11608				    tile = this._tiles[key];
11609
11610				if (tile && tile.active) {
11611					tile.retain = true;
11612					continue;
11613
11614				} else if (tile && tile.loaded) {
11615					tile.retain = true;
11616				}
11617
11618				if (z + 1 < maxZoom) {
11619					this._retainChildren(i, j, z + 1, maxZoom);
11620				}
11621			}
11622		}
11623	},
11624
11625	_resetView: function (e) {
11626		var animating = e && (e.pinch || e.flyTo);
11627		this._setView(this._map.getCenter(), this._map.getZoom(), animating, animating);
11628	},
11629
11630	_animateZoom: function (e) {
11631		this._setView(e.center, e.zoom, true, e.noUpdate);
11632	},
11633
11634	_clampZoom: function (zoom) {
11635		var options = this.options;
11636
11637		if (undefined !== options.minNativeZoom && zoom < options.minNativeZoom) {
11638			return options.minNativeZoom;
11639		}
11640
11641		if (undefined !== options.maxNativeZoom && options.maxNativeZoom < zoom) {
11642			return options.maxNativeZoom;
11643		}
11644
11645		return zoom;
11646	},
11647
11648	_setView: function (center, zoom, noPrune, noUpdate) {
11649		var tileZoom = Math.round(zoom);
11650		if ((this.options.maxZoom !== undefined && tileZoom > this.options.maxZoom) ||
11651		    (this.options.minZoom !== undefined && tileZoom < this.options.minZoom)) {
11652			tileZoom = undefined;
11653		} else {
11654			tileZoom = this._clampZoom(tileZoom);
11655		}
11656
11657		var tileZoomChanged = this.options.updateWhenZooming && (tileZoom !== this._tileZoom);
11658
11659		if (!noUpdate || tileZoomChanged) {
11660
11661			this._tileZoom = tileZoom;
11662
11663			if (this._abortLoading) {
11664				this._abortLoading();
11665			}
11666
11667			this._updateLevels();
11668			this._resetGrid();
11669
11670			if (tileZoom !== undefined) {
11671				this._update(center);
11672			}
11673
11674			if (!noPrune) {
11675				this._pruneTiles();
11676			}
11677
11678			// Flag to prevent _updateOpacity from pruning tiles during
11679			// a zoom anim or a pinch gesture
11680			this._noPrune = !!noPrune;
11681		}
11682
11683		this._setZoomTransforms(center, zoom);
11684	},
11685
11686	_setZoomTransforms: function (center, zoom) {
11687		for (var i in this._levels) {
11688			this._setZoomTransform(this._levels[i], center, zoom);
11689		}
11690	},
11691
11692	_setZoomTransform: function (level, center, zoom) {
11693		var scale = this._map.getZoomScale(zoom, level.zoom),
11694		    translate = level.origin.multiplyBy(scale)
11695		        .subtract(this._map._getNewPixelOrigin(center, zoom)).round();
11696
11697		if (Browser.any3d) {
11698			setTransform(level.el, translate, scale);
11699		} else {
11700			setPosition(level.el, translate);
11701		}
11702	},
11703
11704	_resetGrid: function () {
11705		var map = this._map,
11706		    crs = map.options.crs,
11707		    tileSize = this._tileSize = this.getTileSize(),
11708		    tileZoom = this._tileZoom;
11709
11710		var bounds = this._map.getPixelWorldBounds(this._tileZoom);
11711		if (bounds) {
11712			this._globalTileRange = this._pxBoundsToTileRange(bounds);
11713		}
11714
11715		this._wrapX = crs.wrapLng && !this.options.noWrap && [
11716			Math.floor(map.project([0, crs.wrapLng[0]], tileZoom).x / tileSize.x),
11717			Math.ceil(map.project([0, crs.wrapLng[1]], tileZoom).x / tileSize.y)
11718		];
11719		this._wrapY = crs.wrapLat && !this.options.noWrap && [
11720			Math.floor(map.project([crs.wrapLat[0], 0], tileZoom).y / tileSize.x),
11721			Math.ceil(map.project([crs.wrapLat[1], 0], tileZoom).y / tileSize.y)
11722		];
11723	},
11724
11725	_onMoveEnd: function () {
11726		if (!this._map || this._map._animatingZoom) { return; }
11727
11728		this._update();
11729	},
11730
11731	_getTiledPixelBounds: function (center) {
11732		var map = this._map,
11733		    mapZoom = map._animatingZoom ? Math.max(map._animateToZoom, map.getZoom()) : map.getZoom(),
11734		    scale = map.getZoomScale(mapZoom, this._tileZoom),
11735		    pixelCenter = map.project(center, this._tileZoom).floor(),
11736		    halfSize = map.getSize().divideBy(scale * 2);
11737
11738		return new Bounds(pixelCenter.subtract(halfSize), pixelCenter.add(halfSize));
11739	},
11740
11741	// Private method to load tiles in the grid's active zoom level according to map bounds
11742	_update: function (center) {
11743		var map = this._map;
11744		if (!map) { return; }
11745		var zoom = this._clampZoom(map.getZoom());
11746
11747		if (center === undefined) { center = map.getCenter(); }
11748		if (this._tileZoom === undefined) { return; }	// if out of minzoom/maxzoom
11749
11750		var pixelBounds = this._getTiledPixelBounds(center),
11751		    tileRange = this._pxBoundsToTileRange(pixelBounds),
11752		    tileCenter = tileRange.getCenter(),
11753		    queue = [],
11754		    margin = this.options.keepBuffer,
11755		    noPruneRange = new Bounds(tileRange.getBottomLeft().subtract([margin, -margin]),
11756		                              tileRange.getTopRight().add([margin, -margin]));
11757
11758		// Sanity check: panic if the tile range contains Infinity somewhere.
11759		if (!(isFinite(tileRange.min.x) &&
11760		      isFinite(tileRange.min.y) &&
11761		      isFinite(tileRange.max.x) &&
11762		      isFinite(tileRange.max.y))) { throw new Error('Attempted to load an infinite number of tiles'); }
11763
11764		for (var key in this._tiles) {
11765			var c = this._tiles[key].coords;
11766			if (c.z !== this._tileZoom || !noPruneRange.contains(new Point(c.x, c.y))) {
11767				this._tiles[key].current = false;
11768			}
11769		}
11770
11771		// _update just loads more tiles. If the tile zoom level differs too much
11772		// from the map's, let _setView reset levels and prune old tiles.
11773		if (Math.abs(zoom - this._tileZoom) > 1) { this._setView(center, zoom); return; }
11774
11775		// create a queue of coordinates to load tiles from
11776		for (var j = tileRange.min.y; j <= tileRange.max.y; j++) {
11777			for (var i = tileRange.min.x; i <= tileRange.max.x; i++) {
11778				var coords = new Point(i, j);
11779				coords.z = this._tileZoom;
11780
11781				if (!this._isValidTile(coords)) { continue; }
11782
11783				var tile = this._tiles[this._tileCoordsToKey(coords)];
11784				if (tile) {
11785					tile.current = true;
11786				} else {
11787					queue.push(coords);
11788				}
11789			}
11790		}
11791
11792		// sort tile queue to load tiles in order of their distance to center
11793		queue.sort(function (a, b) {
11794			return a.distanceTo(tileCenter) - b.distanceTo(tileCenter);
11795		});
11796
11797		if (queue.length !== 0) {
11798			// if it's the first batch of tiles to load
11799			if (!this._loading) {
11800				this._loading = true;
11801				// @event loading: Event
11802				// Fired when the grid layer starts loading tiles.
11803				this.fire('loading');
11804			}
11805
11806			// create DOM fragment to append tiles in one batch
11807			var fragment = document.createDocumentFragment();
11808
11809			for (i = 0; i < queue.length; i++) {
11810				this._addTile(queue[i], fragment);
11811			}
11812
11813			this._level.el.appendChild(fragment);
11814		}
11815	},
11816
11817	_isValidTile: function (coords) {
11818		var crs = this._map.options.crs;
11819
11820		if (!crs.infinite) {
11821			// don't load tile if it's out of bounds and not wrapped
11822			var bounds = this._globalTileRange;
11823			if ((!crs.wrapLng && (coords.x < bounds.min.x || coords.x > bounds.max.x)) ||
11824			    (!crs.wrapLat && (coords.y < bounds.min.y || coords.y > bounds.max.y))) { return false; }
11825		}
11826
11827		if (!this.options.bounds) { return true; }
11828
11829		// don't load tile if it doesn't intersect the bounds in options
11830		var tileBounds = this._tileCoordsToBounds(coords);
11831		return toLatLngBounds(this.options.bounds).overlaps(tileBounds);
11832	},
11833
11834	_keyToBounds: function (key) {
11835		return this._tileCoordsToBounds(this._keyToTileCoords(key));
11836	},
11837
11838	_tileCoordsToNwSe: function (coords) {
11839		var map = this._map,
11840		    tileSize = this.getTileSize(),
11841		    nwPoint = coords.scaleBy(tileSize),
11842		    sePoint = nwPoint.add(tileSize),
11843		    nw = map.unproject(nwPoint, coords.z),
11844		    se = map.unproject(sePoint, coords.z);
11845		return [nw, se];
11846	},
11847
11848	// converts tile coordinates to its geographical bounds
11849	_tileCoordsToBounds: function (coords) {
11850		var bp = this._tileCoordsToNwSe(coords),
11851		    bounds = new LatLngBounds(bp[0], bp[1]);
11852
11853		if (!this.options.noWrap) {
11854			bounds = this._map.wrapLatLngBounds(bounds);
11855		}
11856		return bounds;
11857	},
11858	// converts tile coordinates to key for the tile cache
11859	_tileCoordsToKey: function (coords) {
11860		return coords.x + ':' + coords.y + ':' + coords.z;
11861	},
11862
11863	// converts tile cache key to coordinates
11864	_keyToTileCoords: function (key) {
11865		var k = key.split(':'),
11866		    coords = new Point(+k[0], +k[1]);
11867		coords.z = +k[2];
11868		return coords;
11869	},
11870
11871	_removeTile: function (key) {
11872		var tile = this._tiles[key];
11873		if (!tile) { return; }
11874
11875		remove(tile.el);
11876
11877		delete this._tiles[key];
11878
11879		// @event tileunload: TileEvent
11880		// Fired when a tile is removed (e.g. when a tile goes off the screen).
11881		this.fire('tileunload', {
11882			tile: tile.el,
11883			coords: this._keyToTileCoords(key)
11884		});
11885	},
11886
11887	_initTile: function (tile) {
11888		addClass(tile, 'leaflet-tile');
11889
11890		var tileSize = this.getTileSize();
11891		tile.style.width = tileSize.x + 'px';
11892		tile.style.height = tileSize.y + 'px';
11893
11894		tile.onselectstart = falseFn;
11895		tile.onmousemove = falseFn;
11896
11897		// update opacity on tiles in IE7-8 because of filter inheritance problems
11898		if (Browser.ielt9 && this.options.opacity < 1) {
11899			setOpacity(tile, this.options.opacity);
11900		}
11901	},
11902
11903	_addTile: function (coords, container) {
11904		var tilePos = this._getTilePos(coords),
11905		    key = this._tileCoordsToKey(coords);
11906
11907		var tile = this.createTile(this._wrapCoords(coords), bind(this._tileReady, this, coords));
11908
11909		this._initTile(tile);
11910
11911		// if createTile is defined with a second argument ("done" callback),
11912		// we know that tile is async and will be ready later; otherwise
11913		if (this.createTile.length < 2) {
11914			// mark tile as ready, but delay one frame for opacity animation to happen
11915			requestAnimFrame(bind(this._tileReady, this, coords, null, tile));
11916		}
11917
11918		setPosition(tile, tilePos);
11919
11920		// save tile in cache
11921		this._tiles[key] = {
11922			el: tile,
11923			coords: coords,
11924			current: true
11925		};
11926
11927		container.appendChild(tile);
11928		// @event tileloadstart: TileEvent
11929		// Fired when a tile is requested and starts loading.
11930		this.fire('tileloadstart', {
11931			tile: tile,
11932			coords: coords
11933		});
11934	},
11935
11936	_tileReady: function (coords, err, tile) {
11937		if (err) {
11938			// @event tileerror: TileErrorEvent
11939			// Fired when there is an error loading a tile.
11940			this.fire('tileerror', {
11941				error: err,
11942				tile: tile,
11943				coords: coords
11944			});
11945		}
11946
11947		var key = this._tileCoordsToKey(coords);
11948
11949		tile = this._tiles[key];
11950		if (!tile) { return; }
11951
11952		tile.loaded = +new Date();
11953		if (this._map._fadeAnimated) {
11954			setOpacity(tile.el, 0);
11955			cancelAnimFrame(this._fadeFrame);
11956			this._fadeFrame = requestAnimFrame(this._updateOpacity, this);
11957		} else {
11958			tile.active = true;
11959			this._pruneTiles();
11960		}
11961
11962		if (!err) {
11963			addClass(tile.el, 'leaflet-tile-loaded');
11964
11965			// @event tileload: TileEvent
11966			// Fired when a tile loads.
11967			this.fire('tileload', {
11968				tile: tile.el,
11969				coords: coords
11970			});
11971		}
11972
11973		if (this._noTilesToLoad()) {
11974			this._loading = false;
11975			// @event load: Event
11976			// Fired when the grid layer loaded all visible tiles.
11977			this.fire('load');
11978
11979			if (Browser.ielt9 || !this._map._fadeAnimated) {
11980				requestAnimFrame(this._pruneTiles, this);
11981			} else {
11982				// Wait a bit more than 0.2 secs (the duration of the tile fade-in)
11983				// to trigger a pruning.
11984				setTimeout(bind(this._pruneTiles, this), 250);
11985			}
11986		}
11987	},
11988
11989	_getTilePos: function (coords) {
11990		return coords.scaleBy(this.getTileSize()).subtract(this._level.origin);
11991	},
11992
11993	_wrapCoords: function (coords) {
11994		var newCoords = new Point(
11995			this._wrapX ? wrapNum(coords.x, this._wrapX) : coords.x,
11996			this._wrapY ? wrapNum(coords.y, this._wrapY) : coords.y);
11997		newCoords.z = coords.z;
11998		return newCoords;
11999	},
12000
12001	_pxBoundsToTileRange: function (bounds) {
12002		var tileSize = this.getTileSize();
12003		return new Bounds(
12004			bounds.min.unscaleBy(tileSize).floor(),
12005			bounds.max.unscaleBy(tileSize).ceil().subtract([1, 1]));
12006	},
12007
12008	_noTilesToLoad: function () {
12009		for (var key in this._tiles) {
12010			if (!this._tiles[key].loaded) { return false; }
12011		}
12012		return true;
12013	}
12014});
12015
12016// @factory L.gridLayer(options?: GridLayer options)
12017// Creates a new instance of GridLayer with the supplied options.
12018function gridLayer(options) {
12019	return new GridLayer(options);
12020}
12021
12022/*
12022
12023 * @class TileLayer
12024 * @inherits GridLayer
12025 * @aka L.TileLayer
12026 * 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`.
12027 *
12028 * @example
12029 *
12030 * ```js
12031 * 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);
12032 * ```
12033 *
12034 * @section URL template
12035 * @example
12036 *
12037 * A string of the following form:
12038 *
12039 * ```
12040 * 'https://{s}.somedomain.com/blabla/{z}/{x}/{y}{r}.png'
12041 * ```
12042 *
12043 * `{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.
12044 *
12045 * You can use custom keys in the template, which will be [evaluated](#util-template) from TileLayer options, like this:
12046 *
12047 * ```
12048 * L.tileLayer('https://{s}.somedomain.com/{foo}/{z}/{x}/{y}.png', {foo: 'bar'});
12049 * ```
12050 */
12051
12052
12053var TileLayer = GridLayer.extend({
12054
12055	// @section
12056	// @aka TileLayer options
12057	options: {
12058		// @option minZoom: Number = 0
12059		// The minimum zoom level down to which this layer will be displayed (inclusive).
12060		minZoom: 0,
12061
12062		// @option maxZoom: Number = 18
12063		// The maximum zoom level up to which this layer will be displayed (inclusive).
12064		maxZoom: 18,
12065
12066		// @option subdomains: String|String[] = 'abc'
12067		// 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 str
vendor: 11,431 bytes, lines 12067-12434
12067ings.
12068		subdomains: 'abc',
12069
12070		// @option errorTileUrl: String = ''
12071		// URL to the tile image to show in place of the tile that failed to load.
12072		errorTileUrl: '',
12073
12074		// @option zoomOffset: Number = 0
12075		// The zoom number used in tile URLs will be offset with this value.
12076		zoomOffset: 0,
12077
12078		// @option tms: Boolean = false
12079		// If `true`, inverses Y axis numbering for tiles (turn this on for [TMS](https://en.wikipedia.org/wiki/Tile_Map_Service) services).
12080		tms: false,
12081
12082		// @option zoomReverse: Boolean = false
12083		// If set to true, the zoom number used in tile URLs will be reversed (`maxZoom - zoom` instead of `zoom`)
12084		zoomReverse: false,
12085
12086		// @option detectRetina: Boolean = false
12087		// 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.
12088		detectRetina: false,
12089
12090		// @option crossOrigin: Boolean|String = false
12091		// Whether the crossOrigin attribute will be added to the tiles.
12092		// 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.
12093		// Refer to [CORS Settings](https://developer.mozilla.org/en-US/docs/Web/HTML/CORS_settings_attributes) for valid String values.
12094		crossOrigin: false,
12095
12096		// @option referrerPolicy: Boolean|String = false
12097		// Whether the referrerPolicy attribute will be added to the tiles.
12098		// If a String is provided, all tiles will have their referrerPolicy attribute set to the String provided.
12099		// This may be needed if your map's rendering context has a strict default but your tile provider expects a valid referrer
12100		// (e.g. to validate an API token).
12101		// Refer to [HTMLImageElement.referrerPolicy](https://developer.mozilla.org/en-US/docs/Web/API/HTMLImageElement/referrerPolicy) for valid String values.
12102		referrerPolicy: false
12103	},
12104
12105	initialize: function (url, options) {
12106
12107		this._url = url;
12108
12109		options = setOptions(this, options);
12110
12111		// detecting retina displays, adjusting tileSize and zoom levels
12112		if (options.detectRetina && Browser.retina && options.maxZoom > 0) {
12113
12114			options.tileSize = Math.floor(options.tileSize / 2);
12115
12116			if (!options.zoomReverse) {
12117				options.zoomOffset++;
12118				options.maxZoom = Math.max(options.minZoom, options.maxZoom - 1);
12119			} else {
12120				options.zoomOffset--;
12121				options.minZoom = Math.min(options.maxZoom, options.minZoom + 1);
12122			}
12123
12124			options.minZoom = Math.max(0, options.minZoom);
12125		} else if (!options.zoomReverse) {
12126			// make sure maxZoom is gte minZoom
12127			options.maxZoom = Math.max(options.minZoom, options.maxZoom);
12128		} else {
12129			// make sure minZoom is lte maxZoom
12130			options.minZoom = Math.min(options.maxZoom, options.minZoom);
12131		}
12132
12133		if (typeof options.subdomains === 'string') {
12134			options.subdomains = options.subdomains.split('');
12135		}
12136
12137		this.on('tileunload', this._onTileRemove);
12138	},
12139
12140	// @method setUrl(url: String, noRedraw?: Boolean): this
12141	// Updates the layer's URL template and redraws it (unless `noRedraw` is set to `true`).
12142	// If the URL does not change, the layer will not be redrawn unless
12143	// the noRedraw parameter is set to false.
12144	setUrl: function (url, noRedraw) {
12145		if (this._url === url && noRedraw === undefined) {
12146			noRedraw = true;
12147		}
12148
12149		this._url = url;
12150
12151		if (!noRedraw) {
12152			this.redraw();
12153		}
12154		return this;
12155	},
12156
12157	// @method createTile(coords: Object, done?: Function): HTMLElement
12158	// Called only internally, overrides GridLayer's [`createTile()`](#gridlayer-createtile)
12159	// to return an `<img>` HTML element with the appropriate image URL given `coords`. The `done`
12160	// callback is called when the tile has been loaded.
12161	createTile: function (coords, done) {
12162		var tile = document.createElement('img');
12163
12164		on(tile, 'load', bind(this._tileOnLoad, this, done, tile));
12165		on(tile, 'error', bind(this._tileOnError, this, done, tile));
12166
12167		if (this.options.crossOrigin || this.options.crossOrigin === '') {
12168			tile.crossOrigin = this.options.crossOrigin === true ? '' : this.options.crossOrigin;
12169		}
12170
12171		// for this new option we follow the documented behavior
12172		// more closely by only setting the property when string
12173		if (typeof this.options.referrerPolicy === 'string') {
12174			tile.referrerPolicy = this.options.referrerPolicy;
12175		}
12176
12177		// The alt attribute is set to the empty string,
12178		// allowing screen readers to ignore the decorative image tiles.
12179		// https://www.w3.org/WAI/tutorials/images/decorative/
12180		// https://www.w3.org/TR/html-aria/#el-img-empty-alt
12181		tile.alt = '';
12182
12183		tile.src = this.getTileUrl(coords);
12184
12185		return tile;
12186	},
12187
12188	// @section Extension methods
12189	// @uninheritable
12190	// Layers extending `TileLayer` might reimplement the following method.
12191	// @method getTileUrl(coords: Object): String
12192	// Called only internally, returns the URL for a tile given its coordinates.
12193	// Classes extending `TileLayer` can override this function to provide custom tile URL naming schemes.
12194	getTileUrl: function (coords) {
12195		var data = {
12196			r: Browser.retina ? '@2x' : '',
12197			s: this._getSubdomain(coords),
12198			x: coords.x,
12199			y: coords.y,
12200			z: this._getZoomForUrl()
12201		};
12202		if (this._map && !this._map.options.crs.infinite) {
12203			var invertedY = this._globalTileRange.max.y - coords.y;
12204			if (this.options.tms) {
12205				data['y'] = invertedY;
12206			}
12207			data['-y'] = invertedY;
12208		}
12209
12210		return template(this._url, extend(data, this.options));
12211	},
12212
12213	_tileOnLoad: function (done, tile) {
12214		// For https://github.com/Leaflet/Leaflet/issues/3332
12215		if (Browser.ielt9) {
12216			setTimeout(bind(done, this, null, tile), 0);
12217		} else {
12218			done(null, tile);
12219		}
12220	},
12221
12222	_tileOnError: function (done, tile, e) {
12223		var errorUrl = this.options.errorTileUrl;
12224		if (errorUrl && tile.getAttribute('src') !== errorUrl) {
12225			tile.src = errorUrl;
12226		}
12227		done(e, tile);
12228	},
12229
12230	_onTileRemove: function (e) {
12231		e.tile.onload = null;
12232	},
12233
12234	_getZoomForUrl: function () {
12235		var zoom = this._tileZoom,
12236		maxZoom = this.options.maxZoom,
12237		zoomReverse = this.options.zoomReverse,
12238		zoomOffset = this.options.zoomOffset;
12239
12240		if (zoomReverse) {
12241			zoom = maxZoom - zoom;
12242		}
12243
12244		return zoom + zoomOffset;
12245	},
12246
12247	_getSubdomain: function (tilePoint) {
12248		var index = Math.abs(tilePoint.x + tilePoint.y) % this.options.subdomains.length;
12249		return this.options.subdomains[index];
12250	},
12251
12252	// stops loading all tiles in the background layer
12253	_abortLoading: function () {
12254		var i, tile;
12255		for (i in this._tiles) {
12256			if (this._tiles[i].coords.z !== this._tileZoom) {
12257				tile = this._tiles[i].el;
12258
12259				tile.onload = falseFn;
12260				tile.onerror = falseFn;
12261
12262				if (!tile.complete) {
12263					tile.src = emptyImageUrl;
12264					var coords = this._tiles[i].coords;
12265					remove(tile);
12266					delete this._tiles[i];
12267					// @event tileabort: TileEvent
12268					// Fired when a tile was loading but is now not wanted.
12269					this.fire('tileabort', {
12270						tile: tile,
12271						coords: coords
12272					});
12273				}
12274			}
12275		}
12276	},
12277
12278	_removeTile: function (key) {
12279		var tile = this._tiles[key];
12280		if (!tile) { return; }
12281
12282		// Cancels any pending http requests associated with the tile
12283		tile.el.setAttribute('src', emptyImageUrl);
12284
12285		return GridLayer.prototype._removeTile.call(this, key);
12286	},
12287
12288	_tileReady: function (coords, err, tile) {
12289		if (!this._map || (tile && tile.getAttribute('src') === emptyImageUrl)) {
12290			return;
12291		}
12292
12293		return GridLayer.prototype._tileReady.call(this, coords, err, tile);
12294	}
12295});
12296
12297
12298// @factory L.tilelayer(urlTemplate: String, options?: TileLayer options)
12299// Instantiates a tile layer object given a `URL template` and optionally an options object.
12300
12301function tileLayer(url, options) {
12302	return new TileLayer(url, options);
12303}
12304
12305/*
12306 * @class TileLayer.WMS
12307 * @inherits TileLayer
12308 * @aka L.TileLayer.WMS
12309 * Used to display [WMS](https://en.wikipedia.org/wiki/Web_Map_Service) services as tile layers on the map. Extends `TileLayer`.
12310 *
12311 * @example
12312 *
12313 * ```js
12314 * var nexrad = L.tileLayer.wms("http://mesonet.agron.iastate.edu/cgi-bin/wms/nexrad/n0r.cgi", {
12315 * 	layers: 'nexrad-n0r-900913',
12316 * 	format: 'image/png',
12317 * 	transparent: true,
12318 * 	attribution: "Weather data © 2012 IEM Nexrad"
12319 * });
12320 * ```
12321 */
12322
12323var TileLayerWMS = TileLayer.extend({
12324
12325	// @section
12326	// @aka TileLayer.WMS options
12327	// If any custom options not documented here are used, they will be sent to the
12328	// WMS server as extra parameters in each request URL. This can be useful for
12329	// [non-standard vendor WMS parameters](https://docs.geoserver.org/stable/en/user/services/wms/vendor.html).
12330	defaultWmsParams: {
12331		service: 'WMS',
12332		request: 'GetMap',
12333
12334		// @option layers: String = ''
12335		// **(required)** Comma-separated list of WMS layers to show.
12336		layers: '',
12337
12338		// @option styles: String = ''
12339		// Comma-separated list of WMS styles.
12340		styles: '',
12341
12342		// @option format: String = 'image/jpeg'
12343		// WMS image format (use `'image/png'` for layers with transparency).
12344		format: 'image/jpeg',
12345
12346		// @option transparent: Boolean = false
12347		// If `true`, the WMS service will return images with transparency.
12348		transparent: false,
12349
12350		// @option version: String = '1.1.1'
12351		// Version of the WMS service to use
12352		version: '1.1.1'
12353	},
12354
12355	options: {
12356		// @option crs: CRS = null
12357		// Coordinate Reference System to use for the WMS requests, defaults to
12358		// map CRS. Don't change this if you're not sure what it means.
12359		crs: null,
12360
12361		// @option uppercase: Boolean = false
12362		// If `true`, WMS request parameter keys will be uppercase.
12363		uppercase: false
12364	},
12365
12366	initialize: function (url, options) {
12367
12368		this._url = url;
12369
12370		var wmsParams = extend({}, this.defaultWmsParams);
12371
12372		// all keys that are not TileLayer options go to WMS params
12373		for (var i in options) {
12374			if (!(i in this.options)) {
12375				wmsParams[i] = options[i];
12376			}
12377		}
12378
12379		options = setOptions(this, options);
12380
12381		var realRetina = options.detectRetina && Browser.retina ? 2 : 1;
12382		var tileSize = this.getTileSize();
12383		wmsParams.width = tileSize.x * realRetina;
12384		wmsParams.height = tileSize.y * realRetina;
12385
12386		this.wmsParams = wmsParams;
12387	},
12388
12389	onAdd: function (map) {
12390
12391		this._crs = this.options.crs || map.options.crs;
12392		this._wmsVersion = parseFloat(this.wmsParams.version);
12393
12394		var projectionKey = this._wmsVersion >= 1.3 ? 'crs' : 'srs';
12395		this.wmsParams[projectionKey] = this._crs.code;
12396
12397		TileLayer.prototype.onAdd.call(this, map);
12398	},
12399
12400	getTileUrl: function (coords) {
12401
12402		var tileBounds = this._tileCoordsToNwSe(coords),
12403		    crs = this._crs,
12404		    bounds = toBounds(crs.project(tileBounds[0]), crs.project(tileBounds[1])),
12405		    min = bounds.min,
12406		    max = bounds.max,
12407		    bbox = (this._wmsVersion >= 1.3 && this._crs === EPSG4326 ?
12408		    [min.y, min.x, max.y, max.x] :
12409		    [min.x, min.y, max.x, max.y]).join(','),
12410		    url = TileLayer.prototype.getTileUrl.call(this, coords);
12411		return url +
12412			getParamString(this.wmsParams, url, this.options.uppercase) +
12413			(this.options.uppercase ? '&BBOX=' : '&bbox=') + bbox;
12414	},
12415
12416	// @method setParams(params: Object, noRedraw?: Boolean): this
12417	// Merges an object with the new parameters and re-requests tiles on the current screen (unless `noRedraw` was set to true).
12418	setParams: function (params, noRedraw) {
12419
12420		extend(this.wmsParams, params);
12421
12422		if (!noRedraw) {
12423			this.redraw();
12424		}
12425
12426		return this;
12427	}
12428});
12429
12430
12431// @factory L.tileLayer.wms(baseUrl: String, options: TileLayer.WMS options)
12432// Instantiates a WMS tile layer object given a base URL of the WMS service and a WMS parameters/options object.
12433function tileLayerWMS(url, options) {
12434	return new TileLayerWMS(url, options);
vendor: 52,933 bytes, lines 12434-14420
12434
12435}
12436
12437TileLayer.WMS = TileLayerWMS;
12438tileLayer.wms = tileLayerWMS;
12439
12440/*
12441 * @class Renderer
12442 * @inherits Layer
12443 * @aka L.Renderer
12444 *
12445 * Base class for vector renderer implementations (`SVG`, `Canvas`). Handles the
12446 * DOM container of the renderer, its bounds, and its zoom animation.
12447 *
12448 * A `Renderer` works as an implicit layer group for all `Path`s - the renderer
12449 * itself can be added or removed to the map. All paths use a renderer, which can
12450 * be implicit (the map will decide the type of renderer and use it automatically)
12451 * or explicit (using the [`renderer`](#path-renderer) option of the path).
12452 *
12453 * Do not use this class directly, use `SVG` and `Canvas` instead.
12454 *
12455 * @event update: Event
12456 * Fired when the renderer updates its bounds, center and zoom, for example when
12457 * its map has moved
12458 */
12459
12460var Renderer = Layer.extend({
12461
12462	// @section
12463	// @aka Renderer options
12464	options: {
12465		// @option padding: Number = 0.1
12466		// How much to extend the clip area around the map view (relative to its size)
12467		// e.g. 0.1 would be 10% of map view in each direction
12468		padding: 0.1
12469	},
12470
12471	initialize: function (options) {
12472		setOptions(this, options);
12473		stamp(this);
12474		this._layers = this._layers || {};
12475	},
12476
12477	onAdd: function () {
12478		if (!this._container) {
12479			this._initContainer(); // defined by renderer implementations
12480
12481			// always keep transform-origin as 0 0
12482			addClass(this._container, 'leaflet-zoom-animated');
12483		}
12484
12485		this.getPane().appendChild(this._container);
12486		this._update();
12487		this.on('update', this._updatePaths, this);
12488	},
12489
12490	onRemove: function () {
12491		this.off('update', this._updatePaths, this);
12492		this._destroyContainer();
12493	},
12494
12495	getEvents: function () {
12496		var events = {
12497			viewreset: this._reset,
12498			zoom: this._onZoom,
12499			moveend: this._update,
12500			zoomend: this._onZoomEnd
12501		};
12502		if (this._zoomAnimated) {
12503			events.zoomanim = this._onAnimZoom;
12504		}
12505		return events;
12506	},
12507
12508	_onAnimZoom: function (ev) {
12509		this._updateTransform(ev.center, ev.zoom);
12510	},
12511
12512	_onZoom: function () {
12513		this._updateTransform(this._map.getCenter(), this._map.getZoom());
12514	},
12515
12516	_updateTransform: function (center, zoom) {
12517		var scale = this._map.getZoomScale(zoom, this._zoom),
12518		    viewHalf = this._map.getSize().multiplyBy(0.5 + this.options.padding),
12519		    currentCenterPoint = this._map.project(this._center, zoom),
12520
12521		    topLeftOffset = viewHalf.multiplyBy(-scale).add(currentCenterPoint)
12522				  .subtract(this._map._getNewPixelOrigin(center, zoom));
12523
12524		if (Browser.any3d) {
12525			setTransform(this._container, topLeftOffset, scale);
12526		} else {
12527			setPosition(this._container, topLeftOffset);
12528		}
12529	},
12530
12531	_reset: function () {
12532		this._update();
12533		this._updateTransform(this._center, this._zoom);
12534
12535		for (var id in this._layers) {
12536			this._layers[id]._reset();
12537		}
12538	},
12539
12540	_onZoomEnd: function () {
12541		for (var id in this._layers) {
12542			this._layers[id]._project();
12543		}
12544	},
12545
12546	_updatePaths: function () {
12547		for (var id in this._layers) {
12548			this._layers[id]._update();
12549		}
12550	},
12551
12552	_update: function () {
12553		// Update pixel bounds of renderer container (for positioning/sizing/clipping later)
12554		// Subclasses are responsible of firing the 'update' event.
12555		var p = this.options.padding,
12556		    size = this._map.getSize(),
12557		    min = this._map.containerPointToLayerPoint(size.multiplyBy(-p)).round();
12558
12559		this._bounds = new Bounds(min, min.add(size.multiplyBy(1 + p * 2)).round());
12560
12561		this._center = this._map.getCenter();
12562		this._zoom = this._map.getZoom();
12563	}
12564});
12565
12566/*
12567 * @class Canvas
12568 * @inherits Renderer
12569 * @aka L.Canvas
12570 *
12571 * Allows vector layers to be displayed with [`<canvas>`](https://developer.mozilla.org/docs/Web/API/Canvas_API).
12572 * Inherits `Renderer`.
12573 *
12574 * Due to [technical limitations](https://caniuse.com/canvas), Canvas is not
12575 * available in all web browsers, notably IE8, and overlapping geometries might
12576 * not display properly in some edge cases.
12577 *
12578 * @example
12579 *
12580 * Use Canvas by default for all paths in the map:
12581 *
12582 * ```js
12583 * var map = L.map('map', {
12584 * 	renderer: L.canvas()
12585 * });
12586 * ```
12587 *
12588 * Use a Canvas renderer with extra padding for specific vector geometries:
12589 *
12590 * ```js
12591 * var map = L.map('map');
12592 * var myRenderer = L.canvas({ padding: 0.5 });
12593 * var line = L.polyline( coordinates, { renderer: myRenderer } );
12594 * var circle = L.circle( center, { renderer: myRenderer } );
12595 * ```
12596 */
12597
12598var Canvas = Renderer.extend({
12599
12600	// @section
12601	// @aka Canvas options
12602	options: {
12603		// @option tolerance: Number = 0
12604		// How much to extend the click tolerance around a path/object on the map.
12605		tolerance: 0
12606	},
12607
12608	getEvents: function () {
12609		var events = Renderer.prototype.getEvents.call(this);
12610		events.viewprereset = this._onViewPreReset;
12611		return events;
12612	},
12613
12614	_onViewPreReset: function () {
12615		// Set a flag so that a viewprereset+moveend+viewreset only updates&redraws once
12616		this._postponeUpdatePaths = true;
12617	},
12618
12619	onAdd: function () {
12620		Renderer.prototype.onAdd.call(this);
12621
12622		// Redraw vectors since canvas is cleared upon removal,
12623		// in case of removing the renderer itself from the map.
12624		this._draw();
12625	},
12626
12627	_initContainer: function () {
12628		var container = this._container = document.createElement('canvas');
12629
12630		on(container, 'mousemove', this._onMouseMove, this);
12631		on(container, 'click dblclick mousedown mouseup contextmenu', this._onClick, this);
12632		on(container, 'mouseout', this._handleMouseOut, this);
12633		container['_leaflet_disable_events'] = true;
12634
12635		this._ctx = container.getContext('2d');
12636	},
12637
12638	_destroyContainer: function () {
12639		cancelAnimFrame(this._redrawRequest);
12640		delete this._ctx;
12641		remove(this._container);
12642		off(this._container);
12643		delete this._container;
12644	},
12645
12646	_updatePaths: function () {
12647		if (this._postponeUpdatePaths) { return; }
12648
12649		var layer;
12650		this._redrawBounds = null;
12651		for (var id in this._layers) {
12652			layer = this._layers[id];
12653			layer._update();
12654		}
12655		this._redraw();
12656	},
12657
12658	_update: function () {
12659		if (this._map._animatingZoom && this._bounds) { return; }
12660
12661		Renderer.prototype._update.call(this);
12662
12663		var b = this._bounds,
12664		    container = this._container,
12665		    size = b.getSize(),
12666		    m = Browser.retina ? 2 : 1;
12667
12668		setPosition(container, b.min);
12669
12670		// set canvas size (also clearing it); use double size on retina
12671		container.width = m * size.x;
12672		container.height = m * size.y;
12673		container.style.width = size.x + 'px';
12674		container.style.height = size.y + 'px';
12675
12676		if (Browser.retina) {
12677			this._ctx.scale(2, 2);
12678		}
12679
12680		// translate so we use the same path coordinates after canvas element moves
12681		this._ctx.translate(-b.min.x, -b.min.y);
12682
12683		// Tell paths to redraw themselves
12684		this.fire('update');
12685	},
12686
12687	_reset: function () {
12688		Renderer.prototype._reset.call(this);
12689
12690		if (this._postponeUpdatePaths) {
12691			this._postponeUpdatePaths = false;
12692			this._updatePaths();
12693		}
12694	},
12695
12696	_initPath: function (layer) {
12697		this._updateDashArray(layer);
12698		this._layers[stamp(layer)] = layer;
12699
12700		var order = layer._order = {
12701			layer: layer,
12702			prev: this._drawLast,
12703			next: null
12704		};
12705		if (this._drawLast) { this._drawLast.next = order; }
12706		this._drawLast = order;
12707		this._drawFirst = this._drawFirst || this._drawLast;
12708	},
12709
12710	_addPath: function (layer) {
12711		this._requestRedraw(layer);
12712	},
12713
12714	_removePath: function (layer) {
12715		var order = layer._order;
12716		var next = order.next;
12717		var prev = order.prev;
12718
12719		if (next) {
12720			next.prev = prev;
12721		} else {
12722			this._drawLast = prev;
12723		}
12724		if (prev) {
12725			prev.next = next;
12726		} else {
12727			this._drawFirst = next;
12728		}
12729
12730		delete layer._order;
12731
12732		delete this._layers[stamp(layer)];
12733
12734		this._requestRedraw(layer);
12735	},
12736
12737	_updatePath: function (layer) {
12738		// Redraw the union of the layer's old pixel
12739		// bounds and the new pixel bounds.
12740		this._extendRedrawBounds(layer);
12741		layer._project();
12742		layer._update();
12743		// The redraw will extend the redraw bounds
12744		// with the new pixel bounds.
12745		this._requestRedraw(layer);
12746	},
12747
12748	_updateStyle: function (layer) {
12749		this._updateDashArray(layer);
12750		this._requestRedraw(layer);
12751	},
12752
12753	_updateDashArray: function (layer) {
12754		if (typeof layer.options.dashArray === 'string') {
12755			var parts = layer.options.dashArray.split(/[, ]+/),
12756			    dashArray = [],
12757			    dashValue,
12758			    i;
12759			for (i = 0; i < parts.length; i++) {
12760				dashValue = Number(parts[i]);
12761				// Ignore dash array containing invalid lengths
12762				if (isNaN(dashValue)) { return; }
12763				dashArray.push(dashValue);
12764			}
12765			layer.options._dashArray = dashArray;
12766		} else {
12767			layer.options._dashArray = layer.options.dashArray;
12768		}
12769	},
12770
12771	_requestRedraw: function (layer) {
12772		if (!this._map) { return; }
12773
12774		this._extendRedrawBounds(layer);
12775		this._redrawRequest = this._redrawRequest || requestAnimFrame(this._redraw, this);
12776	},
12777
12778	_extendRedrawBounds: function (layer) {
12779		if (layer._pxBounds) {
12780			var padding = (layer.options.weight || 0) + 1;
12781			this._redrawBounds = this._redrawBounds || new Bounds();
12782			this._redrawBounds.extend(layer._pxBounds.min.subtract([padding, padding]));
12783			this._redrawBounds.extend(layer._pxBounds.max.add([padding, padding]));
12784		}
12785	},
12786
12787	_redraw: function () {
12788		this._redrawRequest = null;
12789
12790		if (this._redrawBounds) {
12791			this._redrawBounds.min._floor();
12792			this._redrawBounds.max._ceil();
12793		}
12794
12795		this._clear(); // clear layers in redraw bounds
12796		this._draw(); // draw layers
12797
12798		this._redrawBounds = null;
12799	},
12800
12801	_clear: function () {
12802		var bounds = this._redrawBounds;
12803		if (bounds) {
12804			var size = bounds.getSize();
12805			this._ctx.clearRect(bounds.min.x, bounds.min.y, size.x, size.y);
12806		} else {
12807			this._ctx.save();
12808			this._ctx.setTransform(1, 0, 0, 1, 0, 0);
12809			this._ctx.clearRect(0, 0, this._container.width, this._container.height);
12810			this._ctx.restore();
12811		}
12812	},
12813
12814	_draw: function () {
12815		var layer, bounds = this._redrawBounds;
12816		this._ctx.save();
12817		if (bounds) {
12818			var size = bounds.getSize();
12819			this._ctx.beginPath();
12820			this._ctx.rect(bounds.min.x, bounds.min.y, size.x, size.y);
12821			this._ctx.clip();
12822		}
12823
12824		this._drawing = true;
12825
12826		for (var order = this._drawFirst; order; order = order.next) {
12827			layer = order.layer;
12828			if (!bounds || (layer._pxBounds && layer._pxBounds.intersects(bounds))) {
12829				layer._updatePath();
12830			}
12831		}
12832
12833		this._drawing = false;
12834
12835		this._ctx.restore();  // Restore state before clipping.
12836	},
12837
12838	_updatePoly: function (layer, closed) {
12839		if (!this._drawing) { return; }
12840
12841		var i, j, len2, p,
12842		    parts = layer._parts,
12843		    len = parts.length,
12844		    ctx = this._ctx;
12845
12846		if (!len) { return; }
12847
12848		ctx.beginPath();
12849
12850		for (i = 0; i < len; i++) {
12851			for (j = 0, len2 = parts[i].length; j < len2; j++) {
12852				p = parts[i][j];
12853				ctx[j ? 'lineTo' : 'moveTo'](p.x, p.y);
12854			}
12855			if (closed) {
12856				ctx.closePath();
12857			}
12858		}
12859
12860		this._fillStroke(ctx, layer);
12861
12862		// TODO optimization: 1 fill/stroke for all features with equal style instead of 1 for each feature
12863	},
12864
12865	_updateCircle: function (layer) {
12866
12867		if (!this._drawing || layer._empty()) { return; }
12868
12869		var p = layer._point,
12870		    ctx = this._ctx,
12871		    r = Math.max(Math.round(layer._radius), 1),
12872		    s = (Math.max(Math.round(layer._radiusY), 1) || r) / r;
12873
12874		if (s !== 1) {
12875			ctx.save();
12876			ctx.scale(1, s);
12877		}
12878
12879		ctx.beginPath();
12880		ctx.arc(p.x, p.y / s, r, 0, Math.PI * 2, false);
12881
12882		if (s !== 1) {
12883			ctx.restore();
12884		}
12885
12886		this._fillStroke(ctx, layer);
12887	},
12888
12889	_fillStroke: function (ctx, layer) {
12890		var options = layer.options;
12891
12892		if (options.fill) {
12893			ctx.globalAlpha = options.fillOpacity;
12894			ctx.fillStyle = options.fillColor || options.color;
12895			ctx.fill(options.fillRule || 'evenodd');
12896		}
12897
12898		if (options.stroke && options.weight !== 0) {
12899			if (ctx.setLineDash) {
12900				ctx.setLineDash(layer.options && layer.options._dashArray || []);
12901			}
12902			ctx.globalAlpha = options.opacity;
12903			ctx.lineWidth = options.weight;
12904			ctx.strokeStyle = options.color;
12905			ctx.lineCap = options.lineCap;
12906			ctx.lineJoin = options.lineJoin;
12907			ctx.stroke();
12908		}
12909	},
12910
12911	// Canvas obviously doesn't have mouse events for individual drawn objects,
12912	// so we emulate that by calculating what's under the mouse on mousemove/click manually
12913
12914	_onClick: function (e) {
12915		var point = this._map.mouseEventToLayerPoint(e), layer, clickedLayer;
12916
12917		for (var order = this._drawFirst; order; order = order.next) {
12918			layer = order.layer;
12919			if (layer.options.interactive && layer._containsPoint(point)) {
12920				if (!(e.type === 'click' || e.type === 'preclick') || !this._map._draggableMoved(layer)) {
12921					clickedLayer = layer;
12922				}
12923			}
12924		}
12925		this._fireEvent(clickedLayer ? [clickedLayer] : false, e);
12926	},
12927
12928	_onMouseMove: function (e) {
12929		if (!this._map || this._map.dragging.moving() || this._map._animatingZoom) { return; }
12930
12931		var point = this._map.mouseEventToLayerPoint(e);
12932		this._handleMouseHover(e, point);
12933	},
12934
12935
12936	_handleMouseOut: function (e) {
12937		var layer = this._hoveredLayer;
12938		if (layer) {
12939			// if we're leaving the layer, fire mouseout
12940			removeClass(this._container, 'leaflet-interactive');
12941			this._fireEvent([layer], e, 'mouseout');
12942			this._hoveredLayer = null;
12943			this._mouseHoverThrottled = false;
12944		}
12945	},
12946
12947	_handleMouseHover: function (e, point) {
12948		if (this._mouseHoverThrottled) {
12949			return;
12950		}
12951
12952		var layer, candidateHoveredLayer;
12953
12954		for (var order = this._drawFirst; order; order = order.next) {
12955			layer = order.layer;
12956			if (layer.options.interactive && layer._containsPoint(point)) {
12957				candidateHoveredLayer = layer;
12958			}
12959		}
12960
12961		if (candidateHoveredLayer !== this._hoveredLayer) {
12962			this._handleMouseOut(e);
12963
12964			if (candidateHoveredLayer) {
12965				addClass(this._container, 'leaflet-interactive'); // change cursor
12966				this._fireEvent([candidateHoveredLayer], e, 'mouseover');
12967				this._hoveredLayer = candidateHoveredLayer;
12968			}
12969		}
12970
12971		this._fireEvent(this._hoveredLayer ? [this._hoveredLayer] : false, e);
12972
12973		this._mouseHoverThrottled = true;
12974		setTimeout(bind(function () {
12975			this._mouseHoverThrottled = false;
12976		}, this), 32);
12977	},
12978
12979	_fireEvent: function (layers, e, type) {
12980		this._map._fireDOMEvent(e, type || e.type, layers);
12981	},
12982
12983	_bringToFront: function (layer) {
12984		var order = layer._order;
12985
12986		if (!order) { return; }
12987
12988		var next = order.next;
12989		var prev = order.prev;
12990
12991		if (next) {
12992			next.prev = prev;
12993		} else {
12994			// Already last
12995			return;
12996		}
12997		if (prev) {
12998			prev.next = next;
12999		} else if (next) {
13000			// Update first entry unless this is the
13001			// single entry
13002			this._drawFirst = next;
13003		}
13004
13005		order.prev = this._drawLast;
13006		this._drawLast.next = order;
13007
13008		order.next = null;
13009		this._drawLast = order;
13010
13011		this._requestRedraw(layer);
13012	},
13013
13014	_bringToBack: function (layer) {
13015		var order = layer._order;
13016
13017		if (!order) { return; }
13018
13019		var next = order.next;
13020		var prev = order.prev;
13021
13022		if (prev) {
13023			prev.next = next;
13024		} else {
13025			// Already first
13026			return;
13027		}
13028		if (next) {
13029			next.prev = prev;
13030		} else if (prev) {
13031			// Update last entry unless this is the
13032			// single entry
13033			this._drawLast = prev;
13034		}
13035
13036		order.prev = null;
13037
13038		order.next = this._drawFirst;
13039		this._drawFirst.prev = order;
13040		this._drawFirst = order;
13041
13042		this._requestRedraw(layer);
13043	}
13044});
13045
13046// @factory L.canvas(options?: Renderer options)
13047// Creates a Canvas renderer with the given options.
13048function canvas(options) {
13049	return Browser.canvas ? new Canvas(options) : null;
13050}
13051
13052/*
13053 * Thanks to Dmitry Baranovsky and his Raphael library for inspiration!
13054 */
13055
13056
13057var vmlCreate = (function () {
13058	try {
13059		document.namespaces.add('lvml', 'urn:schemas-microsoft-com:vml');
13060		return function (name) {
13061			return document.createElement('<lvml:' + name + ' class="lvml">');
13062		};
13063	} catch (e) {
13064		// Do not return fn from catch block so `e` can be garbage collected
13065		// See https://github.com/Leaflet/Leaflet/pull/7279
13066	}
13067	return function (name) {
13068		return document.createElement('<' + name + ' xmlns="urn:schemas-microsoft.com:vml" class="lvml">');
13069	};
13070})();
13071
13072
13073/*
13074 * @class SVG
13075 *
13076 *
13077 * VML was deprecated in 2012, which means VML functionality exists only for backwards compatibility
13078 * with old versions of Internet Explorer.
13079 */
13080
13081// mixin to redefine some SVG methods to handle VML syntax which is similar but with some differences
13082var vmlMixin = {
13083
13084	_initContainer: function () {
13085		this._container = create$1('div', 'leaflet-vml-container');
13086	},
13087
13088	_update: function () {
13089		if (this._map._animatingZoom) { return; }
13090		Renderer.prototype._update.call(this);
13091		this.fire('update');
13092	},
13093
13094	_initPath: function (layer) {
13095		var container = layer._container = vmlCreate('shape');
13096
13097		addClass(container, 'leaflet-vml-shape ' + (this.options.className || ''));
13098
13099		container.coordsize = '1 1';
13100
13101		layer._path = vmlCreate('path');
13102		container.appendChild(layer._path);
13103
13104		this._updateStyle(layer);
13105		this._layers[stamp(layer)] = layer;
13106	},
13107
13108	_addPath: function (layer) {
13109		var container = layer._container;
13110		this._container.appendChild(container);
13111
13112		if (layer.options.interactive) {
13113			layer.addInteractiveTarget(container);
13114		}
13115	},
13116
13117	_removePath: function (layer) {
13118		var container = layer._container;
13119		remove(container);
13120		layer.removeInteractiveTarget(container);
13121		delete this._layers[stamp(layer)];
13122	},
13123
13124	_updateStyle: function (layer) {
13125		var stroke = layer._stroke,
13126		    fill = layer._fill,
13127		    options = layer.options,
13128		    container = layer._container;
13129
13130		container.stroked = !!options.stroke;
13131		container.filled = !!options.fill;
13132
13133		if (options.stroke) {
13134			if (!stroke) {
13135				stroke = layer._stroke = vmlCreate('stroke');
13136			}
13137			container.appendChild(stroke);
13138			stroke.weight = options.weight + 'px';
13139			stroke.color = options.color;
13140			stroke.opacity = options.opacity;
13141
13142			if (options.dashArray) {
13143				stroke.dashStyle = isArray(options.dashArray) ?
13144				    options.dashArray.join(' ') :
13145				    options.dashArray.replace(/( *, *)/g, ' ');
13146			} else {
13147				stroke.dashStyle = '';
13148			}
13149			stroke.endcap = options.lineCap.replace('butt', 'flat');
13150			stroke.joinstyle = options.lineJoin;
13151
13152		} else if (stroke) {
13153			container.removeChild(stroke);
13154			layer._stroke = null;
13155		}
13156
13157		if (options.fill) {
13158			if (!fill) {
13159				fill = layer._fill = vmlCreate('fill');
13160			}
13161			container.appendChild(fill);
13162			fill.color = options.fillColor || options.color;
13163			fill.opacity = options.fillOpacity;
13164
13165		} else if (fill) {
13166			container.removeChild(fill);
13167			layer._fill = null;
13168		}
13169	},
13170
13171	_updateCircle: function (layer) {
13172		var p = layer._point.round(),
13173		    r = Math.round(layer._radius),
13174		    r2 = Math.round(layer._radiusY || r);
13175
13176		this._setPath(layer, layer._empty() ? 'M0 0' :
13177			'AL ' + p.x + ',' + p.y + ' ' + r + ',' + r2 + ' 0,' + (65535 * 360));
13178	},
13179
13180	_setPath: function (layer, path) {
13181		layer._path.v = path;
13182	},
13183
13184	_bringToFront: function (layer) {
13185		toFront(layer._container);
13186	},
13187
13188	_bringToBack: function (layer) {
13189		toBack(layer._container);
13190	}
13191};
13192
13193var create = Browser.vml ? vmlCreate : svgCreate;
13194
13195/*
13196 * @class SVG
13197 * @inherits Renderer
13198 * @aka L.SVG
13199 *
13200 * Allows vector layers to be displayed with [SVG](https://developer.mozilla.org/docs/Web/SVG).
13201 * Inherits `Renderer`.
13202 *
13203 * Due to [technical limitations](https://caniuse.com/svg), SVG is not
13204 * available in all web browsers, notably Android 2.x and 3.x.
13205 *
13206 * Although SVG is not available on IE7 and IE8, these browsers support
13207 * [VML](https://en.wikipedia.org/wiki/Vector_Markup_Language)
13208 * (a now deprecated technology), and the SVG renderer will fall back to VML in
13209 * this case.
13210 *
13211 * @example
13212 *
13213 * Use SVG by default for all paths in the map:
13214 *
13215 * ```js
13216 * var map = L.map('map', {
13217 * 	renderer: L.svg()
13218 * });
13219 * ```
13220 *
13221 * Use a SVG renderer with extra padding for specific vector geometries:
13222 *
13223 * ```js
13224 * var map = L.map('map');
13225 * var myRenderer = L.svg({ padding: 0.5 });
13226 * var line = L.polyline( coordinates, { renderer: myRenderer } );
13227 * var circle = L.circle( center, { renderer: myRenderer } );
13228 * ```
13229 */
13230
13231var SVG = Renderer.extend({
13232
13233	_initContainer: function () {
13234		this._container = create('svg');
13235
13236		// makes it possible to click through svg root; we'll reset it back in individual paths
13237		this._container.setAttribute('pointer-events', 'none');
13238
13239		this._rootGroup = create('g');
13240		this._container.appendChild(this._rootGroup);
13241	},
13242
13243	_destroyContainer: function () {
13244		remove(this._container);
13245		off(this._container);
13246		delete this._container;
13247		delete this._rootGroup;
13248		delete this._svgSize;
13249	},
13250
13251	_update: function () {
13252		if (this._map._animatingZoom && this._bounds) { return; }
13253
13254		Renderer.prototype._update.call(this);
13255
13256		var b = this._bounds,
13257		    size = b.getSize(),
13258		    container = this._container;
13259
13260		// set size of svg-container if changed
13261		if (!this._svgSize || !this._svgSize.equals(size)) {
13262			this._svgSize = size;
13263			container.setAttribute('width', size.x);
13264			container.setAttribute('height', size.y);
13265		}
13266
13267		// movement: update container viewBox so that we don't have to change coordinates of individual layers
13268		setPosition(container, b.min);
13269		container.setAttribute('viewBox', [b.min.x, b.min.y, size.x, size.y].join(' '));
13270
13271		this.fire('update');
13272	},
13273
13274	// methods below are called by vector layers implementations
13275
13276	_initPath: function (layer) {
13277		var path = layer._path = create('path');
13278
13279		// @namespace Path
13280		// @option className: String = null
13281		// Custom class name set on an element. Only for SVG renderer.
13282		if (layer.options.className) {
13283			addClass(path, layer.options.className);
13284		}
13285
13286		if (layer.options.interactive) {
13287			addClass(path, 'leaflet-interactive');
13288		}
13289
13290		this._updateStyle(layer);
13291		this._layers[stamp(layer)] = layer;
13292	},
13293
13294	_addPath: function (layer) {
13295		if (!this._rootGroup) { this._initContainer(); }
13296		this._rootGroup.appendChild(layer._path);
13297		layer.addInteractiveTarget(layer._path);
13298	},
13299
13300	_removePath: function (layer) {
13301		remove(layer._path);
13302		layer.removeInteractiveTarget(layer._path);
13303		delete this._layers[stamp(layer)];
13304	},
13305
13306	_updatePath: function (layer) {
13307		layer._project();
13308		layer._update();
13309	},
13310
13311	_updateStyle: function (layer) {
13312		var path = layer._path,
13313		    options = layer.options;
13314
13315		if (!path) { return; }
13316
13317		if (options.stroke) {
13318			path.setAttribute('stroke', options.color);
13319			path.setAttribute('stroke-opacity', options.opacity);
13320			path.setAttribute('stroke-width', options.weight);
13321			path.setAttribute('stroke-linecap', options.lineCap);
13322			path.setAttribute('stroke-linejoin', options.lineJoin);
13323
13324			if (options.dashArray) {
13325				path.setAttribute('stroke-dasharray', options.dashArray);
13326			} else {
13327				path.removeAttribute('stroke-dasharray');
13328			}
13329
13330			if (options.dashOffset) {
13331				path.setAttribute('stroke-dashoffset', options.dashOffset);
13332			} else {
13333				path.removeAttribute('stroke-dashoffset');
13334			}
13335		} else {
13336			path.setAttribute('stroke', 'none');
13337		}
13338
13339		if (options.fill) {
13340			path.setAttribute('fill', options.fillColor || options.color);
13341			path.setAttribute('fill-opacity', options.fillOpacity);
13342			path.setAttribute('fill-rule', options.fillRule || 'evenodd');
13343		} else {
13344			path.setAttribute('fill', 'none');
13345		}
13346	},
13347
13348	_updatePoly: function (layer, closed) {
13349		this._setPath(layer, pointsToPath(layer._parts, closed));
13350	},
13351
13352	_updateCircle: function (layer) {
13353		var p = layer._point,
13354		    r = Math.max(Math.round(layer._radius), 1),
13355		    r2 = Math.max(Math.round(layer._radiusY), 1) || r,
13356		    arc = 'a' + r + ',' + r2 + ' 0 1,0 ';
13357
13358		// drawing a circle with two half-arcs
13359		var d = layer._empty() ? 'M0 0' :
13360			'M' + (p.x - r) + ',' + p.y +
13361			arc + (r * 2) + ',0 ' +
13362			arc + (-r * 2) + ',0 ';
13363
13364		this._setPath(layer, d);
13365	},
13366
13367	_setPath: function (layer, path) {
13368		layer._path.setAttribute('d', path);
13369	},
13370
13371	// SVG does not have the concept of zIndex so we resort to changing the DOM order of elements
13372	_bringToFront: function (layer) {
13373		toFront(layer._path);
13374	},
13375
13376	_bringToBack: function (layer) {
13377		toBack(layer._path);
13378	}
13379});
13380
13381if (Browser.vml) {
13382	SVG.include(vmlMixin);
13383}
13384
13385// @namespace SVG
13386// @factory L.svg(options?: Renderer options)
13387// Creates a SVG renderer with the given options.
13388function svg(options) {
13389	return Browser.svg || Browser.vml ? new SVG(options) : null;
13390}
13391
13392Map.include({
13393	// @namespace Map; @method getRenderer(layer: Path): Renderer
13394	// Returns the instance of `Renderer` that should be used to render the given
13395	// `Path`. It will ensure that the `renderer` options of the map and paths
13396	// are respected, and that the renderers do exist on the map.
13397	getRenderer: function (layer) {
13398		// @namespace Path; @option renderer: Renderer
13399		// Use this specific instance of `Renderer` for this path. Takes
13400		// precedence over the map's [default renderer](#map-renderer).
13401		var renderer = layer.options.renderer || this._getPaneRenderer(layer.options.pane) || this.options.renderer || this._renderer;
13402
13403		if (!renderer) {
13404			renderer = this._renderer = this._createRenderer();
13405		}
13406
13407		if (!this.hasLayer(renderer)) {
13408			this.addLayer(renderer);
13409		}
13410		return renderer;
13411	},
13412
13413	_getPaneRenderer: function (name) {
13414		if (name === 'overlayPane' || name === undefined) {
13415			return false;
13416		}
13417
13418		var renderer = this._paneRenderers[name];
13419		if (renderer === undefined) {
13420			renderer = this._createRenderer({pane: name});
13421			this._paneRenderers[name] = renderer;
13422		}
13423		return renderer;
13424	},
13425
13426	_createRenderer: function (options) {
13427		// @namespace Map; @option preferCanvas: Boolean = false
13428		// Whether `Path`s should be rendered on a `Canvas` renderer.
13429		// By default, all `Path`s are rendered in a `SVG` renderer.
13430		return (this.options.preferCanvas && canvas(options)) || svg(options);
13431	}
13432});
13433
13434/*
13435 * L.Rectangle extends Polygon and creates a rectangle when passed a LatLngBounds object.
13436 */
13437
13438/*
13439 * @class Rectangle
13440 * @aka L.Rectangle
13441 * @inherits Polygon
13442 *
13443 * A class for drawing rectangle overlays on a map. Extends `Polygon`.
13444 *
13445 * @example
13446 *
13447 * ```js
13448 * // define rectangle geographical bounds
13449 * var bounds = [[54.559322, -5.767822], [56.1210604, -3.021240]];
13450 *
13451 * // create an orange rectangle
13452 * L.rectangle(bounds, {color: "#ff7800", weight: 1}).addTo(map);
13453 *
13454 * // zoom the map to the rectangle bounds
13455 * map.fitBounds(bounds);
13456 * ```
13457 *
13458 */
13459
13460
13461var Rectangle = Polygon.extend({
13462	initialize: function (latLngBounds, options) {
13463		Polygon.prototype.initialize.call(this, this._boundsToLatLngs(latLngBounds), options);
13464	},
13465
13466	// @method setBounds(latLngBounds: LatLngBounds): this
13467	// Redraws the rectangle with the passed bounds.
13468	setBounds: function (latLngBounds) {
13469		return this.setLatLngs(this._boundsToLatLngs(latLngBounds));
13470	},
13471
13472	_boundsToLatLngs: function (latLngBounds) {
13473		latLngBounds = toLatLngBounds(latLngBounds);
13474		return [
13475			latLngBounds.getSouthWest(),
13476			latLngBounds.getNorthWest(),
13477			latLngBounds.getNorthEast(),
13478			latLngBounds.getSouthEast()
13479		];
13480	}
13481});
13482
13483
13484// @factory L.rectangle(latLngBounds: LatLngBounds, options?: Polyline options)
13485function rectangle(latLngBounds, options) {
13486	return new Rectangle(latLngBounds, options);
13487}
13488
13489SVG.create = create;
13490SVG.pointsToPath = pointsToPath;
13491
13492GeoJSON.geometryToLayer = geometryToLayer;
13493GeoJSON.coordsToLatLng = coordsToLatLng;
13494GeoJSON.coordsToLatLngs = coordsToLatLngs;
13495GeoJSON.latLngToCoords = latLngToCoords;
13496GeoJSON.latLngsToCoords = latLngsToCoords;
13497GeoJSON.getFeature = getFeature;
13498GeoJSON.asFeature = asFeature;
13499
13500/*
13501 * L.Handler.BoxZoom is used to add shift-drag zoom interaction to the map
13502 * (zoom to a selected bounding box), enabled by default.
13503 */
13504
13505// @namespace Map
13506// @section Interaction Options
13507Map.mergeOptions({
13508	// @option boxZoom: Boolean = true
13509	// Whether the map can be zoomed to a rectangular area specified by
13510	// dragging the mouse while pressing the shift key.
13511	boxZoom: true
13512});
13513
13514var BoxZoom = Handler.extend({
13515	initialize: function (map) {
13516		this._map = map;
13517		this._container = map._container;
13518		this._pane = map._panes.overlayPane;
13519		this._resetStateTimeout = 0;
13520		map.on('unload', this._destroy, this);
13521	},
13522
13523	addHooks: function () {
13524		on(this._container, 'mousedown', this._onMouseDown, this);
13525	},
13526
13527	removeHooks: function () {
13528		off(this._container, 'mousedown', this._onMouseDown, this);
13529	},
13530
13531	moved: function () {
13532		return this._moved;
13533	},
13534
13535	_destroy: function () {
13536		remove(this._pane);
13537		delete this._pane;
13538	},
13539
13540	_resetState: function () {
13541		this._resetStateTimeout = 0;
13542		this._moved = false;
13543	},
13544
13545	_clearDeferredResetState: function () {
13546		if (this._resetStateTimeout !== 0) {
13547			clearTimeout(this._resetStateTimeout);
13548			this._resetStateTimeout = 0;
13549		}
13550	},
13551
13552	_onMouseDown: function (e) {
13553		if (!e.shiftKey || ((e.which !== 1) && (e.button !== 1))) { return false; }
13554
13555		// Clear the deferred resetState if it hasn't executed yet, otherwise it
13556		// will interrupt the interaction and orphan a box element in the container.
13557		this._clearDeferredResetState();
13558		this._resetState();
13559
13560		disableTextSelection();
13561		disableImageDrag();
13562
13563		this._startPoint = this._map.mouseEventToContainerPoint(e);
13564
13565		on(document, {
13566			contextmenu: stop,
13567			mousemove: this._onMouseMove,
13568			mouseup: this._onMouseUp,
13569			keydown: this._onKeyDown
13570		}, this);
13571	},
13572
13573	_onMouseMove: function (e) {
13574		if (!this._moved) {
13575			this._moved = true;
13576
13577			this._box = create$1('div', 'leaflet-zoom-box', this._container);
13578			addClass(this._container, 'leaflet-crosshair');
13579
13580			this._map.fire('boxzoomstart');
13581		}
13582
13583		this._point = this._map.mouseEventToContainerPoint(e);
13584
13585		var bounds = new Bounds(this._point, this._startPoint),
13586		    size = bounds.getSize();
13587
13588		setPosition(this._box, bounds.min);
13589
13590		this._box.style.width  = size.x + 'px';
13591		this._box.style.height = size.y + 'px';
13592	},
13593
13594	_finish: function () {
13595		if (this._moved) {
13596			remove(this._box);
13597			removeClass(this._container, 'leaflet-crosshair');
13598		}
13599
13600		enableTextSelection();
13601		enableImageDrag();
13602
13603		off(document, {
13604			contextmenu: stop,
13605			mousemove: this._onMouseMove,
13606			mouseup: this._onMouseUp,
13607			keydown: this._onKeyDown
13608		}, this);
13609	},
13610
13611	_onMouseUp: function (e) {
13612		if ((e.which !== 1) && (e.button !== 1)) { return; }
13613
13614		this._finish();
13615
13616		if (!this._moved) { return; }
13617		// Postpone to next JS tick so internal click event handling
13618		// still see it as "moved".
13619		this._clearDeferredResetState();
13620		this._resetStateTimeout = setTimeout(bind(this._resetState, this), 0);
13621
13622		var bounds = new LatLngBounds(
13623		        this._map.containerPointToLatLng(this._startPoint),
13624		        this._map.containerPointToLatLng(this._point));
13625
13626		this._map
13627			.fitBounds(bounds)
13628			.fire('boxzoomend', {boxZoomBounds: bounds});
13629	},
13630
13631	_onKeyDown: function (e) {
13632		if (e.keyCode === 27) {
13633			this._finish();
13634			this._clearDeferredResetState();
13635			this._resetState();
13636		}
13637	}
13638});
13639
13640// @section Handlers
13641// @property boxZoom: Handler
13642// Box (shift-drag with mouse) zoom handler.
13643Map.addInitHook('addHandler', 'boxZoom', BoxZoom);
13644
13645/*
13646 * L.Handler.DoubleClickZoom is used to handle double-click zoom on the map, enabled by default.
13647 */
13648
13649// @namespace Map
13650// @section Interaction Options
13651
13652Map.mergeOptions({
13653	// @option doubleClickZoom: Boolean|String = true
13654	// Whether the map can be zoomed in by double clicking on it and
13655	// zoomed out by double clicking while holding shift. If passed
13656	// `'center'`, double-click zoom will zoom to the center of the
13657	//  view regardless of where the mouse was.
13658	doubleClickZoom: true
13659});
13660
13661var DoubleClickZoom = Handler.extend({
13662	addHooks: function () {
13663		this._map.on('dblclick', this._onDoubleClick, this);
13664	},
13665
13666	removeHooks: function () {
13667		this._map.off('dblclick', this._onDoubleClick, this);
13668	},
13669
13670	_onDoubleClick: function (e) {
13671		var map = this._map,
13672		    oldZoom = map.getZoom(),
13673		    delta = map.options.zoomDelta,
13674		    zoom = e.originalEvent.shiftKey ? oldZoom - delta : oldZoom + delta;
13675
13676		if (map.options.doubleClickZoom === 'center') {
13677			map.setZoom(zoom);
13678		} else {
13679			map.setZoomAround(e.containerPoint, zoom);
13680		}
13681	}
13682});
13683
13684// @section Handlers
13685//
13686// Map properties include interaction handlers that allow you to control
13687// interaction behavior in runtime, enabling or disabling certain features such
13688// as dragging or touch zoom (see `Handler` methods). For example:
13689//
13690// ```js
13691// map.doubleClickZoom.disable();
13692// ```
13693//
13694// @property doubleClickZoom: Handler
13695// Double click zoom handler.
13696Map.addInitHook('addHandler', 'doubleClickZoom', DoubleClickZoom);
13697
13698/*
13699 * L.Handler.MapDrag is used to make the map draggable (with panning inertia), enabled by default.
13700 */
13701
13702// @namespace Map
13703// @section Interaction Options
13704Map.mergeOptions({
13705	// @option dragging: Boolean = true
13706	// Whether the map is draggable with mouse/touch or not.
13707	dragging: true,
13708
13709	// @section Panning Inertia Options
13710	// @option inertia: Boolean = *
13711	// If enabled, panning of the map will have an inertia effect where
13712	// the map builds momentum while dragging and continues moving in
13713	// the same direction for some time. Feels especially nice on touch
13714	// devices. Enabled by default.
13715	inertia: true,
13716
13717	// @option inertiaDeceleration: Number = 3000
13718	// The rate with which the inertial movement slows down, in pixels/second².
13719	inertiaDeceleration: 3400, // px/s^2
13720
13721	// @option inertiaMaxSpeed: Number = Infinity
13722	// Max speed of the inertial movement, in pixels/second.
13723	inertiaMaxSpeed: Infinity, // px/s
13724
13725	// @option easeLinearity: Number = 0.2
13726	easeLinearity: 0.2,
13727
13728	// TODO refactor, move to CRS
13729	// @option worldCopyJump: Boolean = false
13730	// With this option enabled, the map tracks when you pan to another "copy"
13731	// of the world and seamlessly jumps to the original one so that all overlays
13732	// like markers and vector layers are still visible.
13733	worldCopyJump: false,
13734
13735	// @option maxBoundsViscosity: Number = 0.0
13736	// If `maxBounds` is set, this option will control how solid the bounds
13737	// are when dragging the map around. The default value of `0.0` allows the
13738	// user to drag outside the bounds at normal speed, higher values will
13739	// slow down map dragging outside bounds, and `1.0` makes the bounds fully
13740	// solid, preventing the user from dragging outside the bounds.
13741	maxBoundsViscosity: 0.0
13742});
13743
13744var Drag = Handler.extend({
13745	addHooks: function () {
13746		if (!this._draggable) {
13747			var map = this._map;
13748
13749			this._draggable = new Draggable(map._mapPane, map._container);
13750
13751			this._draggable.on({
13752				dragstart: this._onDragStart,
13753				drag: this._onDrag,
13754				dragend: this._onDragEnd
13755			}, this);
13756
13757			this._draggable.on('predrag', this._onPreDragLimit, this);
13758			if (map.options.worldCopyJump) {
13759				this._draggable.on('predrag', this._onPreDragWrap, this);
13760				map.on('zoomend', this._onZoomEnd, this);
13761
13762				map.whenReady(this._onZoomEnd, this);
13763			}
13764		}
13765		addClass(this._map._container, 'leaflet-grab leaflet-touch-drag');
13766		this._draggable.enable();
13767		this._positions = [];
13768		this._times = [];
13769	},
13770
13771	removeHooks: function () {
13772		removeClass(this._map._container, 'leaflet-grab');
13773		removeClass(this._map._container, 'leaflet-touch-drag');
13774		this._draggable.disable();
13775	},
13776
13777	moved: function () {
13778		return this._draggable && this._draggable._moved;
13779	},
13780
13781	moving: function () {
13782		return this._draggable && this._draggable._moving;
13783	},
13784
13785	_onDragStart: function () {
13786		var map = this._map;
13787
13788		map._stop();
13789		if (this._map.options.maxBounds && this._map.options.maxBoundsViscosity) {
13790			var bounds = toLatLngBounds(this._map.options.maxBounds);
13791
13792			this._offsetLimit = toBounds(
13793				this._map.latLngToContainerPoint(bounds.getNorthWest()).multiplyBy(-1),
13794				this._map.latLngToContainerPoint(bounds.getSouthEast()).multiplyBy(-1)
13795					.add(this._map.getSize()));
13796
13797			this._viscosity = Math.min(1.0, Math.max(0.0, this._map.options.maxBoundsViscosity));
13798		} else {
13799			this._offsetLimit = null;
13800		}
13801
13802		map
13803		    .fire('movestart')
13804		    .fire('dragstart');
13805
13806		if (map.options.inertia) {
13807			this._positions = [];
13808			this._times = [];
13809		}
13810	},
13811
13812	_onDrag: function (e) {
13813		if (this._map.options.inertia) {
13814			var time = this._lastTime = +new Date(),
13815			    pos = this._lastPos = this._draggable._absPos || this._draggable._newPos;
13816
13817			this._positions.push(pos);
13818			this._times.push(time);
13819
13820			this._prunePositions(time);
13821		}
13822
13823		this._map
13824		    .fire('move', e)
13825		    .fire('drag', e);
13826	},
13827
13828	_prunePositions: function (time) {
13829		while (this._positions.length > 1 && time - this._times[0] > 50) {
13830			this._positions.shift();
13831			this._times.shift();
13832		}
13833	},
13834
13835	_onZoomEnd: function () {
13836		var pxCenter = this._map.getSize().divideBy(2),
13837		    pxWorldCenter = this._map.latLngToLayerPoint([0, 0]);
13838
13839		this._initialWorldOffset = pxWorldCenter.subtract(pxCenter).x;
13840		this._worldWidth = this._map.getPixelWorldBounds().getSize().x;
13841	},
13842
13843	_viscousLimit: function (value, threshold) {
13844		return value - (value - threshold) * this._viscosity;
13845	},
13846
13847	_onPreDragLimit: function () {
13848		if (!this._viscosity || !this._offsetLimit) { return; }
13849
13850		var offset = this._draggable._newPos.subtract(this._draggable._startPos);
13851
13852		var limit = this._offsetLimit;
13853		if (offset.x < limit.min.x) { offset.x = this._viscousLimit(offset.x, limit.min.x); }
13854		if (offset.y < limit.min.y) { offset.y = this._viscousLimit(offset.y, limit.min.y); }
13855		if (offset.x > limit.max.x) { offset.x = this._viscousLimit(offset.x, limit.max.x); }
13856		if (offset.y > limit.max.y) { offset.y = this._viscousLimit(offset.y, limit.max.y); }
13857
13858		this._draggable._newPos = this._draggable._startPos.add(offset);
13859	},
13860
13861	_onPreDragWrap: function () {
13862		// TODO refactor to be able to adjust map pane position after zoom
13863		var worldWidth = this._worldWidth,
13864		    halfWidth = Math.round(worldWidth / 2),
13865		    dx = this._initialWorldOffset,
13866		    x = this._draggable._newPos.x,
13867		    newX1 = (x - halfWidth + dx) % worldWidth + halfWidth - dx,
13868		    newX2 = (x + halfWidth + dx) % worldWidth - halfWidth - dx,
13869		    newX = Math.abs(newX1 + dx) < Math.abs(newX2 + dx) ? newX1 : newX2;
13870
13871		this._draggable._absPos = this._draggable._newPos.clone();
13872		this._draggable._newPos.x = newX;
13873	},
13874
13875	_onDragEnd: function (e) {
13876		var map = this._map,
13877		    options = map.options,
13878
13879		    noInertia = !options.inertia || e.noInertia || this._times.length < 2;
13880
13881		map.fire('dragend', e);
13882
13883		if (noInertia) {
13884			map.fire('moveend');
13885
13886		} else {
13887			this._prunePositions(+new Date());
13888
13889			var direction = this._lastPos.subtract(this._positions[0]),
13890			    duration = (this._lastTime - this._times[0]) / 1000,
13891			    ease = options.easeLinearity,
13892
13893			    speedVector = direction.multiplyBy(ease / duration),
13894			    speed = speedVector.distanceTo([0, 0]),
13895
13896			    limitedSpeed = Math.min(options.inertiaMaxSpeed, speed),
13897			    limitedSpeedVector = speedVector.multiplyBy(limitedSpeed / speed),
13898
13899			    decelerationDuration = limitedSpeed / (options.inertiaDeceleration * ease),
13900			    offset = limitedSpeedVector.multiplyBy(-decelerationDuration / 2).round();
13901
13902			if (!offset.x && !offset.y) {
13903				map.fire('moveend');
13904
13905			} else {
13906				offset = map._limitOffset(offset, map.options.maxBounds);
13907
13908				requestAnimFrame(function () {
13909					map.panBy(offset, {
13910						duration: decelerationDuration,
13911						easeLinearity: ease,
13912						noMoveStart: true,
13913						animate: true
13914					});
13915				});
13916			}
13917		}
13918	}
13919});
13920
13921// @section Handlers
13922// @property dragging: Handler
13923// Map dragging handler (by both mouse and touch).
13924Map.addInitHook('addHandler', 'dragging', Drag);
13925
13926/*
13927 * L.Map.Keyboard is handling keyboard interaction with the map, enabled by default.
13928 */
13929
13930// @namespace Map
13931// @section Keyboard Navigation Options
13932Map.mergeOptions({
13933	// @option keyboard: Boolean = true
13934	// Makes the map focusable and allows users to navigate the map with keyboard
13935	// arrows and `+`/`-` keys.
13936	keyboard: true,
13937
13938	// @option keyboardPanDelta: Number = 80
13939	// Amount of pixels to pan when pressing an arrow key.
13940	keyboardPanDelta: 80
13941});
13942
13943var Keyboard = Handler.extend({
13944
13945	keyCodes: {
13946		left:    [37],
13947		right:   [39],
13948		down:    [40],
13949		up:      [38],
13950		zoomIn:  [187, 107, 61, 171],
13951		zoomOut: [189, 109, 54, 173]
13952	},
13953
13954	initialize: function (map) {
13955		this._map = map;
13956
13957		this._setPanDelta(map.options.keyboardPanDelta);
13958		this._setZoomDelta(map.options.zoomDelta);
13959	},
13960
13961	addHooks: function () {
13962		var container = this._map._container;
13963
13964		// make the container focusable by tabbing
13965		if (container.tabIndex <= 0) {
13966			container.tabIndex = '0';
13967		}
13968
13969		on(container, {
13970			focus: this._onFocus,
13971			blur: this._onBlur,
13972			mousedown: this._onMouseDown
13973		}, this);
13974
13975		this._map.on({
13976			focus: this._addHooks,
13977			blur: this._removeHooks
13978		}, this);
13979	},
13980
13981	removeHooks: function () {
13982		this._removeHooks();
13983
13984		off(this._map._container, {
13985			focus: this._onFocus,
13986			blur: this._onBlur,
13987			mousedown: this._onMouseDown
13988		}, this);
13989
13990		this._map.off({
13991			focus: this._addHooks,
13992			blur: this._removeHooks
13993		}, this);
13994	},
13995
13996	_onMouseDown: function () {
13997		if (this._focused) { return; }
13998
13999		var body = document.body,
14000		    docEl = document.documentElement,
14001		    top = body.scrollTop || docEl.scrollTop,
14002		    left = body.scrollLeft || docEl.scrollLeft;
14003
14004		this._map._container.focus();
14005
14006		window.scrollTo(left, top);
14007	},
14008
14009	_onFocus: function () {
14010		this._focused = true;
14011		this._map.fire('focus');
14012	},
14013
14014	_onBlur: function () {
14015		this._focused = false;
14016		this._map.fire('blur');
14017	},
14018
14019	_setPanDelta: function (panDelta) {
14020		var keys = this._panKeys = {},
14021		    codes = this.keyCodes,
14022		    i, len;
14023
14024		for (i = 0, len = codes.left.length; i < len; i++) {
14025			keys[codes.left[i]] = [-1 * panDelta, 0];
14026		}
14027		for (i = 0, len = codes.right.length; i < len; i++) {
14028			keys[codes.right[i]] = [panDelta, 0];
14029		}
14030		for (i = 0, len = codes.down.length; i < len; i++) {
14031			keys[codes.down[i]] = [0, panDelta];
14032		}
14033		for (i = 0, len = codes.up.length; i < len; i++) {
14034			keys[codes.up[i]] = [0, -1 * panDelta];
14035		}
14036	},
14037
14038	_setZoomDelta: function (zoomDelta) {
14039		var keys = this._zoomKeys = {},
14040		    codes = this.keyCodes,
14041		    i, len;
14042
14043		for (i = 0, len = codes.zoomIn.length; i < len; i++) {
14044			keys[codes.zoomIn[i]] = zoomDelta;
14045		}
14046		for (i = 0, len = codes.zoomOut.length; i < len; i++) {
14047			keys[codes.zoomOut[i]] = -zoomDelta;
14048		}
14049	},
14050
14051	_addHooks: function () {
14052		on(document, 'keydown', this._onKeyDown, this);
14053	},
14054
14055	_removeHooks: function () {
14056		off(document, 'keydown', this._onKeyDown, this);
14057	},
14058
14059	_onKeyDown: function (e) {
14060		if (e.altKey || e.ctrlKey || e.metaKey) { return; }
14061
14062		var key = e.keyCode,
14063		    map = this._map,
14064		    offset;
14065
14066		if (key in this._panKeys) {
14067			if (!map._panAnim || !map._panAnim._inProgress) {
14068				offset = this._panKeys[key];
14069				if (e.shiftKey) {
14070					offset = toPoint(offset).multiplyBy(3);
14071				}
14072
14073				if (map.options.maxBounds) {
14074					offset = map._limitOffset(toPoint(offset), map.options.maxBounds);
14075				}
14076
14077				if (map.options.worldCopyJump) {
14078					var newLatLng = map.wrapLatLng(map.unproject(map.project(map.getCenter()).add(offset)));
14079					map.panTo(newLatLng);
14080				} else {
14081					map.panBy(offset);
14082				}
14083			}
14084		} else if (key in this._zoomKeys) {
14085			map.setZoom(map.getZoom() + (e.shiftKey ? 3 : 1) * this._zoomKeys[key]);
14086
14087		} else if (key === 27 && map._popup && map._popup.options.closeOnEscapeKey) {
14088			map.closePopup();
14089
14090		} else {
14091			return;
14092		}
14093
14094		stop(e);
14095	}
14096});
14097
14098// @section Handlers
14099// @section Handlers
14100// @property keyboard: Handler
14101// Keyboard navigation handler.
14102Map.addInitHook('addHandler', 'keyboard', Keyboard);
14103
14104/*
14105 * L.Handler.ScrollWheelZoom is used by L.Map to enable mouse scroll wheel zoom on the map.
14106 */
14107
14108// @namespace Map
14109// @section Interaction Options
14110Map.mergeOptions({
14111	// @section Mouse wheel options
14112	// @option scrollWheelZoom: Boolean|String = true
14113	// Whether the map can be zoomed by using the mouse wheel. If passed `'center'`,
14114	// it will zoom to the center of the view regardless of where the mouse was.
14115	scrollWheelZoom: true,
14116
14117	// @option wheelDebounceTime: Number = 40
14118	// Limits the rate at which a wheel can fire (in milliseconds). By default
14119	// user can't zoom via wheel more often than once per 40 ms.
14120	wheelDebounceTime: 40,
14121
14122	// @option wheelPxPerZoomLevel: Number = 60
14123	// How many scroll pixels (as reported by [L.DomEvent.getWheelDelta](#domevent-getwheeldelta))
14124	// mean a change of one full zoom level. Smaller values will make wheel-zooming
14125	// faster (and vice versa).
14126	wheelPxPerZoomLevel: 60
14127});
14128
14129var ScrollWheelZoom = Handler.extend({
14130	addHooks: function () {
14131		on(this._map._container, 'wheel', this._onWheelScroll, this);
14132
14133		this._delta = 0;
14134	},
14135
14136	removeHooks: function () {
14137		off(this._map._container, 'wheel', this._onWheelScroll, this);
14138	},
14139
14140	_onWheelScroll: function (e) {
14141		var delta = getWheelDelta(e);
14142
14143		var debounce = this._map.options.wheelDebounceTime;
14144
14145		this._delta += delta;
14146		this._lastMousePos = this._map.mouseEventToContainerPoint(e);
14147
14148		if (!this._startTime) {
14149			this._startTime = +new Date();
14150		}
14151
14152		var left = Math.max(debounce - (+new Date() - this._startTime), 0);
14153
14154		clearTimeout(this._timer);
14155		this._timer = setTimeout(bind(this._performZoom, this), left);
14156
14157		stop(e);
14158	},
14159
14160	_performZoom: function () {
14161		var map = this._map,
14162		    zoom = map.getZoom(),
14163		    snap = this._map.options.zoomSnap || 0;
14164
14165		map._stop(); // stop panning and fly animations if any
14166
14167		// map the delta with a sigmoid function to -4..4 range leaning on -1..1
14168		var d2 = this._delta / (this._map.options.wheelPxPerZoomLevel * 4),
14169		    d3 = 4 * Math.log(2 / (1 + Math.exp(-Math.abs(d2)))) / Math.LN2,
14170		    d4 = snap ? Math.ceil(d3 / snap) * snap : d3,
14171		    delta = map._limitZoom(zoom + (this._delta > 0 ? d4 : -d4)) - zoom;
14172
14173		this._delta = 0;
14174		this._startTime = null;
14175
14176		if (!delta) { return; }
14177
14178		if (map.options.scrollWheelZoom === 'center') {
14179			map.setZoom(zoom + delta);
14180		} else {
14181			map.setZoomAround(this._lastMousePos, zoom + delta);
14182		}
14183	}
14184});
14185
14186// @section Handlers
14187// @property scrollWheelZoom: Handler
14188// Scroll wheel zoom handler.
14189Map.addInitHook('addHandler', 'scrollWheelZoom', ScrollWheelZoom);
14190
14191/*
14192 * L.Map.TapHold is used to simulate `contextmenu` event on long hold,
14193 * which otherwise is not fired by mobile Safari.
14194 */
14195
14196var tapHoldDelay = 600;
14197
14198// @namespace Map
14199// @section Interaction Options
14200Map.mergeOptions({
14201	// @section Touch interaction options
14202	// @option tapHold: Boolean
14203	// Enables simulation of `contextmenu` event, default is `true` for mobile Safari.
14204	tapHold: Browser.touchNative && Browser.safari && Browser.mobile,
14205
14206	// @option tapTolerance: Number = 15
14207	// The max number of pixels a user can shift his finger during touch
14208	// for it to be considered a valid tap.
14209	tapTolerance: 15
14210});
14211
14212var TapHold = Handler.extend({
14213	addHooks: function () {
14214		on(this._map._container, 'touchstart', this._onDown, this);
14215	},
14216
14217	removeHooks: function () {
14218		off(this._map._container, 'touchstart', this._onDown, this);
14219	},
14220
14221	_onDown: function (e) {
14222		clearTimeout(this._holdTimeout);
14223		if (e.touches.length !== 1) { return; }
14224
14225		var first = e.touches[0];
14226		this._startPos = this._newPos = new Point(first.clientX, first.clientY);
14227
14228		this._holdTimeout = setTimeout(bind(function () {
14229			this._cancel();
14230			if (!this._isTapValid()) { return; }
14231
14232			// prevent simulated mouse events https://w3c.github.io/touch-events/#mouse-events
14233			on(document, 'touchend', preventDefault);
14234			on(document, 'touchend touchcancel', this._cancelClickPrevent);
14235			this._simulateEvent('contextmenu', first);
14236		}, this), tapHoldDelay);
14237
14238		on(document, 'touchend touchcancel contextmenu', this._cancel, this);
14239		on(document, 'touchmove', this._onMove, this);
14240	},
14241
14242	_cancelClickPrevent: function cancelClickPrevent() {
14243		off(document, 'touchend', preventDefault);
14244		off(document, 'touchend touchcancel', cancelClickPrevent);
14245	},
14246
14247	_cancel: function () {
14248		clearTimeout(this._holdTimeout);
14249		off(document, 'touchend touchcancel contextmenu', this._cancel, this);
14250		off(document, 'touchmove', this._onMove, this);
14251	},
14252
14253	_onMove: function (e) {
14254		var first = e.touches[0];
14255		this._newPos = new Point(first.clientX, first.clientY);
14256	},
14257
14258	_isTapValid: function () {
14259		return this._newPos.distanceTo(this._startPos) <= this._map.options.tapTolerance;
14260	},
14261
14262	_simulateEvent: function (type, e) {
14263		var simulatedEvent = new MouseEvent(type, {
14264			bubbles: true,
14265			cancelable: true,
14266			view: window,
14267			// detail: 1,
14268			screenX: e.screenX,
14269			screenY: e.screenY,
14270			clientX: e.clientX,
14271			clientY: e.clientY,
14272			// button: 2,
14273			// buttons: 2
14274		});
14275
14276		simulatedEvent._simulated = true;
14277
14278		e.target.dispatchEvent(simulatedEvent);
14279	}
14280});
14281
14282// @section Handlers
14283// @property tapHold: Handler
14284// Long tap handler to simulate `contextmenu` event (useful in mobile Safari).
14285Map.addInitHook('addHandler', 'tapHold', TapHold);
14286
14287/*
14288 * L.Handler.TouchZoom is used by L.Map to add pinch zoom on supported mobile browsers.
14289 */
14290
14291// @namespace Map
14292// @section Interaction Options
14293Map.mergeOptions({
14294	// @section Touch interaction options
14295	// @option touchZoom: Boolean|String = *
14296	// Whether the map can be zoomed by touch-dragging with two fingers. If
14297	// passed `'center'`, it will zoom to the center of the view regardless of
14298	// where the touch events (fingers) were. Enabled for touch-capable web
14299	// browsers.
14300	touchZoom: Browser.touch,
14301
14302	// @option bounceAtZoomLimits: Boolean = true
14303	// Set it to false if you don't want the map to zoom beyond min/max zoom
14304	// and then bounce back when pinch-zooming.
14305	bounceAtZoomLimits: true
14306});
14307
14308var TouchZoom = Handler.extend({
14309	addHooks: function () {
14310		addClass(this._map._container, 'leaflet-touch-zoom');
14311		on(this._map._container, 'touchstart', this._onTouchStart, this);
14312	},
14313
14314	removeHooks: function () {
14315		removeClass(this._map._container, 'leaflet-touch-zoom');
14316		off(this._map._container, 'touchstart', this._onTouchStart, this);
14317	},
14318
14319	_onTouchStart: function (e) {
14320		var map = this._map;
14321		if (!e.touches || e.touches.length !== 2 || map._animatingZoom || this._zooming) { return; }
14322
14323		var p1 = map.mouseEventToContainerPoint(e.touches[0]),
14324		    p2 = map.mouseEventToContainerPoint(e.touches[1]);
14325
14326		this._centerPoint = map.getSize()._divideBy(2);
14327		this._startLatLng = map.containerPointToLatLng(this._centerPoint);
14328		if (map.options.touchZoom !== 'center') {
14329			this._pinchStartLatLng = map.containerPointToLatLng(p1.add(p2)._divideBy(2));
14330		}
14331
14332		this._startDist = p1.distanceTo(p2);
14333		this._startZoom = map.getZoom();
14334
14335		this._moved = false;
14336		this._zooming = true;
14337
14338		map._stop();
14339
14340		on(document, 'touchmove', this._onTouchMove, this);
14341		on(document, 'touchend touchcancel', this._onTouchEnd, this);
14342
14343		preventDefault(e);
14344	},
14345
14346	_onTouchMove: function (e) {
14347		if (!e.touches || e.touches.length !== 2 || !this._zooming) { return; }
14348
14349		var map = this._map,
14350		    p1 = map.mouseEventToContainerPoint(e.touches[0]),
14351		    p2 = map.mouseEventToContainerPoint(e.touches[1]),
14352		    scale = p1.distanceTo(p2) / this._startDist;
14353
14354		this._zoom = map.getScaleZoom(scale, this._startZoom);
14355
14356		if (!map.options.bounceAtZoomLimits && (
14357			(this._zoom < map.getMinZoom() && scale < 1) ||
14358			(this._zoom > map.getMaxZoom() && scale > 1))) {
14359			this._zoom = map._limitZoom(this._zoom);
14360		}
14361
14362		if (map.options.touchZoom === 'center') {
14363			this._center = this._startLatLng;
14364			if (scale === 1) { return; }
14365		} else {
14366			// Get delta from pinch to center, so centerLatLng is delta applied to initial pinchLatLng
14367			var delta = p1._add(p2)._divideBy(2)._subtract(this._centerPoint);
14368			if (scale === 1 && delta.x === 0 && delta.y === 0) { return; }
14369			this._center = map.unproject(map.project(this._pinchStartLatLng, this._zoom).subtract(delta), this._zoom);
14370		}
14371
14372		if (!this._moved) {
14373			map._moveStart(true, false);
14374			this._moved = true;
14375		}
14376
14377		cancelAnimFrame(this._animRequest);
14378
14379		var moveFn = bind(map._move, map, this._center, this._zoom, {pinch: true, round: false}, undefined);
14380		this._animRequest = requestAnimFrame(moveFn, this, true);
14381
14382		preventDefault(e);
14383	},
14384
14385	_onTouchEnd: function () {
14386		if (!this._moved || !this._zooming) {
14387			this._zooming = false;
14388			return;
14389		}
14390
14391		this._zooming = false;
14392		cancelAnimFrame(this._animRequest);
14393
14394		off(document, 'touchmove', this._onTouchMove, this);
14395		off(document, 'touchend touchcancel', this._onTouchEnd, this);
14396
14397		// Pinch updates GridLayers' levels only when zoomSnap is off, so zoomSnap becomes noUpdate.
14398		if (this._map.options.zoomAnimation) {
14399			this._map._animateZoom(this._center, this._map._limitZoom(this._zoom), true, this._map.options.zoomSnap);
14400		} else {
14401			this._map._resetView(this._center, this._map._limitZoom(this._zoom));
14402		}
14403	}
14404});
14405
14406// @section Handlers
14407// @property touchZoom: Handler
14408// Touch zoom handler.
14409Map.addInitHook('addHandler', 'touchZoom', TouchZoom);
14410
14411Map.BoxZoom = BoxZoom;
14412Map.DoubleClickZoom = DoubleClickZoom;
14413Map.Drag = Drag;
14414Map.Keyboard = Keyboard;
14415Map.ScrollWheelZoom = ScrollWheelZoom;
14416Map.TapHold = TapHold;
14417Map.TouchZoom = TouchZoom;
14418
14419export { Bounds, Browser, CRS, Canvas, Circle, CircleMarker, Class, Control, DivIcon, DivOverlay, DomEvent, DomUtil, Draggable, Evented, FeatureGroup, GeoJSON, GridLayer, Handler, Icon, ImageOverlay, LatLng, LatLngBounds, Layer, LayerGroup, LineUtil, Map, Marker, Mixin, Path, Point, PolyUtil, Polygon, Polyline, Popup, PosAnimation, index as Projection, Rectangle, Renderer, SVG, SVGOverlay, TileLayer, Tooltip, Transformation, Util, VideoOverlay, bind, toBounds as bounds, canvas, circle, circleMarker, control, divIcon, extend, featureGroup, geoJSON, geoJson, gridLayer, icon, imageOverlay, toLatLng as latLng, toLatLngBounds as latLngBounds, layerGroup, createMap as map, marker, toPoint as point, polygon, polyline, popup, rectangle, setOptions, stamp, svg, svgOverlay, tileLayer, tooltip, toTransformation as transformation, version, videoOverlay };
14420//
14420

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.