PageSourceSearch

https://tambali.ai/assets/leaflet-424e93649b696f9626e95e65f182709c5c7b40380d61cd558adc53f073ed2896.js

js tambali.ai collected 2026-10-05 14:24:14 UTC 415,596 bytes, 14,419 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,750 bytes, lines 9079-10119
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			node.innerHTML = content;
10029		} else {
10030			while (node.hasChildNodes()) {
10031				node.removeChild(node.firstChild);
10032			}
10033			node.appendChild(content);
10034		}
10035
10036		// @namespace DivOverlay
10037		// @section DivOverlay events
10038		// @event contentupdate: Event
10039		// Fired when the content of the overlay is updated
10040		this.fire('contentupdate');
10041	},
10042
10043	_updatePosition: function () {
10044		if (!this._map) { return; }
10045
10046		var pos = this._map.latLngToLayerPoint(this._latlng),
10047		    offset = toPoint(this.options.offset),
10048		    anchor = this._getAnchor();
10049
10050		if (this._zoomAnimated) {
10051			setPosition(this._container, pos.add(anchor));
10052		} else {
10053			offset = offset.add(pos).add(anchor);
10054		}
10055
10056		var bottom = this._containerBottom = -offset.y,
10057		    left = this._containerLeft = -Math.round(this._containerWidth / 2) + offset.x;
10058
10059		// bottom position the overlay in case the height of the overlay changes (images loading etc)
10060		this._container.style.bottom = bottom + 'px';
10061		this._container.style.left = left + 'px';
10062	},
10063
10064	_getAnchor: function () {
10065		return [0, 0];
10066	}
10067
10068});
10069
10070Map.include({
10071	_initOverlay: function (OverlayClass, content, latlng, options) {
10072		var overlay = content;
10073		if (!(overlay instanceof OverlayClass)) {
10074			overlay = new OverlayClass(options).setContent(content);
10075		}
10076		if (latlng) {
10077			overlay.setLatLng(latlng);
10078		}
10079		return overlay;
10080	}
10081});
10082
10083
10084Layer.include({
10085	_initOverlay: function (OverlayClass, old, content, options) {
10086		var overlay = content;
10087		if (overlay instanceof OverlayClass) {
10088			setOptions(overlay, options);
10089			overlay._source = this;
10090		} else {
10091			overlay = (old && !options) ? old : new OverlayClass(options, this);
10092			overlay.setContent(content);
10093		}
10094		return overlay;
10095	}
10096});
10097
10098/*
10099 * @class Popup
10100 * @inherits DivOverlay
10101 * @aka L.Popup
10102 * Used to open popups in certain places of the map. Use [Map.openPopup](#map-openpopup) to
10103 * open popups while making sure that only one popup is open at one time
10104 * (recommended for usability), or use [Map.addLayer](#map-addlayer) to open as many as you want.
10105 *
10106 * @example
10107 *
10108 * If you want to just bind a popup to marker click and then open it, it's really easy:
10109 *
10110 * ```js
10111 * marker.bindPopup(popupContent).openPopup();
10112 * ```
10113 * Path overlays like polylines also have a `bindPopup` method.
10114 *
10115 * A popup can be also standalone:
10116 *
10117 * ```js
10118 * var popup = L.popup()
10119 * 	.setLatLng(latlng)
10120 * 	.setContent('<p>Hello world!<br />This is a nice popup.</p>')
10121 * 	.openOn(map);
10122 * ```
10123 * or
10124 * ```js
10125 * var popup = L.popup(latlng, {content: '<p>Hello world!<br />This is a nice popup.</p>')
10126 * 	.openOn(map);
10127 * ```
10128 */
10129
10130
10131// @namespace Popup
10132var Popup = DivOverlay.extend({
10133
10134	// @section
10135	// @aka Popup options
10136	options: {
10137		// @option pane: String = 'popupPane'
10138		// `Map pane` where the popup will be added.
10139		pane: 'popupPane',
10140
10141		// @option offset: Point = Point(0, 7)
10142		// The offset of the popup position.
10143		offset: [0, 7],
10144
10145		// @option maxWidth: Number = 300
10146		// Max width of the popup, in pixels.
10147		maxWidth: 300,
10148
10149		// @option minWidth: Number = 50
10150		// Min width of the popup, in pixels.
10151		minWidth: 50,
10152
10153		// @option maxHeight: Number = null
10154		// If set, creates a scrollable container of the given height
10155		// inside a popup if its content exceeds it.
10156		// The scrollable container can be styled using the
10157		// `leaflet-popup-scrolled` CSS class selector.
10158		maxHeight: null,
10159
10160		// @option autoPan: Boolean = true
10161		// Set it to `false` if you don't want the map to do panning animation
10162		// to fit the opened popup.
10163		autoPan: true,
10164
10165		// @option autoPanPaddingTopLeft: Point = null
10166		// The margin between the popup and the top left corner of the map
10167		// view after autopanning was performed.
10168		autoPanPaddingTopLeft: null,
10169
10170		// @option autoPanPaddingBottomRight: Point = null
10171		// The margin between the popup and the bottom right corner of the map
10172		// view after autopanning was performed.
10173		autoPanPaddingBottomRight: null,
10174
10175		// @option autoPanPadding: Point = Point(5, 5)
10176		// Equivalent of setting both top left and bottom right autopan padding to the same value.
10177		autoPanPadding: [5, 5],
10178
10179		// @option keepInView: Boolean = false
10180		// Set it to `true` if you want to prevent users from panning the popup
10181		// off of the screen while it is open.
10182		keepInView: false,
10183
10184		// @option closeButton: Boolean = true
10185		// Controls the presence of a close button in the popup.
10186		closeButton: true,
10187
10188		// @option autoClose: Boolean = true
10189		// Set it to `false` if you want to override the default behavior of
10190		// the popup closing when another popup is opened.
10191		autoClose: true,
10192
10193		// @option closeOnEscapeKey: Boolean = true
10194		// Set it to `false` if you want to override the default behavior of
10195		// the ESC key for closing of the popup.
10196		closeOnEscapeKey: true,
10197
10198		// @option closeOnClick: Boolean = *
10199		// Set it if you want to override the default behavior of the popup closing when user clicks
10200		// on the map. Defaults to the map's [`closePopupOnClick`](#map-closepopuponclick) option.
10201
10202		// @option className: String = ''
10203		// A custom CSS class name to assign to the popup.
10204		className: ''
10205	},
10206
10207	// @namespace Popup
10208	// @method openOn(map: Map): this
10209	// Alternative to `map.openPopup(popup)`.
10210	// Adds the popup to the map and closes the previous one.
10211	openOn: function (map) {
10212		map = arguments.length ? map : this._source._map; // experimental, not the part of public api
10213
10214		if (!map.hasLayer(this) && map._popup && map._popup.options.autoClose) {
10215			map.removeLayer(map._popup);
10216		}
10217		map._popup = this;
10218
10219		return DivOverlay.prototype.openOn.call(this, map);
10220	},
10221
10222	onAdd: function (map) {
10223		DivOverlay.prototype.onAdd.call(this, map);
10224
10225		// @namespace Map
10226		// @section Popup events
10227		// @event popupopen: PopupEvent
10228		// Fired when a popup is opened in the map
10229		map.fire('popupopen', {popup: this});
10230
10231		if (this._source) {
10232			// @namespace Layer
10233			// @section Popup events
10234			// @event popupopen: PopupEvent
10235			// Fired when a popup bound to this layer is opened
10236			this._source.fire('popupopen', {popup: this}, true);
10237			// For non-path layers, we toggle the popup when clicking
10238			// again the layer, so prevent the map to reopen it.
10239			if (!(this._source instanceof Path)) {
10240				this._source.on('preclick', stopPropagation);
10241			}
10242		}
10243	},
10244
10245	onRemove: function (map) {
10246		DivOverlay.prototype.onRemove.call(this, map);
10247
10248		// @namespace Map
10249		// @section Popup events
10250		// @event popupclose: PopupEvent
10251		// Fired when a popup in the map is closed
10252		map.fire('popupclose', {popup: this});
10253
10254		if (this._source) {
10255			// @namespace Layer
10256			// @section Popup events
10257			// @event popupclose: PopupEvent
10258			// Fired when a popup bound to this layer is closed
10259			this._source.fire('popupclose', {popup: this}, true);
10260			if (!(this._source instanceof Path)) {
10261				this._source.off('preclick', stopPropagation);
10262			}
10263		}
10264	},
10265
10266	getEvents: function () {
10267		var events = DivOverlay.prototype.getEvents.call(this);
10268
10269		if (this.options.closeOnClick !== undefined ? this.options.closeOnClick : this._map.options.closePopupOnClick) {
10270			events.preclick = this.close;
10271		}
10272
10273		if (this.options.keepInView) {
10274			events.moveend = this._adjustPan;
10275		}
10276
10277		return events;
10278	},
10279
10280	_initLayout: function () {
10281		var prefix = 'leaflet-popup',
10282		    container = this._container = create$1('div',
10283			prefix + ' ' + (this.options.className || '') +
10284			' leaflet-zoom-animated');
10285
10286		var wrapper = this._wrapper = create$1('div', prefix + '-content-wrapper', container);
10287		this._contentNode = create$1('div', prefix + '-content', wrapper);
10288
10289		disableClickPropagation(container);
10290		disableScrollPropagation(this._contentNode);
10291		on(container, 'contextmenu', stopPropagation);
10292
10293		this._tipContainer = create$1('div', prefix + '-tip-container', container);
10294		this._tip = create$1('div', prefix + '-tip', this._tipContainer);
10295
10296		if (this.options.closeButton) {
10297			var closeButton = this._closeButton = create$1('a', prefix + '-close-button', container);
10298			closeButton.setAttribute('role', 'button'); // overrides the implicit role=link of <a> elements #7399
10299			closeButton.setAttribute('aria-label', 'Close popup');
10300			closeButton.href = '#close';
10301			closeButton.innerHTML = '<span aria-hidden="true">&#215;</span>';
10302
10303			on(closeButton, 'click', function (ev) {
10304				preventDefault(ev);
10305				this.close();
10306			}, this);
10307		}
10308	},
10309
10310	_updateLayout: function () {
10311		var container = this._contentNode,
10312		    style = container.style;
10313
10314		style.width = '';
10315		style.whiteSpace = 'nowrap';
10316
10317		var width = container.offsetWidth;
10318		width = Math.min(width, this.options.maxWidth);
10319		width = Math.max(width, this.options.minWidth);
10320
10321		style.width = (width + 1) + 'px';
10322		style.whiteSpace = '';
10323
10324		style.height = '';
10325
10326		var height = container.offsetHeight,
10327		    maxHeight = this.options.maxHeight,
10328		    scrolledClass = 'leaflet-popup-scrolled';
10329
10330		if (maxHeight && height > maxHeight) {
10331			style.height = maxHeight + 'px';
10332			addClass(container, scrolledClass);
10333		} else {
10334			removeClass(container, scrolledClass);
10335		}
10336
10337		this._containerWidth = this._container.offsetWidth;
10338	},
10339
10340	_animateZoom: function (e) {
10341		var pos = this._map._latLngToNewLayerPoint(this._latlng, e.zoom, e.center),
10342		    anchor = this._getAnchor();
10343		setPosition(this._container, pos.add(anchor));
10344	},
10345
10346	_adjustPan: function () {
10347		if (!this.options.autoPan) { return; }
10348		if (this._map._panAnim) { this._map._panAnim.stop(); }
10349
10350		// We can endlessly recurse if keepInView is set and the view resets.
10351		// Let's guard against that by exiting early if we're responding to our own autopan.
10352		if (this._autopanning) {
10353			this._autopanning = false;
vendor: 7,050 bytes, lines 10354-10593
10354			return;
10355		}
10356
10357		var map = this._map,
10358		    marginBottom = parseInt(getStyle(this._container, 'marginBottom'), 10) || 0,
10359		    containerHeight = this._container.offsetHeight + marginBottom,
10360		    containerWidth = this._containerWidth,
10361		    layerPos = new Point(this._containerLeft, -containerHeight - this._containerBottom);
10362
10363		layerPos._add(getPosition(this._container));
10364
10365		var containerPos = map.layerPointToContainerPoint(layerPos),
10366		    padding = toPoint(this.options.autoPanPadding),
10367		    paddingTL = toPoint(this.options.autoPanPaddingTopLeft || padding),
10368		    paddingBR = toPoint(this.options.autoPanPaddingBottomRight || padding),
10369		    size = map.getSize(),
10370		    dx = 0,
10371		    dy = 0;
10372
10373		if (containerPos.x + containerWidth + paddingBR.x > size.x) { // right
10374			dx = containerPos.x + containerWidth - size.x + paddingBR.x;
10375		}
10376		if (containerPos.x - dx - paddingTL.x < 0) { // left
10377			dx = containerPos.x - paddingTL.x;
10378		}
10379		if (containerPos.y + containerHeight + paddingBR.y > size.y) { // bottom
10380			dy = containerPos.y + containerHeight - size.y + paddingBR.y;
10381		}
10382		if (containerPos.y - dy - paddingTL.y < 0) { // top
10383			dy = containerPos.y - paddingTL.y;
10384		}
10385
10386		// @namespace Map
10387		// @section Popup events
10388		// @event autopanstart: Event
10389		// Fired when the map starts autopanning when opening a popup.
10390		if (dx || dy) {
10391			// Track that we're autopanning, as this function will be re-ran on moveend
10392			if (this.options.keepInView) {
10393				this._autopanning = true;
10394			}
10395
10396			map
10397			    .fire('autopanstart')
10398			    .panBy([dx, dy]);
10399		}
10400	},
10401
10402	_getAnchor: function () {
10403		// Where should we anchor the popup on the source layer?
10404		return toPoint(this._source && this._source._getPopupAnchor ? this._source._getPopupAnchor() : [0, 0]);
10405	}
10406
10407});
10408
10409// @namespace Popup
10410// @factory L.popup(options?: Popup options, source?: Layer)
10411// 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.
10412// @alternative
10413// @factory L.popup(latlng: LatLng, options?: Popup options)
10414// Instantiates a `Popup` object given `latlng` where the popup will open and an optional `options` object that describes its appearance and location.
10415var popup = function (options, source) {
10416	return new Popup(options, source);
10417};
10418
10419
10420/* @namespace Map
10421 * @section Interaction Options
10422 * @option closePopupOnClick: Boolean = true
10423 * Set it to `false` if you don't want popups to close when user clicks the map.
10424 */
10425Map.mergeOptions({
10426	closePopupOnClick: true
10427});
10428
10429
10430// @namespace Map
10431// @section Methods for Layers and Controls
10432Map.include({
10433	// @method openPopup(popup: Popup): this
10434	// Opens the specified popup while closing the previously opened (to make sure only one is opened at one time for usability).
10435	// @alternative
10436	// @method openPopup(content: String|HTMLElement, latlng: LatLng, options?: Popup options): this
10437	// Creates a popup with the specified content and options and opens it in the given point on a map.
10438	openPopup: function (popup, latlng, options) {
10439		this._initOverlay(Popup, popup, latlng, options)
10440		  .openOn(this);
10441
10442		return this;
10443	},
10444
10445	// @method closePopup(popup?: Popup): this
10446	// Closes the popup previously opened with [openPopup](#map-openpopup) (or the given one).
10447	closePopup: function (popup) {
10448		popup = arguments.length ? popup : this._popup;
10449		if (popup) {
10450			popup.close();
10451		}
10452		return this;
10453	}
10454});
10455
10456/*
10457 * @namespace Layer
10458 * @section Popup methods example
10459 *
10460 * All layers share a set of methods convenient for binding popups to it.
10461 *
10462 * ```js
10463 * var layer = L.Polygon(latlngs).bindPopup('Hi There!').addTo(map);
10464 * layer.openPopup();
10465 * layer.closePopup();
10466 * ```
10467 *
10468 * 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.
10469 */
10470
10471// @section Popup methods
10472Layer.include({
10473
10474	// @method bindPopup(content: String|HTMLElement|Function|Popup, options?: Popup options): this
10475	// Binds a popup to the layer with the passed `content` and sets up the
10476	// necessary event listeners. If a `Function` is passed it will receive
10477	// the layer as the first argument and should return a `String` or `HTMLElement`.
10478	bindPopup: function (content, options) {
10479		this._popup = this._initOverlay(Popup, this._popup, content, options);
10480		if (!this._popupHandlersAdded) {
10481			this.on({
10482				click: this._openPopup,
10483				keypress: this._onKeyPress,
10484				remove: this.closePopup,
10485				move: this._movePopup
10486			});
10487			this._popupHandlersAdded = true;
10488		}
10489
10490		return this;
10491	},
10492
10493	// @method unbindPopup(): this
10494	// Removes the popup previously bound with `bindPopup`.
10495	unbindPopup: function () {
10496		if (this._popup) {
10497			this.off({
10498				click: this._openPopup,
10499				keypress: this._onKeyPress,
10500				remove: this.closePopup,
10501				move: this._movePopup
10502			});
10503			this._popupHandlersAdded = false;
10504			this._popup = null;
10505		}
10506		return this;
10507	},
10508
10509	// @method openPopup(latlng?: LatLng): this
10510	// Opens the bound popup at the specified `latlng` or at the default popup anchor if no `latlng` is passed.
10511	openPopup: function (latlng) {
10512		if (this._popup) {
10513			if (!(this instanceof FeatureGroup)) {
10514				this._popup._source = this;
10515			}
10516			if (this._popup._prepareOpen(latlng || this._latlng)) {
10517				// open the popup on the map
10518				this._popup.openOn(this._map);
10519			}
10520		}
10521		return this;
10522	},
10523
10524	// @method closePopup(): this
10525	// Closes the popup bound to this layer if it is open.
10526	closePopup: function () {
10527		if (this._popup) {
10528			this._popup.close();
10529		}
10530		return this;
10531	},
10532
10533	// @method togglePopup(): this
10534	// Opens or closes the popup bound to this layer depending on its current state.
10535	togglePopup: function () {
10536		if (this._popup) {
10537			this._popup.toggle(this);
10538		}
10539		return this;
10540	},
10541
10542	// @method isPopupOpen(): boolean
10543	// Returns `true` if the popup bound to this layer is currently open.
10544	isPopupOpen: function () {
10545		return (this._popup ? this._popup.isOpen() : false);
10546	},
10547
10548	// @method setPopupContent(content: String|HTMLElement|Popup): this
10549	// Sets the content of the popup bound to this layer.
10550	setPopupContent: function (content) {
10551		if (this._popup) {
10552			this._popup.setContent(content);
10553		}
10554		return this;
10555	},
10556
10557	// @method getPopup(): Popup
10558	// Returns the popup bound to this layer.
10559	getPopup: function () {
10560		return this._popup;
10561	},
10562
10563	_openPopup: function (e) {
10564		if (!this._popup || !this._map) {
10565			return;
10566		}
10567		// prevent map click
10568		stop(e);
10569
10570		var target = e.layer || e.target;
10571		if (this._popup._source === target && !(target instanceof Path)) {
10572			// treat it like a marker and figure out
10573			// if we should toggle it open/closed
10574			if (this._map.hasLayer(this._popup)) {
10575				this.closePopup();
10576			} else {
10577				this.openPopup(e.latlng);
10578			}
10579			return;
10580		}
10581		this._popup._source = target;
10582		this.openPopup(e.latlng);
10583	},
10584
10585	_movePopup: function (e) {
10586		this._popup.setLatLng(e.latlng);
10587	},
10588
10589	_onKeyPress: function (e) {
10590		if (e.originalEvent.keyCode === 13) {
10591			this._openPopup(e);
10592		}
10593	}
vendor: 39,520 bytes, lines 10593-12021
10593
10594});
10595
10596/*
10597 * @class Tooltip
10598 * @inherits DivOverlay
10599 * @aka L.Tooltip
10600 * Used to display small texts on top of map layers.
10601 *
10602 * @example
10603 * If you want to just bind a tooltip to marker:
10604 *
10605 * ```js
10606 * marker.bindTooltip("my tooltip text").openTooltip();
10607 * ```
10608 * Path overlays like polylines also have a `bindTooltip` method.
10609 *
10610 * A tooltip can be also standalone:
10611 *
10612 * ```js
10613 * var tooltip = L.tooltip()
10614 * 	.setLatLng(latlng)
10615 * 	.setContent('Hello world!<br />This is a nice tooltip.')
10616 * 	.addTo(map);
10617 * ```
10618 * or
10619 * ```js
10620 * var tooltip = L.tooltip(latlng, {content: 'Hello world!<br />This is a nice tooltip.'})
10621 * 	.addTo(map);
10622 * ```
10623 *
10624 *
10625 * Note about tooltip offset. Leaflet takes two options in consideration
10626 * for computing tooltip offsetting:
10627 * - the `offset` Tooltip option: it defaults to [0, 0], and it's specific to one tooltip.
10628 *   Add a positive x offset to move the tooltip to the right, and a positive y offset to
10629 *   move it to the bottom. Negatives will move to the left and top.
10630 * - the `tooltipAnchor` Icon option: this will only be considered for Marker. You
10631 *   should adapt this value if you use a custom icon.
10632 */
10633
10634
10635// @namespace Tooltip
10636var Tooltip = DivOverlay.extend({
10637
10638	// @section
10639	// @aka Tooltip options
10640	options: {
10641		// @option pane: String = 'tooltipPane'
10642		// `Map pane` where the tooltip will be added.
10643		pane: 'tooltipPane',
10644
10645		// @option offset: Point = Point(0, 0)
10646		// Optional offset of the tooltip position.
10647		offset: [0, 0],
10648
10649		// @option direction: String = 'auto'
10650		// Direction where to open the tooltip. Possible values are: `right`, `left`,
10651		// `top`, `bottom`, `center`, `auto`.
10652		// `auto` will dynamically switch between `right` and `left` according to the tooltip
10653		// position on the map.
10654		direction: 'auto',
10655
10656		// @option permanent: Boolean = false
10657		// Whether to open the tooltip permanently or only on mouseover.
10658		permanent: false,
10659
10660		// @option sticky: Boolean = false
10661		// If true, the tooltip will follow the mouse instead of being fixed at the feature center.
10662		sticky: false,
10663
10664		// @option opacity: Number = 0.9
10665		// Tooltip container opacity.
10666		opacity: 0.9
10667	},
10668
10669	onAdd: function (map) {
10670		DivOverlay.prototype.onAdd.call(this, map);
10671		this.setOpacity(this.options.opacity);
10672
10673		// @namespace Map
10674		// @section Tooltip events
10675		// @event tooltipopen: TooltipEvent
10676		// Fired when a tooltip is opened in the map.
10677		map.fire('tooltipopen', {tooltip: this});
10678
10679		if (this._source) {
10680			this.addEventParent(this._source);
10681
10682			// @namespace Layer
10683			// @section Tooltip events
10684			// @event tooltipopen: TooltipEvent
10685			// Fired when a tooltip bound to this layer is opened.
10686			this._source.fire('tooltipopen', {tooltip: this}, true);
10687		}
10688	},
10689
10690	onRemove: function (map) {
10691		DivOverlay.prototype.onRemove.call(this, map);
10692
10693		// @namespace Map
10694		// @section Tooltip events
10695		// @event tooltipclose: TooltipEvent
10696		// Fired when a tooltip in the map is closed.
10697		map.fire('tooltipclose', {tooltip: this});
10698
10699		if (this._source) {
10700			this.removeEventParent(this._source);
10701
10702			// @namespace Layer
10703			// @section Tooltip events
10704			// @event tooltipclose: TooltipEvent
10705			// Fired when a tooltip bound to this layer is closed.
10706			this._source.fire('tooltipclose', {tooltip: this}, true);
10707		}
10708	},
10709
10710	getEvents: function () {
10711		var events = DivOverlay.prototype.getEvents.call(this);
10712
10713		if (!this.options.permanent) {
10714			events.preclick = this.close;
10715		}
10716
10717		return events;
10718	},
10719
10720	_initLayout: function () {
10721		var prefix = 'leaflet-tooltip',
10722		    className = prefix + ' ' + (this.options.className || '') + ' leaflet-zoom-' + (this._zoomAnimated ? 'animated' : 'hide');
10723
10724		this._contentNode = this._container = create$1('div', className);
10725
10726		this._container.setAttribute('role', 'tooltip');
10727		this._container.setAttribute('id', 'leaflet-tooltip-' + stamp(this));
10728	},
10729
10730	_updateLayout: function () {},
10731
10732	_adjustPan: function () {},
10733
10734	_setPosition: function (pos) {
10735		var subX, subY,
10736		    map = this._map,
10737		    container = this._container,
10738		    centerPoint = map.latLngToContainerPoint(map.getCenter()),
10739		    tooltipPoint = map.layerPointToContainerPoint(pos),
10740		    direction = this.options.direction,
10741		    tooltipWidth = container.offsetWidth,
10742		    tooltipHeight = container.offsetHeight,
10743		    offset = toPoint(this.options.offset),
10744		    anchor = this._getAnchor();
10745
10746		if (direction === 'top') {
10747			subX = tooltipWidth / 2;
10748			subY = tooltipHeight;
10749		} else if (direction === 'bottom') {
10750			subX = tooltipWidth / 2;
10751			subY = 0;
10752		} else if (direction === 'center') {
10753			subX = tooltipWidth / 2;
10754			subY = tooltipHeight / 2;
10755		} else if (direction === 'right') {
10756			subX = 0;
10757			subY = tooltipHeight / 2;
10758		} else if (direction === 'left') {
10759			subX = tooltipWidth;
10760			subY = tooltipHeight / 2;
10761		} else if (tooltipPoint.x < centerPoint.x) {
10762			direction = 'right';
10763			subX = 0;
10764			subY = tooltipHeight / 2;
10765		} else {
10766			direction = 'left';
10767			subX = tooltipWidth + (offset.x + anchor.x) * 2;
10768			subY = tooltipHeight / 2;
10769		}
10770
10771		pos = pos.subtract(toPoint(subX, subY, true)).add(offset).add(anchor);
10772
10773		removeClass(container, 'leaflet-tooltip-right');
10774		removeClass(container, 'leaflet-tooltip-left');
10775		removeClass(container, 'leaflet-tooltip-top');
10776		removeClass(container, 'leaflet-tooltip-bottom');
10777		addClass(container, 'leaflet-tooltip-' + direction);
10778		setPosition(container, pos);
10779	},
10780
10781	_updatePosition: function () {
10782		var pos = this._map.latLngToLayerPoint(this._latlng);
10783		this._setPosition(pos);
10784	},
10785
10786	setOpacity: function (opacity) {
10787		this.options.opacity = opacity;
10788
10789		if (this._container) {
10790			setOpacity(this._container, opacity);
10791		}
10792	},
10793
10794	_animateZoom: function (e) {
10795		var pos = this._map._latLngToNewLayerPoint(this._latlng, e.zoom, e.center);
10796		this._setPosition(pos);
10797	},
10798
10799	_getAnchor: function () {
10800		// Where should we anchor the tooltip on the source layer?
10801		return toPoint(this._source && this._source._getTooltipAnchor && !this.options.sticky ? this._source._getTooltipAnchor() : [0, 0]);
10802	}
10803
10804});
10805
10806// @namespace Tooltip
10807// @factory L.tooltip(options?: Tooltip options, source?: Layer)
10808// 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.
10809// @alternative
10810// @factory L.tooltip(latlng: LatLng, options?: Tooltip options)
10811// Instantiates a `Tooltip` object given `latlng` where the tooltip will open and an optional `options` object that describes its appearance and location.
10812var tooltip = function (options, source) {
10813	return new Tooltip(options, source);
10814};
10815
10816// @namespace Map
10817// @section Methods for Layers and Controls
10818Map.include({
10819
10820	// @method openTooltip(tooltip: Tooltip): this
10821	// Opens the specified tooltip.
10822	// @alternative
10823	// @method openTooltip(content: String|HTMLElement, latlng: LatLng, options?: Tooltip options): this
10824	// Creates a tooltip with the specified content and options and open it.
10825	openTooltip: function (tooltip, latlng, options) {
10826		this._initOverlay(Tooltip, tooltip, latlng, options)
10827		  .openOn(this);
10828
10829		return this;
10830	},
10831
10832	// @method closeTooltip(tooltip: Tooltip): this
10833	// Closes the tooltip given as parameter.
10834	closeTooltip: function (tooltip) {
10835		tooltip.close();
10836		return this;
10837	}
10838
10839});
10840
10841/*
10842 * @namespace Layer
10843 * @section Tooltip methods example
10844 *
10845 * All layers share a set of methods convenient for binding tooltips to it.
10846 *
10847 * ```js
10848 * var layer = L.Polygon(latlngs).bindTooltip('Hi There!').addTo(map);
10849 * layer.openTooltip();
10850 * layer.closeTooltip();
10851 * ```
10852 */
10853
10854// @section Tooltip methods
10855Layer.include({
10856
10857	// @method bindTooltip(content: String|HTMLElement|Function|Tooltip, options?: Tooltip options): this
10858	// Binds a tooltip to the layer with the passed `content` and sets up the
10859	// necessary event listeners. If a `Function` is passed it will receive
10860	// the layer as the first argument and should return a `String` or `HTMLElement`.
10861	bindTooltip: function (content, options) {
10862
10863		if (this._tooltip && this.isTooltipOpen()) {
10864			this.unbindTooltip();
10865		}
10866
10867		this._tooltip = this._initOverlay(Tooltip, this._tooltip, content, options);
10868		this._initTooltipInteractions();
10869
10870		if (this._tooltip.options.permanent && this._map && this._map.hasLayer(this)) {
10871			this.openTooltip();
10872		}
10873
10874		return this;
10875	},
10876
10877	// @method unbindTooltip(): this
10878	// Removes the tooltip previously bound with `bindTooltip`.
10879	unbindTooltip: function () {
10880		if (this._tooltip) {
10881			this._initTooltipInteractions(true);
10882			this.closeTooltip();
10883			this._tooltip = null;
10884		}
10885		return this;
10886	},
10887
10888	_initTooltipInteractions: function (remove) {
10889		if (!remove && this._tooltipHandlersAdded) { return; }
10890		var onOff = remove ? 'off' : 'on',
10891		    events = {
10892			remove: this.closeTooltip,
10893			move: this._moveTooltip
10894		    };
10895		if (!this._tooltip.options.permanent) {
10896			events.mouseover = this._openTooltip;
10897			events.mouseout = this.closeTooltip;
10898			events.click = this._openTooltip;
10899			if (this._map) {
10900				this._addFocusListeners();
10901			} else {
10902				events.add = this._addFocusListeners;
10903			}
10904		} else {
10905			events.add = this._openTooltip;
10906		}
10907		if (this._tooltip.options.sticky) {
10908			events.mousemove = this._moveTooltip;
10909		}
10910		this[onOff](events);
10911		this._tooltipHandlersAdded = !remove;
10912	},
10913
10914	// @method openTooltip(latlng?: LatLng): this
10915	// Opens the bound tooltip at the specified `latlng` or at the default tooltip anchor if no `latlng` is passed.
10916	openTooltip: function (latlng) {
10917		if (this._tooltip) {
10918			if (!(this instanceof FeatureGroup)) {
10919				this._tooltip._source = this;
10920			}
10921			if (this._tooltip._prepareOpen(latlng)) {
10922				// open the tooltip on the map
10923				this._tooltip.openOn(this._map);
10924
10925				if (this.getElement) {
10926					this._setAriaDescribedByOnLayer(this);
10927				} else if (this.eachLayer) {
10928					this.eachLayer(this._setAriaDescribedByOnLayer, this);
10929				}
10930			}
10931		}
10932		return this;
10933	},
10934
10935	// @method closeTooltip(): this
10936	// Closes the tooltip bound to this layer if it is open.
10937	closeTooltip: function () {
10938		if (this._tooltip) {
10939			return this._tooltip.close();
10940		}
10941	},
10942
10943	// @method toggleTooltip(): this
10944	// Opens or closes the tooltip bound to this layer depending on its current state.
10945	toggleTooltip: function () {
10946		if (this._tooltip) {
10947			this._tooltip.toggle(this);
10948		}
10949		return this;
10950	},
10951
10952	// @method isTooltipOpen(): boolean
10953	// Returns `true` if the tooltip bound to this layer is currently open.
10954	isTooltipOpen: function () {
10955		return this._tooltip.isOpen();
10956	},
10957
10958	// @method setTooltipContent(content: String|HTMLElement|Tooltip): this
10959	// Sets the content of the tooltip bound to this layer.
10960	setTooltipContent: function (content) {
10961		if (this._tooltip) {
10962			this._tooltip.setContent(content);
10963		}
10964		return this;
10965	},
10966
10967	// @method getTooltip(): Tooltip
10968	// Returns the tooltip bound to this layer.
10969	getTooltip: function () {
10970		return this._tooltip;
10971	},
10972
10973	_addFocusListeners: function () {
10974		if (this.getElement) {
10975			this._addFocusListenersOnLayer(this);
10976		} else if (this.eachLayer) {
10977			this.eachLayer(this._addFocusListenersOnLayer, this);
10978		}
10979	},
10980
10981	_addFocusListenersOnLayer: function (layer) {
10982		var el = typeof layer.getElement === 'function' && layer.getElement();
10983		if (el) {
10984			on(el, 'focus', function () {
10985				this._tooltip._source = layer;
10986				this.openTooltip();
10987			}, this);
10988			on(el, 'blur', this.closeTooltip, this);
10989		}
10990	},
10991
10992	_setAriaDescribedByOnLayer: function (layer) {
10993		var el = typeof layer.getElement === 'function' && layer.getElement();
10994		if (el) {
10995			el.setAttribute('aria-describedby', this._tooltip._container.id);
10996		}
10997	},
10998
10999
11000	_openTooltip: function (e) {
11001		if (!this._tooltip || !this._map) {
11002			return;
11003		}
11004
11005		// If the map is moving, we will show the tooltip after it's done.
11006		if (this._map.dragging && this._map.dragging.moving() && !this._openOnceFlag) {
11007			this._openOnceFlag = true;
11008			var that = this;
11009			this._map.once('moveend', function () {
11010				that._openOnceFlag = false;
11011				that._openTooltip(e);
11012			});
11013			return;
11014		}
11015
11016		this._tooltip._source = e.layer || e.target;
11017
11018		this.openTooltip(this._tooltip.options.sticky ? e.latlng : undefined);
11019	},
11020
11021	_moveTooltip: function (e) {
11022		var latlng = e.latlng, containerPoint, layerPoint;
11023		if (this._tooltip.options.sticky && e.originalEvent) {
11024			containerPoint = this._map.mouseEventToContainerPoint(e.originalEvent);
11025			layerPoint = this._map.containerPointToLayerPoint(containerPoint);
11026			latlng = this._map.layerPointToLatLng(layerPoint);
11027		}
11028		this._tooltip.setLatLng(latlng);
11029	}
11030});
11031
11032/*
11033 * @class DivIcon
11034 * @aka L.DivIcon
11035 * @inherits Icon
11036 *
11037 * Represents a lightweight icon for markers that uses a simple `<div>`
11038 * element instead of an image. Inherits from `Icon` but ignores the `iconUrl` and shadow options.
11039 *
11040 * @example
11041 * ```js
11042 * var myIcon = L.divIcon({className: 'my-div-icon'});
11043 * // you can set .my-div-icon styles in CSS
11044 *
11045 * L.marker([50.505, 30.57], {icon: myIcon}).addTo(map);
11046 * ```
11047 *
11048 * By default, it has a 'leaflet-div-icon' CSS class and is styled as a little white square with a shadow.
11049 */
11050
11051var DivIcon = Icon.extend({
11052	options: {
11053		// @section
11054		// @aka DivIcon options
11055		iconSize: [12, 12], // also can be set through CSS
11056
11057		// iconAnchor: (Point),
11058		// popupAnchor: (Point),
11059
11060		// @option html: String|HTMLElement = ''
11061		// Custom HTML code to put inside the div element, empty by default. Alternatively,
11062		// an instance of `HTMLElement`.
11063		html: false,
11064
11065		// @option bgPos: Point = [0, 0]
11066		// Optional relative position of the background, in pixels
11067		bgPos: null,
11068
11069		className: 'leaflet-div-icon'
11070	},
11071
11072	createIcon: function (oldIcon) {
11073		var div = (oldIcon && oldIcon.tagName === 'DIV') ? oldIcon : document.createElement('div'),
11074		    options = this.options;
11075
11076		if (options.html instanceof Element) {
11077			empty(div);
11078			div.appendChild(options.html);
11079		} else {
11080			div.innerHTML = options.html !== false ? options.html : '';
11081		}
11082
11083		if (options.bgPos) {
11084			var bgPos = toPoint(options.bgPos);
11085			div.style.backgroundPosition = (-bgPos.x) + 'px ' + (-bgPos.y) + 'px';
11086		}
11087		this._setIconStyles(div, 'icon');
11088
11089		return div;
11090	},
11091
11092	createShadow: function () {
11093		return null;
11094	}
11095});
11096
11097// @factory L.divIcon(options: DivIcon options)
11098// Creates a `DivIcon` instance with the given options.
11099function divIcon(options) {
11100	return new DivIcon(options);
11101}
11102
11103Icon.Default = IconDefault;
11104
11105/*
11106 * @class GridLayer
11107 * @inherits Layer
11108 * @aka L.GridLayer
11109 *
11110 * Generic class for handling a tiled grid of HTML elements. This is the base class for all tile layers and replaces `TileLayer.Canvas`.
11111 * 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.
11112 *
11113 *
11114 * @section Synchronous usage
11115 * @example
11116 *
11117 * 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.
11118 *
11119 * ```js
11120 * var CanvasLayer = L.GridLayer.extend({
11121 *     createTile: function(coords){
11122 *         // create a <canvas> element for drawing
11123 *         var tile = L.DomUtil.create('canvas', 'leaflet-tile');
11124 *
11125 *         // setup tile width and height according to the options
11126 *         var size = this.getTileSize();
11127 *         tile.width = size.x;
11128 *         tile.height = size.y;
11129 *
11130 *         // get a canvas context and draw something on it using coords.x, coords.y and coords.z
11131 *         var ctx = tile.getContext('2d');
11132 *
11133 *         // return the tile so it can be rendered on screen
11134 *         return tile;
11135 *     }
11136 * });
11137 * ```
11138 *
11139 * @section Asynchronous usage
11140 * @example
11141 *
11142 * 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.
11143 *
11144 * ```js
11145 * var CanvasLayer = L.GridLayer.extend({
11146 *     createTile: function(coords, done){
11147 *         var error;
11148 *
11149 *         // create a <canvas> element for drawing
11150 *         var tile = L.DomUtil.create('canvas', 'leaflet-tile');
11151 *
11152 *         // setup tile width and height according to the options
11153 *         var size = this.getTileSize();
11154 *         tile.width = size.x;
11155 *         tile.height = size.y;
11156 *
11157 *         // draw something asynchronously and pass the tile to the done() callback
11158 *         setTimeout(function() {
11159 *             done(error, tile);
11160 *         }, 1000);
11161 *
11162 *         return tile;
11163 *     }
11164 * });
11165 * ```
11166 *
11167 * @section
11168 */
11169
11170
11171var GridLayer = Layer.extend({
11172
11173	// @section
11174	// @aka GridLayer options
11175	options: {
11176		// @option tileSize: Number|Point = 256
11177		// Width and height of tiles in the grid. Use a number if width and height are equal, or `L.point(width, height)` otherwise.
11178		tileSize: 256,
11179
11180		// @option opacity: Number = 1.0
11181		// Opacity of the tiles. Can be used in the `createTile()` function.
11182		opacity: 1,
11183
11184		// @option updateWhenIdle: Boolean = (depends)
11185		// Load new tiles only when panning ends.
11186		// `true` by default on mobile browsers, in order to avoid too many requests and keep smooth navigation.
11187		// `false` otherwise in order to display new tiles _during_ panning, since it is easy to pan outside the
11188		// [`keepBuffer`](#gridlayer-keepbuffer) option in desktop browsers.
11189		updateWhenIdle: Browser.mobile,
11190
11191		// @option updateWhenZooming: Boolean = true
11192		// 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.
11193		updateWhenZooming: true,
11194
11195		// @option updateInterval: Number = 200
11196		// Tiles will not update more than once every `updateInterval` milliseconds when panning.
11197		updateInterval: 200,
11198
11199		// @option zIndex: Number = 1
11200		// The explicit zIndex of the tile layer.
11201		zIndex: 1,
11202
11203		// @option bounds: LatLngBounds = undefined
11204		// If set, tiles will only be loaded inside the set `LatLngBounds`.
11205		bounds: null,
11206
11207		// @option minZoom: Number = 0
11208		// The minimum zoom level down to which this layer will be displayed (inclusive).
11209		minZoom: 0,
11210
11211		// @option maxZoom: Number = undefined
11212		// The maximum zoom level up to which this layer will be displayed (inclusive).
11213		maxZoom: undefined,
11214
11215		// @option maxNativeZoom: Number = undefined
11216		// Maximum zoom number the tile source has available. If it is specified,
11217		// the tiles on all zoom levels higher than `maxNativeZoom` will be loaded
11218		// from `maxNativeZoom` level and auto-scaled.
11219		maxNativeZoom: undefined,
11220
11221		// @option minNativeZoom: Number = undefined
11222		// Minimum zoom number the tile source has available. If it is specified,
11223		// the tiles on all zoom levels lower than `minNativeZoom` will be loaded
11224		// from `minNativeZoom` level and auto-scaled.
11225		minNativeZoom: undefined,
11226
11227		// @option noWrap: Boolean = false
11228		// Whether the layer is wrapped around the antimeridian. If `true`, the
11229		// GridLayer will only be displayed once at low zoom levels. Has no
11230		// effect when the [map CRS](#map-crs) doesn't wrap around. Can be used
11231		// in combination with [`bounds`](#gridlayer-bounds) to prevent requesting
11232		// tiles outside the CRS limits.
11233		noWrap: false,
11234
11235		// @option pane: String = 'tilePane'
11236		// `Map pane` where the grid layer will be added.
11237		pane: 'tilePane',
11238
11239		// @option className: String = ''
11240		// A custom class name to assign to the tile layer. Empty by default.
11241		className: '',
11242
11243		// @option keepBuffer: Number = 2
11244		// When panning the map, keep this many rows and columns of tiles before unloading them.
11245		keepBuffer: 2
11246	},
11247
11248	initialize: function (options) {
11249		setOptions(this, options);
11250	},
11251
11252	onAdd: function () {
11253		this._initContainer();
11254
11255		this._levels = {};
11256		this._tiles = {};
11257
11258		this._resetView(); // implicit _update() call
11259	},
11260
11261	beforeAdd: function (map) {
11262		map._addZoomLimit(this);
11263	},
11264
11265	onRemove: function (map) {
11266		this._removeAllTiles();
11267		remove(this._container);
11268		map._removeZoomLimit(this);
11269		this._container = null;
11270		this._tileZoom = undefined;
11271	},
11272
11273	// @method bringToFront: this
11274	// Brings the tile layer to the top of all tile layers.
11275	bringToFront: function () {
11276		if (this._map) {
11277			toFront(this._container);
11278			this._setAutoZIndex(Math.max);
11279		}
11280		return this;
11281	},
11282
11283	// @method bringToBack: this
11284	// Brings the tile layer to the bottom of all tile layers.
11285	bringToBack: function () {
11286		if (this._map) {
11287			toBack(this._container);
11288			this._setAutoZIndex(Math.min);
11289		}
11290		return this;
11291	},
11292
11293	// @method getContainer: HTMLElement
11294	// Returns the HTML element that contains the tiles for this layer.
11295	getContainer: function () {
11296		return this._container;
11297	},
11298
11299	// @method setOpacity(opacity: Number): this
11300	// Changes the [opacity](#gridlayer-opacity) of the grid layer.
11301	setOpacity: function (opacity) {
11302		this.options.opacity = opacity;
11303		this._updateOpacity();
11304		return this;
11305	},
11306
11307	// @method setZIndex(zIndex: Number): this
11308	// Changes the [zIndex](#gridlayer-zindex) of the grid layer.
11309	setZIndex: function (zIndex) {
11310		this.options.zIndex = zIndex;
11311		this._updateZIndex();
11312
11313		return this;
11314	},
11315
11316	// @method isLoading: Boolean
11317	// Returns `true` if any tile in the grid layer has not finished loading.
11318	isLoading: function () {
11319		return this._loading;
11320	},
11321
11322	// @method redraw: this
11323	// Causes the layer to clear all the tiles and request them again.
11324	redraw: function () {
11325		if (this._map) {
11326			this._removeAllTiles();
11327			var tileZoom = this._clampZoom(this._map.getZoom());
11328			if (tileZoom !== this._tileZoom) {
11329				this._tileZoom = tileZoom;
11330				this._updateLevels();
11331			}
11332			this._update();
11333		}
11334		return this;
11335	},
11336
11337	getEvents: function () {
11338		var events = {
11339			viewprereset: this._invalidateAll,
11340			viewreset: this._resetView,
11341			zoom: this._resetView,
11342			moveend: this._onMoveEnd
11343		};
11344
11345		if (!this.options.updateWhenIdle) {
11346			// update tiles on move, but not more often than once per given interval
11347			if (!this._onMove) {
11348				this._onMove = throttle(this._onMoveEnd, this.options.updateInterval, this);
11349			}
11350
11351			events.move = this._onMove;
11352		}
11353
11354		if (this._zoomAnimated) {
11355			events.zoomanim = this._animateZoom;
11356		}
11357
11358		return events;
11359	},
11360
11361	// @section Extension methods
11362	// Layers extending `GridLayer` shall reimplement the following method.
11363	// @method createTile(coords: Object, done?: Function): HTMLElement
11364	// Called only internally, must be overridden by classes extending `GridLayer`.
11365	// Returns the `HTMLElement` corresponding to the given `coords`. If the `done` callback
11366	// is specified, it must be called when the tile has finished loading and drawing.
11367	createTile: function () {
11368		return document.createElement('div');
11369	},
11370
11371	// @section
11372	// @method getTileSize: Point
11373	// Normalizes the [tileSize option](#gridlayer-tilesize) into a point. Used by the `createTile()` method.
11374	getTileSize: function () {
11375		var s = this.options.tileSize;
11376		return s instanceof Point ? s : new Point(s, s);
11377	},
11378
11379	_updateZIndex: function () {
11380		if (this._container && this.options.zIndex !== undefined && this.options.zIndex !== null) {
11381			this._container.style.zIndex = this.options.zIndex;
11382		}
11383	},
11384
11385	_setAutoZIndex: function (compare) {
11386		// go through all other layers of the same pane, set zIndex to max + 1 (front) or min - 1 (back)
11387
11388		var layers = this.getPane().children,
11389		    edgeZIndex = -compare(-Infinity, Infinity); // -Infinity for max, Infinity for min
11390
11391		for (var i = 0, len = layers.length, zIndex; i < len; i++) {
11392
11393			zIndex = layers[i].style.zIndex;
11394
11395			if (layers[i] !== this._container && zIndex) {
11396				edgeZIndex = compare(edgeZIndex, +zIndex);
11397			}
11398		}
11399
11400		if (isFinite(edgeZIndex)) {
11401			this.options.zIndex = edgeZIndex + compare(-1, 1);
11402			this._updateZIndex();
11403		}
11404	},
11405
11406	_updateOpacity: function () {
11407		if (!this._map) { return; }
11408
11409		// IE doesn't inherit filter opacity properly, so we're forced to set it on tiles
11410		if (Browser.ielt9) { return; }
11411
11412		setOpacity(this._container, this.options.opacity);
11413
11414		var now = +new Date(),
11415		    nextFrame = false,
11416		    willPrune = false;
11417
11418		for (var key in this._tiles) {
11419			var tile = this._tiles[key];
11420			if (!tile.current || !tile.loaded) { continue; }
11421
11422			var fade = Math.min(1, (now - tile.loaded) / 200);
11423
11424			setOpacity(tile.el, fade);
11425			if (fade < 1) {
11426				nextFrame = true;
11427			} else {
11428				if (tile.active) {
11429					willPrune = true;
11430				} else {
11431					this._onOpaqueTile(tile);
11432				}
11433				tile.active = true;
11434			}
11435		}
11436
11437		if (willPrune && !this._noPrune) { this._pruneTiles(); }
11438
11439		if (nextFrame) {
11440			cancelAnimFrame(this._fadeFrame);
11441			this._fadeFrame = requestAnimFrame(this._updateOpacity, this);
11442		}
11443	},
11444
11445	_onOpaqueTile: falseFn,
11446
11447	_initContainer: function () {
11448		if (this._container) { return; }
11449
11450		this._container = create$1('div', 'leaflet-layer ' + (this.options.className || ''));
11451		this._updateZIndex();
11452
11453		if (this.options.opacity < 1) {
11454			this._updateOpacity();
11455		}
11456
11457		this.getPane().appendChild(this._container);
11458	},
11459
11460	_updateLevels: function () {
11461
11462		var zoom = this._tileZoom,
11463		    maxZoom = this.options.maxZoom;
11464
11465		if (zoom === undefined) { return undefined; }
11466
11467		for (var z in this._levels) {
11468			z = Number(z);
11469			if (this._levels[z].el.children.length || z === zoom) {
11470				this._levels[z].el.style.zIndex = maxZoom - Math.abs(zoom - z);
11471				this._onUpdateLevel(z);
11472			} else {
11473				remove(this._levels[z].el);
11474				this._removeTilesAtZoom(z);
11475				this._onRemoveLevel(z);
11476				delete this._levels[z];
11477			}
11478		}
11479
11480		var level = this._levels[zoom],
11481		    map = this._map;
11482
11483		if (!level) {
11484			level = this._levels[zoom] = {};
11485
11486			level.el = create$1('div', 'leaflet-tile-container leaflet-zoom-animated', this._container);
11487			level.el.style.zIndex = maxZoom;
11488
11489			level.origin = map.project(map.unproject(map.getPixelOrigin()), zoom).round();
11490			level.zoom = zoom;
11491
11492			this._setZoomTransform(level, map.getCenter(), map.getZoom());
11493
11494			// force the browser to consider the newly added element for transition
11495			falseFn(level.el.offsetWidth);
11496
11497			this._onCreateLevel(level);
11498		}
11499
11500		this._level = level;
11501
11502		return level;
11503	},
11504
11505	_onUpdateLevel: falseFn,
11506
11507	_onRemoveLevel: falseFn,
11508
11509	_onCreateLevel: falseFn,
11510
11511	_pruneTiles: function () {
11512		if (!this._map) {
11513			return;
11514		}
11515
11516		var key, tile;
11517
11518		var zoom = this._map.getZoom();
11519		if (zoom > this.options.maxZoom ||
11520			zoom < this.options.minZoom) {
11521			this._removeAllTiles();
11522			return;
11523		}
11524
11525		for (key in this._tiles) {
11526			tile = this._tiles[key];
11527			tile.retain = tile.current;
11528		}
11529
11530		for (key in this._tiles) {
11531			tile = this._tiles[key];
11532			if (tile.current && !tile.active) {
11533				var coords = tile.coords;
11534				if (!this._retainParent(coords.x, coords.y, coords.z, coords.z - 5)) {
11535					this._retainChildren(coords.x, coords.y, coords.z, coords.z + 2);
11536				}
11537			}
11538		}
11539
11540		for (key in this._tiles) {
11541			if (!this._tiles[key].retain) {
11542				this._removeTile(key);
11543			}
11544		}
11545	},
11546
11547	_removeTilesAtZoom: function (zoom) {
11548		for (var key in this._tiles) {
11549			if (this._tiles[key].coords.z !== zoom) {
11550				continue;
11551			}
11552			this._removeTile(key);
11553		}
11554	},
11555
11556	_removeAllTiles: function () {
11557		for (var key in this._tiles) {
11558			this._removeTile(key);
11559		}
11560	},
11561
11562	_invalidateAll: function () {
11563		for (var z in this._levels) {
11564			remove(this._levels[z].el);
11565			this._onRemoveLevel(Number(z));
11566			delete this._levels[z];
11567		}
11568		this._removeAllTiles();
11569
11570		this._tileZoom = undefined;
11571	},
11572
11573	_retainParent: function (x, y, z, minZoom) {
11574		var x2 = Math.floor(x / 2),
11575		    y2 = Math.floor(y / 2),
11576		    z2 = z - 1,
11577		    coords2 = new Point(+x2, +y2);
11578		coords2.z = +z2;
11579
11580		var key = this._tileCoordsToKey(coords2),
11581		    tile = this._tiles[key];
11582
11583		if (tile && tile.active) {
11584			tile.retain = true;
11585			return true;
11586
11587		} else if (tile && tile.loaded) {
11588			tile.retain = true;
11589		}
11590
11591		if (z2 > minZoom) {
11592			return this._retainParent(x2, y2, z2, minZoom);
11593		}
11594
11595		return false;
11596	},
11597
11598	_retainChildren: function (x, y, z, maxZoom) {
11599
11600		for (var i = 2 * x; i < 2 * x + 2; i++) {
11601			for (var j = 2 * y; j < 2 * y + 2; j++) {
11602
11603				var coords = new Point(i, j);
11604				coords.z = z + 1;
11605
11606				var key = this._tileCoordsToKey(coords),
11607				    tile = this._tiles[key];
11608
11609				if (tile && tile.active) {
11610					tile.retain = true;
11611					continue;
11612
11613				} else if (tile && tile.loaded) {
11614					tile.retain = true;
11615				}
11616
11617				if (z + 1 < maxZoom) {
11618					this._retainChildren(i, j, z + 1, maxZoom);
11619				}
11620			}
11621		}
11622	},
11623
11624	_resetView: function (e) {
11625		var animating = e && (e.pinch || e.flyTo);
11626		this._setView(this._map.getCenter(), this._map.getZoom(), animating, animating);
11627	},
11628
11629	_animateZoom: function (e) {
11630		this._setView(e.center, e.zoom, true, e.noUpdate);
11631	},
11632
11633	_clampZoom: function (zoom) {
11634		var options = this.options;
11635
11636		if (undefined !== options.minNativeZoom && zoom < options.minNativeZoom) {
11637			return options.minNativeZoom;
11638		}
11639
11640		if (undefined !== options.maxNativeZoom && options.maxNativeZoom < zoom) {
11641			return options.maxNativeZoom;
11642		}
11643
11644		return zoom;
11645	},
11646
11647	_setView: function (center, zoom, noPrune, noUpdate) {
11648		var tileZoom = Math.round(zoom);
11649		if ((this.options.maxZoom !== undefined && tileZoom > this.options.maxZoom) ||
11650		    (this.options.minZoom !== undefined && tileZoom < this.options.minZoom)) {
11651			tileZoom = undefined;
11652		} else {
11653			tileZoom = this._clampZoom(tileZoom);
11654		}
11655
11656		var tileZoomChanged = this.options.updateWhenZooming && (tileZoom !== this._tileZoom);
11657
11658		if (!noUpdate || tileZoomChanged) {
11659
11660			this._tileZoom = tileZoom;
11661
11662			if (this._abortLoading) {
11663				this._abortLoading();
11664			}
11665
11666			this._updateLevels();
11667			this._resetGrid();
11668
11669			if (tileZoom !== undefined) {
11670				this._update(center);
11671			}
11672
11673			if (!noPrune) {
11674				this._pruneTiles();
11675			}
11676
11677			// Flag to prevent _updateOpacity from pruning tiles during
11678			// a zoom anim or a pinch gesture
11679			this._noPrune = !!noPrune;
11680		}
11681
11682		this._setZoomTransforms(center, zoom);
11683	},
11684
11685	_setZoomTransforms: function (center, zoom) {
11686		for (var i in this._levels) {
11687			this._setZoomTransform(this._levels[i], center, zoom);
11688		}
11689	},
11690
11691	_setZoomTransform: function (level, center, zoom) {
11692		var scale = this._map.getZoomScale(zoom, level.zoom),
11693		    translate = level.origin.multiplyBy(scale)
11694		        .subtract(this._map._getNewPixelOrigin(center, zoom)).round();
11695
11696		if (Browser.any3d) {
11697			setTransform(level.el, translate, scale);
11698		} else {
11699			setPosition(level.el, translate);
11700		}
11701	},
11702
11703	_resetGrid: function () {
11704		var map = this._map,
11705		    crs = map.options.crs,
11706		    tileSize = this._tileSize = this.getTileSize(),
11707		    tileZoom = this._tileZoom;
11708
11709		var bounds = this._map.getPixelWorldBounds(this._tileZoom);
11710		if (bounds) {
11711			this._globalTileRange = this._pxBoundsToTileRange(bounds);
11712		}
11713
11714		this._wrapX = crs.wrapLng && !this.options.noWrap && [
11715			Math.floor(map.project([0, crs.wrapLng[0]], tileZoom).x / tileSize.x),
11716			Math.ceil(map.project([0, crs.wrapLng[1]], tileZoom).x / tileSize.y)
11717		];
11718		this._wrapY = crs.wrapLat && !this.options.noWrap && [
11719			Math.floor(map.project([crs.wrapLat[0], 0], tileZoom).y / tileSize.x),
11720			Math.ceil(map.project([crs.wrapLat[1], 0], tileZoom).y / tileSize.y)
11721		];
11722	},
11723
11724	_onMoveEnd: function () {
11725		if (!this._map || this._map._animatingZoom) { return; }
11726
11727		this._update();
11728	},
11729
11730	_getTiledPixelBounds: function (center) {
11731		var map = this._map,
11732		    mapZoom = map._animatingZoom ? Math.max(map._animateToZoom, map.getZoom()) : map.getZoom(),
11733		    scale = map.getZoomScale(mapZoom, this._tileZoom),
11734		    pixelCenter = map.project(center, this._tileZoom).floor(),
11735		    halfSize = map.getSize().divideBy(scale * 2);
11736
11737		return new Bounds(pixelCenter.subtract(halfSize), pixelCenter.add(halfSize));
11738	},
11739
11740	// Private method to load tiles in the grid's active zoom level according to map bounds
11741	_update: function (center) {
11742		var map = this._map;
11743		if (!map) { return; }
11744		var zoom = this._clampZoom(map.getZoom());
11745
11746		if (center === undefined) { center = map.getCenter(); }
11747		if (this._tileZoom === undefined) { return; }	// if out of minzoom/maxzoom
11748
11749		var pixelBounds = this._getTiledPixelBounds(center),
11750		    tileRange = this._pxBoundsToTileRange(pixelBounds),
11751		    tileCenter = tileRange.getCenter(),
11752		    queue = [],
11753		    margin = this.options.keepBuffer,
11754		    noPruneRange = new Bounds(tileRange.getBottomLeft().subtract([margin, -margin]),
11755		                              tileRange.getTopRight().add([margin, -margin]));
11756
11757		// Sanity check: panic if the tile range contains Infinity somewhere.
11758		if (!(isFinite(tileRange.min.x) &&
11759		      isFinite(tileRange.min.y) &&
11760		      isFinite(tileRange.max.x) &&
11761		      isFinite(tileRange.max.y))) { throw new Error('Attempted to load an infinite number of tiles'); }
11762
11763		for (var key in this._tiles) {
11764			var c = this._tiles[key].coords;
11765			if (c.z !== this._tileZoom || !noPruneRange.contains(new Point(c.x, c.y))) {
11766				this._tiles[key].current = false;
11767			}
11768		}
11769
11770		// _update just loads more tiles. If the tile zoom level differs too much
11771		// from the map's, let _setView reset levels and prune old tiles.
11772		if (Math.abs(zoom - this._tileZoom) > 1) { this._setView(center, zoom); return; }
11773
11774		// create a queue of coordinates to load tiles from
11775		for (var j = tileRange.min.y; j <= tileRange.max.y; j++) {
11776			for (var i = tileRange.min.x; i <= tileRange.max.x; i++) {
11777				var coords = new Point(i, j);
11778				coords.z = this._tileZoom;
11779
11780				if (!this._isValidTile(coords)) { continue; }
11781
11782				var tile = this._tiles[this._tileCoordsToKey(coords)];
11783				if (tile) {
11784					tile.current = true;
11785				} else {
11786					queue.push(coords);
11787				}
11788			}
11789		}
11790
11791		// sort tile queue to load tiles in order of their distance to center
11792		queue.sort(function (a, b) {
11793			return a.distanceTo(tileCenter) - b.distanceTo(tileCenter);
11794		});
11795
11796		if (queue.length !== 0) {
11797			// if it's the first batch of tiles to load
11798			if (!this._loading) {
11799				this._loading = true;
11800				// @event loading: Event
11801				// Fired when the grid layer starts loading tiles.
11802				this.fire('loading');
11803			}
11804
11805			// create DOM fragment to append tiles in one batch
11806			var fragment = document.createDocumentFragment();
11807
11808			for (i = 0; i < queue.length; i++) {
11809				this._addTile(queue[i], fragment);
11810			}
11811
11812			this._level.el.appendChild(fragment);
11813		}
11814	},
11815
11816	_isValidTile: function (coords) {
11817		var crs = this._map.options.crs;
11818
11819		if (!crs.infinite) {
11820			// don't load tile if it's out of bounds and not wrapped
11821			var bounds = this._globalTileRange;
11822			if ((!crs.wrapLng && (coords.x < bounds.min.x || coords.x > bounds.max.x)) ||
11823			    (!crs.wrapLat && (coords.y < bounds.min.y || coords.y > bounds.max.y))) { return false; }
11824		}
11825
11826		if (!this.options.bounds) { return true; }
11827
11828		// don't load tile if it doesn't intersect the bounds in options
11829		var tileBounds = this._tileCoordsToBounds(coords);
11830		return toLatLngBounds(this.options.bounds).overlaps(tileBounds);
11831	},
11832
11833	_keyToBounds: function (key) {
11834		return this._tileCoordsToBounds(this._keyToTileCoords(key));
11835	},
11836
11837	_tileCoordsToNwSe: function (coords) {
11838		var map = this._map,
11839		    tileSize = this.getTileSize(),
11840		    nwPoint = coords.scaleBy(tileSize),
11841		    sePoint = nwPoint.add(tileSize),
11842		    nw = map.unproject(nwPoint, coords.z),
11843		    se = map.unproject(sePoint, coords.z);
11844		return [nw, se];
11845	},
11846
11847	// converts tile coordinates to its geographical bounds
11848	_tileCoordsToBounds: function (coords) {
11849		var bp = this._tileCoordsToNwSe(coords),
11850		    bounds = new LatLngBounds(bp[0], bp[1]);
11851
11852		if (!this.options.noWrap) {
11853			bounds = this._map.wrapLatLngBounds(bounds);
11854		}
11855		return bounds;
11856	},
11857	// converts tile coordinates to key for the tile cache
11858	_tileCoordsToKey: function (coords) {
11859		return coords.x + ':' + coords.y + ':' + coords.z;
11860	},
11861
11862	// converts tile cache key to coordinates
11863	_keyToTileCoords: function (key) {
11864		var k = key.split(':'),
11865		    coords = new Point(+k[0], +k[1]);
11866		coords.z = +k[2];
11867		return coords;
11868	},
11869
11870	_removeTile: function (key) {
11871		var tile = this._tiles[key];
11872		if (!tile) { return; }
11873
11874		remove(tile.el);
11875
11876		delete this._tiles[key];
11877
11878		// @event tileunload: TileEvent
11879		// Fired when a tile is removed (e.g. when a tile goes off the screen).
11880		this.fire('tileunload', {
11881			tile: tile.el,
11882			coords: this._keyToTileCoords(key)
11883		});
11884	},
11885
11886	_initTile: function (tile) {
11887		addClass(tile, 'leaflet-tile');
11888
11889		var tileSize = this.getTileSize();
11890		tile.style.width = tileSize.x + 'px';
11891		tile.style.height = tileSize.y + 'px';
11892
11893		tile.onselectstart = falseFn;
11894		tile.onmousemove = falseFn;
11895
11896		// update opacity on tiles in IE7-8 because of filter inheritance problems
11897		if (Browser.ielt9 && this.options.opacity < 1) {
11898			setOpacity(tile, this.options.opacity);
11899		}
11900	},
11901
11902	_addTile: function (coords, container) {
11903		var tilePos = this._getTilePos(coords),
11904		    key = this._tileCoordsToKey(coords);
11905
11906		var tile = this.createTile(this._wrapCoords(coords), bind(this._tileReady, this, coords));
11907
11908		this._initTile(tile);
11909
11910		// if createTile is defined with a second argument ("done" callback),
11911		// we know that tile is async and will be ready later; otherwise
11912		if (this.createTile.length < 2) {
11913			// mark tile as ready, but delay one frame for opacity animation to happen
11914			requestAnimFrame(bind(this._tileReady, this, coords, null, tile));
11915		}
11916
11917		setPosition(tile, tilePos);
11918
11919		// save tile in cache
11920		this._tiles[key] = {
11921			el: tile,
11922			coords: coords,
11923			current: true
11924		};
11925
11926		container.appendChild(tile);
11927		// @event tileloadstart: TileEvent
11928		// Fired when a tile is requested and starts loading.
11929		this.fire('tileloadstart', {
11930			tile: tile,
11931			coords: coords
11932		});
11933	},
11934
11935	_tileReady: function (coords, err, tile) {
11936		if (err) {
11937			// @event tileerror: TileErrorEvent
11938			// Fired when there is an error loading a tile.
11939			this.fire('tileerror', {
11940				error: err,
11941				tile: tile,
11942				coords: coords
11943			});
11944		}
11945
11946		var key = this._tileCoordsToKey(coords);
11947
11948		tile = this._tiles[key];
11949		if (!tile) { return; }
11950
11951		tile.loaded = +new Date();
11952		if (this._map._fadeAnimated) {
11953			setOpacity(tile.el, 0);
11954			cancelAnimFrame(this._fadeFrame);
11955			this._fadeFrame = requestAnimFrame(this._updateOpacity, this);
11956		} else {
11957			tile.active = true;
11958			this._pruneTiles();
11959		}
11960
11961		if (!err) {
11962			addClass(tile.el, 'leaflet-tile-loaded');
11963
11964			// @event tileload: TileEvent
11965			// Fired when a tile loads.
11966			this.fire('tileload', {
11967				tile: tile.el,
11968				coords: coords
11969			});
11970		}
11971
11972		if (this._noTilesToLoad()) {
11973			this._loading = false;
11974			// @event load: Event
11975			// Fired when the grid layer loaded all visible tiles.
11976			this.fire('load');
11977
11978			if (Browser.ielt9 || !this._map._fadeAnimated) {
11979				requestAnimFrame(this._pruneTiles, this);
11980			} else {
11981				// Wait a bit more than 0.2 secs (the duration of the tile fade-in)
11982				// to trigger a pruning.
11983				setTimeout(bind(this._pruneTiles, this), 250);
11984			}
11985		}
11986	},
11987
11988	_getTilePos: function (coords) {
11989		return coords.scaleBy(this.getTileSize()).subtract(this._level.origin);
11990	},
11991
11992	_wrapCoords: function (coords) {
11993		var newCoords = new Point(
11994			this._wrapX ? wrapNum(coords.x, this._wrapX) : coords.x,
11995			this._wrapY ? wrapNum(coords.y, this._wrapY) : coords.y);
11996		newCoords.z = coords.z;
11997		return newCoords;
11998	},
11999
12000	_pxBoundsToTileRange: function (bounds) {
12001		var tileSize = this.getTileSize();
12002		return new Bounds(
12003			bounds.min.unscaleBy(tileSize).floor(),
12004			bounds.max.unscaleBy(tileSize).ceil().subtract([1, 1]));
12005	},
12006
12007	_noTilesToLoad: function () {
12008		for (var key in this._tiles) {
12009			if (!this._tiles[key].loaded) { return false; }
12010		}
12011		return true;
12012	}
12013});
12014
12015// @factory L.gridLayer(options?: GridLayer options)
12016// Creates a new instance of GridLayer with the supplied options.
12017function gridLayer(options) {
12018	return new GridLayer(options);
12019}
12020
12021/*
12021
12022 * @class TileLayer
12023 * @inherits GridLayer
12024 * @aka L.TileLayer
12025 * 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`.
12026 *
12027 * @example
12028 *
12029 * ```js
12030 * 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);
12031 * ```
12032 *
12033 * @section URL template
12034 * @example
12035 *
12036 * A string of the following form:
12037 *
12038 * ```
12039 * 'https://{s}.somedomain.com/blabla/{z}/{x}/{y}{r}.png'
12040 * ```
12041 *
12042 * `{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.
12043 *
12044 * You can use custom keys in the template, which will be [evaluated](#util-template) from TileLayer options, like this:
12045 *
12046 * ```
12047 * L.tileLayer('https://{s}.somedomain.com/{foo}/{z}/{x}/{y}.png', {foo: 'bar'});
12048 * ```
12049 */
12050
12051
12052var TileLayer = GridLayer.extend({
12053
12054	// @section
12055	// @aka TileLayer options
12056	options: {
12057		// @option minZoom: Number = 0
12058		// The minimum zoom level down to which this layer will be displayed (inclusive).
12059		minZoom: 0,
12060
12061		// @option maxZoom: Number = 18
12062		// The maximum zoom level up to which this layer will be displayed (inclusive).
12063		maxZoom: 18,
12064
12065		// @option subdomains: String|String[] = 'abc'
12066		// 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 12066-12433
12066ings.
12067		subdomains: 'abc',
12068
12069		// @option errorTileUrl: String = ''
12070		// URL to the tile image to show in place of the tile that failed to load.
12071		errorTileUrl: '',
12072
12073		// @option zoomOffset: Number = 0
12074		// The zoom number used in tile URLs will be offset with this value.
12075		zoomOffset: 0,
12076
12077		// @option tms: Boolean = false
12078		// If `true`, inverses Y axis numbering for tiles (turn this on for [TMS](https://en.wikipedia.org/wiki/Tile_Map_Service) services).
12079		tms: false,
12080
12081		// @option zoomReverse: Boolean = false
12082		// If set to true, the zoom number used in tile URLs will be reversed (`maxZoom - zoom` instead of `zoom`)
12083		zoomReverse: false,
12084
12085		// @option detectRetina: Boolean = false
12086		// 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.
12087		detectRetina: false,
12088
12089		// @option crossOrigin: Boolean|String = false
12090		// Whether the crossOrigin attribute will be added to the tiles.
12091		// 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.
12092		// Refer to [CORS Settings](https://developer.mozilla.org/en-US/docs/Web/HTML/CORS_settings_attributes) for valid String values.
12093		crossOrigin: false,
12094
12095		// @option referrerPolicy: Boolean|String = false
12096		// Whether the referrerPolicy attribute will be added to the tiles.
12097		// If a String is provided, all tiles will have their referrerPolicy attribute set to the String provided.
12098		// This may be needed if your map's rendering context has a strict default but your tile provider expects a valid referrer
12099		// (e.g. to validate an API token).
12100		// Refer to [HTMLImageElement.referrerPolicy](https://developer.mozilla.org/en-US/docs/Web/API/HTMLImageElement/referrerPolicy) for valid String values.
12101		referrerPolicy: false
12102	},
12103
12104	initialize: function (url, options) {
12105
12106		this._url = url;
12107
12108		options = setOptions(this, options);
12109
12110		// detecting retina displays, adjusting tileSize and zoom levels
12111		if (options.detectRetina && Browser.retina && options.maxZoom > 0) {
12112
12113			options.tileSize = Math.floor(options.tileSize / 2);
12114
12115			if (!options.zoomReverse) {
12116				options.zoomOffset++;
12117				options.maxZoom = Math.max(options.minZoom, options.maxZoom - 1);
12118			} else {
12119				options.zoomOffset--;
12120				options.minZoom = Math.min(options.maxZoom, options.minZoom + 1);
12121			}
12122
12123			options.minZoom = Math.max(0, options.minZoom);
12124		} else if (!options.zoomReverse) {
12125			// make sure maxZoom is gte minZoom
12126			options.maxZoom = Math.max(options.minZoom, options.maxZoom);
12127		} else {
12128			// make sure minZoom is lte maxZoom
12129			options.minZoom = Math.min(options.maxZoom, options.minZoom);
12130		}
12131
12132		if (typeof options.subdomains === 'string') {
12133			options.subdomains = options.subdomains.split('');
12134		}
12135
12136		this.on('tileunload', this._onTileRemove);
12137	},
12138
12139	// @method setUrl(url: String, noRedraw?: Boolean): this
12140	// Updates the layer's URL template and redraws it (unless `noRedraw` is set to `true`).
12141	// If the URL does not change, the layer will not be redrawn unless
12142	// the noRedraw parameter is set to false.
12143	setUrl: function (url, noRedraw) {
12144		if (this._url === url && noRedraw === undefined) {
12145			noRedraw = true;
12146		}
12147
12148		this._url = url;
12149
12150		if (!noRedraw) {
12151			this.redraw();
12152		}
12153		return this;
12154	},
12155
12156	// @method createTile(coords: Object, done?: Function): HTMLElement
12157	// Called only internally, overrides GridLayer's [`createTile()`](#gridlayer-createtile)
12158	// to return an `<img>` HTML element with the appropriate image URL given `coords`. The `done`
12159	// callback is called when the tile has been loaded.
12160	createTile: function (coords, done) {
12161		var tile = document.createElement('img');
12162
12163		on(tile, 'load', bind(this._tileOnLoad, this, done, tile));
12164		on(tile, 'error', bind(this._tileOnError, this, done, tile));
12165
12166		if (this.options.crossOrigin || this.options.crossOrigin === '') {
12167			tile.crossOrigin = this.options.crossOrigin === true ? '' : this.options.crossOrigin;
12168		}
12169
12170		// for this new option we follow the documented behavior
12171		// more closely by only setting the property when string
12172		if (typeof this.options.referrerPolicy === 'string') {
12173			tile.referrerPolicy = this.options.referrerPolicy;
12174		}
12175
12176		// The alt attribute is set to the empty string,
12177		// allowing screen readers to ignore the decorative image tiles.
12178		// https://www.w3.org/WAI/tutorials/images/decorative/
12179		// https://www.w3.org/TR/html-aria/#el-img-empty-alt
12180		tile.alt = '';
12181
12182		tile.src = this.getTileUrl(coords);
12183
12184		return tile;
12185	},
12186
12187	// @section Extension methods
12188	// @uninheritable
12189	// Layers extending `TileLayer` might reimplement the following method.
12190	// @method getTileUrl(coords: Object): String
12191	// Called only internally, returns the URL for a tile given its coordinates.
12192	// Classes extending `TileLayer` can override this function to provide custom tile URL naming schemes.
12193	getTileUrl: function (coords) {
12194		var data = {
12195			r: Browser.retina ? '@2x' : '',
12196			s: this._getSubdomain(coords),
12197			x: coords.x,
12198			y: coords.y,
12199			z: this._getZoomForUrl()
12200		};
12201		if (this._map && !this._map.options.crs.infinite) {
12202			var invertedY = this._globalTileRange.max.y - coords.y;
12203			if (this.options.tms) {
12204				data['y'] = invertedY;
12205			}
12206			data['-y'] = invertedY;
12207		}
12208
12209		return template(this._url, extend(data, this.options));
12210	},
12211
12212	_tileOnLoad: function (done, tile) {
12213		// For https://github.com/Leaflet/Leaflet/issues/3332
12214		if (Browser.ielt9) {
12215			setTimeout(bind(done, this, null, tile), 0);
12216		} else {
12217			done(null, tile);
12218		}
12219	},
12220
12221	_tileOnError: function (done, tile, e) {
12222		var errorUrl = this.options.errorTileUrl;
12223		if (errorUrl && tile.getAttribute('src') !== errorUrl) {
12224			tile.src = errorUrl;
12225		}
12226		done(e, tile);
12227	},
12228
12229	_onTileRemove: function (e) {
12230		e.tile.onload = null;
12231	},
12232
12233	_getZoomForUrl: function () {
12234		var zoom = this._tileZoom,
12235		maxZoom = this.options.maxZoom,
12236		zoomReverse = this.options.zoomReverse,
12237		zoomOffset = this.options.zoomOffset;
12238
12239		if (zoomReverse) {
12240			zoom = maxZoom - zoom;
12241		}
12242
12243		return zoom + zoomOffset;
12244	},
12245
12246	_getSubdomain: function (tilePoint) {
12247		var index = Math.abs(tilePoint.x + tilePoint.y) % this.options.subdomains.length;
12248		return this.options.subdomains[index];
12249	},
12250
12251	// stops loading all tiles in the background layer
12252	_abortLoading: function () {
12253		var i, tile;
12254		for (i in this._tiles) {
12255			if (this._tiles[i].coords.z !== this._tileZoom) {
12256				tile = this._tiles[i].el;
12257
12258				tile.onload = falseFn;
12259				tile.onerror = falseFn;
12260
12261				if (!tile.complete) {
12262					tile.src = emptyImageUrl;
12263					var coords = this._tiles[i].coords;
12264					remove(tile);
12265					delete this._tiles[i];
12266					// @event tileabort: TileEvent
12267					// Fired when a tile was loading but is now not wanted.
12268					this.fire('tileabort', {
12269						tile: tile,
12270						coords: coords
12271					});
12272				}
12273			}
12274		}
12275	},
12276
12277	_removeTile: function (key) {
12278		var tile = this._tiles[key];
12279		if (!tile) { return; }
12280
12281		// Cancels any pending http requests associated with the tile
12282		tile.el.setAttribute('src', emptyImageUrl);
12283
12284		return GridLayer.prototype._removeTile.call(this, key);
12285	},
12286
12287	_tileReady: function (coords, err, tile) {
12288		if (!this._map || (tile && tile.getAttribute('src') === emptyImageUrl)) {
12289			return;
12290		}
12291
12292		return GridLayer.prototype._tileReady.call(this, coords, err, tile);
12293	}
12294});
12295
12296
12297// @factory L.tilelayer(urlTemplate: String, options?: TileLayer options)
12298// Instantiates a tile layer object given a `URL template` and optionally an options object.
12299
12300function tileLayer(url, options) {
12301	return new TileLayer(url, options);
12302}
12303
12304/*
12305 * @class TileLayer.WMS
12306 * @inherits TileLayer
12307 * @aka L.TileLayer.WMS
12308 * Used to display [WMS](https://en.wikipedia.org/wiki/Web_Map_Service) services as tile layers on the map. Extends `TileLayer`.
12309 *
12310 * @example
12311 *
12312 * ```js
12313 * var nexrad = L.tileLayer.wms("http://mesonet.agron.iastate.edu/cgi-bin/wms/nexrad/n0r.cgi", {
12314 * 	layers: 'nexrad-n0r-900913',
12315 * 	format: 'image/png',
12316 * 	transparent: true,
12317 * 	attribution: "Weather data © 2012 IEM Nexrad"
12318 * });
12319 * ```
12320 */
12321
12322var TileLayerWMS = TileLayer.extend({
12323
12324	// @section
12325	// @aka TileLayer.WMS options
12326	// If any custom options not documented here are used, they will be sent to the
12327	// WMS server as extra parameters in each request URL. This can be useful for
12328	// [non-standard vendor WMS parameters](https://docs.geoserver.org/stable/en/user/services/wms/vendor.html).
12329	defaultWmsParams: {
12330		service: 'WMS',
12331		request: 'GetMap',
12332
12333		// @option layers: String = ''
12334		// **(required)** Comma-separated list of WMS layers to show.
12335		layers: '',
12336
12337		// @option styles: String = ''
12338		// Comma-separated list of WMS styles.
12339		styles: '',
12340
12341		// @option format: String = 'image/jpeg'
12342		// WMS image format (use `'image/png'` for layers with transparency).
12343		format: 'image/jpeg',
12344
12345		// @option transparent: Boolean = false
12346		// If `true`, the WMS service will return images with transparency.
12347		transparent: false,
12348
12349		// @option version: String = '1.1.1'
12350		// Version of the WMS service to use
12351		version: '1.1.1'
12352	},
12353
12354	options: {
12355		// @option crs: CRS = null
12356		// Coordinate Reference System to use for the WMS requests, defaults to
12357		// map CRS. Don't change this if you're not sure what it means.
12358		crs: null,
12359
12360		// @option uppercase: Boolean = false
12361		// If `true`, WMS request parameter keys will be uppercase.
12362		uppercase: false
12363	},
12364
12365	initialize: function (url, options) {
12366
12367		this._url = url;
12368
12369		var wmsParams = extend({}, this.defaultWmsParams);
12370
12371		// all keys that are not TileLayer options go to WMS params
12372		for (var i in options) {
12373			if (!(i in this.options)) {
12374				wmsParams[i] = options[i];
12375			}
12376		}
12377
12378		options = setOptions(this, options);
12379
12380		var realRetina = options.detectRetina && Browser.retina ? 2 : 1;
12381		var tileSize = this.getTileSize();
12382		wmsParams.width = tileSize.x * realRetina;
12383		wmsParams.height = tileSize.y * realRetina;
12384
12385		this.wmsParams = wmsParams;
12386	},
12387
12388	onAdd: function (map) {
12389
12390		this._crs = this.options.crs || map.options.crs;
12391		this._wmsVersion = parseFloat(this.wmsParams.version);
12392
12393		var projectionKey = this._wmsVersion >= 1.3 ? 'crs' : 'srs';
12394		this.wmsParams[projectionKey] = this._crs.code;
12395
12396		TileLayer.prototype.onAdd.call(this, map);
12397	},
12398
12399	getTileUrl: function (coords) {
12400
12401		var tileBounds = this._tileCoordsToNwSe(coords),
12402		    crs = this._crs,
12403		    bounds = toBounds(crs.project(tileBounds[0]), crs.project(tileBounds[1])),
12404		    min = bounds.min,
12405		    max = bounds.max,
12406		    bbox = (this._wmsVersion >= 1.3 && this._crs === EPSG4326 ?
12407		    [min.y, min.x, max.y, max.x] :
12408		    [min.x, min.y, max.x, max.y]).join(','),
12409		    url = TileLayer.prototype.getTileUrl.call(this, coords);
12410		return url +
12411			getParamString(this.wmsParams, url, this.options.uppercase) +
12412			(this.options.uppercase ? '&BBOX=' : '&bbox=') + bbox;
12413	},
12414
12415	// @method setParams(params: Object, noRedraw?: Boolean): this
12416	// Merges an object with the new parameters and re-requests tiles on the current screen (unless `noRedraw` was set to true).
12417	setParams: function (params, noRedraw) {
12418
12419		extend(this.wmsParams, params);
12420
12421		if (!noRedraw) {
12422			this.redraw();
12423		}
12424
12425		return this;
12426	}
12427});
12428
12429
12430// @factory L.tileLayer.wms(baseUrl: String, options: TileLayer.WMS options)
12431// Instantiates a WMS tile layer object given a base URL of the WMS service and a WMS parameters/options object.
12432function tileLayerWMS(url, options) {
12433	return new TileLayerWMS(url, options);
vendor: 52,931 bytes, lines 12433-14418
12433
12434}
12435
12436TileLayer.WMS = TileLayerWMS;
12437tileLayer.wms = tileLayerWMS;
12438
12439/*
12440 * @class Renderer
12441 * @inherits Layer
12442 * @aka L.Renderer
12443 *
12444 * Base class for vector renderer implementations (`SVG`, `Canvas`). Handles the
12445 * DOM container of the renderer, its bounds, and its zoom animation.
12446 *
12447 * A `Renderer` works as an implicit layer group for all `Path`s - the renderer
12448 * itself can be added or removed to the map. All paths use a renderer, which can
12449 * be implicit (the map will decide the type of renderer and use it automatically)
12450 * or explicit (using the [`renderer`](#path-renderer) option of the path).
12451 *
12452 * Do not use this class directly, use `SVG` and `Canvas` instead.
12453 *
12454 * @event update: Event
12455 * Fired when the renderer updates its bounds, center and zoom, for example when
12456 * its map has moved
12457 */
12458
12459var Renderer = Layer.extend({
12460
12461	// @section
12462	// @aka Renderer options
12463	options: {
12464		// @option padding: Number = 0.1
12465		// How much to extend the clip area around the map view (relative to its size)
12466		// e.g. 0.1 would be 10% of map view in each direction
12467		padding: 0.1
12468	},
12469
12470	initialize: function (options) {
12471		setOptions(this, options);
12472		stamp(this);
12473		this._layers = this._layers || {};
12474	},
12475
12476	onAdd: function () {
12477		if (!this._container) {
12478			this._initContainer(); // defined by renderer implementations
12479
12480			// always keep transform-origin as 0 0
12481			addClass(this._container, 'leaflet-zoom-animated');
12482		}
12483
12484		this.getPane().appendChild(this._container);
12485		this._update();
12486		this.on('update', this._updatePaths, this);
12487	},
12488
12489	onRemove: function () {
12490		this.off('update', this._updatePaths, this);
12491		this._destroyContainer();
12492	},
12493
12494	getEvents: function () {
12495		var events = {
12496			viewreset: this._reset,
12497			zoom: this._onZoom,
12498			moveend: this._update,
12499			zoomend: this._onZoomEnd
12500		};
12501		if (this._zoomAnimated) {
12502			events.zoomanim = this._onAnimZoom;
12503		}
12504		return events;
12505	},
12506
12507	_onAnimZoom: function (ev) {
12508		this._updateTransform(ev.center, ev.zoom);
12509	},
12510
12511	_onZoom: function () {
12512		this._updateTransform(this._map.getCenter(), this._map.getZoom());
12513	},
12514
12515	_updateTransform: function (center, zoom) {
12516		var scale = this._map.getZoomScale(zoom, this._zoom),
12517		    viewHalf = this._map.getSize().multiplyBy(0.5 + this.options.padding),
12518		    currentCenterPoint = this._map.project(this._center, zoom),
12519
12520		    topLeftOffset = viewHalf.multiplyBy(-scale).add(currentCenterPoint)
12521				  .subtract(this._map._getNewPixelOrigin(center, zoom));
12522
12523		if (Browser.any3d) {
12524			setTransform(this._container, topLeftOffset, scale);
12525		} else {
12526			setPosition(this._container, topLeftOffset);
12527		}
12528	},
12529
12530	_reset: function () {
12531		this._update();
12532		this._updateTransform(this._center, this._zoom);
12533
12534		for (var id in this._layers) {
12535			this._layers[id]._reset();
12536		}
12537	},
12538
12539	_onZoomEnd: function () {
12540		for (var id in this._layers) {
12541			this._layers[id]._project();
12542		}
12543	},
12544
12545	_updatePaths: function () {
12546		for (var id in this._layers) {
12547			this._layers[id]._update();
12548		}
12549	},
12550
12551	_update: function () {
12552		// Update pixel bounds of renderer container (for positioning/sizing/clipping later)
12553		// Subclasses are responsible of firing the 'update' event.
12554		var p = this.options.padding,
12555		    size = this._map.getSize(),
12556		    min = this._map.containerPointToLayerPoint(size.multiplyBy(-p)).round();
12557
12558		this._bounds = new Bounds(min, min.add(size.multiplyBy(1 + p * 2)).round());
12559
12560		this._center = this._map.getCenter();
12561		this._zoom = this._map.getZoom();
12562	}
12563});
12564
12565/*
12566 * @class Canvas
12567 * @inherits Renderer
12568 * @aka L.Canvas
12569 *
12570 * Allows vector layers to be displayed with [`<canvas>`](https://developer.mozilla.org/docs/Web/API/Canvas_API).
12571 * Inherits `Renderer`.
12572 *
12573 * Due to [technical limitations](https://caniuse.com/canvas), Canvas is not
12574 * available in all web browsers, notably IE8, and overlapping geometries might
12575 * not display properly in some edge cases.
12576 *
12577 * @example
12578 *
12579 * Use Canvas by default for all paths in the map:
12580 *
12581 * ```js
12582 * var map = L.map('map', {
12583 * 	renderer: L.canvas()
12584 * });
12585 * ```
12586 *
12587 * Use a Canvas renderer with extra padding for specific vector geometries:
12588 *
12589 * ```js
12590 * var map = L.map('map');
12591 * var myRenderer = L.canvas({ padding: 0.5 });
12592 * var line = L.polyline( coordinates, { renderer: myRenderer } );
12593 * var circle = L.circle( center, { renderer: myRenderer } );
12594 * ```
12595 */
12596
12597var Canvas = Renderer.extend({
12598
12599	// @section
12600	// @aka Canvas options
12601	options: {
12602		// @option tolerance: Number = 0
12603		// How much to extend the click tolerance around a path/object on the map.
12604		tolerance: 0
12605	},
12606
12607	getEvents: function () {
12608		var events = Renderer.prototype.getEvents.call(this);
12609		events.viewprereset = this._onViewPreReset;
12610		return events;
12611	},
12612
12613	_onViewPreReset: function () {
12614		// Set a flag so that a viewprereset+moveend+viewreset only updates&redraws once
12615		this._postponeUpdatePaths = true;
12616	},
12617
12618	onAdd: function () {
12619		Renderer.prototype.onAdd.call(this);
12620
12621		// Redraw vectors since canvas is cleared upon removal,
12622		// in case of removing the renderer itself from the map.
12623		this._draw();
12624	},
12625
12626	_initContainer: function () {
12627		var container = this._container = document.createElement('canvas');
12628
12629		on(container, 'mousemove', this._onMouseMove, this);
12630		on(container, 'click dblclick mousedown mouseup contextmenu', this._onClick, this);
12631		on(container, 'mouseout', this._handleMouseOut, this);
12632		container['_leaflet_disable_events'] = true;
12633
12634		this._ctx = container.getContext('2d');
12635	},
12636
12637	_destroyContainer: function () {
12638		cancelAnimFrame(this._redrawRequest);
12639		delete this._ctx;
12640		remove(this._container);
12641		off(this._container);
12642		delete this._container;
12643	},
12644
12645	_updatePaths: function () {
12646		if (this._postponeUpdatePaths) { return; }
12647
12648		var layer;
12649		this._redrawBounds = null;
12650		for (var id in this._layers) {
12651			layer = this._layers[id];
12652			layer._update();
12653		}
12654		this._redraw();
12655	},
12656
12657	_update: function () {
12658		if (this._map._animatingZoom && this._bounds) { return; }
12659
12660		Renderer.prototype._update.call(this);
12661
12662		var b = this._bounds,
12663		    container = this._container,
12664		    size = b.getSize(),
12665		    m = Browser.retina ? 2 : 1;
12666
12667		setPosition(container, b.min);
12668
12669		// set canvas size (also clearing it); use double size on retina
12670		container.width = m * size.x;
12671		container.height = m * size.y;
12672		container.style.width = size.x + 'px';
12673		container.style.height = size.y + 'px';
12674
12675		if (Browser.retina) {
12676			this._ctx.scale(2, 2);
12677		}
12678
12679		// translate so we use the same path coordinates after canvas element moves
12680		this._ctx.translate(-b.min.x, -b.min.y);
12681
12682		// Tell paths to redraw themselves
12683		this.fire('update');
12684	},
12685
12686	_reset: function () {
12687		Renderer.prototype._reset.call(this);
12688
12689		if (this._postponeUpdatePaths) {
12690			this._postponeUpdatePaths = false;
12691			this._updatePaths();
12692		}
12693	},
12694
12695	_initPath: function (layer) {
12696		this._updateDashArray(layer);
12697		this._layers[stamp(layer)] = layer;
12698
12699		var order = layer._order = {
12700			layer: layer,
12701			prev: this._drawLast,
12702			next: null
12703		};
12704		if (this._drawLast) { this._drawLast.next = order; }
12705		this._drawLast = order;
12706		this._drawFirst = this._drawFirst || this._drawLast;
12707	},
12708
12709	_addPath: function (layer) {
12710		this._requestRedraw(layer);
12711	},
12712
12713	_removePath: function (layer) {
12714		var order = layer._order;
12715		var next = order.next;
12716		var prev = order.prev;
12717
12718		if (next) {
12719			next.prev = prev;
12720		} else {
12721			this._drawLast = prev;
12722		}
12723		if (prev) {
12724			prev.next = next;
12725		} else {
12726			this._drawFirst = next;
12727		}
12728
12729		delete layer._order;
12730
12731		delete this._layers[stamp(layer)];
12732
12733		this._requestRedraw(layer);
12734	},
12735
12736	_updatePath: function (layer) {
12737		// Redraw the union of the layer's old pixel
12738		// bounds and the new pixel bounds.
12739		this._extendRedrawBounds(layer);
12740		layer._project();
12741		layer._update();
12742		// The redraw will extend the redraw bounds
12743		// with the new pixel bounds.
12744		this._requestRedraw(layer);
12745	},
12746
12747	_updateStyle: function (layer) {
12748		this._updateDashArray(layer);
12749		this._requestRedraw(layer);
12750	},
12751
12752	_updateDashArray: function (layer) {
12753		if (typeof layer.options.dashArray === 'string') {
12754			var parts = layer.options.dashArray.split(/[, ]+/),
12755			    dashArray = [],
12756			    dashValue,
12757			    i;
12758			for (i = 0; i < parts.length; i++) {
12759				dashValue = Number(parts[i]);
12760				// Ignore dash array containing invalid lengths
12761				if (isNaN(dashValue)) { return; }
12762				dashArray.push(dashValue);
12763			}
12764			layer.options._dashArray = dashArray;
12765		} else {
12766			layer.options._dashArray = layer.options.dashArray;
12767		}
12768	},
12769
12770	_requestRedraw: function (layer) {
12771		if (!this._map) { return; }
12772
12773		this._extendRedrawBounds(layer);
12774		this._redrawRequest = this._redrawRequest || requestAnimFrame(this._redraw, this);
12775	},
12776
12777	_extendRedrawBounds: function (layer) {
12778		if (layer._pxBounds) {
12779			var padding = (layer.options.weight || 0) + 1;
12780			this._redrawBounds = this._redrawBounds || new Bounds();
12781			this._redrawBounds.extend(layer._pxBounds.min.subtract([padding, padding]));
12782			this._redrawBounds.extend(layer._pxBounds.max.add([padding, padding]));
12783		}
12784	},
12785
12786	_redraw: function () {
12787		this._redrawRequest = null;
12788
12789		if (this._redrawBounds) {
12790			this._redrawBounds.min._floor();
12791			this._redrawBounds.max._ceil();
12792		}
12793
12794		this._clear(); // clear layers in redraw bounds
12795		this._draw(); // draw layers
12796
12797		this._redrawBounds = null;
12798	},
12799
12800	_clear: function () {
12801		var bounds = this._redrawBounds;
12802		if (bounds) {
12803			var size = bounds.getSize();
12804			this._ctx.clearRect(bounds.min.x, bounds.min.y, size.x, size.y);
12805		} else {
12806			this._ctx.save();
12807			this._ctx.setTransform(1, 0, 0, 1, 0, 0);
12808			this._ctx.clearRect(0, 0, this._container.width, this._container.height);
12809			this._ctx.restore();
12810		}
12811	},
12812
12813	_draw: function () {
12814		var layer, bounds = this._redrawBounds;
12815		this._ctx.save();
12816		if (bounds) {
12817			var size = bounds.getSize();
12818			this._ctx.beginPath();
12819			this._ctx.rect(bounds.min.x, bounds.min.y, size.x, size.y);
12820			this._ctx.clip();
12821		}
12822
12823		this._drawing = true;
12824
12825		for (var order = this._drawFirst; order; order = order.next) {
12826			layer = order.layer;
12827			if (!bounds || (layer._pxBounds && layer._pxBounds.intersects(bounds))) {
12828				layer._updatePath();
12829			}
12830		}
12831
12832		this._drawing = false;
12833
12834		this._ctx.restore();  // Restore state before clipping.
12835	},
12836
12837	_updatePoly: function (layer, closed) {
12838		if (!this._drawing) { return; }
12839
12840		var i, j, len2, p,
12841		    parts = layer._parts,
12842		    len = parts.length,
12843		    ctx = this._ctx;
12844
12845		if (!len) { return; }
12846
12847		ctx.beginPath();
12848
12849		for (i = 0; i < len; i++) {
12850			for (j = 0, len2 = parts[i].length; j < len2; j++) {
12851				p = parts[i][j];
12852				ctx[j ? 'lineTo' : 'moveTo'](p.x, p.y);
12853			}
12854			if (closed) {
12855				ctx.closePath();
12856			}
12857		}
12858
12859		this._fillStroke(ctx, layer);
12860
12861		// TODO optimization: 1 fill/stroke for all features with equal style instead of 1 for each feature
12862	},
12863
12864	_updateCircle: function (layer) {
12865
12866		if (!this._drawing || layer._empty()) { return; }
12867
12868		var p = layer._point,
12869		    ctx = this._ctx,
12870		    r = Math.max(Math.round(layer._radius), 1),
12871		    s = (Math.max(Math.round(layer._radiusY), 1) || r) / r;
12872
12873		if (s !== 1) {
12874			ctx.save();
12875			ctx.scale(1, s);
12876		}
12877
12878		ctx.beginPath();
12879		ctx.arc(p.x, p.y / s, r, 0, Math.PI * 2, false);
12880
12881		if (s !== 1) {
12882			ctx.restore();
12883		}
12884
12885		this._fillStroke(ctx, layer);
12886	},
12887
12888	_fillStroke: function (ctx, layer) {
12889		var options = layer.options;
12890
12891		if (options.fill) {
12892			ctx.globalAlpha = options.fillOpacity;
12893			ctx.fillStyle = options.fillColor || options.color;
12894			ctx.fill(options.fillRule || 'evenodd');
12895		}
12896
12897		if (options.stroke && options.weight !== 0) {
12898			if (ctx.setLineDash) {
12899				ctx.setLineDash(layer.options && layer.options._dashArray || []);
12900			}
12901			ctx.globalAlpha = options.opacity;
12902			ctx.lineWidth = options.weight;
12903			ctx.strokeStyle = options.color;
12904			ctx.lineCap = options.lineCap;
12905			ctx.lineJoin = options.lineJoin;
12906			ctx.stroke();
12907		}
12908	},
12909
12910	// Canvas obviously doesn't have mouse events for individual drawn objects,
12911	// so we emulate that by calculating what's under the mouse on mousemove/click manually
12912
12913	_onClick: function (e) {
12914		var point = this._map.mouseEventToLayerPoint(e), layer, clickedLayer;
12915
12916		for (var order = this._drawFirst; order; order = order.next) {
12917			layer = order.layer;
12918			if (layer.options.interactive && layer._containsPoint(point)) {
12919				if (!(e.type === 'click' || e.type === 'preclick') || !this._map._draggableMoved(layer)) {
12920					clickedLayer = layer;
12921				}
12922			}
12923		}
12924		this._fireEvent(clickedLayer ? [clickedLayer] : false, e);
12925	},
12926
12927	_onMouseMove: function (e) {
12928		if (!this._map || this._map.dragging.moving() || this._map._animatingZoom) { return; }
12929
12930		var point = this._map.mouseEventToLayerPoint(e);
12931		this._handleMouseHover(e, point);
12932	},
12933
12934
12935	_handleMouseOut: function (e) {
12936		var layer = this._hoveredLayer;
12937		if (layer) {
12938			// if we're leaving the layer, fire mouseout
12939			removeClass(this._container, 'leaflet-interactive');
12940			this._fireEvent([layer], e, 'mouseout');
12941			this._hoveredLayer = null;
12942			this._mouseHoverThrottled = false;
12943		}
12944	},
12945
12946	_handleMouseHover: function (e, point) {
12947		if (this._mouseHoverThrottled) {
12948			return;
12949		}
12950
12951		var layer, candidateHoveredLayer;
12952
12953		for (var order = this._drawFirst; order; order = order.next) {
12954			layer = order.layer;
12955			if (layer.options.interactive && layer._containsPoint(point)) {
12956				candidateHoveredLayer = layer;
12957			}
12958		}
12959
12960		if (candidateHoveredLayer !== this._hoveredLayer) {
12961			this._handleMouseOut(e);
12962
12963			if (candidateHoveredLayer) {
12964				addClass(this._container, 'leaflet-interactive'); // change cursor
12965				this._fireEvent([candidateHoveredLayer], e, 'mouseover');
12966				this._hoveredLayer = candidateHoveredLayer;
12967			}
12968		}
12969
12970		this._fireEvent(this._hoveredLayer ? [this._hoveredLayer] : false, e);
12971
12972		this._mouseHoverThrottled = true;
12973		setTimeout(bind(function () {
12974			this._mouseHoverThrottled = false;
12975		}, this), 32);
12976	},
12977
12978	_fireEvent: function (layers, e, type) {
12979		this._map._fireDOMEvent(e, type || e.type, layers);
12980	},
12981
12982	_bringToFront: function (layer) {
12983		var order = layer._order;
12984
12985		if (!order) { return; }
12986
12987		var next = order.next;
12988		var prev = order.prev;
12989
12990		if (next) {
12991			next.prev = prev;
12992		} else {
12993			// Already last
12994			return;
12995		}
12996		if (prev) {
12997			prev.next = next;
12998		} else if (next) {
12999			// Update first entry unless this is the
13000			// single entry
13001			this._drawFirst = next;
13002		}
13003
13004		order.prev = this._drawLast;
13005		this._drawLast.next = order;
13006
13007		order.next = null;
13008		this._drawLast = order;
13009
13010		this._requestRedraw(layer);
13011	},
13012
13013	_bringToBack: function (layer) {
13014		var order = layer._order;
13015
13016		if (!order) { return; }
13017
13018		var next = order.next;
13019		var prev = order.prev;
13020
13021		if (prev) {
13022			prev.next = next;
13023		} else {
13024			// Already first
13025			return;
13026		}
13027		if (next) {
13028			next.prev = prev;
13029		} else if (prev) {
13030			// Update last entry unless this is the
13031			// single entry
13032			this._drawLast = prev;
13033		}
13034
13035		order.prev = null;
13036
13037		order.next = this._drawFirst;
13038		this._drawFirst.prev = order;
13039		this._drawFirst = order;
13040
13041		this._requestRedraw(layer);
13042	}
13043});
13044
13045// @factory L.canvas(options?: Renderer options)
13046// Creates a Canvas renderer with the given options.
13047function canvas(options) {
13048	return Browser.canvas ? new Canvas(options) : null;
13049}
13050
13051/*
13052 * Thanks to Dmitry Baranovsky and his Raphael library for inspiration!
13053 */
13054
13055
13056var vmlCreate = (function () {
13057	try {
13058		document.namespaces.add('lvml', 'urn:schemas-microsoft-com:vml');
13059		return function (name) {
13060			return document.createElement('<lvml:' + name + ' class="lvml">');
13061		};
13062	} catch (e) {
13063		// Do not return fn from catch block so `e` can be garbage collected
13064		// See https://github.com/Leaflet/Leaflet/pull/7279
13065	}
13066	return function (name) {
13067		return document.createElement('<' + name + ' xmlns="urn:schemas-microsoft.com:vml" class="lvml">');
13068	};
13069})();
13070
13071
13072/*
13073 * @class SVG
13074 *
13075 *
13076 * VML was deprecated in 2012, which means VML functionality exists only for backwards compatibility
13077 * with old versions of Internet Explorer.
13078 */
13079
13080// mixin to redefine some SVG methods to handle VML syntax which is similar but with some differences
13081var vmlMixin = {
13082
13083	_initContainer: function () {
13084		this._container = create$1('div', 'leaflet-vml-container');
13085	},
13086
13087	_update: function () {
13088		if (this._map._animatingZoom) { return; }
13089		Renderer.prototype._update.call(this);
13090		this.fire('update');
13091	},
13092
13093	_initPath: function (layer) {
13094		var container = layer._container = vmlCreate('shape');
13095
13096		addClass(container, 'leaflet-vml-shape ' + (this.options.className || ''));
13097
13098		container.coordsize = '1 1';
13099
13100		layer._path = vmlCreate('path');
13101		container.appendChild(layer._path);
13102
13103		this._updateStyle(layer);
13104		this._layers[stamp(layer)] = layer;
13105	},
13106
13107	_addPath: function (layer) {
13108		var container = layer._container;
13109		this._container.appendChild(container);
13110
13111		if (layer.options.interactive) {
13112			layer.addInteractiveTarget(container);
13113		}
13114	},
13115
13116	_removePath: function (layer) {
13117		var container = layer._container;
13118		remove(container);
13119		layer.removeInteractiveTarget(container);
13120		delete this._layers[stamp(layer)];
13121	},
13122
13123	_updateStyle: function (layer) {
13124		var stroke = layer._stroke,
13125		    fill = layer._fill,
13126		    options = layer.options,
13127		    container = layer._container;
13128
13129		container.stroked = !!options.stroke;
13130		container.filled = !!options.fill;
13131
13132		if (options.stroke) {
13133			if (!stroke) {
13134				stroke = layer._stroke = vmlCreate('stroke');
13135			}
13136			container.appendChild(stroke);
13137			stroke.weight = options.weight + 'px';
13138			stroke.color = options.color;
13139			stroke.opacity = options.opacity;
13140
13141			if (options.dashArray) {
13142				stroke.dashStyle = isArray(options.dashArray) ?
13143				    options.dashArray.join(' ') :
13144				    options.dashArray.replace(/( *, *)/g, ' ');
13145			} else {
13146				stroke.dashStyle = '';
13147			}
13148			stroke.endcap = options.lineCap.replace('butt', 'flat');
13149			stroke.joinstyle = options.lineJoin;
13150
13151		} else if (stroke) {
13152			container.removeChild(stroke);
13153			layer._stroke = null;
13154		}
13155
13156		if (options.fill) {
13157			if (!fill) {
13158				fill = layer._fill = vmlCreate('fill');
13159			}
13160			container.appendChild(fill);
13161			fill.color = options.fillColor || options.color;
13162			fill.opacity = options.fillOpacity;
13163
13164		} else if (fill) {
13165			container.removeChild(fill);
13166			layer._fill = null;
13167		}
13168	},
13169
13170	_updateCircle: function (layer) {
13171		var p = layer._point.round(),
13172		    r = Math.round(layer._radius),
13173		    r2 = Math.round(layer._radiusY || r);
13174
13175		this._setPath(layer, layer._empty() ? 'M0 0' :
13176			'AL ' + p.x + ',' + p.y + ' ' + r + ',' + r2 + ' 0,' + (65535 * 360));
13177	},
13178
13179	_setPath: function (layer, path) {
13180		layer._path.v = path;
13181	},
13182
13183	_bringToFront: function (layer) {
13184		toFront(layer._container);
13185	},
13186
13187	_bringToBack: function (layer) {
13188		toBack(layer._container);
13189	}
13190};
13191
13192var create = Browser.vml ? vmlCreate : svgCreate;
13193
13194/*
13195 * @class SVG
13196 * @inherits Renderer
13197 * @aka L.SVG
13198 *
13199 * Allows vector layers to be displayed with [SVG](https://developer.mozilla.org/docs/Web/SVG).
13200 * Inherits `Renderer`.
13201 *
13202 * Due to [technical limitations](https://caniuse.com/svg), SVG is not
13203 * available in all web browsers, notably Android 2.x and 3.x.
13204 *
13205 * Although SVG is not available on IE7 and IE8, these browsers support
13206 * [VML](https://en.wikipedia.org/wiki/Vector_Markup_Language)
13207 * (a now deprecated technology), and the SVG renderer will fall back to VML in
13208 * this case.
13209 *
13210 * @example
13211 *
13212 * Use SVG by default for all paths in the map:
13213 *
13214 * ```js
13215 * var map = L.map('map', {
13216 * 	renderer: L.svg()
13217 * });
13218 * ```
13219 *
13220 * Use a SVG renderer with extra padding for specific vector geometries:
13221 *
13222 * ```js
13223 * var map = L.map('map');
13224 * var myRenderer = L.svg({ padding: 0.5 });
13225 * var line = L.polyline( coordinates, { renderer: myRenderer } );
13226 * var circle = L.circle( center, { renderer: myRenderer } );
13227 * ```
13228 */
13229
13230var SVG = Renderer.extend({
13231
13232	_initContainer: function () {
13233		this._container = create('svg');
13234
13235		// makes it possible to click through svg root; we'll reset it back in individual paths
13236		this._container.setAttribute('pointer-events', 'none');
13237
13238		this._rootGroup = create('g');
13239		this._container.appendChild(this._rootGroup);
13240	},
13241
13242	_destroyContainer: function () {
13243		remove(this._container);
13244		off(this._container);
13245		delete this._container;
13246		delete this._rootGroup;
13247		delete this._svgSize;
13248	},
13249
13250	_update: function () {
13251		if (this._map._animatingZoom && this._bounds) { return; }
13252
13253		Renderer.prototype._update.call(this);
13254
13255		var b = this._bounds,
13256		    size = b.getSize(),
13257		    container = this._container;
13258
13259		// set size of svg-container if changed
13260		if (!this._svgSize || !this._svgSize.equals(size)) {
13261			this._svgSize = size;
13262			container.setAttribute('width', size.x);
13263			container.setAttribute('height', size.y);
13264		}
13265
13266		// movement: update container viewBox so that we don't have to change coordinates of individual layers
13267		setPosition(container, b.min);
13268		container.setAttribute('viewBox', [b.min.x, b.min.y, size.x, size.y].join(' '));
13269
13270		this.fire('update');
13271	},
13272
13273	// methods below are called by vector layers implementations
13274
13275	_initPath: function (layer) {
13276		var path = layer._path = create('path');
13277
13278		// @namespace Path
13279		// @option className: String = null
13280		// Custom class name set on an element. Only for SVG renderer.
13281		if (layer.options.className) {
13282			addClass(path, layer.options.className);
13283		}
13284
13285		if (layer.options.interactive) {
13286			addClass(path, 'leaflet-interactive');
13287		}
13288
13289		this._updateStyle(layer);
13290		this._layers[stamp(layer)] = layer;
13291	},
13292
13293	_addPath: function (layer) {
13294		if (!this._rootGroup) { this._initContainer(); }
13295		this._rootGroup.appendChild(layer._path);
13296		layer.addInteractiveTarget(layer._path);
13297	},
13298
13299	_removePath: function (layer) {
13300		remove(layer._path);
13301		layer.removeInteractiveTarget(layer._path);
13302		delete this._layers[stamp(layer)];
13303	},
13304
13305	_updatePath: function (layer) {
13306		layer._project();
13307		layer._update();
13308	},
13309
13310	_updateStyle: function (layer) {
13311		var path = layer._path,
13312		    options = layer.options;
13313
13314		if (!path) { return; }
13315
13316		if (options.stroke) {
13317			path.setAttribute('stroke', options.color);
13318			path.setAttribute('stroke-opacity', options.opacity);
13319			path.setAttribute('stroke-width', options.weight);
13320			path.setAttribute('stroke-linecap', options.lineCap);
13321			path.setAttribute('stroke-linejoin', options.lineJoin);
13322
13323			if (options.dashArray) {
13324				path.setAttribute('stroke-dasharray', options.dashArray);
13325			} else {
13326				path.removeAttribute('stroke-dasharray');
13327			}
13328
13329			if (options.dashOffset) {
13330				path.setAttribute('stroke-dashoffset', options.dashOffset);
13331			} else {
13332				path.removeAttribute('stroke-dashoffset');
13333			}
13334		} else {
13335			path.setAttribute('stroke', 'none');
13336		}
13337
13338		if (options.fill) {
13339			path.setAttribute('fill', options.fillColor || options.color);
13340			path.setAttribute('fill-opacity', options.fillOpacity);
13341			path.setAttribute('fill-rule', options.fillRule || 'evenodd');
13342		} else {
13343			path.setAttribute('fill', 'none');
13344		}
13345	},
13346
13347	_updatePoly: function (layer, closed) {
13348		this._setPath(layer, pointsToPath(layer._parts, closed));
13349	},
13350
13351	_updateCircle: function (layer) {
13352		var p = layer._point,
13353		    r = Math.max(Math.round(layer._radius), 1),
13354		    r2 = Math.max(Math.round(layer._radiusY), 1) || r,
13355		    arc = 'a' + r + ',' + r2 + ' 0 1,0 ';
13356
13357		// drawing a circle with two half-arcs
13358		var d = layer._empty() ? 'M0 0' :
13359			'M' + (p.x - r) + ',' + p.y +
13360			arc + (r * 2) + ',0 ' +
13361			arc + (-r * 2) + ',0 ';
13362
13363		this._setPath(layer, d);
13364	},
13365
13366	_setPath: function (layer, path) {
13367		layer._path.setAttribute('d', path);
13368	},
13369
13370	// SVG does not have the concept of zIndex so we resort to changing the DOM order of elements
13371	_bringToFront: function (layer) {
13372		toFront(layer._path);
13373	},
13374
13375	_bringToBack: function (layer) {
13376		toBack(layer._path);
13377	}
13378});
13379
13380if (Browser.vml) {
13381	SVG.include(vmlMixin);
13382}
13383
13384// @namespace SVG
13385// @factory L.svg(options?: Renderer options)
13386// Creates a SVG renderer with the given options.
13387function svg(options) {
13388	return Browser.svg || Browser.vml ? new SVG(options) : null;
13389}
13390
13391Map.include({
13392	// @namespace Map; @method getRenderer(layer: Path): Renderer
13393	// Returns the instance of `Renderer` that should be used to render the given
13394	// `Path`. It will ensure that the `renderer` options of the map and paths
13395	// are respected, and that the renderers do exist on the map.
13396	getRenderer: function (layer) {
13397		// @namespace Path; @option renderer: Renderer
13398		// Use this specific instance of `Renderer` for this path. Takes
13399		// precedence over the map's [default renderer](#map-renderer).
13400		var renderer = layer.options.renderer || this._getPaneRenderer(layer.options.pane) || this.options.renderer || this._renderer;
13401
13402		if (!renderer) {
13403			renderer = this._renderer = this._createRenderer();
13404		}
13405
13406		if (!this.hasLayer(renderer)) {
13407			this.addLayer(renderer);
13408		}
13409		return renderer;
13410	},
13411
13412	_getPaneRenderer: function (name) {
13413		if (name === 'overlayPane' || name === undefined) {
13414			return false;
13415		}
13416
13417		var renderer = this._paneRenderers[name];
13418		if (renderer === undefined) {
13419			renderer = this._createRenderer({pane: name});
13420			this._paneRenderers[name] = renderer;
13421		}
13422		return renderer;
13423	},
13424
13425	_createRenderer: function (options) {
13426		// @namespace Map; @option preferCanvas: Boolean = false
13427		// Whether `Path`s should be rendered on a `Canvas` renderer.
13428		// By default, all `Path`s are rendered in a `SVG` renderer.
13429		return (this.options.preferCanvas && canvas(options)) || svg(options);
13430	}
13431});
13432
13433/*
13434 * L.Rectangle extends Polygon and creates a rectangle when passed a LatLngBounds object.
13435 */
13436
13437/*
13438 * @class Rectangle
13439 * @aka L.Rectangle
13440 * @inherits Polygon
13441 *
13442 * A class for drawing rectangle overlays on a map. Extends `Polygon`.
13443 *
13444 * @example
13445 *
13446 * ```js
13447 * // define rectangle geographical bounds
13448 * var bounds = [[54.559322, -5.767822], [56.1210604, -3.021240]];
13449 *
13450 * // create an orange rectangle
13451 * L.rectangle(bounds, {color: "#ff7800", weight: 1}).addTo(map);
13452 *
13453 * // zoom the map to the rectangle bounds
13454 * map.fitBounds(bounds);
13455 * ```
13456 *
13457 */
13458
13459
13460var Rectangle = Polygon.extend({
13461	initialize: function (latLngBounds, options) {
13462		Polygon.prototype.initialize.call(this, this._boundsToLatLngs(latLngBounds), options);
13463	},
13464
13465	// @method setBounds(latLngBounds: LatLngBounds): this
13466	// Redraws the rectangle with the passed bounds.
13467	setBounds: function (latLngBounds) {
13468		return this.setLatLngs(this._boundsToLatLngs(latLngBounds));
13469	},
13470
13471	_boundsToLatLngs: function (latLngBounds) {
13472		latLngBounds = toLatLngBounds(latLngBounds);
13473		return [
13474			latLngBounds.getSouthWest(),
13475			latLngBounds.getNorthWest(),
13476			latLngBounds.getNorthEast(),
13477			latLngBounds.getSouthEast()
13478		];
13479	}
13480});
13481
13482
13483// @factory L.rectangle(latLngBounds: LatLngBounds, options?: Polyline options)
13484function rectangle(latLngBounds, options) {
13485	return new Rectangle(latLngBounds, options);
13486}
13487
13488SVG.create = create;
13489SVG.pointsToPath = pointsToPath;
13490
13491GeoJSON.geometryToLayer = geometryToLayer;
13492GeoJSON.coordsToLatLng = coordsToLatLng;
13493GeoJSON.coordsToLatLngs = coordsToLatLngs;
13494GeoJSON.latLngToCoords = latLngToCoords;
13495GeoJSON.latLngsToCoords = latLngsToCoords;
13496GeoJSON.getFeature = getFeature;
13497GeoJSON.asFeature = asFeature;
13498
13499/*
13500 * L.Handler.BoxZoom is used to add shift-drag zoom interaction to the map
13501 * (zoom to a selected bounding box), enabled by default.
13502 */
13503
13504// @namespace Map
13505// @section Interaction Options
13506Map.mergeOptions({
13507	// @option boxZoom: Boolean = true
13508	// Whether the map can be zoomed to a rectangular area specified by
13509	// dragging the mouse while pressing the shift key.
13510	boxZoom: true
13511});
13512
13513var BoxZoom = Handler.extend({
13514	initialize: function (map) {
13515		this._map = map;
13516		this._container = map._container;
13517		this._pane = map._panes.overlayPane;
13518		this._resetStateTimeout = 0;
13519		map.on('unload', this._destroy, this);
13520	},
13521
13522	addHooks: function () {
13523		on(this._container, 'mousedown', this._onMouseDown, this);
13524	},
13525
13526	removeHooks: function () {
13527		off(this._container, 'mousedown', this._onMouseDown, this);
13528	},
13529
13530	moved: function () {
13531		return this._moved;
13532	},
13533
13534	_destroy: function () {
13535		remove(this._pane);
13536		delete this._pane;
13537	},
13538
13539	_resetState: function () {
13540		this._resetStateTimeout = 0;
13541		this._moved = false;
13542	},
13543
13544	_clearDeferredResetState: function () {
13545		if (this._resetStateTimeout !== 0) {
13546			clearTimeout(this._resetStateTimeout);
13547			this._resetStateTimeout = 0;
13548		}
13549	},
13550
13551	_onMouseDown: function (e) {
13552		if (!e.shiftKey || ((e.which !== 1) && (e.button !== 1))) { return false; }
13553
13554		// Clear the deferred resetState if it hasn't executed yet, otherwise it
13555		// will interrupt the interaction and orphan a box element in the container.
13556		this._clearDeferredResetState();
13557		this._resetState();
13558
13559		disableTextSelection();
13560		disableImageDrag();
13561
13562		this._startPoint = this._map.mouseEventToContainerPoint(e);
13563
13564		on(document, {
13565			contextmenu: stop,
13566			mousemove: this._onMouseMove,
13567			mouseup: this._onMouseUp,
13568			keydown: this._onKeyDown
13569		}, this);
13570	},
13571
13572	_onMouseMove: function (e) {
13573		if (!this._moved) {
13574			this._moved = true;
13575
13576			this._box = create$1('div', 'leaflet-zoom-box', this._container);
13577			addClass(this._container, 'leaflet-crosshair');
13578
13579			this._map.fire('boxzoomstart');
13580		}
13581
13582		this._point = this._map.mouseEventToContainerPoint(e);
13583
13584		var bounds = new Bounds(this._point, this._startPoint),
13585		    size = bounds.getSize();
13586
13587		setPosition(this._box, bounds.min);
13588
13589		this._box.style.width  = size.x + 'px';
13590		this._box.style.height = size.y + 'px';
13591	},
13592
13593	_finish: function () {
13594		if (this._moved) {
13595			remove(this._box);
13596			removeClass(this._container, 'leaflet-crosshair');
13597		}
13598
13599		enableTextSelection();
13600		enableImageDrag();
13601
13602		off(document, {
13603			contextmenu: stop,
13604			mousemove: this._onMouseMove,
13605			mouseup: this._onMouseUp,
13606			keydown: this._onKeyDown
13607		}, this);
13608	},
13609
13610	_onMouseUp: function (e) {
13611		if ((e.which !== 1) && (e.button !== 1)) { return; }
13612
13613		this._finish();
13614
13615		if (!this._moved) { return; }
13616		// Postpone to next JS tick so internal click event handling
13617		// still see it as "moved".
13618		this._clearDeferredResetState();
13619		this._resetStateTimeout = setTimeout(bind(this._resetState, this), 0);
13620
13621		var bounds = new LatLngBounds(
13622		        this._map.containerPointToLatLng(this._startPoint),
13623		        this._map.containerPointToLatLng(this._point));
13624
13625		this._map
13626			.fitBounds(bounds)
13627			.fire('boxzoomend', {boxZoomBounds: bounds});
13628	},
13629
13630	_onKeyDown: function (e) {
13631		if (e.keyCode === 27) {
13632			this._finish();
13633			this._clearDeferredResetState();
13634			this._resetState();
13635		}
13636	}
13637});
13638
13639// @section Handlers
13640// @property boxZoom: Handler
13641// Box (shift-drag with mouse) zoom handler.
13642Map.addInitHook('addHandler', 'boxZoom', BoxZoom);
13643
13644/*
13645 * L.Handler.DoubleClickZoom is used to handle double-click zoom on the map, enabled by default.
13646 */
13647
13648// @namespace Map
13649// @section Interaction Options
13650
13651Map.mergeOptions({
13652	// @option doubleClickZoom: Boolean|String = true
13653	// Whether the map can be zoomed in by double clicking on it and
13654	// zoomed out by double clicking while holding shift. If passed
13655	// `'center'`, double-click zoom will zoom to the center of the
13656	//  view regardless of where the mouse was.
13657	doubleClickZoom: true
13658});
13659
13660var DoubleClickZoom = Handler.extend({
13661	addHooks: function () {
13662		this._map.on('dblclick', this._onDoubleClick, this);
13663	},
13664
13665	removeHooks: function () {
13666		this._map.off('dblclick', this._onDoubleClick, this);
13667	},
13668
13669	_onDoubleClick: function (e) {
13670		var map = this._map,
13671		    oldZoom = map.getZoom(),
13672		    delta = map.options.zoomDelta,
13673		    zoom = e.originalEvent.shiftKey ? oldZoom - delta : oldZoom + delta;
13674
13675		if (map.options.doubleClickZoom === 'center') {
13676			map.setZoom(zoom);
13677		} else {
13678			map.setZoomAround(e.containerPoint, zoom);
13679		}
13680	}
13681});
13682
13683// @section Handlers
13684//
13685// Map properties include interaction handlers that allow you to control
13686// interaction behavior in runtime, enabling or disabling certain features such
13687// as dragging or touch zoom (see `Handler` methods). For example:
13688//
13689// ```js
13690// map.doubleClickZoom.disable();
13691// ```
13692//
13693// @property doubleClickZoom: Handler
13694// Double click zoom handler.
13695Map.addInitHook('addHandler', 'doubleClickZoom', DoubleClickZoom);
13696
13697/*
13698 * L.Handler.MapDrag is used to make the map draggable (with panning inertia), enabled by default.
13699 */
13700
13701// @namespace Map
13702// @section Interaction Options
13703Map.mergeOptions({
13704	// @option dragging: Boolean = true
13705	// Whether the map is draggable with mouse/touch or not.
13706	dragging: true,
13707
13708	// @section Panning Inertia Options
13709	// @option inertia: Boolean = *
13710	// If enabled, panning of the map will have an inertia effect where
13711	// the map builds momentum while dragging and continues moving in
13712	// the same direction for some time. Feels especially nice on touch
13713	// devices. Enabled by default.
13714	inertia: true,
13715
13716	// @option inertiaDeceleration: Number = 3000
13717	// The rate with which the inertial movement slows down, in pixels/second².
13718	inertiaDeceleration: 3400, // px/s^2
13719
13720	// @option inertiaMaxSpeed: Number = Infinity
13721	// Max speed of the inertial movement, in pixels/second.
13722	inertiaMaxSpeed: Infinity, // px/s
13723
13724	// @option easeLinearity: Number = 0.2
13725	easeLinearity: 0.2,
13726
13727	// TODO refactor, move to CRS
13728	// @option worldCopyJump: Boolean = false
13729	// With this option enabled, the map tracks when you pan to another "copy"
13730	// of the world and seamlessly jumps to the original one so that all overlays
13731	// like markers and vector layers are still visible.
13732	worldCopyJump: false,
13733
13734	// @option maxBoundsViscosity: Number = 0.0
13735	// If `maxBounds` is set, this option will control how solid the bounds
13736	// are when dragging the map around. The default value of `0.0` allows the
13737	// user to drag outside the bounds at normal speed, higher values will
13738	// slow down map dragging outside bounds, and `1.0` makes the bounds fully
13739	// solid, preventing the user from dragging outside the bounds.
13740	maxBoundsViscosity: 0.0
13741});
13742
13743var Drag = Handler.extend({
13744	addHooks: function () {
13745		if (!this._draggable) {
13746			var map = this._map;
13747
13748			this._draggable = new Draggable(map._mapPane, map._container);
13749
13750			this._draggable.on({
13751				dragstart: this._onDragStart,
13752				drag: this._onDrag,
13753				dragend: this._onDragEnd
13754			}, this);
13755
13756			this._draggable.on('predrag', this._onPreDragLimit, this);
13757			if (map.options.worldCopyJump) {
13758				this._draggable.on('predrag', this._onPreDragWrap, this);
13759				map.on('zoomend', this._onZoomEnd, this);
13760
13761				map.whenReady(this._onZoomEnd, this);
13762			}
13763		}
13764		addClass(this._map._container, 'leaflet-grab leaflet-touch-drag');
13765		this._draggable.enable();
13766		this._positions = [];
13767		this._times = [];
13768	},
13769
13770	removeHooks: function () {
13771		removeClass(this._map._container, 'leaflet-grab');
13772		removeClass(this._map._container, 'leaflet-touch-drag');
13773		this._draggable.disable();
13774	},
13775
13776	moved: function () {
13777		return this._draggable && this._draggable._moved;
13778	},
13779
13780	moving: function () {
13781		return this._draggable && this._draggable._moving;
13782	},
13783
13784	_onDragStart: function () {
13785		var map = this._map;
13786
13787		map._stop();
13788		if (this._map.options.maxBounds && this._map.options.maxBoundsViscosity) {
13789			var bounds = toLatLngBounds(this._map.options.maxBounds);
13790
13791			this._offsetLimit = toBounds(
13792				this._map.latLngToContainerPoint(bounds.getNorthWest()).multiplyBy(-1),
13793				this._map.latLngToContainerPoint(bounds.getSouthEast()).multiplyBy(-1)
13794					.add(this._map.getSize()));
13795
13796			this._viscosity = Math.min(1.0, Math.max(0.0, this._map.options.maxBoundsViscosity));
13797		} else {
13798			this._offsetLimit = null;
13799		}
13800
13801		map
13802		    .fire('movestart')
13803		    .fire('dragstart');
13804
13805		if (map.options.inertia) {
13806			this._positions = [];
13807			this._times = [];
13808		}
13809	},
13810
13811	_onDrag: function (e) {
13812		if (this._map.options.inertia) {
13813			var time = this._lastTime = +new Date(),
13814			    pos = this._lastPos = this._draggable._absPos || this._draggable._newPos;
13815
13816			this._positions.push(pos);
13817			this._times.push(time);
13818
13819			this._prunePositions(time);
13820		}
13821
13822		this._map
13823		    .fire('move', e)
13824		    .fire('drag', e);
13825	},
13826
13827	_prunePositions: function (time) {
13828		while (this._positions.length > 1 && time - this._times[0] > 50) {
13829			this._positions.shift();
13830			this._times.shift();
13831		}
13832	},
13833
13834	_onZoomEnd: function () {
13835		var pxCenter = this._map.getSize().divideBy(2),
13836		    pxWorldCenter = this._map.latLngToLayerPoint([0, 0]);
13837
13838		this._initialWorldOffset = pxWorldCenter.subtract(pxCenter).x;
13839		this._worldWidth = this._map.getPixelWorldBounds().getSize().x;
13840	},
13841
13842	_viscousLimit: function (value, threshold) {
13843		return value - (value - threshold) * this._viscosity;
13844	},
13845
13846	_onPreDragLimit: function () {
13847		if (!this._viscosity || !this._offsetLimit) { return; }
13848
13849		var offset = this._draggable._newPos.subtract(this._draggable._startPos);
13850
13851		var limit = this._offsetLimit;
13852		if (offset.x < limit.min.x) { offset.x = this._viscousLimit(offset.x, limit.min.x); }
13853		if (offset.y < limit.min.y) { offset.y = this._viscousLimit(offset.y, limit.min.y); }
13854		if (offset.x > limit.max.x) { offset.x = this._viscousLimit(offset.x, limit.max.x); }
13855		if (offset.y > limit.max.y) { offset.y = this._viscousLimit(offset.y, limit.max.y); }
13856
13857		this._draggable._newPos = this._draggable._startPos.add(offset);
13858	},
13859
13860	_onPreDragWrap: function () {
13861		// TODO refactor to be able to adjust map pane position after zoom
13862		var worldWidth = this._worldWidth,
13863		    halfWidth = Math.round(worldWidth / 2),
13864		    dx = this._initialWorldOffset,
13865		    x = this._draggable._newPos.x,
13866		    newX1 = (x - halfWidth + dx) % worldWidth + halfWidth - dx,
13867		    newX2 = (x + halfWidth + dx) % worldWidth - halfWidth - dx,
13868		    newX = Math.abs(newX1 + dx) < Math.abs(newX2 + dx) ? newX1 : newX2;
13869
13870		this._draggable._absPos = this._draggable._newPos.clone();
13871		this._draggable._newPos.x = newX;
13872	},
13873
13874	_onDragEnd: function (e) {
13875		var map = this._map,
13876		    options = map.options,
13877
13878		    noInertia = !options.inertia || e.noInertia || this._times.length < 2;
13879
13880		map.fire('dragend', e);
13881
13882		if (noInertia) {
13883			map.fire('moveend');
13884
13885		} else {
13886			this._prunePositions(+new Date());
13887
13888			var direction = this._lastPos.subtract(this._positions[0]),
13889			    duration = (this._lastTime - this._times[0]) / 1000,
13890			    ease = options.easeLinearity,
13891
13892			    speedVector = direction.multiplyBy(ease / duration),
13893			    speed = speedVector.distanceTo([0, 0]),
13894
13895			    limitedSpeed = Math.min(options.inertiaMaxSpeed, speed),
13896			    limitedSpeedVector = speedVector.multiplyBy(limitedSpeed / speed),
13897
13898			    decelerationDuration = limitedSpeed / (options.inertiaDeceleration * ease),
13899			    offset = limitedSpeedVector.multiplyBy(-decelerationDuration / 2).round();
13900
13901			if (!offset.x && !offset.y) {
13902				map.fire('moveend');
13903
13904			} else {
13905				offset = map._limitOffset(offset, map.options.maxBounds);
13906
13907				requestAnimFrame(function () {
13908					map.panBy(offset, {
13909						duration: decelerationDuration,
13910						easeLinearity: ease,
13911						noMoveStart: true,
13912						animate: true
13913					});
13914				});
13915			}
13916		}
13917	}
13918});
13919
13920// @section Handlers
13921// @property dragging: Handler
13922// Map dragging handler (by both mouse and touch).
13923Map.addInitHook('addHandler', 'dragging', Drag);
13924
13925/*
13926 * L.Map.Keyboard is handling keyboard interaction with the map, enabled by default.
13927 */
13928
13929// @namespace Map
13930// @section Keyboard Navigation Options
13931Map.mergeOptions({
13932	// @option keyboard: Boolean = true
13933	// Makes the map focusable and allows users to navigate the map with keyboard
13934	// arrows and `+`/`-` keys.
13935	keyboard: true,
13936
13937	// @option keyboardPanDelta: Number = 80
13938	// Amount of pixels to pan when pressing an arrow key.
13939	keyboardPanDelta: 80
13940});
13941
13942var Keyboard = Handler.extend({
13943
13944	keyCodes: {
13945		left:    [37],
13946		right:   [39],
13947		down:    [40],
13948		up:      [38],
13949		zoomIn:  [187, 107, 61, 171],
13950		zoomOut: [189, 109, 54, 173]
13951	},
13952
13953	initialize: function (map) {
13954		this._map = map;
13955
13956		this._setPanDelta(map.options.keyboardPanDelta);
13957		this._setZoomDelta(map.options.zoomDelta);
13958	},
13959
13960	addHooks: function () {
13961		var container = this._map._container;
13962
13963		// make the container focusable by tabbing
13964		if (container.tabIndex <= 0) {
13965			container.tabIndex = '0';
13966		}
13967
13968		on(container, {
13969			focus: this._onFocus,
13970			blur: this._onBlur,
13971			mousedown: this._onMouseDown
13972		}, this);
13973
13974		this._map.on({
13975			focus: this._addHooks,
13976			blur: this._removeHooks
13977		}, this);
13978	},
13979
13980	removeHooks: function () {
13981		this._removeHooks();
13982
13983		off(this._map._container, {
13984			focus: this._onFocus,
13985			blur: this._onBlur,
13986			mousedown: this._onMouseDown
13987		}, this);
13988
13989		this._map.off({
13990			focus: this._addHooks,
13991			blur: this._removeHooks
13992		}, this);
13993	},
13994
13995	_onMouseDown: function () {
13996		if (this._focused) { return; }
13997
13998		var body = document.body,
13999		    docEl = document.documentElement,
14000		    top = body.scrollTop || docEl.scrollTop,
14001		    left = body.scrollLeft || docEl.scrollLeft;
14002
14003		this._map._container.focus();
14004
14005		window.scrollTo(left, top);
14006	},
14007
14008	_onFocus: function () {
14009		this._focused = true;
14010		this._map.fire('focus');
14011	},
14012
14013	_onBlur: function () {
14014		this._focused = false;
14015		this._map.fire('blur');
14016	},
14017
14018	_setPanDelta: function (panDelta) {
14019		var keys = this._panKeys = {},
14020		    codes = this.keyCodes,
14021		    i, len;
14022
14023		for (i = 0, len = codes.left.length; i < len; i++) {
14024			keys[codes.left[i]] = [-1 * panDelta, 0];
14025		}
14026		for (i = 0, len = codes.right.length; i < len; i++) {
14027			keys[codes.right[i]] = [panDelta, 0];
14028		}
14029		for (i = 0, len = codes.down.length; i < len; i++) {
14030			keys[codes.down[i]] = [0, panDelta];
14031		}
14032		for (i = 0, len = codes.up.length; i < len; i++) {
14033			keys[codes.up[i]] = [0, -1 * panDelta];
14034		}
14035	},
14036
14037	_setZoomDelta: function (zoomDelta) {
14038		var keys = this._zoomKeys = {},
14039		    codes = this.keyCodes,
14040		    i, len;
14041
14042		for (i = 0, len = codes.zoomIn.length; i < len; i++) {
14043			keys[codes.zoomIn[i]] = zoomDelta;
14044		}
14045		for (i = 0, len = codes.zoomOut.length; i < len; i++) {
14046			keys[codes.zoomOut[i]] = -zoomDelta;
14047		}
14048	},
14049
14050	_addHooks: function () {
14051		on(document, 'keydown', this._onKeyDown, this);
14052	},
14053
14054	_removeHooks: function () {
14055		off(document, 'keydown', this._onKeyDown, this);
14056	},
14057
14058	_onKeyDown: function (e) {
14059		if (e.altKey || e.ctrlKey || e.metaKey) { return; }
14060
14061		var key = e.keyCode,
14062		    map = this._map,
14063		    offset;
14064
14065		if (key in this._panKeys) {
14066			if (!map._panAnim || !map._panAnim._inProgress) {
14067				offset = this._panKeys[key];
14068				if (e.shiftKey) {
14069					offset = toPoint(offset).multiplyBy(3);
14070				}
14071
14072				if (map.options.maxBounds) {
14073					offset = map._limitOffset(toPoint(offset), map.options.maxBounds);
14074				}
14075
14076				if (map.options.worldCopyJump) {
14077					var newLatLng = map.wrapLatLng(map.unproject(map.project(map.getCenter()).add(offset)));
14078					map.panTo(newLatLng);
14079				} else {
14080					map.panBy(offset);
14081				}
14082			}
14083		} else if (key in this._zoomKeys) {
14084			map.setZoom(map.getZoom() + (e.shiftKey ? 3 : 1) * this._zoomKeys[key]);
14085
14086		} else if (key === 27 && map._popup && map._popup.options.closeOnEscapeKey) {
14087			map.closePopup();
14088
14089		} else {
14090			return;
14091		}
14092
14093		stop(e);
14094	}
14095});
14096
14097// @section Handlers
14098// @section Handlers
14099// @property keyboard: Handler
14100// Keyboard navigation handler.
14101Map.addInitHook('addHandler', 'keyboard', Keyboard);
14102
14103/*
14104 * L.Handler.ScrollWheelZoom is used by L.Map to enable mouse scroll wheel zoom on the map.
14105 */
14106
14107// @namespace Map
14108// @section Interaction Options
14109Map.mergeOptions({
14110	// @section Mouse wheel options
14111	// @option scrollWheelZoom: Boolean|String = true
14112	// Whether the map can be zoomed by using the mouse wheel. If passed `'center'`,
14113	// it will zoom to the center of the view regardless of where the mouse was.
14114	scrollWheelZoom: true,
14115
14116	// @option wheelDebounceTime: Number = 40
14117	// Limits the rate at which a wheel can fire (in milliseconds). By default
14118	// user can't zoom via wheel more often than once per 40 ms.
14119	wheelDebounceTime: 40,
14120
14121	// @option wheelPxPerZoomLevel: Number = 60
14122	// How many scroll pixels (as reported by [L.DomEvent.getWheelDelta](#domevent-getwheeldelta))
14123	// mean a change of one full zoom level. Smaller values will make wheel-zooming
14124	// faster (and vice versa).
14125	wheelPxPerZoomLevel: 60
14126});
14127
14128var ScrollWheelZoom = Handler.extend({
14129	addHooks: function () {
14130		on(this._map._container, 'wheel', this._onWheelScroll, this);
14131
14132		this._delta = 0;
14133	},
14134
14135	removeHooks: function () {
14136		off(this._map._container, 'wheel', this._onWheelScroll, this);
14137	},
14138
14139	_onWheelScroll: function (e) {
14140		var delta = getWheelDelta(e);
14141
14142		var debounce = this._map.options.wheelDebounceTime;
14143
14144		this._delta += delta;
14145		this._lastMousePos = this._map.mouseEventToContainerPoint(e);
14146
14147		if (!this._startTime) {
14148			this._startTime = +new Date();
14149		}
14150
14151		var left = Math.max(debounce - (+new Date() - this._startTime), 0);
14152
14153		clearTimeout(this._timer);
14154		this._timer = setTimeout(bind(this._performZoom, this), left);
14155
14156		stop(e);
14157	},
14158
14159	_performZoom: function () {
14160		var map = this._map,
14161		    zoom = map.getZoom(),
14162		    snap = this._map.options.zoomSnap || 0;
14163
14164		map._stop(); // stop panning and fly animations if any
14165
14166		// map the delta with a sigmoid function to -4..4 range leaning on -1..1
14167		var d2 = this._delta / (this._map.options.wheelPxPerZoomLevel * 4),
14168		    d3 = 4 * Math.log(2 / (1 + Math.exp(-Math.abs(d2)))) / Math.LN2,
14169		    d4 = snap ? Math.ceil(d3 / snap) * snap : d3,
14170		    delta = map._limitZoom(zoom + (this._delta > 0 ? d4 : -d4)) - zoom;
14171
14172		this._delta = 0;
14173		this._startTime = null;
14174
14175		if (!delta) { return; }
14176
14177		if (map.options.scrollWheelZoom === 'center') {
14178			map.setZoom(zoom + delta);
14179		} else {
14180			map.setZoomAround(this._lastMousePos, zoom + delta);
14181		}
14182	}
14183});
14184
14185// @section Handlers
14186// @property scrollWheelZoom: Handler
14187// Scroll wheel zoom handler.
14188Map.addInitHook('addHandler', 'scrollWheelZoom', ScrollWheelZoom);
14189
14190/*
14191 * L.Map.TapHold is used to simulate `contextmenu` event on long hold,
14192 * which otherwise is not fired by mobile Safari.
14193 */
14194
14195var tapHoldDelay = 600;
14196
14197// @namespace Map
14198// @section Interaction Options
14199Map.mergeOptions({
14200	// @section Touch interaction options
14201	// @option tapHold: Boolean
14202	// Enables simulation of `contextmenu` event, default is `true` for mobile Safari.
14203	tapHold: Browser.touchNative && Browser.safari && Browser.mobile,
14204
14205	// @option tapTolerance: Number = 15
14206	// The max number of pixels a user can shift his finger during touch
14207	// for it to be considered a valid tap.
14208	tapTolerance: 15
14209});
14210
14211var TapHold = Handler.extend({
14212	addHooks: function () {
14213		on(this._map._container, 'touchstart', this._onDown, this);
14214	},
14215
14216	removeHooks: function () {
14217		off(this._map._container, 'touchstart', this._onDown, this);
14218	},
14219
14220	_onDown: function (e) {
14221		clearTimeout(this._holdTimeout);
14222		if (e.touches.length !== 1) { return; }
14223
14224		var first = e.touches[0];
14225		this._startPos = this._newPos = new Point(first.clientX, first.clientY);
14226
14227		this._holdTimeout = setTimeout(bind(function () {
14228			this._cancel();
14229			if (!this._isTapValid()) { return; }
14230
14231			// prevent simulated mouse events https://w3c.github.io/touch-events/#mouse-events
14232			on(document, 'touchend', preventDefault);
14233			on(document, 'touchend touchcancel', this._cancelClickPrevent);
14234			this._simulateEvent('contextmenu', first);
14235		}, this), tapHoldDelay);
14236
14237		on(document, 'touchend touchcancel contextmenu', this._cancel, this);
14238		on(document, 'touchmove', this._onMove, this);
14239	},
14240
14241	_cancelClickPrevent: function cancelClickPrevent() {
14242		off(document, 'touchend', preventDefault);
14243		off(document, 'touchend touchcancel', cancelClickPrevent);
14244	},
14245
14246	_cancel: function () {
14247		clearTimeout(this._holdTimeout);
14248		off(document, 'touchend touchcancel contextmenu', this._cancel, this);
14249		off(document, 'touchmove', this._onMove, this);
14250	},
14251
14252	_onMove: function (e) {
14253		var first = e.touches[0];
14254		this._newPos = new Point(first.clientX, first.clientY);
14255	},
14256
14257	_isTapValid: function () {
14258		return this._newPos.distanceTo(this._startPos) <= this._map.options.tapTolerance;
14259	},
14260
14261	_simulateEvent: function (type, e) {
14262		var simulatedEvent = new MouseEvent(type, {
14263			bubbles: true,
14264			cancelable: true,
14265			view: window,
14266			// detail: 1,
14267			screenX: e.screenX,
14268			screenY: e.screenY,
14269			clientX: e.clientX,
14270			clientY: e.clientY,
14271			// button: 2,
14272			// buttons: 2
14273		});
14274
14275		simulatedEvent._simulated = true;
14276
14277		e.target.dispatchEvent(simulatedEvent);
14278	}
14279});
14280
14281// @section Handlers
14282// @property tapHold: Handler
14283// Long tap handler to simulate `contextmenu` event (useful in mobile Safari).
14284Map.addInitHook('addHandler', 'tapHold', TapHold);
14285
14286/*
14287 * L.Handler.TouchZoom is used by L.Map to add pinch zoom on supported mobile browsers.
14288 */
14289
14290// @namespace Map
14291// @section Interaction Options
14292Map.mergeOptions({
14293	// @section Touch interaction options
14294	// @option touchZoom: Boolean|String = *
14295	// Whether the map can be zoomed by touch-dragging with two fingers. If
14296	// passed `'center'`, it will zoom to the center of the view regardless of
14297	// where the touch events (fingers) were. Enabled for touch-capable web
14298	// browsers.
14299	touchZoom: Browser.touch,
14300
14301	// @option bounceAtZoomLimits: Boolean = true
14302	// Set it to false if you don't want the map to zoom beyond min/max zoom
14303	// and then bounce back when pinch-zooming.
14304	bounceAtZoomLimits: true
14305});
14306
14307var TouchZoom = Handler.extend({
14308	addHooks: function () {
14309		addClass(this._map._container, 'leaflet-touch-zoom');
14310		on(this._map._container, 'touchstart', this._onTouchStart, this);
14311	},
14312
14313	removeHooks: function () {
14314		removeClass(this._map._container, 'leaflet-touch-zoom');
14315		off(this._map._container, 'touchstart', this._onTouchStart, this);
14316	},
14317
14318	_onTouchStart: function (e) {
14319		var map = this._map;
14320		if (!e.touches || e.touches.length !== 2 || map._animatingZoom || this._zooming) { return; }
14321
14322		var p1 = map.mouseEventToContainerPoint(e.touches[0]),
14323		    p2 = map.mouseEventToContainerPoint(e.touches[1]);
14324
14325		this._centerPoint = map.getSize()._divideBy(2);
14326		this._startLatLng = map.containerPointToLatLng(this._centerPoint);
14327		if (map.options.touchZoom !== 'center') {
14328			this._pinchStartLatLng = map.containerPointToLatLng(p1.add(p2)._divideBy(2));
14329		}
14330
14331		this._startDist = p1.distanceTo(p2);
14332		this._startZoom = map.getZoom();
14333
14334		this._moved = false;
14335		this._zooming = true;
14336
14337		map._stop();
14338
14339		on(document, 'touchmove', this._onTouchMove, this);
14340		on(document, 'touchend touchcancel', this._onTouchEnd, this);
14341
14342		preventDefault(e);
14343	},
14344
14345	_onTouchMove: function (e) {
14346		if (!e.touches || e.touches.length !== 2 || !this._zooming) { return; }
14347
14348		var map = this._map,
14349		    p1 = map.mouseEventToContainerPoint(e.touches[0]),
14350		    p2 = map.mouseEventToContainerPoint(e.touches[1]),
14351		    scale = p1.distanceTo(p2) / this._startDist;
14352
14353		this._zoom = map.getScaleZoom(scale, this._startZoom);
14354
14355		if (!map.options.bounceAtZoomLimits && (
14356			(this._zoom < map.getMinZoom() && scale < 1) ||
14357			(this._zoom > map.getMaxZoom() && scale > 1))) {
14358			this._zoom = map._limitZoom(this._zoom);
14359		}
14360
14361		if (map.options.touchZoom === 'center') {
14362			this._center = this._startLatLng;
14363			if (scale === 1) { return; }
14364		} else {
14365			// Get delta from pinch to center, so centerLatLng is delta applied to initial pinchLatLng
14366			var delta = p1._add(p2)._divideBy(2)._subtract(this._centerPoint);
14367			if (scale === 1 && delta.x === 0 && delta.y === 0) { return; }
14368			this._center = map.unproject(map.project(this._pinchStartLatLng, this._zoom).subtract(delta), this._zoom);
14369		}
14370
14371		if (!this._moved) {
14372			map._moveStart(true, false);
14373			this._moved = true;
14374		}
14375
14376		cancelAnimFrame(this._animRequest);
14377
14378		var moveFn = bind(map._move, map, this._center, this._zoom, {pinch: true, round: false}, undefined);
14379		this._animRequest = requestAnimFrame(moveFn, this, true);
14380
14381		preventDefault(e);
14382	},
14383
14384	_onTouchEnd: function () {
14385		if (!this._moved || !this._zooming) {
14386			this._zooming = false;
14387			return;
14388		}
14389
14390		this._zooming = false;
14391		cancelAnimFrame(this._animRequest);
14392
14393		off(document, 'touchmove', this._onTouchMove, this);
14394		off(document, 'touchend touchcancel', this._onTouchEnd, this);
14395
14396		// Pinch updates GridLayers' levels only when zoomSnap is off, so zoomSnap becomes noUpdate.
14397		if (this._map.options.zoomAnimation) {
14398			this._map._animateZoom(this._center, this._map._limitZoom(this._zoom), true, this._map.options.zoomSnap);
14399		} else {
14400			this._map._resetView(this._center, this._map._limitZoom(this._zoom));
14401		}
14402	}
14403});
14404
14405// @section Handlers
14406// @property touchZoom: Handler
14407// Touch zoom handler.
14408Map.addInitHook('addHandler', 'touchZoom', TouchZoom);
14409
14410Map.BoxZoom = BoxZoom;
14411Map.DoubleClickZoom = DoubleClickZoom;
14412Map.Drag = Drag;
14413Map.Keyboard = Keyboard;
14414Map.ScrollWheelZoom = ScrollWheelZoom;
14415Map.TapHold = TapHold;
14416Map.TouchZoom = TouchZoom;
14417
14418export { 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 };
14419

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.