PageSourceSearch

https://4brewers.ru/public/js/leaflet.js

js 4brewers.ru collected 2026-10-04 08:38:31 UTC 400,592 bytes, 14,080 lines download raw bytes

1/* @preserve
2 * Leaflet 1.6.0, a JS library for interactive maps. http://leafletjs.com
3 * (c) 2010-2019 Vladimir Agafonkin, (c) 2010-2011 CloudMade
4 */
5
6(function (global, factory) {
7	typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) :
8	typeof define === 'function' && define.amd ? define(['exports'], factory) :
9	(factory((global.L = {})));
10}(this, (function (exports) { 'use strict';
11
12var version = "1.6.0";
13
14/*
15 * @namespace Util
16 *
17 * Various utility functions, used by Leaflet internally.
18 */
19
20var freeze = Object.freeze;
21Object.freeze = function (obj) { return obj; };
22
23// @function extend(dest: Object, src?: Object): Object
24// Merges the properties of the `src` object (or multiple objects) into `dest` object and returns the latter. Has an `L.extend` shortcut.
25function extend(dest) {
26	var i, j, len, src;
27
28	for (j = 1, len = arguments.length; j < len; j++) {
29		src = arguments[j];
30		for (i in src) {
31			dest[i] = src[i];
32		}
33	}
34	return dest;
35}
36
37// @function create(proto: Object, properties?: Object): Object
38// Compatibility polyfill for [Object.create](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Object/create)
39var create = Object.create || (function () {
40	function F() {}
41	return function (proto) {
42		F.prototype = proto;
43		return new F();
44	};
45})();
46
47// @function bind(fn: Function, …): Function
48// 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).
49// Has a `L.bind()` shortcut.
50function bind(fn, obj) {
51	var slice = Array.prototype.slice;
52
53	if (fn.bind) {
54		return fn.bind.apply(fn, slice.call(arguments, 1));
55	}
56
57	var args = slice.call(arguments, 2);
58
59	return function () {
60		return fn.apply(obj, args.length ? args.concat(slice.call(arguments)) : arguments);
61	};
62}
63
64// @property lastId: Number
65// Last unique ID used by [`stamp()`](#util-stamp)
66var lastId = 0;
67
68// @function stamp(obj: Object): Number
69// Returns the unique ID of an object, assigning it one if it doesn't have it.
70function stamp(obj) {
71	/*eslint-disable */
72	obj._leaflet_id = obj._leaflet_id || ++lastId;
73	return obj._leaflet_id;
74	/* eslint-enable */
75}
76
vendor: 5,772 bytes, lines 77-233
77// @function throttle(fn: Function, time: Number, context: Object): Function
78// Returns a function which executes function `fn` with the given scope `context`
79// (so that the `this` keyword refers to `context` inside `fn`'s code). The function
80// `fn` will be called no more than one time per given amount of `time`. The arguments
81// received by the bound function will be any arguments passed when binding the
82// function, followed by any arguments passed when invoking the bound function.
83// Has an `L.throttle` shortcut.
84function throttle(fn, time, context) {
85	var lock, args, wrapperFn, later;
86
87	later = function () {
88		// reset lock and call if queued
89		lock = false;
90		if (args) {
91			wrapperFn.apply(context, args);
92			args = false;
93		}
94	};
95
96	wrapperFn = function () {
97		if (lock) {
98			// called too soon, queue to call later
99			args = arguments;
100
101		} else {
102			// call and lock until later
103			fn.apply(context, arguments);
104			setTimeout(later, time);
105			lock = true;
106		}
107	};
108
109	return wrapperFn;
110}
111
112// @function wrapNum(num: Number, range: Number[], includeMax?: Boolean): Number
113// Returns the number `num` modulo `range` in such a way so it lies within
114// `range[0]` and `range[1]`. The returned value will be always smaller than
115// `range[1]` unless `includeMax` is set to `true`.
116function wrapNum(x, range, includeMax) {
117	var max = range[1],
118	    min = range[0],
119	    d = max - min;
120	return x === max && includeMax ? x : ((x - min) % d + d) % d + min;
121}
122
123// @function falseFn(): Function
124// Returns a function which always returns `false`.
125function falseFn() { return false; }
126
127// @function formatNum(num: Number, digits?: Number): Number
128// Returns the number `num` rounded to `digits` decimals, or to 6 decimals by default.
129function formatNum(num, digits) {
130	var pow = Math.pow(10, (digits === undefined ? 6 : digits));
131	return Math.round(num * pow) / pow;
132}
133
134// @function trim(str: String): String
135// Compatibility polyfill for [String.prototype.trim](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String/Trim)
136function trim(str) {
137	return str.trim ? str.trim() : str.replace(/^\s+|\s+$/g, '');
138}
139
140// @function splitWords(str: String): String[]
141// Trims and splits the string on whitespace and returns the array of parts.
142function splitWords(str) {
143	return trim(str).split(/\s+/);
144}
145
146// @function setOptions(obj: Object, options: Object): Object
147// Merges the given properties to the `options` of the `obj` object, returning the resulting options. See `Class options`. Has an `L.setOptions` shortcut.
148function setOptions(obj, options) {
149	if (!obj.hasOwnProperty('options')) {
150		obj.options = obj.options ? create(obj.options) : {};
151	}
152	for (var i in options) {
153		obj.options[i] = options[i];
154	}
155	return obj.options;
156}
157
158// @function getParamString(obj: Object, existingUrl?: String, uppercase?: Boolean): String
159// Converts an object into a parameter URL string, e.g. `{a: "foo", b: "bar"}`
160// translates to `'?a=foo&b=bar'`. If `existingUrl` is set, the parameters will
161// be appended at the end. If `uppercase` is `true`, the parameter names will
162// be uppercased (e.g. `'?A=foo&B=bar'`)
163function getParamString(obj, existingUrl, uppercase) {
164	var params = [];
165	for (var i in obj) {
166		params.push(encodeURIComponent(uppercase ? i.toUpperCase() : i) + '=' + encodeURIComponent(obj[i]));
167	}
168	return ((!existingUrl || existingUrl.indexOf('?') === -1) ? '?' : '&') + params.join('&');
169}
170
171var templateRe = /\{ *([\w_-]+) *\}/g;
172
173// @function template(str: String, data: Object): String
174// Simple templating facility, accepts a template string of the form `'Hello {a}, {b}'`
175// and a data object like `{a: 'foo', b: 'bar'}`, returns evaluated string
176// `('Hello foo, bar')`. You can also specify functions instead of strings for
177// data values — they will be evaluated passing `data` as an argument.
178function template(str, data) {
179	return str.replace(templateRe, function (str, key) {
180		var value = data[key];
181
182		if (value === undefined) {
183			throw new Error('No value provided for variable ' + str);
184
185		} else if (typeof value === 'function') {
186			value = value(data);
187		}
188		return value;
189	});
190}
191
192// @function isArray(obj): Boolean
193// Compatibility polyfill for [Array.isArray](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Array/isArray)
194var isArray = Array.isArray || function (obj) {
195	return (Object.prototype.toString.call(obj) === '[object Array]');
196};
197
198// @function indexOf(array: Array, el: Object): Number
199// Compatibility polyfill for [Array.prototype.indexOf](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Array/indexOf)
200function indexOf(array, el) {
201	for (var i = 0; i < array.length; i++) {
202		if (array[i] === el) { return i; }
203	}
204	return -1;
205}
206
207// @property emptyImageUrl: String
208// Data URI string containing a base64-encoded empty GIF image.
209// Used as a hack to free memory from unused images on WebKit-powered
210// mobile devices (by setting image `src` to this string).
211var emptyImageUrl = 'data:image/gif;base64,R0lGODlhAQABAAD/ACwAAAAAAQABAAACADs=';
212
213// inspired by http://paulirish.com/2011/requestanimationframe-for-smart-animating/
214
215function getPrefixed(name) {
216	return window['webkit' + name] || window['moz' + name] || window['ms' + name];
217}
218
219var lastTime = 0;
220
221// fallback for IE 7-8
222function timeoutDefer(fn) {
223	var time = +new Date(),
224	    timeToCall = Math.max(0, 16 - (time - lastTime));
225
226	lastTime = time + timeToCall;
227	return window.setTimeout(fn, timeToCall);
228}
229
230var requestFn = window.requestAnimationFrame || getPrefixed('RequestAnimationFrame') || timeoutDefer;
231var cancelFn = window.cancelAnimationFrame || getPrefixed('CancelAnimationFrame') ||
232		getPrefixed('CancelRequestAnimationFrame') || function (id) { window.clearTimeout(id); };
233
234// @function requestAnimFrame(fn: Function, context?: Object, immediate?: Boolean): Number
235// Schedules `fn` to be executed when the browser repaints. `fn` is bound to
236// `context` if given. When `immediate` is set, `fn` is called immediately if
237// the browser doesn't have native support for
238// [`window.requestAnimationFrame`](https://developer.mozilla.org/docs/Web/API/window/requestAnimationFrame),
239// otherwise it's delayed. Returns a request ID that can be used to cancel the request.
240function requestAnimFrame(fn, context, immediate) {
241	if (immediate && requestFn === timeoutDefer) {
242		fn.call(context);
243	} else {
244		return requestFn.call(window, bind(fn, context));
245	}
246}
247
248// @function cancelAnimFrame(id: Number): undefined
249// Cancels a previous `requestAnimFrame`. See also [window.cancelAnimationFrame](https://developer.mozilla.org/docs/Web/API/window/cancelAnimationFrame).
250function cancelAnimFrame(id) {
251	if (id) {
252		cancelFn.call(window, id);
253	}
254}
255
256
257var Util = (Object.freeze || Object)({
258	freeze: freeze,
259	extend: extend,
260	create: create,
261	bind: bind,
262	lastId: lastId,
263	stamp: stamp,
264	throttle: throttle,
265	wrapNum: wrapNum,
266	falseFn: falseFn,
267	formatNum: formatNum,
268	trim: trim,
269	splitWords: splitWords,
270	setOptions: setOptions,
271	getParamString: getParamString,
272	template: template,
273	isArray: isArray,
274	indexOf: indexOf,
275	emptyImageUrl: emptyImageUrl,
276	requestFn: requestFn,
277	cancelFn: cancelFn,
278	requestAnimFrame: requestAnimFrame,
279	cancelAnimFrame: cancelAnimFrame
280});
281
282// @class Class
283// @aka L.Class
284
285// @section
286// @uninheritable
287
288// Thanks to John Resig and Dean Edwards for inspiration!
289
290function Class() {}
291
292Class.extend = function (props) {
293
294	// @function extend(props: Object): Function
295	// [Extends the current class](#class-inheritance) given the properties to be included.
296	// Returns a Javascript function that is a class constructor (to be called with `new`).
297	var NewClass = function () {
298
299		// call the constructor
300		if (this.initialize) {
301			this.initialize.apply(this, arguments);
302		}
303
304		// call all constructor hooks
305		this.callInitHooks();
306	};
307
308	var parentProto = NewClass.__super__ = this.prototype;
309
310	var proto = create(parentProto);
311	proto.constructor = NewClass;
312
313	NewClass.prototype = proto;
314
315	// inherit parent's statics
316	for (var i in this) {
317		if (this.hasOwnProperty(i) && i !== 'prototype' && i !== '__super__') {
318			NewClass[i] = this[i];
319		}
320	}
321
322	// mix static properties into the class
323	if (props.statics) {
324		extend(NewClass, props.statics);
325		delete props.statics;
326	}
327
328	// mix includes into the prototype
329	if (props.includes) {
330		checkDeprecatedMixinEvents(props.includes);
331		extend.apply(null, [proto].concat(props.includes));
332		delete props.includes;
333	}
334
335	// merge options
336	if (proto.options) {
337		props.options = extend(create(proto.options), props.options);
338	}
339
340	// mix given properties into the prototype
341	extend(proto, props);
342
343	proto._initHooks = [];
344
345	// add method for calling all hooks
346	proto.callInitHooks = function () {
347
348		if (this._initHooksCalled) { return; }
349
350		if (parentProto.callInitHooks) {
351			parentProto.callInitHooks.call(this);
352		}
353
354		this._initHooksCalled = true;
355
356		for (var i = 0, len = proto._initHooks.length; i < len; i++) {
357			proto._initHooks[i].call(this);
358		}
359	};
360
361	return NewClass;
362};
363
364
365// @function include(properties: Object): this
366// [Includes a mixin](#class-includes) into the current class.
367Class.include = function (props) {
368	extend(this.prototype, props);
369	return this;
370};
371
372// @function mergeOptions(options: Object): this
373// [Merges `options`](#class-options) into the defaults of the class.
374Class.mergeOptions = function (options) {
375	extend(this.prototype.options, options);
376	return this;
377};
378
379// @function addInitHook(fn: Function): this
380// Adds a [constructor hook](#class-constructor-hooks) to the class.
381Class.addInitHook = function (fn) { // (Function) || (String, args...)
382	var args = Array.prototype.slice.call(arguments, 1);
383
384	var init = typeof fn === 'function' ? fn : function () {
385		this[fn].apply(this, args);
386	};
387
388	this.prototype._initHooks = this.prototype._initHooks || [];
389	this.prototype._initHooks.push(init);
390	return this;
391};
392
393function checkDeprecatedMixinEvents(includes) {
394	if (typeof L === 'undefined' || !L || !L.Mixin) { return; }
395
396	includes = isArray(includes) ? includes : [includes];
397
398	for (var i = 0; i < includes.length; i++) {
399		if (includes[i] === L.Mixin.Events) {
400			console.warn('Deprecated include of L.Mixin.Events: ' +
401				'this property will be removed in future releases, ' +
402				'please inherit from L.Evented instead.', new Error().stack);
403		}
404	}
405}
406
407/*
408 * @class Evented
409 * @aka L.Evented
410 * @inherits Class
411 *
412 * 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).
413 *
414 * @example
415 *
416 * ```js
417 * map.on('click', function(e) {
418 * 	alert(e.latlng);
419 * } );
420 * ```
421 *
422 * Leaflet deals with event listeners by reference, so if you want to add a listener and then remove it, define it as a function:
423 *
424 * ```js
425 * function onClick(e) { ... }
426 *
427 * map.on('click', onClick);
428 * map.off('click', onClick);
429 * ```
430 */
431
432var Events = {
433	/* @method on(type: String, fn: Function, context?: Object): this
434	 * 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'`).
435	 *
436	 * @alternative
437	 * @method on(eventMap: Object): this
438	 * Adds a set of type/listener pairs, e.g. `{click: onClick, mousemove: onMouseMove}`
439	 */
440	on: function (types, fn, context) {
441
442		// types can be a map of types/handlers
443		if (typeof types === 'object') {
444			for (var type in types) {
445				// we don't process space-separated events here for performance;
446				// it's a hot path since Layer uses the on(obj) syntax
447				this._on(type, types[type], fn);
448			}
449
450		} else {
451			// types can be a string of space-separated words
452			types = splitWords(types);
453
454			for (var i = 0, len = types.length; i < len; i++) {
455				this._on(types[i], fn, context);
456			}
457		}
458
459		return this;
460	},
461
462	/* @method off(type: String, fn?: Function, context?: Object): this
463	 * 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.
464	 *
465	 * @alternative
466	 * @method off(eventMap: Object): this
467	 * Removes a set of type/listener pairs.
468	 *
469	 * @alternative
470	 * @method off: this
471	 * Removes all listeners to all events on the object. This includes implicitly attached events.
472	 */
473	off: function (types, fn, context) {
474
475		if (!types) {
476			// clear all listeners if called without arguments
477			delete this._events;
478
479		} else if (typeof types === 'object') {
480			for (var type in types) {
481				this._off(type, types[type], fn);
482			}
483
484		} else {
485			types = splitWords(types);
486
487			for (var i = 0, len = types.length; i < len; i++) {
488				this._off(types[i], fn, context);
489			}
490		}
491
492		return this;
493	},
494
495	// attach listener (without syntactic sugar now)
496	_on: function (type, fn, context) {
497		this._events = this._events || {};
498
499		/* get/init listeners for type */
500		var typeListeners = this._events[type];
501		if (!typeListeners) {
502			typeListeners = [];
503			this._events[type] = typeListeners;
504		}
505
506		if (context === this) {
507			// Less memory footprint.
508			context = undefined;
509		}
510		var newListener = {fn: fn, ctx: context},
511		    listeners = typeListeners;
512
513		// check if fn already there
514		for (var i = 0, len = listeners.length; i < len; i++) {
515			if (listeners[i].fn === fn && listeners[i].ctx === context) {
516				return;
517			}
518		}
519
520		listeners.push(newListener);
521	},
522
523	_off: function (type, fn, context) {
524		var listeners,
525		    i,
526		    len;
527
528		if (!this._events) { return; }
529
530		listeners = this._events[type];
531
532		if (!listeners) {
533			return;
534		}
535
536		if (!fn) {
537			// Set all removed listeners to noop so they are not called if remove happens in fire
538			for (i = 0, len = listeners.length; i < len; i++) {
539				listeners[i].fn = falseFn;
540			}
541			// clear all listeners for a type if function isn't specified
542			delete this._events[type];
543			return;
544		}
545
546		if (context === this) {
547			context = undefined;
548		}
549
550		if (listeners) {
551
552			// find fn and remove it
553			for (i = 0, len = listeners.length; i < len; i++) {
554				var l = listeners[i];
555				if (l.ctx !== context) { continue; }
556				if (l.fn === fn) {
557
558					// set the removed listener to noop so that's not called if remove happens in fire
559					l.fn = falseFn;
560
561					if (this._firingCount) {
562						/* copy array in case events are being fired */
563						this._events[type] = listeners = listeners.slice();
564					}
565					listeners.splice(i, 1);
566
567					return;
568				}
569			}
570		}
571	},
572
573	// @method fire(type: String, data?: Object, propagate?: Boolean): this
574	// Fires an event of the specified type. You can optionally provide an data
575	// object — the first argument of the listener function will contain its
576	// properties. The event can optionally be propagated to event parents.
577	fire: function (type, data, propagate) {
vendor: 16,671 bytes, lines 578-1195
578		if (!this.listens(type, propagate)) { return this; }
579
580		var event = extend({}, data, {
581			type: type,
582			target: this,
583			sourceTarget: data && data.sourceTarget || this
584		});
585
586		if (this._events) {
587			var listeners = this._events[type];
588
589			if (listeners) {
590				this._firingCount = (this._firingCount + 1) || 1;
591				for (var i = 0, len = listeners.length; i < len; i++) {
592					var l = listeners[i];
593					l.fn.call(l.ctx || this, event);
594				}
595
596				this._firingCount--;
597			}
598		}
599
600		if (propagate) {
601			// propagate the event to parents (set with addEventParent)
602			this._propagateEvent(event);
603		}
604
605		return this;
606	},
607
608	// @method listens(type: String): Boolean
609	// Returns `true` if a particular event type has any listeners attached to it.
610	listens: function (type, propagate) {
611		var listeners = this._events && this._events[type];
612		if (listeners && listeners.length) { return true; }
613
614		if (propagate) {
615			// also check parents for listeners if event propagates
616			for (var id in this._eventParents) {
617				if (this._eventParents[id].listens(type, propagate)) { return true; }
618			}
619		}
620		return false;
621	},
622
623	// @method once(…): this
624	// Behaves as [`on(…)`](#evented-on), except the listener will only get fired once and then removed.
625	once: function (types, fn, context) {
626
627		if (typeof types === 'object') {
628			for (var type in types) {
629				this.once(type, types[type], fn);
630			}
631			return this;
632		}
633
634		var handler = bind(function () {
635			this
636			    .off(types, fn, context)
637			    .off(types, handler, context);
638		}, this);
639
640		// add a listener that's executed once and removed after that
641		return this
642		    .on(types, fn, context)
643		    .on(types, handler, context);
644	},
645
646	// @method addEventParent(obj: Evented): this
647	// Adds an event parent - an `Evented` that will receive propagated events
648	addEventParent: function (obj) {
649		this._eventParents = this._eventParents || {};
650		this._eventParents[stamp(obj)] = obj;
651		return this;
652	},
653
654	// @method removeEventParent(obj: Evented): this
655	// Removes an event parent, so it will stop receiving propagated events
656	removeEventParent: function (obj) {
657		if (this._eventParents) {
658			delete this._eventParents[stamp(obj)];
659		}
660		return this;
661	},
662
663	_propagateEvent: function (e) {
664		for (var id in this._eventParents) {
665			this._eventParents[id].fire(e.type, extend({
666				layer: e.target,
667				propagatedFrom: e.target
668			}, e), true);
669		}
670	}
671};
672
673// aliases; we should ditch those eventually
674
675// @method addEventListener(…): this
676// Alias to [`on(…)`](#evented-on)
677Events.addEventListener = Events.on;
678
679// @method removeEventListener(…): this
680// Alias to [`off(…)`](#evented-off)
681
682// @method clearAllEventListeners(…): this
683// Alias to [`off()`](#evented-off)
684Events.removeEventListener = Events.clearAllEventListeners = Events.off;
685
686// @method addOneTimeEventListener(…): this
687// Alias to [`once(…)`](#evented-once)
688Events.addOneTimeEventListener = Events.once;
689
690// @method fireEvent(…): this
691// Alias to [`fire(…)`](#evented-fire)
692Events.fireEvent = Events.fire;
693
694// @method hasEventListeners(…): Boolean
695// Alias to [`listens(…)`](#evented-listens)
696Events.hasEventListeners = Events.listens;
697
698var Evented = Class.extend(Events);
699
700/*
701 * @class Point
702 * @aka L.Point
703 *
704 * Represents a point with `x` and `y` coordinates in pixels.
705 *
706 * @example
707 *
708 * ```js
709 * var point = L.point(200, 300);
710 * ```
711 *
712 * 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:
713 *
714 * ```js
715 * map.panBy([200, 300]);
716 * map.panBy(L.point(200, 300));
717 * ```
718 *
719 * Note that `Point` does not inherit from Leafet's `Class` object,
720 * which means new classes can't inherit from it, and new methods
721 * can't be added to it with the `include` function.
722 */
723
724function Point(x, y, round) {
725	// @property x: Number; The `x` coordinate of the point
726	this.x = (round ? Math.round(x) : x);
727	// @property y: Number; The `y` coordinate of the point
728	this.y = (round ? Math.round(y) : y);
729}
730
731var trunc = Math.trunc || function (v) {
732	return v > 0 ? Math.floor(v) : Math.ceil(v);
733};
734
735Point.prototype = {
736
737	// @method clone(): Point
738	// Returns a copy of the current point.
739	clone: function () {
740		return new Point(this.x, this.y);
741	},
742
743	// @method add(otherPoint: Point): Point
744	// Returns the result of addition of the current and the given points.
745	add: function (point) {
746		// non-destructive, returns a new point
747		return this.clone()._add(toPoint(point));
748	},
749
750	_add: function (point) {
751		// destructive, used directly for performance in situations where it's safe to modify existing point
752		this.x += point.x;
753		this.y += point.y;
754		return this;
755	},
756
757	// @method subtract(otherPoint: Point): Point
758	// Returns the result of subtraction of the given point from the current.
759	subtract: function (point) {
760		return this.clone()._subtract(toPoint(point));
761	},
762
763	_subtract: function (point) {
764		this.x -= point.x;
765		this.y -= point.y;
766		return this;
767	},
768
769	// @method divideBy(num: Number): Point
770	// Returns the result of division of the current point by the given number.
771	divideBy: function (num) {
772		return this.clone()._divideBy(num);
773	},
774
775	_divideBy: function (num) {
776		this.x /= num;
777		this.y /= num;
778		return this;
779	},
780
781	// @method multiplyBy(num: Number): Point
782	// Returns the result of multiplication of the current point by the given number.
783	multiplyBy: function (num) {
784		return this.clone()._multiplyBy(num);
785	},
786
787	_multiplyBy: function (num) {
788		this.x *= num;
789		this.y *= num;
790		return this;
791	},
792
793	// @method scaleBy(scale: Point): Point
794	// Multiply each coordinate of the current point by each coordinate of
795	// `scale`. In linear algebra terms, multiply the point by the
796	// [scaling matrix](https://en.wikipedia.org/wiki/Scaling_%28geometry%29#Matrix_representation)
797	// defined by `scale`.
798	scaleBy: function (point) {
799		return new Point(this.x * point.x, this.y * point.y);
800	},
801
802	// @method unscaleBy(scale: Point): Point
803	// Inverse of `scaleBy`. Divide each coordinate of the current point by
804	// each coordinate of `scale`.
805	unscaleBy: function (point) {
806		return new Point(this.x / point.x, this.y / point.y);
807	},
808
809	// @method round(): Point
810	// Returns a copy of the current point with rounded coordinates.
811	round: function () {
812		return this.clone()._round();
813	},
814
815	_round: function () {
816		this.x = Math.round(this.x);
817		this.y = Math.round(this.y);
818		return this;
819	},
820
821	// @method floor(): Point
822	// Returns a copy of the current point with floored coordinates (rounded down).
823	floor: function () {
824		return this.clone()._floor();
825	},
826
827	_floor: function () {
828		this.x = Math.floor(this.x);
829		this.y = Math.floor(this.y);
830		return this;
831	},
832
833	// @method ceil(): Point
834	// Returns a copy of the current point with ceiled coordinates (rounded up).
835	ceil: function () {
836		return this.clone()._ceil();
837	},
838
839	_ceil: function () {
840		this.x = Math.ceil(this.x);
841		this.y = Math.ceil(this.y);
842		return this;
843	},
844
845	// @method trunc(): Point
846	// Returns a copy of the current point with truncated coordinates (rounded towards zero).
847	trunc: function () {
848		return this.clone()._trunc();
849	},
850
851	_trunc: function () {
852		this.x = trunc(this.x);
853		this.y = trunc(this.y);
854		return this;
855	},
856
857	// @method distanceTo(otherPoint: Point): Number
858	// Returns the cartesian distance between the current and the given points.
859	distanceTo: function (point) {
860		point = toPoint(point);
861
862		var x = point.x - this.x,
863		    y = point.y - this.y;
864
865		return Math.sqrt(x * x + y * y);
866	},
867
868	// @method equals(otherPoint: Point): Boolean
869	// Returns `true` if the given point has the same coordinates.
870	equals: function (point) {
871		point = toPoint(point);
872
873		return point.x === this.x &&
874		       point.y === this.y;
875	},
876
877	// @method contains(otherPoint: Point): Boolean
878	// Returns `true` if both coordinates of the given point are less than the corresponding current point coordinates (in absolute values).
879	contains: function (point) {
880		point = toPoint(point);
881
882		return Math.abs(point.x) <= Math.abs(this.x) &&
883		       Math.abs(point.y) <= Math.abs(this.y);
884	},
885
886	// @method toString(): String
887	// Returns a string representation of the point for debugging purposes.
888	toString: function () {
889		return 'Point(' +
890		        formatNum(this.x) + ', ' +
891		        formatNum(this.y) + ')';
892	}
893};
894
895// @factory L.point(x: Number, y: Number, round?: Boolean)
896// Creates a Point object with the given `x` and `y` coordinates. If optional `round` is set to true, rounds the `x` and `y` values.
897
898// @alternative
899// @factory L.point(coords: Number[])
900// Expects an array of the form `[x, y]` instead.
901
902// @alternative
903// @factory L.point(coords: Object)
904// Expects a plain object of the form `{x: Number, y: Number}` instead.
905function toPoint(x, y, round) {
906	if (x instanceof Point) {
907		return x;
908	}
909	if (isArray(x)) {
910		return new Point(x[0], x[1]);
911	}
912	if (x === undefined || x === null) {
913		return x;
914	}
915	if (typeof x === 'object' && 'x' in x && 'y' in x) {
916		return new Point(x.x, x.y);
917	}
918	return new Point(x, y, round);
919}
920
921/*
922 * @class Bounds
923 * @aka L.Bounds
924 *
925 * Represents a rectangular area in pixel coordinates.
926 *
927 * @example
928 *
929 * ```js
930 * var p1 = L.point(10, 10),
931 * p2 = L.point(40, 60),
932 * bounds = L.bounds(p1, p2);
933 * ```
934 *
935 * 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:
936 *
937 * ```js
938 * otherBounds.intersects([[10, 10], [40, 60]]);
939 * ```
940 *
941 * Note that `Bounds` does not inherit from Leafet's `Class` object,
942 * which means new classes can't inherit from it, and new methods
943 * can't be added to it with the `include` function.
944 */
945
946function Bounds(a, b) {
947	if (!a) { return; }
948
949	var points = b ? [a, b] : a;
950
951	for (var i = 0, len = points.length; i < len; i++) {
952		this.extend(points[i]);
953	}
954}
955
956Bounds.prototype = {
957	// @method extend(point: Point): this
958	// Extends the bounds to contain the given point.
959	extend: function (point) { // (Point)
960		point = toPoint(point);
961
962		// @property min: Point
963		// The top left corner of the rectangle.
964		// @property max: Point
965		// The bottom right corner of the rectangle.
966		if (!this.min && !this.max) {
967			this.min = point.clone();
968			this.max = point.clone();
969		} else {
970			this.min.x = Math.min(point.x, this.min.x);
971			this.max.x = Math.max(point.x, this.max.x);
972			this.min.y = Math.min(point.y, this.min.y);
973			this.max.y = Math.max(point.y, this.max.y);
974		}
975		return this;
976	},
977
978	// @method getCenter(round?: Boolean): Point
979	// Returns the center point of the bounds.
980	getCenter: function (round) {
981		return new Point(
982		        (this.min.x + this.max.x) / 2,
983		        (this.min.y + this.max.y) / 2, round);
984	},
985
986	// @method getBottomLeft(): Point
987	// Returns the bottom-left point of the bounds.
988	getBottomLeft: function () {
989		return new Point(this.min.x, this.max.y);
990	},
991
992	// @method getTopRight(): Point
993	// Returns the top-right point of the bounds.
994	getTopRight: function () { // -> Point
995		return new Point(this.max.x, this.min.y);
996	},
997
998	// @method getTopLeft(): Point
999	// Returns the top-left point of the bounds (i.e. [`this.min`](#bounds-min)).
1000	getTopLeft: function () {
1001		return this.min; // left, top
1002	},
1003
1004	// @method getBottomRight(): Point
1005	// Returns the bottom-right point of the bounds (i.e. [`this.max`](#bounds-max)).
1006	getBottomRight: function () {
1007		return this.max; // right, bottom
1008	},
1009
1010	// @method getSize(): Point
1011	// Returns the size of the given bounds
1012	getSize: function () {
1013		return this.max.subtract(this.min);
1014	},
1015
1016	// @method contains(otherBounds: Bounds): Boolean
1017	// Returns `true` if the rectangle contains the given one.
1018	// @alternative
1019	// @method contains(point: Point): Boolean
1020	// Returns `true` if the rectangle contains the given point.
1021	contains: function (obj) {
1022		var min, max;
1023
1024		if (typeof obj[0] === 'number' || obj instanceof Point) {
1025			obj = toPoint(obj);
1026		} else {
1027			obj = toBounds(obj);
1028		}
1029
1030		if (obj instanceof Bounds) {
1031			min = obj.min;
1032			max = obj.max;
1033		} else {
1034			min = max = obj;
1035		}
1036
1037		return (min.x >= this.min.x) &&
1038		       (max.x <= this.max.x) &&
1039		       (min.y >= this.min.y) &&
1040		       (max.y <= this.max.y);
1041	},
1042
1043	// @method intersects(otherBounds: Bounds): Boolean
1044	// Returns `true` if the rectangle intersects the given bounds. Two bounds
1045	// intersect if they have at least one point in common.
1046	intersects: function (bounds) { // (Bounds) -> Boolean
1047		bounds = toBounds(bounds);
1048
1049		var min = this.min,
1050		    max = this.max,
1051		    min2 = bounds.min,
1052		    max2 = bounds.max,
1053		    xIntersects = (max2.x >= min.x) && (min2.x <= max.x),
1054		    yIntersects = (max2.y >= min.y) && (min2.y <= max.y);
1055
1056		return xIntersects && yIntersects;
1057	},
1058
1059	// @method overlaps(otherBounds: Bounds): Boolean
1060	// Returns `true` if the rectangle overlaps the given bounds. Two bounds
1061	// overlap if their intersection is an area.
1062	overlaps: function (bounds) { // (Bounds) -> Boolean
1063		bounds = toBounds(bounds);
1064
1065		var min = this.min,
1066		    max = this.max,
1067		    min2 = bounds.min,
1068		    max2 = bounds.max,
1069		    xOverlaps = (max2.x > min.x) && (min2.x < max.x),
1070		    yOverlaps = (max2.y > min.y) && (min2.y < max.y);
1071
1072		return xOverlaps && yOverlaps;
1073	},
1074
1075	isValid: function () {
1076		return !!(this.min && this.max);
1077	}
1078};
1079
1080
1081// @factory L.bounds(corner1: Point, corner2: Point)
1082// Creates a Bounds object from two corners coordinate pairs.
1083// @alternative
1084// @factory L.bounds(points: Point[])
1085// Creates a Bounds object from the given array of points.
1086function toBounds(a, b) {
1087	if (!a || a instanceof Bounds) {
1088		return a;
1089	}
1090	return new Bounds(a, b);
1091}
1092
1093/*
1094 * @class LatLngBounds
1095 * @aka L.LatLngBounds
1096 *
1097 * Represents a rectangular geographical area on a map.
1098 *
1099 * @example
1100 *
1101 * ```js
1102 * var corner1 = L.latLng(40.712, -74.227),
1103 * corner2 = L.latLng(40.774, -74.125),
1104 * bounds = L.latLngBounds(corner1, corner2);
1105 * ```
1106 *
1107 * 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:
1108 *
1109 * ```js
1110 * map.fitBounds([
1111 * 	[40.712, -74.227],
1112 * 	[40.774, -74.125]
1113 * ]);
1114 * ```
1115 *
1116 * 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.
1117 *
1118 * Note that `LatLngBounds` does not inherit from Leafet's `Class` object,
1119 * which means new classes can't inherit from it, and new methods
1120 * can't be added to it with the `include` function.
1121 */
1122
1123function LatLngBounds(corner1, corner2) { // (LatLng, LatLng) or (LatLng[])
1124	if (!corner1) { return; }
1125
1126	var latlngs = corner2 ? [corner1, corner2] : corner1;
1127
1128	for (var i = 0, len = latlngs.length; i < len; i++) {
1129		this.extend(latlngs[i]);
1130	}
1131}
1132
1133LatLngBounds.prototype = {
1134
1135	// @method extend(latlng: LatLng): this
1136	// Extend the bounds to contain the given point
1137
1138	// @alternative
1139	// @method extend(otherBounds: LatLngBounds): this
1140	// Extend the bounds to contain the given bounds
1141	extend: function (obj) {
1142		var sw = this._southWest,
1143		    ne = this._northEast,
1144		    sw2, ne2;
1145
1146		if (obj instanceof LatLng) {
1147			sw2 = obj;
1148			ne2 = obj;
1149
1150		} else if (obj instanceof LatLngBounds) {
1151			sw2 = obj._southWest;
1152			ne2 = obj._northEast;
1153
1154			if (!sw2 || !ne2) { return this; }
1155
1156		} else {
1157			return obj ? this.extend(toLatLng(obj) || toLatLngBounds(obj)) : this;
1158		}
1159
1160		if (!sw && !ne) {
1161			this._southWest = new LatLng(sw2.lat, sw2.lng);
1162			this._northEast = new LatLng(ne2.lat, ne2.lng);
1163		} else {
1164			sw.lat = Math.min(sw2.lat, sw.lat);
1165			sw.lng = Math.min(sw2.lng, sw.lng);
1166			ne.lat = Math.max(ne2.lat, ne.lat);
1167			ne.lng = Math.max(ne2.lng, ne.lng);
1168		}
1169
1170		return this;
1171	},
1172
1173	// @method pad(bufferRatio: Number): LatLngBounds
1174	// Returns bounds created by extending or retracting the current bounds by a given ratio in each direction.
1175	// For example, a ratio of 0.5 extends the bounds by 50% in each direction.
1176	// Negative values will retract the bounds.
1177	pad: function (bufferRatio) {
1178		var sw = this._southWest,
1179		    ne = this._northEast,
1180		    heightBuffer = Math.abs(sw.lat - ne.lat) * bufferRatio,
1181		    widthBuffer = Math.abs(sw.lng - ne.lng) * bufferRatio;
1182
1183		return new LatLngBounds(
1184		        new LatLng(sw.lat - heightBuffer, sw.lng - widthBuffer),
1185		        new LatLng(ne.lat + heightBuffer, ne.lng + widthBuffer));
1186	},
1187
1188	// @method getCenter(): LatLng
1189	// Returns the center point of the bounds.
1190	getCenter: function () {
1191		return new LatLng(
1192		        (this._southWest.lat + this._northEast.lat) / 2,
1193		        (this._southWest.lng + this._northEast.lng) / 2);
1194	},
1195
1196	// @method getSouthWest(): LatLng
1197	// Returns the south-west point of the bounds.
1198	getSouthWest: function () {
1199		return this._southWest;
1200	},
1201
1202	// @method getNorthEast(): LatLng
1203	// Returns the north-east point of the bounds.
1204	getNorthEast: function () {
1205		return this._northEast;
1206	},
1207
1208	// @method getNorthWest(): LatLng
1209	// Returns the north-west point of the bounds.
1210	getNorthWest: function () {
1211		return new LatLng(this.getNorth(), this.getWest());
1212	},
1213
1214	// @method getSouthEast(): LatLng
1215	// Returns the south-east point of the bounds.
1216	getSouthEast: function () {
1217		return new LatLng(this.getSouth(), this.getEast());
1218	},
1219
1220	// @method getWest(): Number
1221	// Returns the west longitude of the bounds
1222	getWest: function () {
1223		return this._southWest.lng;
1224	},
1225
1226	// @method getSouth(): Number
1227	// Returns the south latitude of the bounds
1228	getSouth: function () {
1229		return this._southWest.lat;
1230	},
1231
1232	// @method getEast(): Number
1233	// Returns the east longitude of the bounds
1234	getEast: function () {
1235		return this._northEast.lng;
1236	},
1237
1238	// @method getNorth(): Number
1239	// Returns the north latitude of the bounds
1240	getNorth: function () {
1241		return this._northEast.lat;
1242	},
1243
1244	// @method contains(otherBounds: LatLngBounds): Boolean
1245	// Returns `true` if the rectangle contains the given one.
1246
1247	// @alternative
1248	// @method contains (latlng: LatLng): Boolean
1249	// Returns `true` if the rectangle contains the given point.
1250	contains: function (obj) { // (LatLngBounds) or (LatLng) -> Boolean
1251		if (typeof obj[0] === 'number' || obj instanceof LatLng || 'lat' in obj) {
1252			obj = toLatLng(obj);
1253		} else {
1254			obj = toLatLngBounds(obj);
1255		}
1256
1257		var sw = this._southWest,
1258		    ne = this._northEast,
1259		    sw2, ne2;
1260
1261		if (obj instanceof LatLngBounds) {
vendor: 7,059 bytes, lines 1262-1481
1262			sw2 = obj.getSouthWest();
1263			ne2 = obj.getNorthEast();
1264		} else {
1265			sw2 = ne2 = obj;
1266		}
1267
1268		return (sw2.lat >= sw.lat) && (ne2.lat <= ne.lat) &&
1269		       (sw2.lng >= sw.lng) && (ne2.lng <= ne.lng);
1270	},
1271
1272	// @method intersects(otherBounds: LatLngBounds): Boolean
1273	// Returns `true` if the rectangle intersects the given bounds. Two bounds intersect if they have at least one point in common.
1274	intersects: function (bounds) {
1275		bounds = toLatLngBounds(bounds);
1276
1277		var sw = this._southWest,
1278		    ne = this._northEast,
1279		    sw2 = bounds.getSouthWest(),
1280		    ne2 = bounds.getNorthEast(),
1281
1282		    latIntersects = (ne2.lat >= sw.lat) && (sw2.lat <= ne.lat),
1283		    lngIntersects = (ne2.lng >= sw.lng) && (sw2.lng <= ne.lng);
1284
1285		return latIntersects && lngIntersects;
1286	},
1287
1288	// @method overlaps(otherBounds: Bounds): Boolean
1289	// Returns `true` if the rectangle overlaps the given bounds. Two bounds overlap if their intersection is an area.
1290	overlaps: function (bounds) {
1291		bounds = toLatLngBounds(bounds);
1292
1293		var sw = this._southWest,
1294		    ne = this._northEast,
1295		    sw2 = bounds.getSouthWest(),
1296		    ne2 = bounds.getNorthEast(),
1297
1298		    latOverlaps = (ne2.lat > sw.lat) && (sw2.lat < ne.lat),
1299		    lngOverlaps = (ne2.lng > sw.lng) && (sw2.lng < ne.lng);
1300
1301		return latOverlaps && lngOverlaps;
1302	},
1303
1304	// @method toBBoxString(): String
1305	// 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.
1306	toBBoxString: function () {
1307		return [this.getWest(), this.getSouth(), this.getEast(), this.getNorth()].join(',');
1308	},
1309
1310	// @method equals(otherBounds: LatLngBounds, maxMargin?: Number): Boolean
1311	// 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.
1312	equals: function (bounds, maxMargin) {
1313		if (!bounds) { return false; }
1314
1315		bounds = toLatLngBounds(bounds);
1316
1317		return this._southWest.equals(bounds.getSouthWest(), maxMargin) &&
1318		       this._northEast.equals(bounds.getNorthEast(), maxMargin);
1319	},
1320
1321	// @method isValid(): Boolean
1322	// Returns `true` if the bounds are properly initialized.
1323	isValid: function () {
1324		return !!(this._southWest && this._northEast);
1325	}
1326};
1327
1328// TODO International date line?
1329
1330// @factory L.latLngBounds(corner1: LatLng, corner2: LatLng)
1331// Creates a `LatLngBounds` object by defining two diagonally opposite corners of the rectangle.
1332
1333// @alternative
1334// @factory L.latLngBounds(latlngs: LatLng[])
1335// 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).
1336function toLatLngBounds(a, b) {
1337	if (a instanceof LatLngBounds) {
1338		return a;
1339	}
1340	return new LatLngBounds(a, b);
1341}
1342
1343/* @class LatLng
1344 * @aka L.LatLng
1345 *
1346 * Represents a geographical point with a certain latitude and longitude.
1347 *
1348 * @example
1349 *
1350 * ```
1351 * var latlng = L.latLng(50.5, 30.5);
1352 * ```
1353 *
1354 * 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:
1355 *
1356 * ```
1357 * map.panTo([50, 30]);
1358 * map.panTo({lon: 30, lat: 50});
1359 * map.panTo({lat: 50, lng: 30});
1360 * map.panTo(L.latLng(50, 30));
1361 * ```
1362 *
1363 * Note that `LatLng` does not inherit from Leaflet's `Class` object,
1364 * which means new classes can't inherit from it, and new methods
1365 * can't be added to it with the `include` function.
1366 */
1367
1368function LatLng(lat, lng, alt) {
1369	if (isNaN(lat) || isNaN(lng)) {
1370		throw new Error('Invalid LatLng object: (' + lat + ', ' + lng + ')');
1371	}
1372
1373	// @property lat: Number
1374	// Latitude in degrees
1375	this.lat = +lat;
1376
1377	// @property lng: Number
1378	// Longitude in degrees
1379	this.lng = +lng;
1380
1381	// @property alt: Number
1382	// Altitude in meters (optional)
1383	if (alt !== undefined) {
1384		this.alt = +alt;
1385	}
1386}
1387
1388LatLng.prototype = {
1389	// @method equals(otherLatLng: LatLng, maxMargin?: Number): Boolean
1390	// 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.
1391	equals: function (obj, maxMargin) {
1392		if (!obj) { return false; }
1393
1394		obj = toLatLng(obj);
1395
1396		var margin = Math.max(
1397		        Math.abs(this.lat - obj.lat),
1398		        Math.abs(this.lng - obj.lng));
1399
1400		return margin <= (maxMargin === undefined ? 1.0E-9 : maxMargin);
1401	},
1402
1403	// @method toString(): String
1404	// Returns a string representation of the point (for debugging purposes).
1405	toString: function (precision) {
1406		return 'LatLng(' +
1407		        formatNum(this.lat, precision) + ', ' +
1408		        formatNum(this.lng, precision) + ')';
1409	},
1410
1411	// @method distanceTo(otherLatLng: LatLng): Number
1412	// 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).
1413	distanceTo: function (other) {
1414		return Earth.distance(this, toLatLng(other));
1415	},
1416
1417	// @method wrap(): LatLng
1418	// Returns a new `LatLng` object with the longitude wrapped so it's always between -180 and +180 degrees.
1419	wrap: function () {
1420		return Earth.wrapLatLng(this);
1421	},
1422
1423	// @method toBounds(sizeInMeters: Number): LatLngBounds
1424	// Returns a new `LatLngBounds` object in which each boundary is `sizeInMeters/2` meters apart from the `LatLng`.
1425	toBounds: function (sizeInMeters) {
1426		var latAccuracy = 180 * sizeInMeters / 40075017,
1427		    lngAccuracy = latAccuracy / Math.cos((Math.PI / 180) * this.lat);
1428
1429		return toLatLngBounds(
1430		        [this.lat - latAccuracy, this.lng - lngAccuracy],
1431		        [this.lat + latAccuracy, this.lng + lngAccuracy]);
1432	},
1433
1434	clone: function () {
1435		return new LatLng(this.lat, this.lng, this.alt);
1436	}
1437};
1438
1439
1440
1441// @factory L.latLng(latitude: Number, longitude: Number, altitude?: Number): LatLng
1442// Creates an object representing a geographical point with the given latitude and longitude (and optionally altitude).
1443
1444// @alternative
1445// @factory L.latLng(coords: Array): LatLng
1446// Expects an array of the form `[Number, Number]` or `[Number, Number, Number]` instead.
1447
1448// @alternative
1449// @factory L.latLng(coords: Object): LatLng
1450// Expects an plain object of the form `{lat: Number, lng: Number}` or `{lat: Number, lng: Number, alt: Number}` instead.
1451
1452function toLatLng(a, b, c) {
1453	if (a instanceof LatLng) {
1454		return a;
1455	}
1456	if (isArray(a) && typeof a[0] !== 'object') {
1457		if (a.length === 3) {
1458			return new LatLng(a[0], a[1], a[2]);
1459		}
1460		if (a.length === 2) {
1461			return new LatLng(a[0], a[1]);
1462		}
1463		return null;
1464	}
1465	if (a === undefined || a === null) {
1466		return a;
1467	}
1468	if (typeof a === 'object' && 'lat' in a) {
1469		return new LatLng(a.lat, 'lng' in a ? a.lng : a.lon, a.alt);
1470	}
1471	if (b === undefined) {
1472		return null;
1473	}
1474	return new LatLng(a, b, c);
1475}
1476
1477/*
1478 * @namespace CRS
1479 * @crs L.CRS.Base
1480 * Object that defines coordinate reference systems for projecting
1481 * geographical points into pixel (screen) coordinates and back (and to
1482 * coordinates in other units for [WMS](https://en.wikipedia.org/wiki/Web_Map_Service) services). See
1483 * [spatial reference system](http://en.wikipedia.org/wiki/Coordinate_reference_system).
1484 *
1485 * Leaflet defines the most usual CRSs by default. If you want to use a
1486 * CRS not defined by default, take a look at the
1487 * [Proj4Leaflet](https://github.com/kartena/Proj4Leaflet) plugin.
1488 *
1489 * Note that the CRS instances do not inherit from Leafet's `Class` object,
1490 * and can't be instantiated. Also, new classes can't inherit from them,
1491 * and methods can't be added to them with the `include` function.
1492 */
1493
1494var CRS = {
1495	// @method latLngToPoint(latlng: LatLng, zoom: Number): Point
1496	// Projects geographical coordinates into pixel coordinates for a given zoom.
1497	latLngToPoint: function (latlng, zoom) {
1498		var projectedPoint = this.projection.project(latlng),
1499		    scale = this.scale(zoom);
1500
1501		return this.transformation._transform(projectedPoint, scale);
1502	},
1503
1504	// @method pointToLatLng(point: Point, zoom: Number): LatLng
1505	// The inverse of `latLngToPoint`. Projects pixel coordinates on a given
1506	// zoom into geographical coordinates.
1507	pointToLatLng: function (point, zoom) {
1508		var scale = this.scale(zoom),
1509		    untransformedPoint = this.transformation.untransform(point, scale);
1510
1511		return this.projection.unproject(untransformedPoint);
1512	},
1513
1514	// @method project(latlng: LatLng): Point
1515	// Projects geographical coordinates into coordinates in units accepted for
1516	// this CRS (e.g. meters for EPSG:3857, for passing it to WMS services).
1517	project: function (latlng) {
1518		return this.projection.project(latlng);
1519	},
1520
1521	// @method unproject(point: Point): LatLng
1522	// Given a projected coordinate returns the corresponding LatLng.
1523	// The inverse of `project`.
1524	unproject: function (point) {
1525		return this.projection.unproject(point);
1526	},
1527
1528	// @method scale(zoom: Number): Number
1529	// Returns the scale used when transforming projected coordinates into
1530	// pixel coordinates for a particular zoom. For example, it returns
1531	// `256 * 2^zoom` for Mercator-based CRS.
1532	scale: function (zoom) {
1533		return 256 * Math.pow(2, zoom);
1534	},
1535
1536	// @method zoom(scale: Number): Number
1537	// Inverse of `scale()`, returns the zoom level corresponding to a scale
1538	// factor of `scale`.
1539	zoom: function (scale) {
1540		return Math.log(scale / 256) / Math.LN2;
1541	},
1542
1543	// @method getProjectedBounds(zoom: Number): Bounds
1544	// Returns the projection's bounds scaled and transformed for the provided `zoom`.
1545	getProjectedBounds: function (zoom) {
1546		if (this.infinite) { return null; }
1547
1548		var b = this.projection.bounds,
1549		    s = this.scale(zoom),
1550		    min = this.transformation.transform(b.min, s),
1551		    max = this.transformation.transform(b.max, s);
1552
1553		return new Bounds(min, max);
1554	},
1555
1556	// @method distance(latlng1: LatLng, latlng2: LatLng): Number
1557	// Returns the distance between two geographical coordinates.
1558
1559	// @property code: String
1560	// Standard code name of the CRS passed into WMS services (e.g. `'EPSG:3857'`)
1561	//
1562	// @property wrapLng: Number[]
1563	// An array of two numbers defining whether the longitude (horizontal) coordinate
1564	// axis wraps around a given range and how. Defaults to `[-180, 180]` in most
1565	// geographical CRSs. If `undefined`, the longitude axis does not wrap around.
1566	//
1567	// @property wrapLat: Number[]
1568	// Like `wrapLng`, but for the latitude (vertical) axis.
1569
1570	// wrapLng: [min, max],
1571	// wrapLat: [min, max],
1572
1573	// @property infinite: Boolean
1574	// If true, the coordinate space will be unbounded (infinite in both axes)
1575	infinite: false,
1576
1577	// @method wrapLatLng(latlng: LatLng): LatLng
1578	// Returns a `LatLng` where lat and lng has been wrapped according to the
1579	// CRS's `wrapLat` and `wrapLng` properties, if they are outside the CRS's bounds.
1580	wrapLatLng: function (latlng) {
1581		var lng = this.wrapLng ? wrapNum(latlng.lng, this.wrapLng, true) : latlng.lng,
1582		    lat = this.wrapLat ? wrapNum(latlng.lat, this.wrapLat, true) : latlng.lat,
1583		    alt = latlng.alt;
1584
1585		return new LatLng(lat, lng, alt);
1586	},
1587
1588	// @method wrapLatLngBounds(bounds: LatLngBounds): LatLngBounds
1589	// Returns a `LatLngBounds` with the same size as the given one, ensuring
1590	// that its center is within the CRS's bounds.
1591	// Only accepts actual `L.LatLngBounds` instances, not arrays.
1592	wrapLatLngBounds: function (bounds) {
1593		var center = bounds.getCenter(),
1594		    newCenter = this.wrapLatLng(center),
1595		    latShift = center.lat - newCenter.lat,
1596		    lngShift = center.lng - newCenter.lng;
1597
1598		if (latShift === 0 && lngShift === 0) {
1599			return bounds;
1600		}
1601
vendor: 8,594 bytes, lines 1602-1880
1602		var sw = bounds.getSouthWest(),
1603		    ne = bounds.getNorthEast(),
1604		    newSw = new LatLng(sw.lat - latShift, sw.lng - lngShift),
1605		    newNe = new LatLng(ne.lat - latShift, ne.lng - lngShift);
1606
1607		return new LatLngBounds(newSw, newNe);
1608	}
1609};
1610
1611/*
1612 * @namespace CRS
1613 * @crs L.CRS.Earth
1614 *
1615 * Serves as the base for CRS that are global such that they cover the earth.
1616 * Can only be used as the base for other CRS and cannot be used directly,
1617 * since it does not have a `code`, `projection` or `transformation`. `distance()` returns
1618 * meters.
1619 */
1620
1621var Earth = extend({}, CRS, {
1622	wrapLng: [-180, 180],
1623
1624	// Mean Earth Radius, as recommended for use by
1625	// the International Union of Geodesy and Geophysics,
1626	// see http://rosettacode.org/wiki/Haversine_formula
1627	R: 6371000,
1628
1629	// distance between two geographical points using spherical law of cosines approximation
1630	distance: function (latlng1, latlng2) {
1631		var rad = Math.PI / 180,
1632		    lat1 = latlng1.lat * rad,
1633		    lat2 = latlng2.lat * rad,
1634		    sinDLat = Math.sin((latlng2.lat - latlng1.lat) * rad / 2),
1635		    sinDLon = Math.sin((latlng2.lng - latlng1.lng) * rad / 2),
1636		    a = sinDLat * sinDLat + Math.cos(lat1) * Math.cos(lat2) * sinDLon * sinDLon,
1637		    c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
1638		return this.R * c;
1639	}
1640});
1641
1642/*
1643 * @namespace Projection
1644 * @projection L.Projection.SphericalMercator
1645 *
1646 * Spherical Mercator projection — the most common projection for online maps,
1647 * used by almost all free and commercial tile providers. Assumes that Earth is
1648 * a sphere. Used by the `EPSG:3857` CRS.
1649 */
1650
1651var earthRadius = 6378137;
1652
1653var SphericalMercator = {
1654
1655	R: earthRadius,
1656	MAX_LATITUDE: 85.0511287798,
1657
1658	project: function (latlng) {
1659		var d = Math.PI / 180,
1660		    max = this.MAX_LATITUDE,
1661		    lat = Math.max(Math.min(max, latlng.lat), -max),
1662		    sin = Math.sin(lat * d);
1663
1664		return new Point(
1665			this.R * latlng.lng * d,
1666			this.R * Math.log((1 + sin) / (1 - sin)) / 2);
1667	},
1668
1669	unproject: function (point) {
1670		var d = 180 / Math.PI;
1671
1672		return new LatLng(
1673			(2 * Math.atan(Math.exp(point.y / this.R)) - (Math.PI / 2)) * d,
1674			point.x * d / this.R);
1675	},
1676
1677	bounds: (function () {
1678		var d = earthRadius * Math.PI;
1679		return new Bounds([-d, -d], [d, d]);
1680	})()
1681};
1682
1683/*
1684 * @class Transformation
1685 * @aka L.Transformation
1686 *
1687 * Represents an affine transformation: a set of coefficients `a`, `b`, `c`, `d`
1688 * for transforming a point of a form `(x, y)` into `(a*x + b, c*y + d)` and doing
1689 * the reverse. Used by Leaflet in its projections code.
1690 *
1691 * @example
1692 *
1693 * ```js
1694 * var transformation = L.transformation(2, 5, -1, 10),
1695 * 	p = L.point(1, 2),
1696 * 	p2 = transformation.transform(p), //  L.point(7, 8)
1697 * 	p3 = transformation.untransform(p2); //  L.point(1, 2)
1698 * ```
1699 */
1700
1701
1702// factory new L.Transformation(a: Number, b: Number, c: Number, d: Number)
1703// Creates a `Transformation` object with the given coefficients.
1704function Transformation(a, b, c, d) {
1705	if (isArray(a)) {
1706		// use array properties
1707		this._a = a[0];
1708		this._b = a[1];
1709		this._c = a[2];
1710		this._d = a[3];
1711		return;
1712	}
1713	this._a = a;
1714	this._b = b;
1715	this._c = c;
1716	this._d = d;
1717}
1718
1719Transformation.prototype = {
1720	// @method transform(point: Point, scale?: Number): Point
1721	// Returns a transformed point, optionally multiplied by the given scale.
1722	// Only accepts actual `L.Point` instances, not arrays.
1723	transform: function (point, scale) { // (Point, Number) -> Point
1724		return this._transform(point.clone(), scale);
1725	},
1726
1727	// destructive transform (faster)
1728	_transform: function (point, scale) {
1729		scale = scale || 1;
1730		point.x = scale * (this._a * point.x + this._b);
1731		point.y = scale * (this._c * point.y + this._d);
1732		return point;
1733	},
1734
1735	// @method untransform(point: Point, scale?: Number): Point
1736	// Returns the reverse transformation of the given point, optionally divided
1737	// by the given scale. Only accepts actual `L.Point` instances, not arrays.
1738	untransform: function (point, scale) {
1739		scale = scale || 1;
1740		return new Point(
1741		        (point.x / scale - this._b) / this._a,
1742		        (point.y / scale - this._d) / this._c);
1743	}
1744};
1745
1746// factory L.transformation(a: Number, b: Number, c: Number, d: Number)
1747
1748// @factory L.transformation(a: Number, b: Number, c: Number, d: Number)
1749// Instantiates a Transformation object with the given coefficients.
1750
1751// @alternative
1752// @factory L.transformation(coefficients: Array): Transformation
1753// Expects an coefficients array of the form
1754// `[a: Number, b: Number, c: Number, d: Number]`.
1755
1756function toTransformation(a, b, c, d) {
1757	return new Transformation(a, b, c, d);
1758}
1759
1760/*
1761 * @namespace CRS
1762 * @crs L.CRS.EPSG3857
1763 *
1764 * The most common CRS for online maps, used by almost all free and commercial
1765 * tile providers. Uses Spherical Mercator projection. Set in by default in
1766 * Map's `crs` option.
1767 */
1768
1769var EPSG3857 = extend({}, Earth, {
1770	code: 'EPSG:3857',
1771	projection: SphericalMercator,
1772
1773	transformation: (function () {
1774		var scale = 0.5 / (Math.PI * SphericalMercator.R);
1775		return toTransformation(scale, 0.5, -scale, 0.5);
1776	}())
1777});
1778
1779var EPSG900913 = extend({}, EPSG3857, {
1780	code: 'EPSG:900913'
1781});
1782
1783// @namespace SVG; @section
1784// There are several static functions which can be called without instantiating L.SVG:
1785
1786// @function create(name: String): SVGElement
1787// Returns a instance of [SVGElement](https://developer.mozilla.org/docs/Web/API/SVGElement),
1788// corresponding to the class name passed. For example, using 'line' will return
1789// an instance of [SVGLineElement](https://developer.mozilla.org/docs/Web/API/SVGLineElement).
1790function svgCreate(name) {
1791	return document.createElementNS('http://www.w3.org/2000/svg', name);
1792}
1793
1794// @function pointsToPath(rings: Point[], closed: Boolean): String
1795// Generates a SVG path string for multiple rings, with each ring turning
1796// into "M..L..L.." instructions
1797function pointsToPath(rings, closed) {
1798	var str = '',
1799	i, j, len, len2, points, p;
1800
1801	for (i = 0, len = rings.length; i < len; i++) {
1802		points = rings[i];
1803
1804		for (j = 0, len2 = points.length; j < len2; j++) {
1805			p = points[j];
1806			str += (j ? 'L' : 'M') + p.x + ' ' + p.y;
1807		}
1808
1809		// closes the ring for polygons; "x" is VML syntax
1810		str += closed ? (svg ? 'z' : 'x') : '';
1811	}
1812
1813	// SVG complains about empty path strings
1814	return str || 'M0 0';
1815}
1816
1817/*
1818 * @namespace Browser
1819 * @aka L.Browser
1820 *
1821 * A namespace with static properties for browser/feature detection used by Leaflet internally.
1822 *
1823 * @example
1824 *
1825 * ```js
1826 * if (L.Browser.ielt9) {
1827 *   alert('Upgrade your browser, dude!');
1828 * }
1829 * ```
1830 */
1831
1832var style$1 = document.documentElement.style;
1833
1834// @property ie: Boolean; `true` for all Internet Explorer versions (not Edge).
1835var ie = 'ActiveXObject' in window;
1836
1837// @property ielt9: Boolean; `true` for Internet Explorer versions less than 9.
1838var ielt9 = ie && !document.addEventListener;
1839
1840// @property edge: Boolean; `true` for the Edge web browser.
1841var edge = 'msLaunchUri' in navigator && !('documentMode' in document);
1842
1843// @property webkit: Boolean;
1844// `true` for webkit-based browsers like Chrome and Safari (including mobile versions).
1845var webkit = userAgentContains('webkit');
1846
1847// @property android: Boolean
1848// `true` for any browser running on an Android platform.
1849var android = userAgentContains('android');
1850
1851// @property android23: Boolean; `true` for browsers running on Android 2 or Android 3.
1852var android23 = userAgentContains('android 2') || userAgentContains('android 3');
1853
1854/* See https://stackoverflow.com/a/17961266 for details on detecting stock Android */
1855var webkitVer = parseInt(/WebKit\/([0-9]+)|$/.exec(navigator.userAgent)[1], 10); // also matches AppleWebKit
1856// @property androidStock: Boolean; `true` for the Android stock browser (i.e. not Chrome)
1857var androidStock = android && userAgentContains('Google') && webkitVer < 537 && !('AudioNode' in window);
1858
1859// @property opera: Boolean; `true` for the Opera browser
1860var opera = !!window.opera;
1861
1862// @property chrome: Boolean; `true` for the Chrome browser.
1863var chrome = userAgentContains('chrome');
1864
1865// @property gecko: Boolean; `true` for gecko-based browsers like Firefox.
1866var gecko = userAgentContains('gecko') && !webkit && !opera && !ie;
1867
1868// @property safari: Boolean; `true` for the Safari browser.
1869var safari = !chrome && userAgentContains('safari');
1870
1871var phantom = userAgentContains('phantom');
1872
1873// @property opera12: Boolean
1874// `true` for the Opera browser supporting CSS transforms (version 12 or later).
1875var opera12 = 'OTransition' in style$1;
1876
1877// @property win: Boolean; `true` when the browser is running in a Windows platform
1878var win = navigator.platform.indexOf('Win') === 0;
1879
1880// @property ie3d: Boolean;
1880 `true` for all Internet Explorer versions supporting CSS transforms.
1881var ie3d = ie && ('transition' in style$1);
1882
1883// @property webkit3d: Boolean; `true` for webkit-based browsers supporting CSS transforms.
1884var webkit3d = ('WebKitCSSMatrix' in window) && ('m11' in new window.WebKitCSSMatrix()) && !android23;
1885
1886// @property gecko3d: Boolean; `true` for gecko-based browsers supporting CSS transforms.
1887var gecko3d = 'MozPerspective' in style$1;
1888
1889// @property any3d: Boolean
1890// `true` for all browsers supporting CSS transforms.
1891var any3d = !window.L_DISABLE_3D && (ie3d || webkit3d || gecko3d) && !opera12 && !phantom;
1892
1893// @property mobile: Boolean; `true` for all browsers running in a mobile device.
1894var mobile = typeof orientation !== 'undefined' || userAgentContains('mobile');
1895
1896// @property mobileWebkit: Boolean; `true` for all webkit-based browsers in a mobile device.
1897var mobileWebkit = mobile && webkit;
1898
1899// @property mobileWebkit3d: Boolean
1900// `true` for all webkit-based browsers in a mobile device supporting CSS transforms.
1901var mobileWebkit3d = mobile && webkit3d;
1902
1903// @property msPointer: Boolean
1904// `true` for browsers implementing the Microsoft touch events model (notably IE10).
1905var msPointer = !window.PointerEvent && window.MSPointerEvent;
1906
1907// @property pointer: Boolean
1908// `true` for all browsers supporting [pointer events](https://msdn.microsoft.com/en-us/library/dn433244%28v=vs.85%29.aspx).
1909var pointer = !webkit && !!(window.PointerEvent || msPointer);
1910
1911// @property touch: Boolean
1912// `true` for all browsers supporting [touch events](https://developer.mozilla.org/docs/Web/API/Touch_events).
1913// This does not necessarily mean that the browser is running in a computer with
1914// a touchscreen, it only means that the browser is capable of understanding
1915// touch events.
1916var touch = !window.L_NO_TOUCH && (pointer || 'ontouchstart' in window ||
1917		(window.DocumentTouch && document instanceof window.DocumentTouch));
1918
1919// @property mobileOpera: Boolean; `true` for the Opera browser in a mobile device.
1920var mobileOpera = mobile && opera;
1921
1922// @property mobileGecko: Boolean
1923// `true` for gecko-based browsers running in a mobile device.
1924var mobileGecko = mobile && gecko;
1925
1926// @property retina: Boolean
1927// `true` for browsers on a high-resolution "retina" screen or on any screen when browser's display zoom is more than 100%.
1928var retina = (window.devicePixelRatio || (window.screen.deviceXDPI / window.screen.logicalXDPI)) > 1;
1929
1930// @property passiveEvents: Boolean
1931// `true` for browsers that support passive events.
1932var passiveEvents = (function () {
1933	var supportsPassiveOption = false;
1934	try {
1935		var opts = Object.defineProperty({}, 'passive', {
1936			get: function () {
1937				supportsPassiveOption = true;
1938			}
1939		});
1940		window.addEventListener('testPassiveEventSupport', falseFn, opts);
1941		window.removeEventListener('testPassiveEventSupport', falseFn, opts);
1942	} catch (e) {
1943		// Errors can safely be ignored since this is only a browser support test.
1944	}
1945	return supportsPassiveOption;
1946});
1947
1948// @property canvas: Boolean
1949// `true` when the browser supports [`<canvas>`](https://developer.mozilla.org/docs/Web/API/Canvas_API).
1950var canvas = (function () {
1951	return !!document.createElement('canvas').getContext;
1952}());
1953
1954// @property svg: Boolean
1955// `true` when the browser supports [SVG](https://developer.mozilla.org/docs/Web/SVG).
1956var svg = !!(document.createElementNS && svgCreate('svg').createSVGRect);
1957
1958// @property vml: Boolean
1959// `true` if the browser supports [VML](https://en.wikipedia.org/wiki/Vector_Markup_Language).
1960var vml = !svg && (function () {
1961	try {
1962		var div = document.createElement('div');
1963		div.innerHTML = '<v:shape adj="1"/>';
1964
1965		var shape = div.firstChild;
1966		shape.style.behavior = 'url(#default#VML)';
1967
1968		return shape && (typeof shape.adj === 'object');
1969
1970	} catch (e) {
1971		return false;
1972	}
1973}());
1974
1975
1976function userAgentContains(str) {
1977	return navigator.userAgent.toLowerCase().indexOf(str) >= 0;
1978}
1979
1980
1981var Browser = (Object.freeze || Object)({
1982	ie: ie,
1983	ielt9: ielt9,
1984	edge: edge,
1985	webkit: webkit,
1986	android: android,
1987	android23: android23,
1988	androidStock: androidStock,
1989	opera: opera,
1990	chrome: chrome,
1991	gecko: gecko,
1992	safari: safari,
1993	phantom: phantom,
1994	opera12: opera12,
1995	win: win,
1996	ie3d: ie3d,
1997	webkit3d: webkit3d,
1998	gecko3d: gecko3d,
1999	any3d: any3d,
2000	mobile: mobile,
2001	mobileWebkit: mobileWebkit,
2002	mobileWebkit3d: mobileWebkit3d,
2003	msPointer: msPointer,
2004	pointer: pointer,
2005	touch: touch,
2006	mobileOpera: mobileOpera,
2007	mobileGecko: mobileGecko,
2008	retina: retina,
2009	passiveEvents: passiveEvents,
2010	canvas: canvas,
2011	svg: svg,
2012	vml: vml
2013});
2014
2015/*
2016 * Extends L.DomEvent to provide touch support for Internet Explorer and Windows-based devices.
2017 */
2018
2019
2020var POINTER_DOWN =   msPointer ? 'MSPointerDown'   : 'pointerdown';
2021var POINTER_MOVE =   msPointer ? 'MSPointerMove'   : 'pointermove';
2022var POINTER_UP =     msPointer ? 'MSPointerUp'     : 'pointerup';
2023var POINTER_CANCEL = msPointer ? 'MSPointerCancel' : 'pointercancel';
2024var TAG_WHITE_LIST = ['INPUT', 'SELECT', 'OPTION'];
2025
2026var _pointers = {};
2027var _pointerDocListener = false;
2028
2029// DomEvent.DoubleTap needs to know about this
2030var _pointersCount = 0;
2031
2032// Provides a touch events wrapper for (ms)pointer events.
2033// ref http://www.w3.org/TR/pointerevents/ https://www.w3.org/Bugs/Public/show_bug.cgi?id=22890
2034
2035function addPointerListener(obj, type, handler, id) {
2036	if (type === 'touchstart') {
2037		_addPointerStart(obj, handler, id);
2038
2039	} else if (type === 'touchmove') {
2040		_addPointerMove(obj, handler, id);
2041
2042	} else if (type === 'touchend') {
2043		_addPointerEnd(obj, handler, id);
2044	}
2045
2046	return this;
2047}
2048
2049function removePointerListener(obj, type, id) {
2050	var handler = obj['_leaflet_' + type + id];
2051
2052	if (type === 'touchstart') {
2053		obj.removeEventListener(POINTER_DOWN, handler, false);
2054
2055	} else if (type === 'touchmove') {
2056		obj.removeEventListener(POINTER_MOVE, handler, false);
2057
2058	} else if (type === 'touchend') {
2059		obj.removeEventListener(POINTER_UP, handler, false);
2060		obj.removeEventListener(POINTER_CANCEL, handler, false);
2061	}
2062
2063	return this;
2064}
2065
2066function _addPointerStart(obj, handler, id) {
2067	var onDown = bind(function (e) {
2068		if (e.pointerType !== 'mouse' && e.MSPOINTER_TYPE_MOUSE && e.pointerType !== e.MSPOINTER_TYPE_MOUSE) {
2069			// In IE11, some touch events needs to fire for form controls, or
2070			// the controls will stop working. We keep a whitelist of tag names that
2071			// need these events. For other target tags, we prevent default on the event.
2072			if (TAG_WHITE_LIST.indexOf(e.target.tagName) < 0) {
2073				preventDefault(e);
2074			} else {
2075				return;
2076			}
2077		}
2078
2079		_handlePointer(e, handler);
2080	});
2081
2082	obj['_leaflet_touchstart' + id] = onDown;
2083	obj.addEventListener(POINTER_DOWN, onDown, false);
2084
2085	// need to keep track of what pointers and how many are active to provide e.touches emulation
2086	if (!_pointerDocListener) {
2087		// we listen documentElement as any drags that end by moving the touch off the screen get fired there
2088		document.documentElement.addEventListener(POINTER_DOWN, _globalPointerDown, true);
2089		document.documentElement.addEventListener(POINTER_MOVE, _globalPointerMove, true);
2090		document.documentElement.addEventListener(POINTER_UP, _globalPointerUp, true);
2091		document.documentElement.addEventListener(POINTER_CANCEL, _globalPointerUp, true);
2092
2093		_pointerDocListener = true;
2094	}
2095}
2096
2097function _globalPointerDown(e) {
2098	_pointers[e.pointerId] = e;
2099	_pointersCount++;
2100}
2101
2102function _globalPointerMove(e) {
2103	if (_pointers[e.pointerId]) {
2104		_pointers[e.pointerId] = e;
2105	}
2106}
2107
2108function _globalPointerUp(e) {
2109	delete _pointers[e.pointerId];
2110	_pointersCount--;
2111}
2112
2113function _handlePointer(e, handler) {
2114	e.touches = [];
2115	for (var i in _pointers) {
2116		e.touches.push(_pointers[i]);
2117	}
2118	e.changedTouches = [e];
2119
2120	handler(e);
2121}
2122
2123function _addPointerMove(obj, handler, id) {
2124	var onMove = function (e) {
2125		// don't fire touch moves when mouse isn't down
2126		if ((e.pointerType === e.MSPOINTER_TYPE_MOUSE || e.pointerType === 'mouse') && e.buttons === 0) { return; }
2127
2128		_handlePointer(e, handler);
2129	};
2130
2131	obj['_leaflet_touchmove' + id] = onMove;
2132	obj.addEventListener(POINTER_MOVE, onMove, false);
2133}
2134
2135function _addPointerEnd(obj, handler, id) {
2136	var onUp = function (e) {
2137		_handlePointer(e, handler);
2138	};
2139
2140	obj['_leaflet_touchend' + id] = onUp;
2141	obj.addEventListener(POINTER_UP, onUp, false);
2142	obj.addEventListener(POINTER_CANCEL, onUp, false);
2143}
2144
2145/*
2146 * Extends the event handling code with double tap support for mobile browsers.
2147 */
2148
2149var _touchstart = msPointer ? 'MSPointerDown' : pointer ? 'pointerdown' : 'touchstart';
2150var _touchend = msPointer ? 'MSPointerUp' : pointer ? 'pointerup' : 'touchend';
2151var _pre = '_leaflet_';
2152
2153// inspired by Zepto touch code by Thomas Fuchs
2154function addDoubleTapListener(obj, handler, id) {
2155	var last, touch$$1,
2156	    doubleTap = false,
2157	    delay = 250;
2158
2159	function onTouchStart(e) {
2160		var count;
2161
2162		if (pointer) {
2163			if ((!edge) || e.pointerType === 'mouse') { return; }
2164			count = _pointersCount;
2165		} else {
2166			count = e.touches.length;
2167		}
2168
2169		if (count > 1) { return; }
2170
2171		var now = Date.now(),
2172		    delta = now - (last || now);
2173
2174		touch$$1 = e.touches ? e.touches[0] : e;
2175		doubleTap = (delta > 0 && delta <= delay);
2176		last = now;
2177	}
2178
2179	function onTouchEnd(e) {
2180		if (doubleTap && !touch$$1.cancelBubble) {
2181			if (pointer) {
2182				if ((!edge) || e.pointerType === 'mouse') { return; }
2183				// work around .type being readonly with MSPointer* events
2184				var newTouch = {},
2185				    prop, i;
2186
2187				for (i in touch$$1) {
2188					prop = touch$$1[i];
2189					newTouch[i] = prop && prop.bind ? prop.bind(touch$$1) : prop;
2190				}
2191				touch$$1 = newTouch;
2192			}
2193			touch$$1.type = 'dblclick';
2194			touch$$1.button = 0;
2195			handler(touch$$1);
2196			last = null;
2197		}
2198	}
2199
2200	obj[_pre + _touchstart + id] = onTouchStart;
2201	obj[_pre + _touchend + id] = onTouchEnd;
2202	obj[_pre + 'dblclick' + id] = handler;
2203
2204	obj.addEventListener(_touchstart, onTouchStart, passiveEvents ? {passive: false} : false);
2205	obj.addEventListener(_touchend, onTouchEnd, passiveEvents ? {passive: false} : false);
2206
2207	// On some platforms (notably, chrome<55 on win10 + touchscreen + mouse),
2208	// the browser doesn't fire touchend/pointerup events but does fire
2209	// native dblclicks. See #4127.
2210	// Edge 14 also fires native dblclicks, but only for pointerType mouse, see #5180.
2211	obj.addEventListener('dblclick', handler, false);
2212
2213	return this;
2214}
2215
2216function removeDoubleTapListener(obj, id) {
2217	var touchstart = obj[_pre + _touchstart + id],
2218	    touchend = obj[_pre + _touchend + id],
2219	    dblclick = obj[_pre + 'dblclick' + id];
2220
2221	obj.removeEventListener(_touchstart, touchstart, passiveEvents ? {passive: false} : false);
2222	obj.removeEventListener(_touchend, touchend, passiveEvents ? {passive: false} : false);
2223	if (!edge) {
2224		obj.removeEventListener('dblclick', dblclick, false);
2225	}
2226
2227	return this;
2228}
2229
2230/*
2231 * @namespace DomUtil
2232 *
2233 * Utility functions to work with the [DOM](https://developer.mozilla.org/docs/Web/API/Document_Object_Model)
2234 * tree, used by Leaflet internally.
2235 *
2236 * Most functions expecting or returning a `HTMLElement` also work for
2237 * SVG elements. The only difference is that classes refer to CSS classes
2238 * in HTML and SVG classes in SVG.
2239 */
2240
2241
2242// @property TRANSFORM: String
2243// Vendor-prefixed transform style name (e.g. `'webkitTransform'` for WebKit).
2244var TRANSFORM = testProp(
2245	['transform', 'webkitTransform', 'OTransform', 'MozTransform', 'msTransform']);
2246
2247// webkitTransition comes first because some browser versions that drop vendor prefix don't do
2248// the same for the transitionend event, in particular the Android 4.1 stock browser
2249
2250// @property TRANSITION: String
2251// Vendor-prefixed transition style name.
2252var TRANSITION = testProp(
2253	['webkitTransition', 'transition', 'OTransition', 'MozTransition', 'msTransition']);
2254
2255// @property TRANSITION_END: String
2256// Vendor-prefixed transitionend event name.
2257var TRANSITION_END =
2258	TRANSITION === 'webkitTransition' || TRANSITION === 'OTransition' ? TRANSITION + 'End' : 'transitionend';
2259
2260
2261// @function get(id: String|HTMLElement): HTMLElement
2262// Returns an element given its DOM id, or returns the element itself
2263// if it was passed directly.
2264function get(id) {
2265	return typeof id === 'string' ? document.getElementById(id) : id;
2266}
2267
2268// @function getStyle(el: HTMLElement, styleAttrib: String): String
2269// Returns the value for a certain style attribute on an element,
2270// including computed values or values set through CSS.
2271function getStyle(el, style) {
2272	var value = el.style[style] || (el.currentStyle && el.currentStyle[style]);
2273
2274	if ((!value || value === 'auto') && document.defaultView) {
2275		var css = document.defaultView.getComputedStyle(el, null);
2276		value = css ? css[style] : null;
2277	}
2278	return value === 'auto' ? null : value;
2279}
2280
2281// @function create(tagName: String, className?: String, container?: HTMLElement): HTMLElement
2282// Creates an HTML element with `tagName`, sets its class to `className`, and optionally appends it to `container` element.
2283function create$1(tagName, className, container) {
2284	var el = document.createElement(tagName);
2285	el.className = className || '';
2286
2287	if (container) {
2288		container.appendChild(el);
2289	}
2290	return el;
2291}
2292
2293// @function remove(el: HTMLElement)
2294// Removes `el` from its parent element
2295function remove(el) {
2296	var parent = el.parentNode;
2297	if (parent) {
2298		parent.removeChild(el);
2299	}
2300}
2301
2302// @function empty(el: HTMLElement)
2303// Removes all of `el`'s children elements from `el`
2304function empty(el) {
2305	while (el.firstChild) {
2306		el.removeChild(el.firstChild);
2307	}
2308}
2309
2310// @function toFront(el: HTMLElement)
vendor: 9,504 bytes, lines 2311-2612
2311// Makes `el` the last child of its parent, so it renders in front of the other children.
2312function toFront(el) {
2313	var parent = el.parentNode;
2314	if (parent && parent.lastChild !== el) {
2315		parent.appendChild(el);
2316	}
2317}
2318
2319// @function toBack(el: HTMLElement)
2320// Makes `el` the first child of its parent, so it renders behind the other children.
2321function toBack(el) {
2322	var parent = el.parentNode;
2323	if (parent && parent.firstChild !== el) {
2324		parent.insertBefore(el, parent.firstChild);
2325	}
2326}
2327
2328// @function hasClass(el: HTMLElement, name: String): Boolean
2329// Returns `true` if the element's class attribute contains `name`.
2330function hasClass(el, name) {
2331	if (el.classList !== undefined) {
2332		return el.classList.contains(name);
2333	}
2334	var className = getClass(el);
2335	return className.length > 0 && new RegExp('(^|\\s)' + name + '(\\s|$)').test(className);
2336}
2337
2338// @function addClass(el: HTMLElement, name: String)
2339// Adds `name` to the element's class attribute.
2340function addClass(el, name) {
2341	if (el.classList !== undefined) {
2342		var classes = splitWords(name);
2343		for (var i = 0, len = classes.length; i < len; i++) {
2344			el.classList.add(classes[i]);
2345		}
2346	} else if (!hasClass(el, name)) {
2347		var className = getClass(el);
2348		setClass(el, (className ? className + ' ' : '') + name);
2349	}
2350}
2351
2352// @function removeClass(el: HTMLElement, name: String)
2353// Removes `name` from the element's class attribute.
2354function removeClass(el, name) {
2355	if (el.classList !== undefined) {
2356		el.classList.remove(name);
2357	} else {
2358		setClass(el, trim((' ' + getClass(el) + ' ').replace(' ' + name + ' ', ' ')));
2359	}
2360}
2361
2362// @function setClass(el: HTMLElement, name: String)
2363// Sets the element's class.
2364function setClass(el, name) {
2365	if (el.className.baseVal === undefined) {
2366		el.className = name;
2367	} else {
2368		// in case of SVG element
2369		el.className.baseVal = name;
2370	}
2371}
2372
2373// @function getClass(el: HTMLElement): String
2374// Returns the element's class.
2375function getClass(el) {
2376	// Check if the element is an SVGElementInstance and use the correspondingElement instead
2377	// (Required for linked SVG elements in IE11.)
2378	if (el.correspondingElement) {
2379		el = el.correspondingElement;
2380	}
2381	return el.className.baseVal === undefined ? el.className : el.className.baseVal;
2382}
2383
2384// @function setOpacity(el: HTMLElement, opacity: Number)
2385// Set the opacity of an element (including old IE support).
2386// `opacity` must be a number from `0` to `1`.
2387function setOpacity(el, value) {
2388	if ('opacity' in el.style) {
2389		el.style.opacity = value;
2390	} else if ('filter' in el.style) {
2391		_setOpacityIE(el, value);
2392	}
2393}
2394
2395function _setOpacityIE(el, value) {
2396	var filter = false,
2397	    filterName = 'DXImageTransform.Microsoft.Alpha';
2398
2399	// filters collection throws an error if we try to retrieve a filter that doesn't exist
2400	try {
2401		filter = el.filters.item(filterName);
2402	} catch (e) {
2403		// don't set opacity to 1 if we haven't already set an opacity,
2404		// it isn't needed and breaks transparent pngs.
2405		if (value === 1) { return; }
2406	}
2407
2408	value = Math.round(value * 100);
2409
2410	if (filter) {
2411		filter.Enabled = (value !== 100);
2412		filter.Opacity = value;
2413	} else {
2414		el.style.filter += ' progid:' + filterName + '(opacity=' + value + ')';
2415	}
2416}
2417
2418// @function testProp(props: String[]): String|false
2419// Goes through the array of style names and returns the first name
2420// that is a valid style name for an element. If no such name is found,
2421// it returns false. Useful for vendor-prefixed styles like `transform`.
2422function testProp(props) {
2423	var style = document.documentElement.style;
2424
2425	for (var i = 0; i < props.length; i++) {
2426		if (props[i] in style) {
2427			return props[i];
2428		}
2429	}
2430	return false;
2431}
2432
2433// @function setTransform(el: HTMLElement, offset: Point, scale?: Number)
2434// Resets the 3D CSS transform of `el` so it is translated by `offset` pixels
2435// and optionally scaled by `scale`. Does not have an effect if the
2436// browser doesn't support 3D CSS transforms.
2437function setTransform(el, offset, scale) {
2438	var pos = offset || new Point(0, 0);
2439
2440	el.style[TRANSFORM] =
2441		(ie3d ?
2442			'translate(' + pos.x + 'px,' + pos.y + 'px)' :
2443			'translate3d(' + pos.x + 'px,' + pos.y + 'px,0)') +
2444		(scale ? ' scale(' + scale + ')' : '');
2445}
2446
2447// @function setPosition(el: HTMLElement, position: Point)
2448// Sets the position of `el` to coordinates specified by `position`,
2449// using CSS translate or top/left positioning depending on the browser
2450// (used by Leaflet internally to position its layers).
2451function setPosition(el, point) {
2452
2453	/*eslint-disable */
2454	el._leaflet_pos = point;
2455	/* eslint-enable */
2456
2457	if (any3d) {
2458		setTransform(el, point);
2459	} else {
2460		el.style.left = point.x + 'px';
2461		el.style.top = point.y + 'px';
2462	}
2463}
2464
2465// @function getPosition(el: HTMLElement): Point
2466// Returns the coordinates of an element previously positioned with setPosition.
2467function getPosition(el) {
2468	// this method is only used for elements previously positioned using setPosition,
2469	// so it's safe to cache the position for performance
2470
2471	return el._leaflet_pos || new Point(0, 0);
2472}
2473
2474// @function disableTextSelection()
2475// Prevents the user from generating `selectstart` DOM events, usually generated
2476// when the user drags the mouse through a page with text. Used internally
2477// by Leaflet to override the behaviour of any click-and-drag interaction on
2478// the map. Affects drag interactions on the whole document.
2479
2480// @function enableTextSelection()
2481// Cancels the effects of a previous [`L.DomUtil.disableTextSelection`](#domutil-disabletextselection).
2482var disableTextSelection;
2483var enableTextSelection;
2484var _userSelect;
2485if ('onselectstart' in document) {
2486	disableTextSelection = function () {
2487		on(window, 'selectstart', preventDefault);
2488	};
2489	enableTextSelection = function () {
2490		off(window, 'selectstart', preventDefault);
2491	};
2492} else {
2493	var userSelectProperty = testProp(
2494		['userSelect', 'WebkitUserSelect', 'OUserSelect', 'MozUserSelect', 'msUserSelect']);
2495
2496	disableTextSelection = function () {
2497		if (userSelectProperty) {
2498			var style = document.documentElement.style;
2499			_userSelect = style[userSelectProperty];
2500			style[userSelectProperty] = 'none';
2501		}
2502	};
2503	enableTextSelection = function () {
2504		if (userSelectProperty) {
2505			document.documentElement.style[userSelectProperty] = _userSelect;
2506			_userSelect = undefined;
2507		}
2508	};
2509}
2510
2511// @function disableImageDrag()
2512// As [`L.DomUtil.disableTextSelection`](#domutil-disabletextselection), but
2513// for `dragstart` DOM events, usually generated when the user drags an image.
2514function disableImageDrag() {
2515	on(window, 'dragstart', preventDefault);
2516}
2517
2518// @function enableImageDrag()
2519// Cancels the effects of a previous [`L.DomUtil.disableImageDrag`](#domutil-disabletextselection).
2520function enableImageDrag() {
2521	off(window, 'dragstart', preventDefault);
2522}
2523
2524var _outlineElement;
2525var _outlineStyle;
2526// @function preventOutline(el: HTMLElement)
2527// Makes the [outline](https://developer.mozilla.org/docs/Web/CSS/outline)
2528// of the element `el` invisible. Used internally by Leaflet to prevent
2529// focusable elements from displaying an outline when the user performs a
2530// drag interaction on them.
2531function preventOutline(element) {
2532	while (element.tabIndex === -1) {
2533		element = element.parentNode;
2534	}
2535	if (!element.style) { return; }
2536	restoreOutline();
2537	_outlineElement = element;
2538	_outlineStyle = element.style.outline;
2539	element.style.outline = 'none';
2540	on(window, 'keydown', restoreOutline);
2541}
2542
2543// @function restoreOutline()
2544// Cancels the effects of a previous [`L.DomUtil.preventOutline`]().
2545function restoreOutline() {
2546	if (!_outlineElement) { return; }
2547	_outlineElement.style.outline = _outlineStyle;
2548	_outlineElement = undefined;
2549	_outlineStyle = undefined;
2550	off(window, 'keydown', restoreOutline);
2551}
2552
2553// @function getSizedParentNode(el: HTMLElement): HTMLElement
2554// Finds the closest parent node which size (width and height) is not null.
2555function getSizedParentNode(element) {
2556	do {
2557		element = element.parentNode;
2558	} while ((!element.offsetWidth || !element.offsetHeight) && element !== document.body);
2559	return element;
2560}
2561
2562// @function getScale(el: HTMLElement): Object
2563// Computes the CSS scale currently applied on the element.
2564// Returns an object with `x` and `y` members as horizontal and vertical scales respectively,
2565// and `boundingClientRect` as the result of [`getBoundingClientRect()`](https://developer.mozilla.org/en-US/docs/Web/API/Element/getBoundingClientRect).
2566function getScale(element) {
2567	var rect = element.getBoundingClientRect(); // Read-only in old browsers.
2568
2569	return {
2570		x: rect.width / element.offsetWidth || 1,
2571		y: rect.height / element.offsetHeight || 1,
2572		boundingClientRect: rect
2573	};
2574}
2575
2576
2577var DomUtil = (Object.freeze || Object)({
2578	TRANSFORM: TRANSFORM,
2579	TRANSITION: TRANSITION,
2580	TRANSITION_END: TRANSITION_END,
2581	get: get,
2582	getStyle: getStyle,
2583	create: create$1,
2584	remove: remove,
2585	empty: empty,
2586	toFront: toFront,
2587	toBack: toBack,
2588	hasClass: hasClass,
2589	addClass: addClass,
2590	removeClass: removeClass,
2591	setClass: setClass,
2592	getClass: getClass,
2593	setOpacity: setOpacity,
2594	testProp: testProp,
2595	setTransform: setTransform,
2596	setPosition: setPosition,
2597	getPosition: getPosition,
2598	disableTextSelection: disableTextSelection,
2599	enableTextSelection: enableTextSelection,
2600	disableImageDrag: disableImageDrag,
2601	enableImageDrag: enableImageDrag,
2602	preventOutline: preventOutline,
2603	restoreOutline: restoreOutline,
2604	getSizedParentNode: getSizedParentNode,
2605	getScale: getScale
2606});
2607
2608/*
2609 * @namespace DomEvent
2610 * Utility functions to work with the [DOM events](https://developer.mozilla.org/docs/Web/API/Event), used by Leaflet internally.
2611 */
2612
2613// Inspired by John Resig, Dean Edwards and YUI addEvent implementations.
2614
2615// @function on(el: HTMLElement, types: String, fn: Function, context?: Object): this
2616// Adds a listener function (`fn`) to a particular DOM event type of the
2617// element `el`. You can optionally specify the context of the listener
2618// (object the `this` keyword will point to). You can also pass several
2619// space-separated types (e.g. `'click dblclick'`).
2620
2621// @alternative
2622// @function on(el: HTMLElement, eventMap: Object, context?: Object): this
2623// Adds a set of type/listener pairs, e.g. `{click: onClick, mousemove: onMouseMove}`
2624function on(obj, types, fn, context) {
2625
2626	if (typeof types === 'object') {
2627		for (var type in types) {
2628			addOne(obj, type, types[type], fn);
2629		}
2630	} else {
2631		types = splitWords(types);
2632
2633		for (var i = 0, len = types.length; i < len; i++) {
2634			addOne(obj, types[i], fn, context);
2635		}
2636	}
2637
2638	return this;
2639}
2640
2641var eventsKey = '_leaflet_events';
2642
2643// @function off(el: HTMLElement, types: String, fn: Function, context?: Object): this
2644// Removes a previously added listener function.
2645// Note that if you passed a custom context to on, you must pass the same
2646// context to `off` in order to remove the listener.
2647
2648// @alternative
2649// @function off(el: HTMLElement, eventMap: Object, context?: Object): this
2650// Removes a set of type/listener pairs, e.g. `{click: onClick, mousemove: onMouseMove}`
2651function off(obj, types, fn, context) {
2652
2653	if (typeof types === 'object') {
2654		for (var type in types) {
2655			removeOne(obj, type, types[type], fn);
2656		}
2657	} else if (types) {
2658		types = splitWords(types);
2659
2660		for (var i = 0, len = types.length; i < len; i++) {
2661			removeOne(obj, types[i], fn, context);
2662		}
2663	} else {
2664		for (var j in obj[eventsKey]) {
2665			removeOne(obj, j, obj[eventsKey][j]);
2666		}
2667		delete obj[eventsKey];
2668	}
2669
2670	return this;
2671}
2672
2673function addOne(obj, type, fn, context) {
2674	var id = type + stamp(fn) + (context ? '_' + stamp(context) : '');
2675
2676	if (obj[eventsKey] && obj[eventsKey][id]) { return this; }
2677
2678	var handler = function (e) {
2679		return fn.call(context || obj, e || window.event);
2680	};
2681
2682	var originalHandler = handler;
2683
2684	if (pointer && type.indexOf('touch') === 0) {
2685		// Needs DomEvent.Pointer.js
2686		addPointerListener(obj, type, handler, id);
2687
2688	} else if (touch && (type === 'dblclick') && addDoubleTapListener &&
2689	           !(pointer && chrome)) {
2690		// Chrome >55 does not need the synthetic dblclicks from addDoubleTapListener
2691		// See #5180
2692		addDoubleTapListener(obj, handler, id);
2693
2694	} else if ('addEventListener' in obj) {
2695
2696		if (type === 'mousewheel') {
2697			obj.addEventListener('onwheel' in obj ? 'wheel' : 'mousewheel', handler, passiveEvents ? {passive: false} : false);
2698
2699		} else if ((type === 'mouseenter') || (type === 'mouseleave')) {
2700			handler = function (e) {
2701				e = e || window.event;
2702				if (isExternalTarget(obj, e)) {
2703					originalHandler(e);
2704				}
2705			};
2706			obj.addEventListener(type === 'mouseenter' ? 'mouseover' : 'mouseout', handler, false);
2707
2708		} else {
2709			if (type === 'click' && android) {
2710				handler = function (e) {
2711					filterClick(e, originalHandler);
2712				};
2713			}
2714			obj.addEventListener(type, handler, false);
2715		}
2716
2717	} else if ('attachEvent' in obj) {
2718		obj.attachEvent('on' + type, handler);
2719	}
2720
2721	obj[eventsKey] = obj[eventsKey] || {};
2722	obj[eventsKey][id] = handler;
2723}
2724
2725function removeOne(obj, type, fn, context) {
2726
2727	var id = type + stamp(fn) + (context ? '_' + stamp(context) : ''),
2728	    handler = obj[eventsKey] && obj[eventsKey][id];
2729
2730	if (!handler) { return this; }
2731
2732	if (pointer && type.indexOf('touch') === 0) {
2733		removePointerListener(obj, type, id);
2734
2735	} else if (touch && (type === 'dblclick') && removeDoubleTapListener &&
2736	           !(pointer && chrome)) {
2737		removeDoubleTapListener(obj, id);
2738
2739	} else if ('removeEventListener' in obj) {
2740
2741		if (type === 'mousewheel') {
2742			obj.removeEventListener('onwheel' in obj ? 'wheel' : 'mousewheel', handler, passiveEvents ? {passive: false} : false);
2743
2744		} else {
2745			obj.removeEventListener(
2746				type === 'mouseenter' ? 'mouseover' :
2747				type === 'mouseleave' ? 'mouseout' : type, handler, false);
2748		}
2749
2750	} else if ('detachEvent' in obj) {
2751		obj.detachEvent('on' + type, handler);
2752	}
2753
2754	obj[eventsKey][id] = null;
2755}
2756
2757// @function stopPropagation(ev: DOMEvent): this
2758// Stop the given event from propagation to parent elements. Used inside the listener functions:
2759// ```js
2760// L.DomEvent.on(div, 'click', function (ev) {
2761// 	L.DomEvent.stopPropagation(ev);
2762// });
2763// ```
2764function stopPropagation(e) {
2765
2766	if (e.stopPropagation) {
2767		e.stopPropagation();
2768	} else if (e.originalEvent) {  // In case of Leaflet event.
2769		e.originalEvent._stopped = true;
2770	} else {
2771		e.cancelBubble = true;
2772	}
2773	skipped(e);
2774
2775	return this;
2776}
2777
2778// @function disableScrollPropagation(el: HTMLElement): this
2779// Adds `stopPropagation` to the element's `'mousewheel'` events (plus browser variants).
2780function disableScrollPropagation(el) {
2781	addOne(el, 'mousewheel', stopPropagation);
2782	return this;
2783}
2784
2785// @function disableClickPropagation(el: HTMLElement): this
2786// Adds `stopPropagation` to the element's `'click'`, `'doubleclick'`,
2787// `'mousedown'` and `'touchstart'` events (plus browser variants).
2788function disableClickPropagation(el) {
2789	on(el, 'mousedown touchstart dblclick', stopPropagation);
2790	addOne(el, 'click', fakeStop);
2791	return this;
2792}
2793
2794// @function preventDefault(ev: DOMEvent): this
2795// Prevents the default action of the DOM Event `ev` from happening (such as
2796// following a link in the href of the a element, or doing a POST request
2797// with page reload when a `<form>` is submitted).
2798// Use it inside listener functions.
2799function preventDefault(e) {
2800	if (e.preventDefault) {
2801		e.preventDefault();
2802	} else {
2803		e.returnValue = false;
2804	}
2805	return this;
2806}
2807
2808// @function stop(ev: DOMEvent): this
2809// Does `stopPropagation` and `preventDefault` at the same time.
2810function stop(e) {
2811	preventDefault(e);
2812	stopPropagation(e);
2813	return this;
2814}
2815
2816// @function getMousePosition(ev: DOMEvent, container?: HTMLElement): Point
2817// Gets normalized mouse position from a DOM event relative to the
2818// `container` (border excluded) or to the whole page if not specified.
2819function getMousePosition(e, container) {
2820	if (!container) {
2821		return new Point(e.clientX, e.clientY);
2822	}
2823
2824	var scale = getScale(container),
2825	    offset = scale.boundingClientRect; // left and top  values are in page scale (like the event clientX/Y)
2826
2827	return new Point(
2828		// offset.left/top values are in page scale (like clientX/Y),
2829		// whereas clientLeft/Top (border width) values are the original values (before CSS scale applies).
2830		(e.clientX - offset.left) / scale.x - container.clientLeft,
2831		(e.clientY - offset.top) / scale.y - container.clientTop
2832	);
2833}
2834
2835// Chrome on Win scrolls double the pixels as in other platforms (see #4538),
2836// and Firefox scrolls device pixels, not CSS pixels
2837var wheelPxFactor =
2838	(win && chrome) ? 2 * window.devicePixelRatio :
2839	gecko ? window.devicePixelRatio : 1;
2840
2841// @function getWheelDelta(ev: DOMEvent): Number
2842// Gets normalized wheel delta from a mousewheel DOM event, in vertical
2843// pixels scrolled (negative if scrolling down).
2844// Events from pointing devices without precise scrolling are mapped to
2845// a best guess of 60 pixels.
2846function getWheelDelta(e) {
2847	return (edge) ? e.wheelDeltaY / 2 : // Don't trust window-geometry-based delta
2848	       (e.deltaY && e.deltaMode === 0) ? -e.deltaY / wheelPxFactor : // Pixels
2849	       (e.deltaY && e.deltaMode === 1) ? -e.deltaY * 20 : // Lines
2850	       (e.deltaY && e.deltaMode === 2) ? -e.deltaY * 60 : // Pages
2851	       (e.deltaX || e.deltaZ) ? 0 :	// Skip horizontal/depth wheel events
2852	       e.wheelDelta ? (e.wheelDeltaY || e.wheelDelta) / 2 : // Legacy IE pixels
2853	       (e.detail && Math.abs(e.detail) < 32765) ? -e.detail * 20 : // Legacy Moz lines
2854	       e.detail ? e.detail / -32765 * 60 : // Legacy Moz pages
2855	       0;
2856}
2857
2858var skipEvents = {};
2859
2860function fakeStop(e) {
2861	// fakes stopPropagation by setting a special event flag, checked/reset with skipped(e)
2862	skipEvents[e.type] = true;
2863}
2864
2865function skipped(e) {
2866	var events = skipEvents[e.type];
2867	// reset when checking, as it's only used in map container and propagates outside of the map
2868	skipEvents[e.type] = false;
2869	return events;
2870}
2871
2872// check if element really left/entered the event target (for mouseenter/mouseleave)
2873function isExternalTarget(el, e) {
2874
2875	var related = e.relatedTarget;
2876
2877	if (!related) { return true; }
2878
2879	try {
2880		while (related && (related !== el)) {
2881			related = related.parentNode;
2882		}
2883	} catch (err) {
2884		return false;
2885	}
2886	return (related !== el);
2887}
2888
2889var lastClick;
2890
2891// this is a horrible workaround for a bug in Android where a single touch triggers two click events
2892function filterClick(e, handler) {
2893	var timeStamp = (e.timeStamp || (e.originalEvent && e.originalEvent.timeStamp)),
2894	    elapsed = lastClick && (timeStamp - lastClick);
2895
2896	// are they closer together than 500ms yet more than 100ms?
2897	// Android typically triggers them ~300ms apart while multiple listeners
2898	// on the same event should be triggered far faster;
2899	// or check if click is simulated on the element, and if it is, reject any non-simulated events
2900
2901	if ((elapsed && elapsed > 100 && elapsed < 500) || (e.target._simulatedClick && !e._simulated)) {
2902		stop(e);
2903		return;
2904	}
2905	lastClick = timeStamp;
2906
2907	handler(e);
2908}
2909
2910
2911
2912
2913var DomEvent = (Object.freeze || Object)({
2914	on: on,
2915	off: off,
2916	stopPropagation: stopPropagation,
2917	disableScrollPropagation: disableScrollPropagation,
2918	disableClickPropagation: disableClickPropagation,
2919	preventDefault: preventDefault,
2920	stop: stop,
2921	getMousePosition: getMousePosition,
2922	getWheelDelta: getWheelDelta,
2923	fakeStop: fakeStop,
2924	skipped: skipped,
2925	isExternalTarget: isExternalTarget,
2926	addListener: on,
2927	removeListener: off
2928});
2929
2930/*
2931 * @class PosAnimation
2932 * @aka L.PosAnimation
2933 * @inherits Evented
2934 * Used internally for panning animations, utilizing CSS3 Transitions for modern browsers and a timer fallback for IE6-9.
2935 *
2936 * @example
2937 * ```js
2938 * var fx = new L.PosAnimation();
2939 * fx.run(el, [300, 500], 0.5);
2940 * ```
2941 *
2942 * @constructor L.PosAnimation()
2943 * Creates a `PosAnimation` object.
2944 *
2945 */
2946
2947var PosAnimation = Evented.extend({
2948
2949	// @method run(el: HTMLElement, newPos: Point, duration?: Number, easeLinearity?: Number)
2950	// Run an animation of a given element to a new position, optionally setting
2951	// duration in seconds (`0.25` by default) and easing linearity factor (3rd
2952	// argument of the [cubic bezier curve](http://cubic-bezier.com/#0,0,.5,1),
2953	// `0.5` by default).
2954	run: function (el, newPos, duration, easeLinearity) {
2955		this.stop();
2956
2957		this._el = el;
2958		this._inProgress = true;
2959		this._duration = duration || 0.25;
2960		this._easeOutPower = 1 / Math.max(easeLinearity || 0.5, 0.2);
2961
2962		this._startPos = getPosition(el);
2963		this._offset = newPos.subtract(this._startPos);
2964		this._startTime = +new Date();
2965
2966		// @event start: Event
2967		// Fired when the animation starts
2968		this.fire('start');
2969
2970		this._animate();
2971	},
2972
2973	// @method stop()
2974	// Stops the animation (if currently running).
2975	stop: function () {
2976		if (!this._inProgress) { return; }
2977
2978		this._step(true);
2979		this._complete();
2980	},
2981
2982	_animate: function () {
2983		// animation loop
2984		this._animId = requestAnimFrame(this._animate, this);
2985		this._step();
2986	},
2987
2988	_step: function (round) {
2989		var elapsed = (+new Date()) - this._startTime,
2990		    duration = this._duration * 1000;
2991
2992		if (elapsed < duration) {
2993			this._runFrame(this._easeOut(elapsed / duration), round);
2994		} else {
2995			this._runFrame(1);
2996			this._complete();
2997		}
2998	},
2999
3000	_runFrame: function (progress, round) {
3001		var pos = this._startPos.add(this._offset.multiplyBy(progress));
3002		if (round) {
3003			pos._round();
3004		}
3005		setPosition(this._el, pos);
3006
3007		// @event step: Event
3008		// Fired continuously during the animation.
3009		this.fire('step');
3010	},
3011
3012	_complete: function () {
vendor: 13,656 bytes, lines 3013-3428
3013		cancelAnimFrame(this._animId);
3014
3015		this._inProgress = false;
3016		// @event end: Event
3017		// Fired when the animation ends.
3018		this.fire('end');
3019	},
3020
3021	_easeOut: function (t) {
3022		return 1 - Math.pow(1 - t, this._easeOutPower);
3023	}
3024});
3025
3026/*
3027 * @class Map
3028 * @aka L.Map
3029 * @inherits Evented
3030 *
3031 * The central class of the API — it is used to create a map on a page and manipulate it.
3032 *
3033 * @example
3034 *
3035 * ```js
3036 * // initialize the map on the "map" div with a given center and zoom
3037 * var map = L.map('map', {
3038 * 	center: [51.505, -0.09],
3039 * 	zoom: 13
3040 * });
3041 * ```
3042 *
3043 */
3044
3045var Map = Evented.extend({
3046
3047	options: {
3048		// @section Map State Options
3049		// @option crs: CRS = L.CRS.EPSG3857
3050		// The [Coordinate Reference System](#crs) to use. Don't change this if you're not
3051		// sure what it means.
3052		crs: EPSG3857,
3053
3054		// @option center: LatLng = undefined
3055		// Initial geographic center of the map
3056		center: undefined,
3057
3058		// @option zoom: Number = undefined
3059		// Initial map zoom level
3060		zoom: undefined,
3061
3062		// @option minZoom: Number = *
3063		// Minimum zoom level of the map.
3064		// If not specified and at least one `GridLayer` or `TileLayer` is in the map,
3065		// the lowest of their `minZoom` options will be used instead.
3066		minZoom: undefined,
3067
3068		// @option maxZoom: Number = *
3069		// Maximum zoom level of the map.
3070		// If not specified and at least one `GridLayer` or `TileLayer` is in the map,
3071		// the highest of their `maxZoom` options will be used instead.
3072		maxZoom: undefined,
3073
3074		// @option layers: Layer[] = []
3075		// Array of layers that will be added to the map initially
3076		layers: [],
3077
3078		// @option maxBounds: LatLngBounds = null
3079		// When this option is set, the map restricts the view to the given
3080		// geographical bounds, bouncing the user back if the user tries to pan
3081		// outside the view. To set the restriction dynamically, use
3082		// [`setMaxBounds`](#map-setmaxbounds) method.
3083		maxBounds: undefined,
3084
3085		// @option renderer: Renderer = *
3086		// The default method for drawing vector layers on the map. `L.SVG`
3087		// or `L.Canvas` by default depending on browser support.
3088		renderer: undefined,
3089
3090
3091		// @section Animation Options
3092		// @option zoomAnimation: Boolean = true
3093		// Whether the map zoom animation is enabled. By default it's enabled
3094		// in all browsers that support CSS3 Transitions except Android.
3095		zoomAnimation: true,
3096
3097		// @option zoomAnimationThreshold: Number = 4
3098		// Won't animate zoom if the zoom difference exceeds this value.
3099		zoomAnimationThreshold: 4,
3100
3101		// @option fadeAnimation: Boolean = true
3102		// Whether the tile fade animation is enabled. By default it's enabled
3103		// in all browsers that support CSS3 Transitions except Android.
3104		fadeAnimation: true,
3105
3106		// @option markerZoomAnimation: Boolean = true
3107		// Whether markers animate their zoom with the zoom animation, if disabled
3108		// they will disappear for the length of the animation. By default it's
3109		// enabled in all browsers that support CSS3 Transitions except Android.
3110		markerZoomAnimation: true,
3111
3112		// @option transform3DLimit: Number = 2^23
3113		// Defines the maximum size of a CSS translation transform. The default
3114		// value should not be changed unless a web browser positions layers in
3115		// the wrong place after doing a large `panBy`.
3116		transform3DLimit: 8388608, // Precision limit of a 32-bit float
3117
3118		// @section Interaction Options
3119		// @option zoomSnap: Number = 1
3120		// Forces the map's zoom level to always be a multiple of this, particularly
3121		// right after a [`fitBounds()`](#map-fitbounds) or a pinch-zoom.
3122		// By default, the zoom level snaps to the nearest integer; lower values
3123		// (e.g. `0.5` or `0.1`) allow for greater granularity. A value of `0`
3124		// means the zoom level will not be snapped after `fitBounds` or a pinch-zoom.
3125		zoomSnap: 1,
3126
3127		// @option zoomDelta: Number = 1
3128		// Controls how much the map's zoom level will change after a
3129		// [`zoomIn()`](#map-zoomin), [`zoomOut()`](#map-zoomout), pressing `+`
3130		// or `-` on the keyboard, or using the [zoom controls](#control-zoom).
3131		// Values smaller than `1` (e.g. `0.5`) allow for greater granularity.
3132		zoomDelta: 1,
3133
3134		// @option trackResize: Boolean = true
3135		// Whether the map automatically handles browser window resize to update itself.
3136		trackResize: true
3137	},
3138
3139	initialize: function (id, options) { // (HTMLElement or String, Object)
3140		options = setOptions(this, options);
3141
3142		// Make sure to assign internal flags at the beginning,
3143		// to avoid inconsistent state in some edge cases.
3144		this._handlers = [];
3145		this._layers = {};
3146		this._zoomBoundLayers = {};
3147		this._sizeChanged = true;
3148
3149		this._initContainer(id);
3150		this._initLayout();
3151
3152		// hack for https://github.com/Leaflet/Leaflet/issues/1980
3153		this._onResize = bind(this._onResize, this);
3154
3155		this._initEvents();
3156
3157		if (options.maxBounds) {
3158			this.setMaxBounds(options.maxBounds);
3159		}
3160
3161		if (options.zoom !== undefined) {
3162			this._zoom = this._limitZoom(options.zoom);
3163		}
3164
3165		if (options.center && options.zoom !== undefined) {
3166			this.setView(toLatLng(options.center), options.zoom, {reset: true});
3167		}
3168
3169		this.callInitHooks();
3170
3171		// don't animate on browsers without hardware-accelerated transitions or old Android/Opera
3172		this._zoomAnimated = TRANSITION && any3d && !mobileOpera &&
3173				this.options.zoomAnimation;
3174
3175		// zoom transitions run with the same duration for all layers, so if one of transitionend events
3176		// happens after starting zoom animation (propagating to the map pane), we know that it ended globally
3177		if (this._zoomAnimated) {
3178			this._createAnimProxy();
3179			on(this._proxy, TRANSITION_END, this._catchTransitionEnd, this);
3180		}
3181
3182		this._addLayers(this.options.layers);
3183	},
3184
3185
3186	// @section Methods for modifying map state
3187
3188	// @method setView(center: LatLng, zoom: Number, options?: Zoom/pan options): this
3189	// Sets the view of the map (geographical center and zoom) with the given
3190	// animation options.
3191	setView: function (center, zoom, options) {
3192
3193		zoom = zoom === undefined ? this._zoom : this._limitZoom(zoom);
3194		center = this._limitCenter(toLatLng(center), zoom, this.options.maxBounds);
3195		options = options || {};
3196
3197		this._stop();
3198
3199		if (this._loaded && !options.reset && options !== true) {
3200
3201			if (options.animate !== undefined) {
3202				options.zoom = extend({animate: options.animate}, options.zoom);
3203				options.pan = extend({animate: options.animate, duration: options.duration}, options.pan);
3204			}
3205
3206			// try animating pan or zoom
3207			var moved = (this._zoom !== zoom) ?
3208				this._tryAnimatedZoom && this._tryAnimatedZoom(center, zoom, options.zoom) :
3209				this._tryAnimatedPan(center, options.pan);
3210
3211			if (moved) {
3212				// prevent resize handler call, the view will refresh after animation anyway
3213				clearTimeout(this._sizeTimer);
3214				return this;
3215			}
3216		}
3217
3218		// animation didn't start, just reset the map view
3219		this._resetView(center, zoom);
3220
3221		return this;
3222	},
3223
3224	// @method setZoom(zoom: Number, options?: Zoom/pan options): this
3225	// Sets the zoom of the map.
3226	setZoom: function (zoom, options) {
3227		if (!this._loaded) {
3228			this._zoom = zoom;
3229			return this;
3230		}
3231		return this.setView(this.getCenter(), zoom, {zoom: options});
3232	},
3233
3234	// @method zoomIn(delta?: Number, options?: Zoom options): this
3235	// Increases the zoom of the map by `delta` ([`zoomDelta`](#map-zoomdelta) by default).
3236	zoomIn: function (delta, options) {
3237		delta = delta || (any3d ? this.options.zoomDelta : 1);
3238		return this.setZoom(this._zoom + delta, options);
3239	},
3240
3241	// @method zoomOut(delta?: Number, options?: Zoom options): this
3242	// Decreases the zoom of the map by `delta` ([`zoomDelta`](#map-zoomdelta) by default).
3243	zoomOut: function (delta, options) {
3244		delta = delta || (any3d ? this.options.zoomDelta : 1);
3245		return this.setZoom(this._zoom - delta, options);
3246	},
3247
3248	// @method setZoomAround(latlng: LatLng, zoom: Number, options: Zoom options): this
3249	// Zooms the map while keeping a specified geographical point on the map
3250	// stationary (e.g. used internally for scroll zoom and double-click zoom).
3251	// @alternative
3252	// @method setZoomAround(offset: Point, zoom: Number, options: Zoom options): this
3253	// Zooms the map while keeping a specified pixel on the map (relative to the top-left corner) stationary.
3254	setZoomAround: function (latlng, zoom, options) {
3255		var scale = this.getZoomScale(zoom),
3256		    viewHalf = this.getSize().divideBy(2),
3257		    containerPoint = latlng instanceof Point ? latlng : this.latLngToContainerPoint(latlng),
3258
3259		    centerOffset = containerPoint.subtract(viewHalf).multiplyBy(1 - 1 / scale),
3260		    newCenter = this.containerPointToLatLng(viewHalf.add(centerOffset));
3261
3262		return this.setView(newCenter, zoom, {zoom: options});
3263	},
3264
3265	_getBoundsCenterZoom: function (bounds, options) {
3266
3267		options = options || {};
3268		bounds = bounds.getBounds ? bounds.getBounds() : toLatLngBounds(bounds);
3269
3270		var paddingTL = toPoint(options.paddingTopLeft || options.padding || [0, 0]),
3271		    paddingBR = toPoint(options.paddingBottomRight || options.padding || [0, 0]),
3272
3273		    zoom = this.getBoundsZoom(bounds, false, paddingTL.add(paddingBR));
3274
3275		zoom = (typeof options.maxZoom === 'number') ? Math.min(options.maxZoom, zoom) : zoom;
3276
3277		if (zoom === Infinity) {
3278			return {
3279				center: bounds.getCenter(),
3280				zoom: zoom
3281			};
3282		}
3283
3284		var paddingOffset = paddingBR.subtract(paddingTL).divideBy(2),
3285
3286		    swPoint = this.project(bounds.getSouthWest(), zoom),
3287		    nePoint = this.project(bounds.getNorthEast(), zoom),
3288		    center = this.unproject(swPoint.add(nePoint).divideBy(2).add(paddingOffset), zoom);
3289
3290		return {
3291			center: center,
3292			zoom: zoom
3293		};
3294	},
3295
3296	// @method fitBounds(bounds: LatLngBounds, options?: fitBounds options): this
3297	// Sets a map view that contains the given geographical bounds with the
3298	// maximum zoom level possible.
3299	fitBounds: function (bounds, options) {
3300
3301		bounds = toLatLngBounds(bounds);
3302
3303		if (!bounds.isValid()) {
3304			throw new Error('Bounds are not valid.');
3305		}
3306
3307		var target = this._getBoundsCenterZoom(bounds, options);
3308		return this.setView(target.center, target.zoom, options);
3309	},
3310
3311	// @method fitWorld(options?: fitBounds options): this
3312	// Sets a map view that mostly contains the whole world with the maximum
3313	// zoom level possible.
3314	fitWorld: function (options) {
3315		return this.fitBounds([[-90, -180], [90, 180]], options);
3316	},
3317
3318	// @method panTo(latlng: LatLng, options?: Pan options): this
3319	// Pans the map to a given center.
3320	panTo: function (center, options) { // (LatLng)
3321		return this.setView(center, this._zoom, {pan: options});
3322	},
3323
3324	// @method panBy(offset: Point, options?: Pan options): this
3325	// Pans the map by a given number of pixels (animated).
3326	panBy: function (offset, options) {
3327		offset = toPoint(offset).round();
3328		options = options || {};
3329
3330		if (!offset.x && !offset.y) {
3331			return this.fire('moveend');
3332		}
3333		// If we pan too far, Chrome gets issues with tiles
3334		// and makes them disappear or appear in the wrong place (slightly offset) #2602
3335		if (options.animate !== true && !this.getSize().contains(offset)) {
3336			this._resetView(this.unproject(this.project(this.getCenter()).add(offset)), this.getZoom());
3337			return this;
3338		}
3339
3340		if (!this._panAnim) {
3341			this._panAnim = new PosAnimation();
3342
3343			this._panAnim.on({
3344				'step': this._onPanTransitionStep,
3345				'end': this._onPanTransitionEnd
3346			}, this);
3347		}
3348
3349		// don't fire movestart if animating inertia
3350		if (!options.noMoveStart) {
3351			this.fire('movestart');
3352		}
3353
3354		// animate pan unless animate: false specified
3355		if (options.animate !== false) {
3356			addClass(this._mapPane, 'leaflet-pan-anim');
3357
3358			var newPos = this._getMapPanePos().subtract(offset).round();
3359			this._panAnim.run(this._mapPane, newPos, options.duration || 0.25, options.easeLinearity);
3360		} else {
3361			this._rawPanBy(offset);
3362			this.fire('move').fire('moveend');
3363		}
3364
3365		return this;
3366	},
3367
3368	// @method flyTo(latlng: LatLng, zoom?: Number, options?: Zoom/pan options): this
3369	// Sets the view of the map (geographical center and zoom) performing a smooth
3370	// pan-zoom animation.
3371	flyTo: function (targetCenter, targetZoom, options) {
3372
3373		options = options || {};
3374		if (options.animate === false || !any3d) {
3375			return this.setView(targetCenter, targetZoom, options);
3376		}
3377
3378		this._stop();
3379
3380		var from = this.project(this.getCenter()),
3381		    to = this.project(targetCenter),
3382		    size = this.getSize(),
3383		    startZoom = this._zoom;
3384
3385		targetCenter = toLatLng(targetCenter);
3386		targetZoom = targetZoom === undefined ? startZoom : targetZoom;
3387
3388		var w0 = Math.max(size.x, size.y),
3389		    w1 = w0 * this.getZoomScale(startZoom, targetZoom),
3390		    u1 = (to.distanceTo(from)) || 1,
3391		    rho = 1.42,
3392		    rho2 = rho * rho;
3393
3394		function r(i) {
3395			var s1 = i ? -1 : 1,
3396			    s2 = i ? w1 : w0,
3397			    t1 = w1 * w1 - w0 * w0 + s1 * rho2 * rho2 * u1 * u1,
3398			    b1 = 2 * s2 * rho2 * u1,
3399			    b = t1 / b1,
3400			    sq = Math.sqrt(b * b + 1) - b;
3401
3402			    // workaround for floating point precision bug when sq = 0, log = -Infinite,
3403			    // thus triggering an infinite loop in flyTo
3404			    var log = sq < 0.000000001 ? -18 : Math.log(sq);
3405
3406			return log;
3407		}
3408
3409		function sinh(n) { return (Math.exp(n) - Math.exp(-n)) / 2; }
3410		function cosh(n) { return (Math.exp(n) + Math.exp(-n)) / 2; }
3411		function tanh(n) { return sinh(n) / cosh(n); }
3412
3413		var r0 = r(0);
3414
3415		function w(s) { return w0 * (cosh(r0) / cosh(r0 + rho * s)); }
3416		function u(s) { return w0 * (cosh(r0) * tanh(r0 + rho * s) - sinh(r0)) / rho2; }
3417
3418		function easeOut(t) { return 1 - Math.pow(1 - t, 1.5); }
3419
3420		var start = Date.now(),
3421		    S = (r(1) - r0) / rho,
3422		    duration = options.duration ? 1000 * options.duration : 1000 * S * 0.8;
3423
3424		function frame() {
3425			var t = (Date.now() - start) / duration,
3426			    s = easeOut(t) * S;
3427
3428			if (t <= 1) {
3429				this._flyToFrame = requestAnimFrame(frame, this);
3430
3431				this._move(
3432					this.unproject(from.add(to.subtract(from).multiplyBy(u(s) / u1)), startZoom),
3433					this.getScaleZoom(w0 / w(s), startZoom),
3434					{flyTo: true});
3435
3436			} else {
3437				this
3438					._move(targetCenter, targetZoom)
3439					._moveEnd(true);
3440			}
3441		}
3442
3443		this._moveStart(true, options.noMoveStart);
3444
3445		frame.call(this);
3446		return this;
3447	},
3448
3449	// @method flyToBounds(bounds: LatLngBounds, options?: fitBounds options): this
3450	// Sets the view of the map with a smooth animation like [`flyTo`](#map-flyto),
3451	// but takes a bounds parameter like [`fitBounds`](#map-fitbounds).
3452	flyToBounds: function (bounds, options) {
3453		var target = this._getBoundsCenterZoom(bounds, options);
3454		return this.flyTo(target.center, target.zoom, options);
3455	},
3456
3457	// @method setMaxBounds(bounds: Bounds): this
3458	// Restricts the map view to the given bounds (see the [maxBounds](#map-maxbounds) option).
3459	setMaxBounds: function (bounds) {
3460		bounds = toLatLngBounds(bounds);
3461
3462		if (!bounds.isValid()) {
3463			this.options.maxBounds = null;
3464			return this.off('moveend', this._panInsideMaxBounds);
3465		} else if (this.options.maxBounds) {
3466			this.off('moveend', this._panInsideMaxBounds);
3467		}
3468
3469		this.options.maxBounds = bounds;
3470
3471		if (this._loaded) {
3472			this._panInsideMaxBounds();
3473		}
3474
3475		return this.on('moveend', this._panInsideMaxBounds);
3476	},
3477
3478	// @method setMinZoom(zoom: Number): this
3479	// Sets the lower limit for the available zoom levels (see the [minZoom](#map-minzoom) option).
3480	setMinZoom: function (zoom) {
3481		var oldZoom = this.options.minZoom;
3482		this.options.minZoom = zoom;
3483
3484		if (this._loaded && oldZoom !== zoom) {
3485			this.fire('zoomlevelschange');
3486
3487			if (this.getZoom() < this.options.minZoom) {
3488				return this.setZoom(zoom);
3489			}
3490		}
3491
3492		return this;
3493	},
3494
3495	// @method setMaxZoom(zoom: Number): this
3496	// Sets the upper limit for the available zoom levels (see the [maxZoom](#map-maxzoom) option).
3497	setMaxZoom: function (zoom) {
3498		var oldZoom = this.options.maxZoom;
3499		this.options.maxZoom = zoom;
3500
3501		if (this._loaded && oldZoom !== zoom) {
3502			this.fire('zoomlevelschange');
3503
3504			if (this.getZoom() > this.options.maxZoom) {
3505				return this.setZoom(zoom);
3506			}
3507		}
3508
3509		return this;
3510	},
3511
3512	// @method panInsideBounds(bounds: LatLngBounds, options?: Pan options): this
3513	// 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.
3514	panInsideBounds: function (bounds, options) {
3515		this._enforcingBounds = true;
3516		var center = this.getCenter(),
3517		    newCenter = this._limitCenter(center, this._zoom, toLatLngBounds(bounds));
3518
3519		if (!center.equals(newCenter)) {
3520			this.panTo(newCenter, options);
3521		}
3522
3523		this._enforcingBounds = false;
3524		return this;
3525	},
3526
3527	// @method panInside(latlng: LatLng, options?: options): this
3528	// Pans the map the minimum amount to make the `latlng` visible. Use
3529	// `padding`, `paddingTopLeft` and `paddingTopRight` options to fit
3530	// the display to more restricted bounds, like [`fitBounds`](#map-fitbounds).
3531	// If `latlng` is already within the (optionally padded) display bounds,
3532	// the map will not be panned.
3533	panInside: function (latlng, options) {
3534		options = options || {};
3535
3536		var paddingTL = toPoint(options.paddingTopLeft || options.padding || [0, 0]),
3537		    paddingBR = toPoint(options.paddingBottomRight || options.padding || [0, 0]),
3538		    center = this.getCenter(),
3539		    pixelCenter = this.project(center),
3540		    pixelPoint = this.project(latlng),
3541		    pixelBounds = this.getPixelBounds(),
3542		    halfPixelBounds = pixelBounds.getSize().divideBy(2),
3543		    paddedBounds = toBounds([pixelBounds.min.add(paddingTL), pixelBounds.max.subtract(paddingBR)]);
3544
3545		if (!paddedBounds.contains(pixelPoint)) {
3546			this._enforcingBounds = true;
3547			var diff = pixelCenter.subtract(pixelPoint),
3548			    newCenter = toPoint(pixelPoint.x + diff.x, pixelPoint.y + diff.y);
3549
3550			if (pixelPoint.x < paddedBounds.min.x || pixelPoint.x > paddedBounds.max.x) {
3551				newCenter.x = pixelCenter.x - diff.x;
3552				if (diff.x > 0) {
3553					newCenter.x += halfPixelBounds.x - paddingTL.x;
3554				} else {
3555					newCenter.x -= halfPixelBounds.x - paddingBR.x;
3556				}
3557			}
3558			if (pixelPoint.y < paddedBounds.min.y || pixelPoint.y > paddedBounds.max.y) {
3559				newCenter.y = pixelCenter.y - diff.y;
3560				if (diff.y > 0) {
3561					newCenter.y += halfPixelBounds.y - paddingTL.y;
3562				} else {
3563					newCenter.y -= halfPixelBounds.y - paddingBR.y;
3564				}
3565			}
3566			this.panTo(this.unproject(newCenter), options);
3567			this._enforcingBounds = false;
vendor: 24,536 bytes, lines 3568-4328
3568		}
3569		return this;
3570	},
3571
3572	// @method invalidateSize(options: Zoom/pan options): this
3573	// Checks if the map container size changed and updates the map if so —
3574	// call it after you've changed the map size dynamically, also animating
3575	// pan by default. If `options.pan` is `false`, panning will not occur.
3576	// If `options.debounceMoveend` is `true`, it will delay `moveend` event so
3577	// that it doesn't happen often even if the method is called many
3578	// times in a row.
3579
3580	// @alternative
3581	// @method invalidateSize(animate: Boolean): this
3582	// Checks if the map container size changed and updates the map if so —
3583	// call it after you've changed the map size dynamically, also animating
3584	// pan by default.
3585	invalidateSize: function (options) {
3586		if (!this._loaded) { return this; }
3587
3588		options = extend({
3589			animate: false,
3590			pan: true
3591		}, options === true ? {animate: true} : options);
3592
3593		var oldSize = this.getSize();
3594		this._sizeChanged = true;
3595		this._lastCenter = null;
3596
3597		var newSize = this.getSize(),
3598		    oldCenter = oldSize.divideBy(2).round(),
3599		    newCenter = newSize.divideBy(2).round(),
3600		    offset = oldCenter.subtract(newCenter);
3601
3602		if (!offset.x && !offset.y) { return this; }
3603
3604		if (options.animate && options.pan) {
3605			this.panBy(offset);
3606
3607		} else {
3608			if (options.pan) {
3609				this._rawPanBy(offset);
3610			}
3611
3612			this.fire('move');
3613
3614			if (options.debounceMoveend) {
3615				clearTimeout(this._sizeTimer);
3616				this._sizeTimer = setTimeout(bind(this.fire, this, 'moveend'), 200);
3617			} else {
3618				this.fire('moveend');
3619			}
3620		}
3621
3622		// @section Map state change events
3623		// @event resize: ResizeEvent
3624		// Fired when the map is resized.
3625		return this.fire('resize', {
3626			oldSize: oldSize,
3627			newSize: newSize
3628		});
3629	},
3630
3631	// @section Methods for modifying map state
3632	// @method stop(): this
3633	// Stops the currently running `panTo` or `flyTo` animation, if any.
3634	stop: function () {
3635		this.setZoom(this._limitZoom(this._zoom));
3636		if (!this.options.zoomSnap) {
3637			this.fire('viewreset');
3638		}
3639		return this._stop();
3640	},
3641
3642	// @section Geolocation methods
3643	// @method locate(options?: Locate options): this
3644	// Tries to locate the user using the Geolocation API, firing a [`locationfound`](#map-locationfound)
3645	// event with location data on success or a [`locationerror`](#map-locationerror) event on failure,
3646	// and optionally sets the map view to the user's location with respect to
3647	// detection accuracy (or to the world view if geolocation failed).
3648	// Note that, if your page doesn't use HTTPS, this method will fail in
3649	// modern browsers ([Chrome 50 and newer](https://sites.google.com/a/chromium.org/dev/Home/chromium-security/deprecating-powerful-features-on-insecure-origins))
3650	// See `Locate options` for more details.
3651	locate: function (options) {
3652
3653		options = this._locateOptions = extend({
3654			timeout: 10000,
3655			watch: false
3656			// setView: false
3657			// maxZoom: <Number>
3658			// maximumAge: 0
3659			// enableHighAccuracy: false
3660		}, options);
3661
3662		if (!('geolocation' in navigator)) {
3663			this._handleGeolocationError({
3664				code: 0,
3665				message: 'Geolocation not supported.'
3666			});
3667			return this;
3668		}
3669
3670		var onResponse = bind(this._handleGeolocationResponse, this),
3671		    onError = bind(this._handleGeolocationError, this);
3672
3673		if (options.watch) {
3674			this._locationWatchId =
3675			        navigator.geolocation.watchPosition(onResponse, onError, options);
3676		} else {
3677			navigator.geolocation.getCurrentPosition(onResponse, onError, options);
3678		}
3679		return this;
3680	},
3681
3682	// @method stopLocate(): this
3683	// Stops watching location previously initiated by `map.locate({watch: true})`
3684	// and aborts resetting the map view if map.locate was called with
3685	// `{setView: true}`.
3686	stopLocate: function () {
3687		if (navigator.geolocation && navigator.geolocation.clearWatch) {
3688			navigator.geolocation.clearWatch(this._locationWatchId);
3689		}
3690		if (this._locateOptions) {
3691			this._locateOptions.setView = false;
3692		}
3693		return this;
3694	},
3695
3696	_handleGeolocationError: function (error) {
3697		var c = error.code,
3698		    message = error.message ||
3699		            (c === 1 ? 'permission denied' :
3700		            (c === 2 ? 'position unavailable' : 'timeout'));
3701
3702		if (this._locateOptions.setView && !this._loaded) {
3703			this.fitWorld();
3704		}
3705
3706		// @section Location events
3707		// @event locationerror: ErrorEvent
3708		// Fired when geolocation (using the [`locate`](#map-locate) method) failed.
3709		this.fire('locationerror', {
3710			code: c,
3711			message: 'Geolocation error: ' + message + '.'
3712		});
3713	},
3714
3715	_handleGeolocationResponse: function (pos) {
3716		var lat = pos.coords.latitude,
3717		    lng = pos.coords.longitude,
3718		    latlng = new LatLng(lat, lng),
3719		    bounds = latlng.toBounds(pos.coords.accuracy * 2),
3720		    options = this._locateOptions;
3721
3722		if (options.setView) {
3723			var zoom = this.getBoundsZoom(bounds);
3724			this.setView(latlng, options.maxZoom ? Math.min(zoom, options.maxZoom) : zoom);
3725		}
3726
3727		var data = {
3728			latlng: latlng,
3729			bounds: bounds,
3730			timestamp: pos.timestamp
3731		};
3732
3733		for (var i in pos.coords) {
3734			if (typeof pos.coords[i] === 'number') {
3735				data[i] = pos.coords[i];
3736			}
3737		}
3738
3739		// @event locationfound: LocationEvent
3740		// Fired when geolocation (using the [`locate`](#map-locate) method)
3741		// went successfully.
3742		this.fire('locationfound', data);
3743	},
3744
3745	// TODO Appropriate docs section?
3746	// @section Other Methods
3747	// @method addHandler(name: String, HandlerClass: Function): this
3748	// Adds a new `Handler` to the map, given its name and constructor function.
3749	addHandler: function (name, HandlerClass) {
3750		if (!HandlerClass) { return this; }
3751
3752		var handler = this[name] = new HandlerClass(this);
3753
3754		this._handlers.push(handler);
3755
3756		if (this.options[name]) {
3757			handler.enable();
3758		}
3759
3760		return this;
3761	},
3762
3763	// @method remove(): this
3764	// Destroys the map and clears all related event listeners.
3765	remove: function () {
3766
3767		this._initEvents(true);
3768
3769		if (this._containerId !== this._container._leaflet_id) {
3770			throw new Error('Map container is being reused by another instance');
3771		}
3772
3773		try {
3774			// throws error in IE6-8
3775			delete this._container._leaflet_id;
3776			delete this._containerId;
3777		} catch (e) {
3778			/*eslint-disable */
3779			this._container._leaflet_id = undefined;
3780			/* eslint-enable */
3781			this._containerId = undefined;
3782		}
3783
3784		if (this._locationWatchId !== undefined) {
3785			this.stopLocate();
3786		}
3787
3788		this._stop();
3789
3790		remove(this._mapPane);
3791
3792		if (this._clearControlPos) {
3793			this._clearControlPos();
3794		}
3795		if (this._resizeRequest) {
3796			cancelAnimFrame(this._resizeRequest);
3797			this._resizeRequest = null;
3798		}
3799
3800		this._clearHandlers();
3801
3802		if (this._loaded) {
3803			// @section Map state change events
3804			// @event unload: Event
3805			// Fired when the map is destroyed with [remove](#map-remove) method.
3806			this.fire('unload');
3807		}
3808
3809		var i;
3810		for (i in this._layers) {
3811			this._layers[i].remove();
3812		}
3813		for (i in this._panes) {
3814			remove(this._panes[i]);
3815		}
3816
3817		this._layers = [];
3818		this._panes = [];
3819		delete this._mapPane;
3820		delete this._renderer;
3821
3822		return this;
3823	},
3824
3825	// @section Other Methods
3826	// @method createPane(name: String, container?: HTMLElement): HTMLElement
3827	// Creates a new [map pane](#map-pane) with the given name if it doesn't exist already,
3828	// then returns it. The pane is created as a child of `container`, or
3829	// as a child of the main map pane if not set.
3830	createPane: function (name, container) {
3831		var className = 'leaflet-pane' + (name ? ' leaflet-' + name.replace('Pane', '') + '-pane' : ''),
3832		    pane = create$1('div', className, container || this._mapPane);
3833
3834		if (name) {
3835			this._panes[name] = pane;
3836		}
3837		return pane;
3838	},
3839
3840	// @section Methods for Getting Map State
3841
3842	// @method getCenter(): LatLng
3843	// Returns the geographical center of the map view
3844	getCenter: function () {
3845		this._checkIfLoaded();
3846
3847		if (this._lastCenter && !this._moved()) {
3848			return this._lastCenter;
3849		}
3850		return this.layerPointToLatLng(this._getCenterLayerPoint());
3851	},
3852
3853	// @method getZoom(): Number
3854	// Returns the current zoom level of the map view
3855	getZoom: function () {
3856		return this._zoom;
3857	},
3858
3859	// @method getBounds(): LatLngBounds
3860	// Returns the geographical bounds visible in the current map view
3861	getBounds: function () {
3862		var bounds = this.getPixelBounds(),
3863		    sw = this.unproject(bounds.getBottomLeft()),
3864		    ne = this.unproject(bounds.getTopRight());
3865
3866		return new LatLngBounds(sw, ne);
3867	},
3868
3869	// @method getMinZoom(): Number
3870	// 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.
3871	getMinZoom: function () {
3872		return this.options.minZoom === undefined ? this._layersMinZoom || 0 : this.options.minZoom;
3873	},
3874
3875	// @method getMaxZoom(): Number
3876	// Returns the maximum zoom level of the map (if set in the `maxZoom` option of the map or of any layers).
3877	getMaxZoom: function () {
3878		return this.options.maxZoom === undefined ?
3879			(this._layersMaxZoom === undefined ? Infinity : this._layersMaxZoom) :
3880			this.options.maxZoom;
3881	},
3882
3883	// @method getBoundsZoom(bounds: LatLngBounds, inside?: Boolean, padding?: Point): Number
3884	// Returns the maximum zoom level on which the given bounds fit to the map
3885	// view in its entirety. If `inside` (optional) is set to `true`, the method
3886	// instead returns the minimum zoom level on which the map view fits into
3887	// the given bounds in its entirety.
3888	getBoundsZoom: function (bounds, inside, padding) { // (LatLngBounds[, Boolean, Point]) -> Number
3889		bounds = toLatLngBounds(bounds);
3890		padding = toPoint(padding || [0, 0]);
3891
3892		var zoom = this.getZoom() || 0,
3893		    min = this.getMinZoom(),
3894		    max = this.getMaxZoom(),
3895		    nw = bounds.getNorthWest(),
3896		    se = bounds.getSouthEast(),
3897		    size = this.getSize().subtract(padding),
3898		    boundsSize = toBounds(this.project(se, zoom), this.project(nw, zoom)).getSize(),
3899		    snap = any3d ? this.options.zoomSnap : 1,
3900		    scalex = size.x / boundsSize.x,
3901		    scaley = size.y / boundsSize.y,
3902		    scale = inside ? Math.max(scalex, scaley) : Math.min(scalex, scaley);
3903
3904		zoom = this.getScaleZoom(scale, zoom);
3905
3906		if (snap) {
3907			zoom = Math.round(zoom / (snap / 100)) * (snap / 100); // don't jump if within 1% of a snap level
3908			zoom = inside ? Math.ceil(zoom / snap) * snap : Math.floor(zoom / snap) * snap;
3909		}
3910
3911		return Math.max(min, Math.min(max, zoom));
3912	},
3913
3914	// @method getSize(): Point
3915	// Returns the current size of the map container (in pixels).
3916	getSize: function () {
3917		if (!this._size || this._sizeChanged) {
3918			this._size = new Point(
3919				this._container.clientWidth || 0,
3920				this._container.clientHeight || 0);
3921
3922			this._sizeChanged = false;
3923		}
3924		return this._size.clone();
3925	},
3926
3927	// @method getPixelBounds(): Bounds
3928	// Returns the bounds of the current map view in projected pixel
3929	// coordinates (sometimes useful in layer and overlay implementations).
3930	getPixelBounds: function (center, zoom) {
3931		var topLeftPoint = this._getTopLeftPoint(center, zoom);
3932		return new Bounds(topLeftPoint, topLeftPoint.add(this.getSize()));
3933	},
3934
3935	// TODO: Check semantics - isn't the pixel origin the 0,0 coord relative to
3936	// the map pane? "left point of the map layer" can be confusing, specially
3937	// since there can be negative offsets.
3938	// @method getPixelOrigin(): Point
3939	// Returns the projected pixel coordinates of the top left point of
3940	// the map layer (useful in custom layer and overlay implementations).
3941	getPixelOrigin: function () {
3942		this._checkIfLoaded();
3943		return this._pixelOrigin;
3944	},
3945
3946	// @method getPixelWorldBounds(zoom?: Number): Bounds
3947	// Returns the world's bounds in pixel coordinates for zoom level `zoom`.
3948	// If `zoom` is omitted, the map's current zoom level is used.
3949	getPixelWorldBounds: function (zoom) {
3950		return this.options.crs.getProjectedBounds(zoom === undefined ? this.getZoom() : zoom);
3951	},
3952
3953	// @section Other Methods
3954
3955	// @method getPane(pane: String|HTMLElement): HTMLElement
3956	// Returns a [map pane](#map-pane), given its name or its HTML element (its identity).
3957	getPane: function (pane) {
3958		return typeof pane === 'string' ? this._panes[pane] : pane;
3959	},
3960
3961	// @method getPanes(): Object
3962	// Returns a plain object containing the names of all [panes](#map-pane) as keys and
3963	// the panes as values.
3964	getPanes: function () {
3965		return this._panes;
3966	},
3967
3968	// @method getContainer: HTMLElement
3969	// Returns the HTML element that contains the map.
3970	getContainer: function () {
3971		return this._container;
3972	},
3973
3974
3975	// @section Conversion Methods
3976
3977	// @method getZoomScale(toZoom: Number, fromZoom: Number): Number
3978	// Returns the scale factor to be applied to a map transition from zoom level
3979	// `fromZoom` to `toZoom`. Used internally to help with zoom animations.
3980	getZoomScale: function (toZoom, fromZoom) {
3981		// TODO replace with universal implementation after refactoring projections
3982		var crs = this.options.crs;
3983		fromZoom = fromZoom === undefined ? this._zoom : fromZoom;
3984		return crs.scale(toZoom) / crs.scale(fromZoom);
3985	},
3986
3987	// @method getScaleZoom(scale: Number, fromZoom: Number): Number
3988	// Returns the zoom level that the map would end up at, if it is at `fromZoom`
3989	// level and everything is scaled by a factor of `scale`. Inverse of
3990	// [`getZoomScale`](#map-getZoomScale).
3991	getScaleZoom: function (scale, fromZoom) {
3992		var crs = this.options.crs;
3993		fromZoom = fromZoom === undefined ? this._zoom : fromZoom;
3994		var zoom = crs.zoom(scale * crs.scale(fromZoom));
3995		return isNaN(zoom) ? Infinity : zoom;
3996	},
3997
3998	// @method project(latlng: LatLng, zoom: Number): Point
3999	// Projects a geographical coordinate `LatLng` according to the projection
4000	// of the map's CRS, then scales it according to `zoom` and the CRS's
4001	// `Transformation`. The result is pixel coordinate relative to
4002	// the CRS origin.
4003	project: function (latlng, zoom) {
4004		zoom = zoom === undefined ? this._zoom : zoom;
4005		return this.options.crs.latLngToPoint(toLatLng(latlng), zoom);
4006	},
4007
4008	// @method unproject(point: Point, zoom: Number): LatLng
4009	// Inverse of [`project`](#map-project).
4010	unproject: function (point, zoom) {
4011		zoom = zoom === undefined ? this._zoom : zoom;
4012		return this.options.crs.pointToLatLng(toPoint(point), zoom);
4013	},
4014
4015	// @method layerPointToLatLng(point: Point): LatLng
4016	// Given a pixel coordinate relative to the [origin pixel](#map-getpixelorigin),
4017	// returns the corresponding geographical coordinate (for the current zoom level).
4018	layerPointToLatLng: function (point) {
4019		var projectedPoint = toPoint(point).add(this.getPixelOrigin());
4020		return this.unproject(projectedPoint);
4021	},
4022
4023	// @method latLngToLayerPoint(latlng: LatLng): Point
4024	// Given a geographical coordinate, returns the corresponding pixel coordinate
4025	// relative to the [origin pixel](#map-getpixelorigin).
4026	latLngToLayerPoint: function (latlng) {
4027		var projectedPoint = this.project(toLatLng(latlng))._round();
4028		return projectedPoint._subtract(this.getPixelOrigin());
4029	},
4030
4031	// @method wrapLatLng(latlng: LatLng): LatLng
4032	// Returns a `LatLng` where `lat` and `lng` has been wrapped according to the
4033	// map's CRS's `wrapLat` and `wrapLng` properties, if they are outside the
4034	// CRS's bounds.
4035	// By default this means longitude is wrapped around the dateline so its
4036	// value is between -180 and +180 degrees.
4037	wrapLatLng: function (latlng) {
4038		return this.options.crs.wrapLatLng(toLatLng(latlng));
4039	},
4040
4041	// @method wrapLatLngBounds(bounds: LatLngBounds): LatLngBounds
4042	// Returns a `LatLngBounds` with the same size as the given one, ensuring that
4043	// its center is within the CRS's bounds.
4044	// By default this means the center longitude is wrapped around the dateline so its
4045	// value is between -180 and +180 degrees, and the majority of the bounds
4046	// overlaps the CRS's bounds.
4047	wrapLatLngBounds: function (latlng) {
4048		return this.options.crs.wrapLatLngBounds(toLatLngBounds(latlng));
4049	},
4050
4051	// @method distance(latlng1: LatLng, latlng2: LatLng): Number
4052	// Returns the distance between two geographical coordinates according to
4053	// the map's CRS. By default this measures distance in meters.
4054	distance: function (latlng1, latlng2) {
4055		return this.options.crs.distance(toLatLng(latlng1), toLatLng(latlng2));
4056	},
4057
4058	// @method containerPointToLayerPoint(point: Point): Point
4059	// Given a pixel coordinate relative to the map container, returns the corresponding
4060	// pixel coordinate relative to the [origin pixel](#map-getpixelorigin).
4061	containerPointToLayerPoint: function (point) { // (Point)
4062		return toPoint(point).subtract(this._getMapPanePos());
4063	},
4064
4065	// @method layerPointToContainerPoint(point: Point): Point
4066	// Given a pixel coordinate relative to the [origin pixel](#map-getpixelorigin),
4067	// returns the corresponding pixel coordinate relative to the map container.
4068	layerPointToContainerPoint: function (point) { // (Point)
4069		return toPoint(point).add(this._getMapPanePos());
4070	},
4071
4072	// @method containerPointToLatLng(point: Point): LatLng
4073	// Given a pixel coordinate relative to the map container, returns
4074	// the corresponding geographical coordinate (for the current zoom level).
4075	containerPointToLatLng: function (point) {
4076		var layerPoint = this.containerPointToLayerPoint(toPoint(point));
4077		return this.layerPointToLatLng(layerPoint);
4078	},
4079
4080	// @method latLngToContainerPoint(latlng: LatLng): Point
4081	// Given a geographical coordinate, returns the corresponding pixel coordinate
4082	// relative to the map container.
4083	latLngToContainerPoint: function (latlng) {
4084		return this.layerPointToContainerPoint(this.latLngToLayerPoint(toLatLng(latlng)));
4085	},
4086
4087	// @method mouseEventToContainerPoint(ev: MouseEvent): Point
4088	// Given a MouseEvent object, returns the pixel coordinate relative to the
4089	// map container where the event took place.
4090	mouseEventToContainerPoint: function (e) {
4091		return getMousePosition(e, this._container);
4092	},
4093
4094	// @method mouseEventToLayerPoint(ev: MouseEvent): Point
4095	// Given a MouseEvent object, returns the pixel coordinate relative to
4096	// the [origin pixel](#map-getpixelorigin) where the event took place.
4097	mouseEventToLayerPoint: function (e) {
4098		return this.containerPointToLayerPoint(this.mouseEventToContainerPoint(e));
4099	},
4100
4101	// @method mouseEventToLatLng(ev: MouseEvent): LatLng
4102	// Given a MouseEvent object, returns geographical coordinate where the
4103	// event took place.
4104	mouseEventToLatLng: function (e) { // (MouseEvent)
4105		return this.layerPointToLatLng(this.mouseEventToLayerPoint(e));
4106	},
4107
4108
4109	// map initialization methods
4110
4111	_initContainer: function (id) {
4112		var container = this._container = get(id);
4113
4114		if (!container) {
4115			throw new Error('Map container not found.');
4116		} else if (container._leaflet_id) {
4117			throw new Error('Map container is already initialized.');
4118		}
4119
4120		on(container, 'scroll', this._onScroll, this);
4121		this._containerId = stamp(container);
4122	},
4123
4124	_initLayout: function () {
4125		var container = this._container;
4126
4127		this._fadeAnimated = this.options.fadeAnimation && any3d;
4128
4129		addClass(container, 'leaflet-container' +
4130			(touch ? ' leaflet-touch' : '') +
4131			(retina ? ' leaflet-retina' : '') +
4132			(ielt9 ? ' leaflet-oldie' : '') +
4133			(safari ? ' leaflet-safari' : '') +
4134			(this._fadeAnimated ? ' leaflet-fade-anim' : ''));
4135
4136		var position = getStyle(container, 'position');
4137
4138		if (position !== 'absolute' && position !== 'relative' && position !== 'fixed') {
4139			container.style.position = 'relative';
4140		}
4141
4142		this._initPanes();
4143
4144		if (this._initControlPos) {
4145			this._initControlPos();
4146		}
4147	},
4148
4149	_initPanes: function () {
4150		var panes = this._panes = {};
4151		this._paneRenderers = {};
4152
4153		// @section
4154		//
4155		// Panes are DOM elements used to control the ordering of layers on the map. You
4156		// can access panes with [`map.getPane`](#map-getpane) or
4157		// [`map.getPanes`](#map-getpanes) methods. New panes can be created with the
4158		// [`map.createPane`](#map-createpane) method.
4159		//
4160		// Every map has the following default panes that differ only in zIndex.
4161		//
4162		// @pane mapPane: HTMLElement = 'auto'
4163		// Pane that contains all other map panes
4164
4165		this._mapPane = this.createPane('mapPane', this._container);
4166		setPosition(this._mapPane, new Point(0, 0));
4167
4168		// @pane tilePane: HTMLElement = 200
4169		// Pane for `GridLayer`s and `TileLayer`s
4170		this.createPane('tilePane');
4171		// @pane overlayPane: HTMLElement = 400
4172		// Pane for vectors (`Path`s, like `Polyline`s and `Polygon`s), `ImageOverlay`s and `VideoOverlay`s
4173		this.createPane('shadowPane');
4174		// @pane shadowPane: HTMLElement = 500
4175		// Pane for overlay shadows (e.g. `Marker` shadows)
4176		this.createPane('overlayPane');
4177		// @pane markerPane: HTMLElement = 600
4178		// Pane for `Icon`s of `Marker`s
4179		this.createPane('markerPane');
4180		// @pane tooltipPane: HTMLElement = 650
4181		// Pane for `Tooltip`s.
4182		this.createPane('tooltipPane');
4183		// @pane popupPane: HTMLElement = 700
4184		// Pane for `Popup`s.
4185		this.createPane('popupPane');
4186
4187		if (!this.options.markerZoomAnimation) {
4188			addClass(panes.markerPane, 'leaflet-zoom-hide');
4189			addClass(panes.shadowPane, 'leaflet-zoom-hide');
4190		}
4191	},
4192
4193
4194	// private methods that modify map state
4195
4196	// @section Map state change events
4197	_resetView: function (center, zoom) {
4198		setPosition(this._mapPane, new Point(0, 0));
4199
4200		var loading = !this._loaded;
4201		this._loaded = true;
4202		zoom = this._limitZoom(zoom);
4203
4204		this.fire('viewprereset');
4205
4206		var zoomChanged = this._zoom !== zoom;
4207		this
4208			._moveStart(zoomChanged, false)
4209			._move(center, zoom)
4210			._moveEnd(zoomChanged);
4211
4212		// @event viewreset: Event
4213		// Fired when the map needs to redraw its content (this usually happens
4214		// on map zoom or load). Very useful for creating custom overlays.
4215		this.fire('viewreset');
4216
4217		// @event load: Event
4218		// Fired when the map is initialized (when its center and zoom are set
4219		// for the first time).
4220		if (loading) {
4221			this.fire('load');
4222		}
4223	},
4224
4225	_moveStart: function (zoomChanged, noMoveStart) {
4226		// @event zoomstart: Event
4227		// Fired when the map zoom is about to change (e.g. before zoom animation).
4228		// @event movestart: Event
4229		// Fired when the view of the map starts changing (e.g. user starts dragging the map).
4230		if (zoomChanged) {
4231			this.fire('zoomstart');
4232		}
4233		if (!noMoveStart) {
4234			this.fire('movestart');
4235		}
4236		return this;
4237	},
4238
4239	_move: function (center, zoom, data) {
4240		if (zoom === undefined) {
4241			zoom = this._zoom;
4242		}
4243		var zoomChanged = this._zoom !== zoom;
4244
4245		this._zoom = zoom;
4246		this._lastCenter = center;
4247		this._pixelOrigin = this._getNewPixelOrigin(center);
4248
4249		// @event zoom: Event
4250		// Fired repeatedly during any change in zoom level, including zoom
4251		// and fly animations.
4252		if (zoomChanged || (data && data.pinch)) {	// Always fire 'zoom' if pinching because #3530
4253			this.fire('zoom', data);
4254		}
4255
4256		// @event move: Event
4257		// Fired repeatedly during any movement of the map, including pan and
4258		// fly animations.
4259		return this.fire('move', data);
4260	},
4261
4262	_moveEnd: function (zoomChanged) {
4263		// @event zoomend: Event
4264		// Fired when the map has changed, after any animations.
4265		if (zoomChanged) {
4266			this.fire('zoomend');
4267		}
4268
4269		// @event moveend: Event
4270		// Fired when the center of the map stops changing (e.g. user stopped
4271		// dragging the map).
4272		return this.fire('moveend');
4273	},
4274
4275	_stop: function () {
4276		cancelAnimFrame(this._flyToFrame);
4277		if (this._panAnim) {
4278			this._panAnim.stop();
4279		}
4280		return this;
4281	},
4282
4283	_rawPanBy: function (offset) {
4284		setPosition(this._mapPane, this._getMapPanePos().subtract(offset));
4285	},
4286
4287	_getZoomSpan: function () {
4288		return this.getMaxZoom() - this.getMinZoom();
4289	},
4290
4291	_panInsideMaxBounds: function () {
4292		if (!this._enforcingBounds) {
4293			this.panInsideBounds(this.options.maxBounds);
4294		}
4295	},
4296
4297	_checkIfLoaded: function () {
4298		if (!this._loaded) {
4299			throw new Error('Set map center and zoom first.');
4300		}
4301	},
4302
4303	// DOM event handling
4304
4305	// @section Interaction events
4306	_initEvents: function (remove$$1) {
4307		this._targets = {};
4308		this._targets[stamp(this._container)] = this;
4309
4310		var onOff = remove$$1 ? off : on;
4311
4312		// @event click: MouseEvent
4313		// Fired when the user clicks (or taps) the map.
4314		// @event dblclick: MouseEvent
4315		// Fired when the user double-clicks (or double-taps) the map.
4316		// @event mousedown: MouseEvent
4317		// Fired when the user pushes the mouse button on the map.
4318		// @event mouseup: MouseEvent
4319		// Fired when the user releases the mouse button on the map.
4320		// @event mouseover: MouseEvent
4321		// Fired when the mouse enters the map.
4322		// @event mouseout: MouseEvent
4323		// Fired when the mouse leaves the map.
4324		// @event mousemove: MouseEvent
4325		// Fired while the mouse moves over the map.
4326		// @event contextmenu: MouseEvent
4327		// Fired when the user pushes the right mouse button on the map, prevents
4328		// default browser context menu from showing if there are listeners on
4329		// this event. Also fired on mobile when the user holds a single touch
4330		// for a second (also called long press).
4331		// @event keypress: KeyboardEvent
4332		// Fired when the user presses a key from the keyboard that produces a character value while the map is focused.
4333		// @event keydown: KeyboardEvent
4334		// Fired when the user presses a key from the keyboard while the map is focused. Unlike the `keypress` event,
4335		// the `keydown` event is fired for keys that produce a character value and for keys
4336		// that do not produce a character value.
4337		// @event keyup: KeyboardEvent
4338		// Fired when the user releases a key from the keyboard while the map is focused.
4339		onOff(this._container, 'click dblclick mousedown mouseup ' +
4340			'mouseover mouseout mousemove contextmenu keypress keydown keyup', this._handleDOMEvent, this);
4341
4342		if (this.options.trackResize) {
4343			onOff(window, 'resize', this._onResize, this);
4344		}
4345
4346		if (any3d && this.options.transform3DLimit) {
4347			(remove$$1 ? this.off : this.on).call(this, 'moveend', this._onMoveEnd);
4348		}
4349	},
4350
4351	_onResize: function () {
4352		cancelAnimFrame(this._resizeRequest);
4353		this._resizeRequest = requestAnimFrame(
4354		        function () { this.invalidateSize({debounceMoveend: true}); }, this);
4355	},
4356
4357	_onScroll: function () {
4358		this._container.scrollTop  = 0;
4359		this._container.scrollLeft = 0;
4360	},
4361
4362	_onMoveEnd: function () {
4363		var pos = this._getMapPanePos();
4364		if (Math.max(Math.abs(pos.x), Math.abs(pos.y)) >= this.options.transform3DLimit) {
4365			// https://bugzilla.mozilla.org/show_bug.cgi?id=1203873 but Webkit also have
4366			// a pixel offset on very high values, see: http://jsfiddle.net/dg6r5hhb/
4367			this._resetView(this.getCenter(), this.getZoom());
4368		}
4369	},
4370
4371	_findEventTargets: function (e, type) {
4372		var targets = [],
4373		    target,
4374		    isHover = type === 'mouseout' || type === 'mouseover',
4375		    src = e.target || e.srcElement,
4376		    dragging = false;
4377
4378		while (src) {
4379			target = this._targets[stamp(src)];
4380			if (target && (type === 'click' || type === 'preclick') && !e._simulated && this._draggableMoved(target)) {
4381				// Prevent firing click after you just dragged an object.
4382				dragging = true;
4383				break;
4384			}
4385			if (target && target.listens(type, true)) {
4386				if (isHover && !isExternalTarget(src, e)) { break; }
4387				targets.push(target);
4388				if (isHover) { break; }
4389			}
4390			if (src === this._container) { break; }
4391			src = src.parentNode;
4392		}
4393		if (!targets.length && !dragging && !isHover && isExternalTarget(src, e)) {
4394			targets = [this];
4395		}
4396		return targets;
4397	},
4398
4399	_handleDOMEvent: function (e) {
4400		if (!this._loaded || skipped(e)) { return; }
4401
4402		var type = e.type;
4403
4404		if (type === 'mousedown' || type === 'keypress' || type === 'keyup' || type === 'keydown') {
4405			// prevents outline when clicking on keyboard-focusable element
4406			preventOutline(e.target || e.srcElement);
4407		}
4408
4409		this._fireDOMEvent(e, type);
4410	},
4411
4412	_mouseEvents: ['click', 'dblclick', 'mouseover', 'mouseout', 'contextmenu'],
4413
4414	_fireDOMEvent: function (e, type, targets) {
4415
4416		if (e.type === 'click') {
4417			// Fire a synthetic 'preclick' event which propagates up (mainly for closing popups).
4418			// @event preclick: MouseEvent
4419			// Fired before mouse click on the map (sometimes useful when you
4420			// want something to happen on click before any existing click
4421			// handlers start running).
4422			var synth = extend({}, e);
4423			synth.type = 'preclick';
4424			this._fireDOMEvent(synth, synth.type, targets);
4425		}
4426
4427		if (e._stopped) { return; }
4428
4429		// Find the layer the event is propagating from and its parents.
4430		targets = (targets || []).concat(this._findEventTargets(e, type));
4431
4432		if (!targets.length) { return; }
4433
4434		var target = targets[0];
4435		if (type === 'contextmenu' && target.listens(type, true)) {
4436			preventDefault(e);
4437		}
4438
4439		var data = {
4440			originalEvent: e
4441		};
4442
4443		if (e.type !== 'keypress' && e.type !== 'keydown' && e.type !== 'keyup') {
4444			var isMarker = target.getLatLng && (!target._radius || target._radius <= 10);
4445			data.containerPoint = isMarker ?
4446				this.latLngToContainerPoint(target.getLatLng()) : this.mouseEventToContainerPoint(e);
4447			data.layerPoint = this.containerPointToLayerPoint(data.containerPoint);
4448			data.latlng = isMarker ? target.getLatLng() : this.layerPointToLatLng(data.layerPoint);
4449		}
4450
4451		for (var i = 0; i < targets.length; i++) {
4452			targets[i].fire(type, data, true);
4453			if (data.originalEvent._stopped ||
4454				(targets[i].options.bubblingMouseEvents === false && indexOf(this._mouseEvents, type) !== -1)) { return; }
4455		}
4456	},
4457
4458	_draggableMoved: function (obj) {
4459		obj = obj.dragging && obj.dragging.enabled() ? obj : this;
4460		return (obj.dragging && obj.dragging.moved()) || (this.boxZoom && this.boxZoom.moved());
4461	},
4462
4463	_clearHandlers: function () {
4464		for (var i = 0, len = this._handlers.length; i < len; i++) {
4465			this._handlers[i].disable();
4466		}
4467	},
4468
4469	// @section Other Methods
4470
4471	// @method whenReady(fn: Function, context?: Object): this
4472	// Runs the given function `fn` when the map gets initialized with
4473	// a view (center and zoom) and at least one layer, or immediately
4474	// if it's already initialized, optionally passing a function context.
4475	whenReady: function (callback, context) {
4476		if (this._loaded) {
4477			callback.call(context || this, {target: this});
4478		} else {
4479			this.on('load', callback, context);
4480		}
4481		return this;
4482	},
4483
4484
4485	// private methods for getting map state
4486
4487	_getMapPanePos: function () {
4488		return getPosition(this._mapPane) || new Point(0, 0);
4489	},
4490
4491	_moved: function () {
4492		var pos = this._getMapPanePos();
4493		return pos && !pos.equals([0, 0]);
4494	},
4495
4496	_getTopLeftPoint: function (center, zoom) {
4497		var pixelOrigin = center && zoom !== undefined ?
4498			this._getNewPixelOrigin(center, zoom) :
4499			this.getPixelOrigin();
4500		return pixelOrigin.subtract(this._getMapPanePos());
4501	},
4502
4503	_getNewPixelOrigin: function (center, zoom) {
vendor: 34,571 bytes, lines 4504-5765
4504		var viewHalf = this.getSize()._divideBy(2);
4505		return this.project(center, zoom)._subtract(viewHalf)._add(this._getMapPanePos())._round();
4506	},
4507
4508	_latLngToNewLayerPoint: function (latlng, zoom, center) {
4509		var topLeft = this._getNewPixelOrigin(center, zoom);
4510		return this.project(latlng, zoom)._subtract(topLeft);
4511	},
4512
4513	_latLngBoundsToNewLayerBounds: function (latLngBounds, zoom, center) {
4514		var topLeft = this._getNewPixelOrigin(center, zoom);
4515		return toBounds([
4516			this.project(latLngBounds.getSouthWest(), zoom)._subtract(topLeft),
4517			this.project(latLngBounds.getNorthWest(), zoom)._subtract(topLeft),
4518			this.project(latLngBounds.getSouthEast(), zoom)._subtract(topLeft),
4519			this.project(latLngBounds.getNorthEast(), zoom)._subtract(topLeft)
4520		]);
4521	},
4522
4523	// layer point of the current center
4524	_getCenterLayerPoint: function () {
4525		return this.containerPointToLayerPoint(this.getSize()._divideBy(2));
4526	},
4527
4528	// offset of the specified place to the current center in pixels
4529	_getCenterOffset: function (latlng) {
4530		return this.latLngToLayerPoint(latlng).subtract(this._getCenterLayerPoint());
4531	},
4532
4533	// adjust center for view to get inside bounds
4534	_limitCenter: function (center, zoom, bounds) {
4535
4536		if (!bounds) { return center; }
4537
4538		var centerPoint = this.project(center, zoom),
4539		    viewHalf = this.getSize().divideBy(2),
4540		    viewBounds = new Bounds(centerPoint.subtract(viewHalf), centerPoint.add(viewHalf)),
4541		    offset = this._getBoundsOffset(viewBounds, bounds, zoom);
4542
4543		// If offset is less than a pixel, ignore.
4544		// This prevents unstable projections from getting into
4545		// an infinite loop of tiny offsets.
4546		if (offset.round().equals([0, 0])) {
4547			return center;
4548		}
4549
4550		return this.unproject(centerPoint.add(offset), zoom);
4551	},
4552
4553	// adjust offset for view to get inside bounds
4554	_limitOffset: function (offset, bounds) {
4555		if (!bounds) { return offset; }
4556
4557		var viewBounds = this.getPixelBounds(),
4558		    newBounds = new Bounds(viewBounds.min.add(offset), viewBounds.max.add(offset));
4559
4560		return offset.add(this._getBoundsOffset(newBounds, bounds));
4561	},
4562
4563	// returns offset needed for pxBounds to get inside maxBounds at a specified zoom
4564	_getBoundsOffset: function (pxBounds, maxBounds, zoom) {
4565		var projectedMaxBounds = toBounds(
4566		        this.project(maxBounds.getNorthEast(), zoom),
4567		        this.project(maxBounds.getSouthWest(), zoom)
4568		    ),
4569		    minOffset = projectedMaxBounds.min.subtract(pxBounds.min),
4570		    maxOffset = projectedMaxBounds.max.subtract(pxBounds.max),
4571
4572		    dx = this._rebound(minOffset.x, -maxOffset.x),
4573		    dy = this._rebound(minOffset.y, -maxOffset.y);
4574
4575		return new Point(dx, dy);
4576	},
4577
4578	_rebound: function (left, right) {
4579		return left + right > 0 ?
4580			Math.round(left - right) / 2 :
4581			Math.max(0, Math.ceil(left)) - Math.max(0, Math.floor(right));
4582	},
4583
4584	_limitZoom: function (zoom) {
4585		var min = this.getMinZoom(),
4586		    max = this.getMaxZoom(),
4587		    snap = any3d ? this.options.zoomSnap : 1;
4588		if (snap) {
4589			zoom = Math.round(zoom / snap) * snap;
4590		}
4591		return Math.max(min, Math.min(max, zoom));
4592	},
4593
4594	_onPanTransitionStep: function () {
4595		this.fire('move');
4596	},
4597
4598	_onPanTransitionEnd: function () {
4599		removeClass(this._mapPane, 'leaflet-pan-anim');
4600		this.fire('moveend');
4601	},
4602
4603	_tryAnimatedPan: function (center, options) {
4604		// difference between the new and current centers in pixels
4605		var offset = this._getCenterOffset(center)._trunc();
4606
4607		// don't animate too far unless animate: true specified in options
4608		if ((options && options.animate) !== true && !this.getSize().contains(offset)) { return false; }
4609
4610		this.panBy(offset, options);
4611
4612		return true;
4613	},
4614
4615	_createAnimProxy: function () {
4616
4617		var proxy = this._proxy = create$1('div', 'leaflet-proxy leaflet-zoom-animated');
4618		this._panes.mapPane.appendChild(proxy);
4619
4620		this.on('zoomanim', function (e) {
4621			var prop = TRANSFORM,
4622			    transform = this._proxy.style[prop];
4623
4624			setTransform(this._proxy, this.project(e.center, e.zoom), this.getZoomScale(e.zoom, 1));
4625
4626			// workaround for case when transform is the same and so transitionend event is not fired
4627			if (transform === this._proxy.style[prop] && this._animatingZoom) {
4628				this._onZoomTransitionEnd();
4629			}
4630		}, this);
4631
4632		this.on('load moveend', this._animMoveEnd, this);
4633
4634		this._on('unload', this._destroyAnimProxy, this);
4635	},
4636
4637	_destroyAnimProxy: function () {
4638		remove(this._proxy);
4639		this.off('load moveend', this._animMoveEnd, this);
4640		delete this._proxy;
4641	},
4642
4643	_animMoveEnd: function () {
4644		var c = this.getCenter(),
4645		    z = this.getZoom();
4646		setTransform(this._proxy, this.project(c, z), this.getZoomScale(z, 1));
4647	},
4648
4649	_catchTransitionEnd: function (e) {
4650		if (this._animatingZoom && e.propertyName.indexOf('transform') >= 0) {
4651			this._onZoomTransitionEnd();
4652		}
4653	},
4654
4655	_nothingToAnimate: function () {
4656		return !this._container.getElementsByClassName('leaflet-zoom-animated').length;
4657	},
4658
4659	_tryAnimatedZoom: function (center, zoom, options) {
4660
4661		if (this._animatingZoom) { return true; }
4662
4663		options = options || {};
4664
4665		// don't animate if disabled, not supported or zoom difference is too large
4666		if (!this._zoomAnimated || options.animate === false || this._nothingToAnimate() ||
4667		        Math.abs(zoom - this._zoom) > this.options.zoomAnimationThreshold) { return false; }
4668
4669		// offset is the pixel coords of the zoom origin relative to the current center
4670		var scale = this.getZoomScale(zoom),
4671		    offset = this._getCenterOffset(center)._divideBy(1 - 1 / scale);
4672
4673		// don't animate if the zoom origin isn't within one screen from the current center, unless forced
4674		if (options.animate !== true && !this.getSize().contains(offset)) { return false; }
4675
4676		requestAnimFrame(function () {
4677			this
4678			    ._moveStart(true, false)
4679			    ._animateZoom(center, zoom, true);
4680		}, this);
4681
4682		return true;
4683	},
4684
4685	_animateZoom: function (center, zoom, startAnim, noUpdate) {
4686		if (!this._mapPane) { return; }
4687
4688		if (startAnim) {
4689			this._animatingZoom = true;
4690
4691			// remember what center/zoom to set after animation
4692			this._animateToCenter = center;
4693			this._animateToZoom = zoom;
4694
4695			addClass(this._mapPane, 'leaflet-zoom-anim');
4696		}
4697
4698		// @section Other Events
4699		// @event zoomanim: ZoomAnimEvent
4700		// Fired at least once per zoom animation. For continuous zoom, like pinch zooming, fired once per frame during zoom.
4701		this.fire('zoomanim', {
4702			center: center,
4703			zoom: zoom,
4704			noUpdate: noUpdate
4705		});
4706
4707		// Work around webkit not firing 'transitionend', see https://github.com/Leaflet/Leaflet/issues/3689, 2693
4708		setTimeout(bind(this._onZoomTransitionEnd, this), 250);
4709	},
4710
4711	_onZoomTransitionEnd: function () {
4712		if (!this._animatingZoom) { return; }
4713
4714		if (this._mapPane) {
4715			removeClass(this._mapPane, 'leaflet-zoom-anim');
4716		}
4717
4718		this._animatingZoom = false;
4719
4720		this._move(this._animateToCenter, this._animateToZoom);
4721
4722		// This anim frame should prevent an obscure iOS webkit tile loading race condition.
4723		requestAnimFrame(function () {
4724			this._moveEnd(true);
4725		}, this);
4726	}
4727});
4728
4729// @section
4730
4731// @factory L.map(id: String, options?: Map options)
4732// Instantiates a map object given the DOM ID of a `<div>` element
4733// and optionally an object literal with `Map options`.
4734//
4735// @alternative
4736// @factory L.map(el: HTMLElement, options?: Map options)
4737// Instantiates a map object given an instance of a `<div>` HTML element
4738// and optionally an object literal with `Map options`.
4739function createMap(id, options) {
4740	return new Map(id, options);
4741}
4742
4743/*
4744 * @class Control
4745 * @aka L.Control
4746 * @inherits Class
4747 *
4748 * L.Control is a base class for implementing map controls. Handles positioning.
4749 * All other controls extend from this class.
4750 */
4751
4752var Control = Class.extend({
4753	// @section
4754	// @aka Control options
4755	options: {
4756		// @option position: String = 'topright'
4757		// The position of the control (one of the map corners). Possible values are `'topleft'`,
4758		// `'topright'`, `'bottomleft'` or `'bottomright'`
4759		position: 'topright'
4760	},
4761
4762	initialize: function (options) {
4763		setOptions(this, options);
4764	},
4765
4766	/* @section
4767	 * Classes extending L.Control will inherit the following methods:
4768	 *
4769	 * @method getPosition: string
4770	 * Returns the position of the control.
4771	 */
4772	getPosition: function () {
4773		return this.options.position;
4774	},
4775
4776	// @method setPosition(position: string): this
4777	// Sets the position of the control.
4778	setPosition: function (position) {
4779		var map = this._map;
4780
4781		if (map) {
4782			map.removeControl(this);
4783		}
4784
4785		this.options.position = position;
4786
4787		if (map) {
4788			map.addControl(this);
4789		}
4790
4791		return this;
4792	},
4793
4794	// @method getContainer: HTMLElement
4795	// Returns the HTMLElement that contains the control.
4796	getContainer: function () {
4797		return this._container;
4798	},
4799
4800	// @method addTo(map: Map): this
4801	// Adds the control to the given map.
4802	addTo: function (map) {
4803		this.remove();
4804		this._map = map;
4805
4806		var container = this._container = this.onAdd(map),
4807		    pos = this.getPosition(),
4808		    corner = map._controlCorners[pos];
4809
4810		addClass(container, 'leaflet-control');
4811
4812		if (pos.indexOf('bottom') !== -1) {
4813			corner.insertBefore(container, corner.firstChild);
4814		} else {
4815			corner.appendChild(container);
4816		}
4817
4818		this._map.on('unload', this.remove, this);
4819
4820		return this;
4821	},
4822
4823	// @method remove: this
4824	// Removes the control from the map it is currently active on.
4825	remove: function () {
4826		if (!this._map) {
4827			return this;
4828		}
4829
4830		remove(this._container);
4831
4832		if (this.onRemove) {
4833			this.onRemove(this._map);
4834		}
4835
4836		this._map.off('unload', this.remove, this);
4837		this._map = null;
4838
4839		return this;
4840	},
4841
4842	_refocusOnMap: function (e) {
4843		// if map exists and event is not a keyboard event
4844		if (this._map && e && e.screenX > 0 && e.screenY > 0) {
4845			this._map.getContainer().focus();
4846		}
4847	}
4848});
4849
4850var control = function (options) {
4851	return new Control(options);
4852};
4853
4854/* @section Extension methods
4855 * @uninheritable
4856 *
4857 * Every control should extend from `L.Control` and (re-)implement the following methods.
4858 *
4859 * @method onAdd(map: Map): HTMLElement
4860 * Should return the container DOM element for the control and add listeners on relevant map events. Called on [`control.addTo(map)`](#control-addTo).
4861 *
4862 * @method onRemove(map: Map)
4863 * Optional method. Should contain all clean up code that removes the listeners previously added in [`onAdd`](#control-onadd). Called on [`control.remove()`](#control-remove).
4864 */
4865
4866/* @namespace Map
4867 * @section Methods for Layers and Controls
4868 */
4869Map.include({
4870	// @method addControl(control: Control): this
4871	// Adds the given control to the map
4872	addControl: function (control) {
4873		control.addTo(this);
4874		return this;
4875	},
4876
4877	// @method removeControl(control: Control): this
4878	// Removes the given control from the map
4879	removeControl: function (control) {
4880		control.remove();
4881		return this;
4882	},
4883
4884	_initControlPos: function () {
4885		var corners = this._controlCorners = {},
4886		    l = 'leaflet-',
4887		    container = this._controlContainer =
4888		            create$1('div', l + 'control-container', this._container);
4889
4890		function createCorner(vSide, hSide) {
4891			var className = l + vSide + ' ' + l + hSide;
4892
4893			corners[vSide + hSide] = create$1('div', className, container);
4894		}
4895
4896		createCorner('top', 'left');
4897		createCorner('top', 'right');
4898		createCorner('bottom', 'left');
4899		createCorner('bottom', 'right');
4900	},
4901
4902	_clearControlPos: function () {
4903		for (var i in this._controlCorners) {
4904			remove(this._controlCorners[i]);
4905		}
4906		remove(this._controlContainer);
4907		delete this._controlCorners;
4908		delete this._controlContainer;
4909	}
4910});
4911
4912/*
4913 * @class Control.Layers
4914 * @aka L.Control.Layers
4915 * @inherits Control
4916 *
4917 * The layers control gives users the ability to switch between different base layers and switch overlays on/off (check out the [detailed example](http://leafletjs.com/examples/layers-control/)). Extends `Control`.
4918 *
4919 * @example
4920 *
4921 * ```js
4922 * var baseLayers = {
4923 * 	"Mapbox": mapbox,
4924 * 	"OpenStreetMap": osm
4925 * };
4926 *
4927 * var overlays = {
4928 * 	"Marker": marker,
4929 * 	"Roads": roadsLayer
4930 * };
4931 *
4932 * L.control.layers(baseLayers, overlays).addTo(map);
4933 * ```
4934 *
4935 * The `baseLayers` and `overlays` parameters are object literals with layer names as keys and `Layer` objects as values:
4936 *
4937 * ```js
4938 * {
4939 *     "<someName1>": layer1,
4940 *     "<someName2>": layer2
4941 * }
4942 * ```
4943 *
4944 * The layer names can contain HTML, which allows you to add additional styling to the items:
4945 *
4946 * ```js
4947 * {"<img src='my-layer-icon' /> <span class='my-layer-item'>My Layer</span>": myLayer}
4948 * ```
4949 */
4950
4951var Layers = Control.extend({
4952	// @section
4953	// @aka Control.Layers options
4954	options: {
4955		// @option collapsed: Boolean = true
4956		// If `true`, the control will be collapsed into an icon and expanded on mouse hover or touch.
4957		collapsed: true,
4958		position: 'topright',
4959
4960		// @option autoZIndex: Boolean = true
4961		// 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.
4962		autoZIndex: true,
4963
4964		// @option hideSingleBase: Boolean = false
4965		// If `true`, the base layers in the control will be hidden when there is only one.
4966		hideSingleBase: false,
4967
4968		// @option sortLayers: Boolean = false
4969		// Whether to sort the layers. When `false`, layers will keep the order
4970		// in which they were added to the control.
4971		sortLayers: false,
4972
4973		// @option sortFunction: Function = *
4974		// A [compare function](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Array/sort)
4975		// that will be used for sorting the layers, when `sortLayers` is `true`.
4976		// The function receives both the `L.Layer` instances and their names, as in
4977		// `sortFunction(layerA, layerB, nameA, nameB)`.
4978		// By default, it sorts layers alphabetically by their name.
4979		sortFunction: function (layerA, layerB, nameA, nameB) {
4980			return nameA < nameB ? -1 : (nameB < nameA ? 1 : 0);
4981		}
4982	},
4983
4984	initialize: function (baseLayers, overlays, options) {
4985		setOptions(this, options);
4986
4987		this._layerControlInputs = [];
4988		this._layers = [];
4989		this._lastZIndex = 0;
4990		this._handlingClick = false;
4991
4992		for (var i in baseLayers) {
4993			this._addLayer(baseLayers[i], i);
4994		}
4995
4996		for (i in overlays) {
4997			this._addLayer(overlays[i], i, true);
4998		}
4999	},
5000
5001	onAdd: function (map) {
5002		this._initLayout();
5003		this._update();
5004
5005		this._map = map;
5006		map.on('zoomend', this._checkDisabledLayers, this);
5007
5008		for (var i = 0; i < this._layers.length; i++) {
5009			this._layers[i].layer.on('add remove', this._onLayerChange, this);
5010		}
5011
5012		return this._container;
5013	},
5014
5015	addTo: function (map) {
5016		Control.prototype.addTo.call(this, map);
5017		// Trigger expand after Layers Control has been inserted into DOM so that is now has an actual height.
5018		return this._expandIfNotCollapsed();
5019	},
5020
5021	onRemove: function () {
5022		this._map.off('zoomend', this._checkDisabledLayers, this);
5023
5024		for (var i = 0; i < this._layers.length; i++) {
5025			this._layers[i].layer.off('add remove', this._onLayerChange, this);
5026		}
5027	},
5028
5029	// @method addBaseLayer(layer: Layer, name: String): this
5030	// Adds a base layer (radio button entry) with the given name to the control.
5031	addBaseLayer: function (layer, name) {
5032		this._addLayer(layer, name);
5033		return (this._map) ? this._update() : this;
5034	},
5035
5036	// @method addOverlay(layer: Layer, name: String): this
5037	// Adds an overlay (checkbox entry) with the given name to the control.
5038	addOverlay: function (layer, name) {
5039		this._addLayer(layer, name, true);
5040		return (this._map) ? this._update() : this;
5041	},
5042
5043	// @method removeLayer(layer: Layer): this
5044	// Remove the given layer from the control.
5045	removeLayer: function (layer) {
5046		layer.off('add remove', this._onLayerChange, this);
5047
5048		var obj = this._getLayer(stamp(layer));
5049		if (obj) {
5050			this._layers.splice(this._layers.indexOf(obj), 1);
5051		}
5052		return (this._map) ? this._update() : this;
5053	},
5054
5055	// @method expand(): this
5056	// Expand the control container if collapsed.
5057	expand: function () {
5058		addClass(this._container, 'leaflet-control-layers-expanded');
5059		this._section.style.height = null;
5060		var acceptableHeight = this._map.getSize().y - (this._container.offsetTop + 50);
5061		if (acceptableHeight < this._section.clientHeight) {
5062			addClass(this._section, 'leaflet-control-layers-scrollbar');
5063			this._section.style.height = acceptableHeight + 'px';
5064		} else {
5065			removeClass(this._section, 'leaflet-control-layers-scrollbar');
5066		}
5067		this._checkDisabledLayers();
5068		return this;
5069	},
5070
5071	// @method collapse(): this
5072	// Collapse the control container if expanded.
5073	collapse: function () {
5074		removeClass(this._container, 'leaflet-control-layers-expanded');
5075		return this;
5076	},
5077
5078	_initLayout: function () {
5079		var className = 'leaflet-control-layers',
5080		    container = this._container = create$1('div', className),
5081		    collapsed = this.options.collapsed;
5082
5083		// makes this work on IE touch devices by stopping it from firing a mouseout event when the touch is released
5084		container.setAttribute('aria-haspopup', true);
5085
5086		disableClickPropagation(container);
5087		disableScrollPropagation(container);
5088
5089		var section = this._section = create$1('section', className + '-list');
5090
5091		if (collapsed) {
5092			this._map.on('click', this.collapse, this);
5093
5094			if (!android) {
5095				on(container, {
5096					mouseenter: this.expand,
5097					mouseleave: this.collapse
5098				}, this);
5099			}
5100		}
5101
5102		var link = this._layersLink = create$1('a', className + '-toggle', container);
5103		link.href = '#';
5104		link.title = 'Layers';
5105
5106		if (touch) {
5107			on(link, 'click', stop);
5108			on(link, 'click', this.expand, this);
5109		} else {
5110			on(link, 'focus', this.expand, this);
5111		}
5112
5113		if (!collapsed) {
5114			this.expand();
5115		}
5116
5117		this._baseLayersList = create$1('div', className + '-base', section);
5118		this._separator = create$1('div', className + '-separator', section);
5119		this._overlaysList = create$1('div', className + '-overlays', section);
5120
5121		container.appendChild(section);
5122	},
5123
5124	_getLayer: function (id) {
5125		for (var i = 0; i < this._layers.length; i++) {
5126
5127			if (this._layers[i] && stamp(this._layers[i].layer) === id) {
5128				return this._layers[i];
5129			}
5130		}
5131	},
5132
5133	_addLayer: function (layer, name, overlay) {
5134		if (this._map) {
5135			layer.on('add remove', this._onLayerChange, this);
5136		}
5137
5138		this._layers.push({
5139			layer: layer,
5140			name: name,
5141			overlay: overlay
5142		});
5143
5144		if (this.options.sortLayers) {
5145			this._layers.sort(bind(function (a, b) {
5146				return this.options.sortFunction(a.layer, b.layer, a.name, b.name);
5147			}, this));
5148		}
5149
5150		if (this.options.autoZIndex && layer.setZIndex) {
5151			this._lastZIndex++;
5152			layer.setZIndex(this._lastZIndex);
5153		}
5154
5155		this._expandIfNotCollapsed();
5156	},
5157
5158	_update: function () {
5159		if (!this._container) { return this; }
5160
5161		empty(this._baseLayersList);
5162		empty(this._overlaysList);
5163
5164		this._layerControlInputs = [];
5165		var baseLayersPresent, overlaysPresent, i, obj, baseLayersCount = 0;
5166
5167		for (i = 0; i < this._layers.length; i++) {
5168			obj = this._layers[i];
5169			this._addItem(obj);
5170			overlaysPresent = overlaysPresent || obj.overlay;
5171			baseLayersPresent = baseLayersPresent || !obj.overlay;
5172			baseLayersCount += !obj.overlay ? 1 : 0;
5173		}
5174
5175		// Hide base layers section if there's only one layer.
5176		if (this.options.hideSingleBase) {
5177			baseLayersPresent = baseLayersPresent && baseLayersCount > 1;
5178			this._baseLayersList.style.display = baseLayersPresent ? '' : 'none';
5179		}
5180
5181		this._separator.style.display = overlaysPresent && baseLayersPresent ? '' : 'none';
5182
5183		return this;
5184	},
5185
5186	_onLayerChange: function (e) {
5187		if (!this._handlingClick) {
5188			this._update();
5189		}
5190
5191		var obj = this._getLayer(stamp(e.target));
5192
5193		// @namespace Map
5194		// @section Layer events
5195		// @event baselayerchange: LayersControlEvent
5196		// Fired when the base layer is changed through the [layer control](#control-layers).
5197		// @event overlayadd: LayersControlEvent
5198		// Fired when an overlay is selected through the [layer control](#control-layers).
5199		// @event overlayremove: LayersControlEvent
5200		// Fired when an overlay is deselected through the [layer control](#control-layers).
5201		// @namespace Control.Layers
5202		var type = obj.overlay ?
5203			(e.type === 'add' ? 'overlayadd' : 'overlayremove') :
5204			(e.type === 'add' ? 'baselayerchange' : null);
5205
5206		if (type) {
5207			this._map.fire(type, obj);
5208		}
5209	},
5210
5211	// IE7 bugs out if you create a radio dynamically, so you have to do it this hacky way (see http://bit.ly/PqYLBe)
5212	_createRadioElement: function (name, checked) {
5213
5214		var radioHtml = '<input type="radio" class="leaflet-control-layers-selector" name="' +
5215				name + '"' + (checked ? ' checked="checked"' : '') + '/>';
5216
5217		var radioFragment = document.createElement('div');
5218		radioFragment.innerHTML = radioHtml;
5219
5220		return radioFragment.firstChild;
5221	},
5222
5223	_addItem: function (obj) {
5224		var label = document.createElement('label'),
5225		    checked = this._map.hasLayer(obj.layer),
5226		    input;
5227
5228		if (obj.overlay) {
5229			input = document.createElement('input');
5230			input.type = 'checkbox';
5231			input.className = 'leaflet-control-layers-selector';
5232			input.defaultChecked = checked;
5233		} else {
5234			input = this._createRadioElement('leaflet-base-layers_' + stamp(this), checked);
5235		}
5236
5237		this._layerControlInputs.push(input);
5238		input.layerId = stamp(obj.layer);
5239
5240		on(input, 'click', this._onInputClick, this);
5241
5242		var name = document.createElement('span');
5243		name.innerHTML = ' ' + obj.name;
5244
5245		// Helps from preventing layer control flicker when checkboxes are disabled
5246		// https://github.com/Leaflet/Leaflet/issues/2771
5247		var holder = document.createElement('div');
5248
5249		label.appendChild(holder);
5250		holder.appendChild(input);
5251		holder.appendChild(name);
5252
5253		var container = obj.overlay ? this._overlaysList : this._baseLayersList;
5254		container.appendChild(label);
5255
5256		this._checkDisabledLayers();
5257		return label;
5258	},
5259
5260	_onInputClick: function () {
5261		var inputs = this._layerControlInputs,
5262		    input, layer;
5263		var addedLayers = [],
5264		    removedLayers = [];
5265
5266		this._handlingClick = true;
5267
5268		for (var i = inputs.length - 1; i >= 0; i--) {
5269			input = inputs[i];
5270			layer = this._getLayer(input.layerId).layer;
5271
5272			if (input.checked) {
5273				addedLayers.push(layer);
5274			} else if (!input.checked) {
5275				removedLayers.push(layer);
5276			}
5277		}
5278
5279		// Bugfix issue 2318: Should remove all old layers before readding new ones
5280		for (i = 0; i < removedLayers.length; i++) {
5281			if (this._map.hasLayer(removedLayers[i])) {
5282				this._map.removeLayer(removedLayers[i]);
5283			}
5284		}
5285		for (i = 0; i < addedLayers.length; i++) {
5286			if (!this._map.hasLayer(addedLayers[i])) {
5287				this._map.addLayer(addedLayers[i]);
5288			}
5289		}
5290
5291		this._handlingClick = false;
5292
5293		this._refocusOnMap();
5294	},
5295
5296	_checkDisabledLayers: function () {
5297		var inputs = this._layerControlInputs,
5298		    input,
5299		    layer,
5300		    zoom = this._map.getZoom();
5301
5302		for (var i = inputs.length - 1; i >= 0; i--) {
5303			input = inputs[i];
5304			layer = this._getLayer(input.layerId).layer;
5305			input.disabled = (layer.options.minZoom !== undefined && zoom < layer.options.minZoom) ||
5306			                 (layer.options.maxZoom !== undefined && zoom > layer.options.maxZoom);
5307
5308		}
5309	},
5310
5311	_expandIfNotCollapsed: function () {
5312		if (this._map && !this.options.collapsed) {
5313			this.expand();
5314		}
5315		return this;
5316	},
5317
5318	_expand: function () {
5319		// Backward compatibility, remove me in 1.1.
5320		return this.expand();
5321	},
5322
5323	_collapse: function () {
5324		// Backward compatibility, remove me in 1.1.
5325		return this.collapse();
5326	}
5327
5328});
5329
5330
5331// @factory L.control.layers(baselayers?: Object, overlays?: Object, options?: Control.Layers options)
5332// 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.
5333var layers = function (baseLayers, overlays, options) {
5334	return new Layers(baseLayers, overlays, options);
5335};
5336
5337/*
5338 * @class Control.Zoom
5339 * @aka L.Control.Zoom
5340 * @inherits Control
5341 *
5342 * 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`.
5343 */
5344
5345var Zoom = Control.extend({
5346	// @section
5347	// @aka Control.Zoom options
5348	options: {
5349		position: 'topleft',
5350
5351		// @option zoomInText: String = '+'
5352		// The text set on the 'zoom in' button.
5353		zoomInText: '+',
5354
5355		// @option zoomInTitle: String = 'Zoom in'
5356		// The title set on the 'zoom in' button.
5357		zoomInTitle: 'Zoom in',
5358
5359		// @option zoomOutText: String = '&#x2212;'
5360		// The text set on the 'zoom out' button.
5361		zoomOutText: '&#x2212;',
5362
5363		// @option zoomOutTitle: String = 'Zoom out'
5364		// The title set on the 'zoom out' button.
5365		zoomOutTitle: 'Zoom out'
5366	},
5367
5368	onAdd: function (map) {
5369		var zoomName = 'leaflet-control-zoom',
5370		    container = create$1('div', zoomName + ' leaflet-bar'),
5371		    options = this.options;
5372
5373		this._zoomInButton  = this._createButton(options.zoomInText, options.zoomInTitle,
5374		        zoomName + '-in',  container, this._zoomIn);
5375		this._zoomOutButton = this._createButton(options.zoomOutText, options.zoomOutTitle,
5376		        zoomName + '-out', container, this._zoomOut);
5377
5378		this._updateDisabled();
5379		map.on('zoomend zoomlevelschange', this._updateDisabled, this);
5380
5381		return container;
5382	},
5383
5384	onRemove: function (map) {
5385		map.off('zoomend zoomlevelschange', this._updateDisabled, this);
5386	},
5387
5388	disable: function () {
5389		this._disabled = true;
5390		this._updateDisabled();
5391		return this;
5392	},
5393
5394	enable: function () {
5395		this._disabled = false;
5396		this._updateDisabled();
5397		return this;
5398	},
5399
5400	_zoomIn: function (e) {
5401		if (!this._disabled && this._map._zoom < this._map.getMaxZoom()) {
5402			this._map.zoomIn(this._map.options.zoomDelta * (e.shiftKey ? 3 : 1));
5403		}
5404	},
5405
5406	_zoomOut: function (e) {
5407		if (!this._disabled && this._map._zoom > this._map.getMinZoom()) {
5408			this._map.zoomOut(this._map.options.zoomDelta * (e.shiftKey ? 3 : 1));
5409		}
5410	},
5411
5412	_createButton: function (html, title, className, container, fn) {
5413		var link = create$1('a', className, container);
5414		link.innerHTML = html;
5415		link.href = '#';
5416		link.title = title;
5417
5418		/*
5419		 * Will force screen readers like VoiceOver to read this as "Zoom in - button"
5420		 */
5421		link.setAttribute('role', 'button');
5422		link.setAttribute('aria-label', title);
5423
5424		disableClickPropagation(link);
5425		on(link, 'click', stop);
5426		on(link, 'click', fn, this);
5427		on(link, 'click', this._refocusOnMap, this);
5428
5429		return link;
5430	},
5431
5432	_updateDisabled: function () {
5433		var map = this._map,
5434		    className = 'leaflet-disabled';
5435
5436		removeClass(this._zoomInButton, className);
5437		removeClass(this._zoomOutButton, className);
5438
5439		if (this._disabled || map._zoom === map.getMinZoom()) {
5440			addClass(this._zoomOutButton, className);
5441		}
5442		if (this._disabled || map._zoom === map.getMaxZoom()) {
5443			addClass(this._zoomInButton, className);
5444		}
5445	}
5446});
5447
5448// @namespace Map
5449// @section Control options
5450// @option zoomControl: Boolean = true
5451// Whether a [zoom control](#control-zoom) is added to the map by default.
5452Map.mergeOptions({
5453	zoomControl: true
5454});
5455
5456Map.addInitHook(function () {
5457	if (this.options.zoomControl) {
5458		// @section Controls
5459		// @property zoomControl: Control.Zoom
5460		// The default zoom control (only available if the
5461		// [`zoomControl` option](#map-zoomcontrol) was `true` when creating the map).
5462		this.zoomControl = new Zoom();
5463		this.addControl(this.zoomControl);
5464	}
5465});
5466
5467// @namespace Control.Zoom
5468// @factory L.control.zoom(options: Control.Zoom options)
5469// Creates a zoom control
5470var zoom = function (options) {
5471	return new Zoom(options);
5472};
5473
5474/*
5475 * @class Control.Scale
5476 * @aka L.Control.Scale
5477 * @inherits Control
5478 *
5479 * 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`.
5480 *
5481 * @example
5482 *
5483 * ```js
5484 * L.control.scale().addTo(map);
5485 * ```
5486 */
5487
5488var Scale = Control.extend({
5489	// @section
5490	// @aka Control.Scale options
5491	options: {
5492		position: 'bottomleft',
5493
5494		// @option maxWidth: Number = 100
5495		// Maximum width of the control in pixels. The width is set dynamically to show round values (e.g. 100, 200, 500).
5496		maxWidth: 100,
5497
5498		// @option metric: Boolean = True
5499		// Whether to show the metric scale line (m/km).
5500		metric: true,
5501
5502		// @option imperial: Boolean = True
5503		// Whether to show the imperial scale line (mi/ft).
5504		imperial: true
5505
5506		// @option updateWhenIdle: Boolean = false
5507		// If `true`, the control is updated on [`moveend`](#map-moveend), otherwise it's always up-to-date (updated on [`move`](#map-move)).
5508	},
5509
5510	onAdd: function (map) {
5511		var className = 'leaflet-control-scale',
5512		    container = create$1('div', className),
5513		    options = this.options;
5514
5515		this._addScales(options, className + '-line', container);
5516
5517		map.on(options.updateWhenIdle ? 'moveend' : 'move', this._update, this);
5518		map.whenReady(this._update, this);
5519
5520		return container;
5521	},
5522
5523	onRemove: function (map) {
5524		map.off(this.options.updateWhenIdle ? 'moveend' : 'move', this._update, this);
5525	},
5526
5527	_addScales: function (options, className, container) {
5528		if (options.metric) {
5529			this._mScale = create$1('div', className, container);
5530		}
5531		if (options.imperial) {
5532			this._iScale = create$1('div', className, container);
5533		}
5534	},
5535
5536	_update: function () {
5537		var map = this._map,
5538		    y = map.getSize().y / 2;
5539
5540		var maxMeters = map.distance(
5541			map.containerPointToLatLng([0, y]),
5542			map.containerPointToLatLng([this.options.maxWidth, y]));
5543
5544		this._updateScales(maxMeters);
5545	},
5546
5547	_updateScales: function (maxMeters) {
5548		if (this.options.metric && maxMeters) {
5549			this._updateMetric(maxMeters);
5550		}
5551		if (this.options.imperial && maxMeters) {
5552			this._updateImperial(maxMeters);
5553		}
5554	},
5555
5556	_updateMetric: function (maxMeters) {
5557		var meters = this._getRoundNum(maxMeters),
5558		    label = meters < 1000 ? meters + ' m' : (meters / 1000) + ' km';
5559
5560		this._updateScale(this._mScale, label, meters / maxMeters);
5561	},
5562
5563	_updateImperial: function (maxMeters) {
5564		var maxFeet = maxMeters * 3.2808399,
5565		    maxMiles, miles, feet;
5566
5567		if (maxFeet > 5280) {
5568			maxMiles = maxFeet / 5280;
5569			miles = this._getRoundNum(maxMiles);
5570			this._updateScale(this._iScale, miles + ' mi', miles / maxMiles);
5571
5572		} else {
5573			feet = this._getRoundNum(maxFeet);
5574			this._updateScale(this._iScale, feet + ' ft', feet / maxFeet);
5575		}
5576	},
5577
5578	_updateScale: function (scale, text, ratio) {
5579		scale.style.width = Math.round(this.options.maxWidth * ratio) + 'px';
5580		scale.innerHTML = text;
5581	},
5582
5583	_getRoundNum: function (num) {
5584		var pow10 = Math.pow(10, (Math.floor(num) + '').length - 1),
5585		    d = num / pow10;
5586
5587		d = d >= 10 ? 10 :
5588		    d >= 5 ? 5 :
5589		    d >= 3 ? 3 :
5590		    d >= 2 ? 2 : 1;
5591
5592		return pow10 * d;
5593	}
5594});
5595
5596
5597// @factory L.control.scale(options?: Control.Scale options)
5598// Creates an scale control with the given options.
5599var scale = function (options) {
5600	return new Scale(options);
5601};
5602
5603/*
5604 * @class Control.Attribution
5605 * @aka L.Control.Attribution
5606 * @inherits Control
5607 *
5608 * 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.
5609 */
5610
5611var Attribution = Control.extend({
5612	// @section
5613	// @aka Control.Attribution options
5614	options: {
5615		position: 'bottomright',
5616
5617		// @option prefix: String = 'Leaflet'
5618		// The HTML text shown before the attributions. Pass `false` to disable.
5619		prefix: '<a href="https://leafletjs.com" title="A JS library for interactive maps">Leaflet</a>'
5620	},
5621
5622	initialize: function (options) {
5623		setOptions(this, options);
5624
5625		this._attributions = {};
5626	},
5627
5628	onAdd: function (map) {
5629		map.attributionControl = this;
5630		this._container = create$1('div', 'leaflet-control-attribution');
5631		disableClickPropagation(this._container);
5632
5633		// TODO ugly, refactor
5634		for (var i in map._layers) {
5635			if (map._layers[i].getAttribution) {
5636				this.addAttribution(map._layers[i].getAttribution());
5637			}
5638		}
5639
5640		this._update();
5641
5642		return this._container;
5643	},
5644
5645	// @method setPrefix(prefix: String): this
5646	// Sets the text before the attributions.
5647	setPrefix: function (prefix) {
5648		this.options.prefix = prefix;
5649		this._update();
5650		return this;
5651	},
5652
5653	// @method addAttribution(text: String): this
5654	// Adds an attribution text (e.g. `'Vector data &copy; Mapbox'`).
5655	addAttribution: function (text) {
5656		if (!text) { return this; }
5657
5658		if (!this._attributions[text]) {
5659			this._attributions[text] = 0;
5660		}
5661		this._attributions[text]++;
5662
5663		this._update();
5664
5665		return this;
5666	},
5667
5668	// @method removeAttribution(text: String): this
5669	// Removes an attribution text.
5670	removeAttribution: function (text) {
5671		if (!text) { return this; }
5672
5673		if (this._attributions[text]) {
5674			this._attributions[text]--;
5675			this._update();
5676		}
5677
5678		return this;
5679	},
5680
5681	_update: function () {
5682		if (!this._map) { return; }
5683
5684		var attribs = [];
5685
5686		for (var i in this._attributions) {
5687			if (this._attributions[i]) {
5688				attribs.push(i);
5689			}
5690		}
5691
5692		var prefixAndAttribs = [];
5693
5694		if (this.options.prefix) {
5695			prefixAndAttribs.push(this.options.prefix);
5696		}
5697		if (attribs.length) {
5698			prefixAndAttribs.push(attribs.join(', '));
5699		}
5700
5701		this._container.innerHTML = prefixAndAttribs.join(' | ');
5702	}
5703});
5704
5705// @namespace Map
5706// @section Control options
5707// @option attributionControl: Boolean = true
5708// Whether a [attribution control](#control-attribution) is added to the map by default.
5709Map.mergeOptions({
5710	attributionControl: true
5711});
5712
5713Map.addInitHook(function () {
5714	if (this.options.attributionControl) {
5715		new Attribution().addTo(this);
5716	}
5717});
5718
5719// @namespace Control.Attribution
5720// @factory L.control.attribution(options: Control.Attribution options)
5721// Creates an attribution control.
5722var attribution = function (options) {
5723	return new Attribution(options);
5724};
5725
5726Control.Layers = Layers;
5727Control.Zoom = Zoom;
5728Control.Scale = Scale;
5729Control.Attribution = Attribution;
5730
5731control.layers = layers;
5732control.zoom = zoom;
5733control.scale = scale;
5734control.attribution = attribution;
5735
5736/*
5737	L.Handler is a base class for handler classes that are used internally to inject
5738	interaction features like dragging to classes like Map and Marker.
5739*/
5740
5741// @class Handler
5742// @aka L.Handler
5743// Abstract class for map interaction handlers
5744
5745var Handler = Class.extend({
5746	initialize: function (map) {
5747		this._map = map;
5748	},
5749
5750	// @method enable(): this
5751	// Enables the handler
5752	enable: function () {
5753		if (this._enabled) { return this; }
5754
5755		this._enabled = true;
5756		this.addHooks();
5757		return this;
5758	},
5759
5760	// @method disable(): this
5761	// Disables the handler
5762	disable: function () {
5763		if (!this._enabled) { return this; }
5764
5765		this._enabled = false;
5766		this.removeHooks();
5767		return this;
5768	},
5769
5770	// @method enabled(): Boolean
5771	// Returns `true` if the handler is enabled
5772	enabled: function () {
5773		return !!this._enabled;
5774	}
5775
5776	// @section Extension methods
5777	// Classes inheriting from `Handler` must implement the two following methods:
5778	// @method addHooks()
5779	// Called when the handler is enabled, should add event hooks.
5780	// @method removeHooks()
5781	// Called when the handler is disabled, should remove the event hooks added previously.
5782});
5783
5784// @section There is static function which can be called without instantiating L.Handler:
5785// @function addTo(map: Map, name: String): this
5786// Adds a new Handler to the given map with the given name.
5787Handler.addTo = function (map, name) {
5788	map.addHandler(name, this);
5789	return this;
5790};
5791
5792var Mixin = {Events: Events};
5793
5794/*
5795 * @class Draggable
5796 * @aka L.Draggable
5797 * @inherits Evented
5798 *
5799 * A class for making DOM elements draggable (including touch support).
5800 * Used internally for map and marker dragging. Only works for elements
5801 * that were positioned with [`L.DomUtil.setPosition`](#domutil-setposition).
5802 *
5803 * @example
5804 * ```js
5805 * var draggable = new L.Draggable(elementToDrag);
5806 * draggable.enable();
5807 * ```
5808 */
5809
5810var START = touch ? 'touchstart mousedown' : 'mousedown';
5811var END = {
5812	mousedown: 'mouseup',
5813	touchstart: 'touchend',
5814	pointerdown: 'touchend',
5815	MSPointerDown: 'touchend'
5816};
5817var MOVE = {
5818	mousedown: 'mousemove',
5819	touchstart: 'touchmove',
5820	pointerdown: 'touchmove',
5821	MSPointerDown: 'touchmove'
5822};
5823
5824
5825var Draggable = Evented.extend({
5826
5827	options: {
5828		// @section
5829		// @aka Draggable options
5830		// @option clickTolerance: Number = 3
5831		// The max number of pixels a user can shift the mouse pointer during a click
5832		// for it to be considered a valid click (as opposed to a mouse drag).
5833		clickTolerance: 3
5834	},
5835
5836	// @constructor L.Draggable(el: HTMLElement, dragHandle?: HTMLElement, preventOutline?: Boolean, options?: Draggable options)
5837	// Creates a `Draggable` object for moving `el` when you start dragging the `dragHandle` element (equals `el` itself by default).
5838	initialize: function (element, dragStartTarget, preventOutline$$1, options) {
5839		setOptions(this, options);
5840
5841		this._element = element;
5842		this._dragStartTarget = dragStartTarget || element;
5843		this._preventOutline = preventOutline$$1;
5844	},
5845
5846	// @method enable()
5847	// Enables the dragging ability
5848	enable: function () {
5849		if (this._enabled) { return; }
5850
5851		on(this._dragStartTarget, START, this._onDown, this);
5852
5853		this._enabled = true;
5854	},
5855
5856	// @method disable()
5857	// Disables the dragging ability
5858	disable: function () {
5859		if (!this._enabled) { return; }
5860
5861		// If we're currently dragging this draggable,
5862		// disabling it counts as first ending the drag.
5863		if (Draggable._dragging === this) {
5864			this.finishDrag();
5865		}
5866
5867		off(this._dragStartTarget, START, this._onDown, this);
5868
5869		this._enabled = false;
5870		this._moved = false;
5871	},
5872
5873	_onDown: function (e) {
5874		// Ignore simulated events, since we handle both touch and
5875		// mouse explicitly; otherwise we risk getting duplicates of
5876		// touch events, see #4315.
5877		// Also ignore the event if disabled; this happens in IE11
5878		// under some circumstances, see #3666.
5879		if (e._simulated || !this._enabled) { return; }
5880
5881		this._moved = false;
5882
5883		if (hasClass(this._element, 'leaflet-zoom-anim')) { return; }
5884
5885		if (Draggable._dragging || e.shiftKey || ((e.which !== 1) && (e.button !== 1) && !e.touches)) { return; }
5886		Draggable._dragging = this;  // Prevent dragging multiple objects at once.
5887
5888		if (this._preventOutline) {
5889			preventOutline(this._element);
5890		}
5891
5892		disableImageDrag();
5893		disableTextSelection();
5894
5895		if (this._moving) { return; }
5896
5897		// @event down: Event
5898		// Fired when a drag is about to start.
5899		this.fire('down');
5900
5901		var first = e.touches ? e.touches[0] : e,
5902		    sizedParent = getSizedParentNode(this._element);
5903
5904		this._startPoint = new Point(first.clientX, first.clientY);
5905
5906		// Cache the scale, so that we can continuously compensate for it during drag (_onMove).
5907		this._parentScale = getScale(sizedParent);
5908
5909		on(document, MOVE[e.type], this._onMove, this);
5910		on(document, END[e.type], this._onUp, this);
5911	},
5912
5913	_onMove: function (e) {
5914		// Ignore simulated events, since we handle both touch and
5915		// mouse explicitly; otherwise we risk getting duplicates of
5916		// touch events, see #4315.
5917		// Also ignore the event if disabled; this happens in IE11
5918		// under some circumstances, see #3666.
5919		if (e._simulated || !this._enabled) { return; }
5920
5921		if (e.touches && e.touches.length > 1) {
5922			this._moved = true;
5923			return;
5924		}
5925
5926		var first = (e.touches && e.touches.length === 1 ? e.touches[0] : e),
5927		    offset = new Point(first.clientX, first.clientY)._subtract(this._startPoint);
5928
5929		if (!offset.x && !offset.y) { return; }
5930		if (Math.abs(offset.x) + Math.abs(offset.y) < this.options.clickTolerance) { return; }
5931
5932		// We assume that the parent container's position, border and scale do not change for the duration of the drag.
5933		// Therefore there is no need to account for the position and border (they are eliminated by the subtraction)
5934		// and we can use the cached value for the scale.
5935		offset.x /= this._parentScale.x;
5936		offset.y /= this._parentScale.y;
5937
5938		preventDefault(e);
5939
5940		if (!this._moved) {
5941			// @event dragstart: Event
5942			// Fired when a drag starts
5943			this.fire('dragstart');
5944
5945			this._moved = true;
5946			this._startPos = getPosition(this._element).subtract(offset);
5947
5948			addClass(document.body, 'leaflet-dragging');
5949
5950			this._lastTarget = e.target || e.srcElement;
5951			// IE and Edge do not give the <use> element, so fetch it
5952			// if necessary
5953			if ((window.SVGElementInstance) && (this._lastTarget instanceof SVGElementInstance)) {
5954				this._lastTarget = this._lastTarget.correspondingUseElement;
5955			}
5956			addClass(this._lastTarget, 'leaflet-drag-target');
5957		}
5958
5959		this._newPos = this._startPos.add(offset);
5960		this._moving = true;
5961
5962		cancelAnimFrame(this._animRequest);
5963		this._lastEvent = e;
5964		this._animRequest = requestAnimFrame(this._updatePosition, this, true);
5965	},
5966
5967	_updatePosition: function () {
5968		var e = {originalEvent: this._lastEvent};
5969
5970		// @event predrag: Event
5971		// Fired continuously during dragging *before* each corresponding
5972		// update of the element's position.
5973		this.fire('predrag', e);
5974		setPosition(this._element, this._newPos);
5975
5976		// @event drag: Event
5977		// Fired continuously during dragging.
5978		this.fire('drag', e);
5979	},
5980
5981	_onUp: function (e) {
5982		// Ignore simulated events, since we handle both touch and
5983		// mouse explicitly; otherwise we risk getting duplicates of
5984		// touch events, see #4315.
5985		// Also ignore the event if disabled; this happens in IE11
5986		// under some circumstances, see #3666.
5987		if (e._simulated || !this._enabled) { return; }
5988		this.finishDrag();
5989	},
5990
5991	finishDrag: function () {
5992		removeClass(document.body, 'leaflet-dragging');
5993
5994		if (this._lastTarget) {
5995			removeClass(this._lastTarget, 'leaflet-drag-target');
5996			this._lastTarget = null;
5997		}
5998
5999		for (var i in MOVE) {
6000			off(document, MOVE[i], this._onMove, this);
6001			off(document, END[i], this._onUp, this);
6002		}
6003
6004		enableImageDrag();
6005		enableTextSelection();
6006
6007		if (this._moved && this._moving) {
6008			// ensure drag is not fired after dragend
6009			cancelAnimFrame(this._animRequest);
6010
6011			// @event dragend: DragEndEvent
6012			// Fired when the drag ends.
6013			this.fire('dragend', {
6014				distance: this._newPos.distanceTo(this._startPos)
6015			});
6016		}
6017
6018		this._moving = false;
6019		Draggable._dragging = false;
6020	}
6021
6022});
6023
6024/*
6025 * @namespace LineUtil
6026 *
6027 * Various utility functions for polyline points processing, used by Leaflet internally to make polylines lightning-fast.
6028 */
6029
6030// Simplify polyline with vertex reduction and Douglas-Peucker simplification.
6031// Improves rendering performance dramatically by lessening the number of points to draw.
6032
6033// @function simplify(points: Point[], tolerance: Number): Point[]
6034// Dramatically reduces the number of points in a polyline while retaining
6035// its shape and returns a new array of simplified points, using the
6036// [Douglas-Peucker algorithm](http://en.wikipedia.org/wiki/Douglas-Peucker_algorithm).
6037// Used for a huge performance boost when processing/displaying Leaflet polylines for
6038// each zoom level and also reducing visual noise. tolerance affects the amount of
6039// simplification (lesser value means higher quality but slower and with more points).
6040// Also released as a separated micro-library [Simplify.js](http://mourner.github.com/simplify-js/).
6041function simplify(points, tolerance) {
6042	if (!tolerance || !points.length) {
6043		return points.slice();
6044	}
6045
6046	var sqTolerance = tolerance * tolerance;
6047
6048	    // stage 1: vertex reduction
6049	    points = _reducePoints(points, sqTolerance);
6050
6051	    // stage 2: Douglas-Peucker simplification
6052	    points = _simplifyDP(points, sqTolerance);
6053
6054	return points;
6055}
6056
6057// @function pointToSegmentDistance(p: Point, p1: Point, p2: Point): Number
6058// Returns the distance between point `p` and segment `p1` to `p2`.
6059function pointToSegmentDistance(p, p1, p2) {
6060	return Math.sqrt(_sqClosestPointOnSegment(p, p1, p2, true));
6061}
6062
6063// @function closestPointOnSegment(p: Point, p1: Point, p2: Point): Number
6064// Returns the closest point from a point `p` on a segment `p1` to `p2`.
6065function closestPointOnSegment(p, p1, p2) {
6066	return _sqClosestPointOnSegment(p, p1, p2);
6067}
6068
6069// Douglas-Peucker simplification, see http://en.wikipedia.org/wiki/Douglas-Peucker_algorithm
6070function _simplifyDP(points, sqTolerance) {
6071
6072	var len = points.length,
6073	    ArrayConstructor = typeof Uint8Array !== undefined + '' ? Uint8Array : Array,
6074	    markers = new ArrayConstructor(len);
6075
6076	    markers[0] = markers[len - 1] = 1;
6077
6078	_simplifyDPStep(points, markers, sqTolerance, 0, len - 1);
6079
6080	var i,
6081	    newPoints = [];
6082
6083	for (i = 0; i < len; i++) {
6084		if (markers[i]) {
6085			newPoints.push(points[i]);
6086		}
6087	}
6088
6089	return newPoints;
6090}
6091
6092function _simplifyDPStep(points, markers, sqTolerance, first, last) {
6093
6094	var maxSqDist = 0,
6095	index, i, sqDist;
6096
6097	for (i = first + 1; i <= last - 1; i++) {
6098		sqDist = _sqClosestPointOnSegment(points[i], points[first], points[last], true);
6099
6100		if (sqDist > maxSqDist) {
6101			index = i;
6102			maxSqDist = sqDist;
6103		}
6104	}
6105
6106	if (maxSqDist > sqTolerance) {
6107		markers[index] = 1;
6108
6109		_simplifyDPStep(points, markers, sqTolerance, first, index);
6110		_simplifyDPStep(points, markers, sqTolerance, index, last);
6111	}
6112}
6113
6114// reduce points that are too close to each other to a single point
6115function _reducePoints(points, sqTolerance) {
6116	var reducedPoints = [points[0]];
6117
6118	for (var i = 1, prev = 0, len = points.length; i < len; i++) {
6119		if (_sqDist(points[i], points[prev]) > sqTolerance) {
6120			reducedPoints.push(points[i]);
6121			prev = i;
6122		}
6123	}
6124	if (prev < len - 1) {
6125		reducedPoints.push(points[len - 1]);
6126	}
6127	return reducedPoints;
6128}
6129
6130var _lastCode;
6131
6132// @function clipSegment(a: Point, b: Point, bounds: Bounds, useLastCode?: Boolean, roun
vendor: 47,477 bytes, lines 6132-7917
6132d?: Boolean): Point[]|Boolean
6133// Clips the segment a to b by rectangular bounds with the
6134// [Cohen-Sutherland algorithm](https://en.wikipedia.org/wiki/Cohen%E2%80%93Sutherland_algorithm)
6135// (modifying the segment points directly!). Used by Leaflet to only show polyline
6136// points that are on the screen or near, increasing performance.
6137function clipSegment(a, b, bounds, useLastCode, round) {
6138	var codeA = useLastCode ? _lastCode : _getBitCode(a, bounds),
6139	    codeB = _getBitCode(b, bounds),
6140
6141	    codeOut, p, newCode;
6142
6143	    // save 2nd code to avoid calculating it on the next segment
6144	    _lastCode = codeB;
6145
6146	while (true) {
6147		// if a,b is inside the clip window (trivial accept)
6148		if (!(codeA | codeB)) {
6149			return [a, b];
6150		}
6151
6152		// if a,b is outside the clip window (trivial reject)
6153		if (codeA & codeB) {
6154			return false;
6155		}
6156
6157		// other cases
6158		codeOut = codeA || codeB;
6159		p = _getEdgeIntersection(a, b, codeOut, bounds, round);
6160		newCode = _getBitCode(p, bounds);
6161
6162		if (codeOut === codeA) {
6163			a = p;
6164			codeA = newCode;
6165		} else {
6166			b = p;
6167			codeB = newCode;
6168		}
6169	}
6170}
6171
6172function _getEdgeIntersection(a, b, code, bounds, round) {
6173	var dx = b.x - a.x,
6174	    dy = b.y - a.y,
6175	    min = bounds.min,
6176	    max = bounds.max,
6177	    x, y;
6178
6179	if (code & 8) { // top
6180		x = a.x + dx * (max.y - a.y) / dy;
6181		y = max.y;
6182
6183	} else if (code & 4) { // bottom
6184		x = a.x + dx * (min.y - a.y) / dy;
6185		y = min.y;
6186
6187	} else if (code & 2) { // right
6188		x = max.x;
6189		y = a.y + dy * (max.x - a.x) / dx;
6190
6191	} else if (code & 1) { // left
6192		x = min.x;
6193		y = a.y + dy * (min.x - a.x) / dx;
6194	}
6195
6196	return new Point(x, y, round);
6197}
6198
6199function _getBitCode(p, bounds) {
6200	var code = 0;
6201
6202	if (p.x < bounds.min.x) { // left
6203		code |= 1;
6204	} else if (p.x > bounds.max.x) { // right
6205		code |= 2;
6206	}
6207
6208	if (p.y < bounds.min.y) { // bottom
6209		code |= 4;
6210	} else if (p.y > bounds.max.y) { // top
6211		code |= 8;
6212	}
6213
6214	return code;
6215}
6216
6217// square distance (to avoid unnecessary Math.sqrt calls)
6218function _sqDist(p1, p2) {
6219	var dx = p2.x - p1.x,
6220	    dy = p2.y - p1.y;
6221	return dx * dx + dy * dy;
6222}
6223
6224// return closest point on segment or distance to that point
6225function _sqClosestPointOnSegment(p, p1, p2, sqDist) {
6226	var x = p1.x,
6227	    y = p1.y,
6228	    dx = p2.x - x,
6229	    dy = p2.y - y,
6230	    dot = dx * dx + dy * dy,
6231	    t;
6232
6233	if (dot > 0) {
6234		t = ((p.x - x) * dx + (p.y - y) * dy) / dot;
6235
6236		if (t > 1) {
6237			x = p2.x;
6238			y = p2.y;
6239		} else if (t > 0) {
6240			x += dx * t;
6241			y += dy * t;
6242		}
6243	}
6244
6245	dx = p.x - x;
6246	dy = p.y - y;
6247
6248	return sqDist ? dx * dx + dy * dy : new Point(x, y);
6249}
6250
6251
6252// @function isFlat(latlngs: LatLng[]): Boolean
6253// Returns true if `latlngs` is a flat array, false is nested.
6254function isFlat(latlngs) {
6255	return !isArray(latlngs[0]) || (typeof latlngs[0][0] !== 'object' && typeof latlngs[0][0] !== 'undefined');
6256}
6257
6258function _flat(latlngs) {
6259	console.warn('Deprecated use of _flat, please use L.LineUtil.isFlat instead.');
6260	return isFlat(latlngs);
6261}
6262
6263
6264var LineUtil = (Object.freeze || Object)({
6265	simplify: simplify,
6266	pointToSegmentDistance: pointToSegmentDistance,
6267	closestPointOnSegment: closestPointOnSegment,
6268	clipSegment: clipSegment,
6269	_getEdgeIntersection: _getEdgeIntersection,
6270	_getBitCode: _getBitCode,
6271	_sqClosestPointOnSegment: _sqClosestPointOnSegment,
6272	isFlat: isFlat,
6273	_flat: _flat
6274});
6275
6276/*
6277 * @namespace PolyUtil
6278 * Various utility functions for polygon geometries.
6279 */
6280
6281/* @function clipPolygon(points: Point[], bounds: Bounds, round?: Boolean): Point[]
6282 * 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)).
6283 * Used by Leaflet to only show polygon points that are on the screen or near, increasing
6284 * performance. Note that polygon points needs different algorithm for clipping
6285 * than polyline, so there's a separate method for it.
6286 */
6287function clipPolygon(points, bounds, round) {
6288	var clippedPoints,
6289	    edges = [1, 4, 2, 8],
6290	    i, j, k,
6291	    a, b,
6292	    len, edge, p;
6293
6294	for (i = 0, len = points.length; i < len; i++) {
6295		points[i]._code = _getBitCode(points[i], bounds);
6296	}
6297
6298	// for each edge (left, bottom, right, top)
6299	for (k = 0; k < 4; k++) {
6300		edge = edges[k];
6301		clippedPoints = [];
6302
6303		for (i = 0, len = points.length, j = len - 1; i < len; j = i++) {
6304			a = points[i];
6305			b = points[j];
6306
6307			// if a is inside the clip window
6308			if (!(a._code & edge)) {
6309				// if b is outside the clip window (a->b goes out of screen)
6310				if (b._code & edge) {
6311					p = _getEdgeIntersection(b, a, edge, bounds, round);
6312					p._code = _getBitCode(p, bounds);
6313					clippedPoints.push(p);
6314				}
6315				clippedPoints.push(a);
6316
6317			// else if b is inside the clip window (a->b enters the screen)
6318			} else if (!(b._code & edge)) {
6319				p = _getEdgeIntersection(b, a, edge, bounds, round);
6320				p._code = _getBitCode(p, bounds);
6321				clippedPoints.push(p);
6322			}
6323		}
6324		points = clippedPoints;
6325	}
6326
6327	return points;
6328}
6329
6330
6331var PolyUtil = (Object.freeze || Object)({
6332	clipPolygon: clipPolygon
6333});
6334
6335/*
6336 * @namespace Projection
6337 * @section
6338 * Leaflet comes with a set of already defined Projections out of the box:
6339 *
6340 * @projection L.Projection.LonLat
6341 *
6342 * Equirectangular, or Plate Carree projection — the most simple projection,
6343 * mostly used by GIS enthusiasts. Directly maps `x` as longitude, and `y` as
6344 * latitude. Also suitable for flat worlds, e.g. game maps. Used by the
6345 * `EPSG:4326` and `Simple` CRS.
6346 */
6347
6348var LonLat = {
6349	project: function (latlng) {
6350		return new Point(latlng.lng, latlng.lat);
6351	},
6352
6353	unproject: function (point) {
6354		return new LatLng(point.y, point.x);
6355	},
6356
6357	bounds: new Bounds([-180, -90], [180, 90])
6358};
6359
6360/*
6361 * @namespace Projection
6362 * @projection L.Projection.Mercator
6363 *
6364 * Elliptical Mercator projection — more complex than Spherical Mercator. Assumes that Earth is an ellipsoid. Used by the EPSG:3395 CRS.
6365 */
6366
6367var Mercator = {
6368	R: 6378137,
6369	R_MINOR: 6356752.314245179,
6370
6371	bounds: new Bounds([-20037508.34279, -15496570.73972], [20037508.34279, 18764656.23138]),
6372
6373	project: function (latlng) {
6374		var d = Math.PI / 180,
6375		    r = this.R,
6376		    y = latlng.lat * d,
6377		    tmp = this.R_MINOR / r,
6378		    e = Math.sqrt(1 - tmp * tmp),
6379		    con = e * Math.sin(y);
6380
6381		var ts = Math.tan(Math.PI / 4 - y / 2) / Math.pow((1 - con) / (1 + con), e / 2);
6382		y = -r * Math.log(Math.max(ts, 1E-10));
6383
6384		return new Point(latlng.lng * d * r, y);
6385	},
6386
6387	unproject: function (point) {
6388		var d = 180 / Math.PI,
6389		    r = this.R,
6390		    tmp = this.R_MINOR / r,
6391		    e = Math.sqrt(1 - tmp * tmp),
6392		    ts = Math.exp(-point.y / r),
6393		    phi = Math.PI / 2 - 2 * Math.atan(ts);
6394
6395		for (var i = 0, dphi = 0.1, con; i < 15 && Math.abs(dphi) > 1e-7; i++) {
6396			con = e * Math.sin(phi);
6397			con = Math.pow((1 - con) / (1 + con), e / 2);
6398			dphi = Math.PI / 2 - 2 * Math.atan(ts * con) - phi;
6399			phi += dphi;
6400		}
6401
6402		return new LatLng(phi * d, point.x * d / r);
6403	}
6404};
6405
6406/*
6407 * @class Projection
6408
6409 * An object with methods for projecting geographical coordinates of the world onto
6410 * a flat surface (and back). See [Map projection](http://en.wikipedia.org/wiki/Map_projection).
6411
6412 * @property bounds: Bounds
6413 * The bounds (specified in CRS units) where the projection is valid
6414
6415 * @method project(latlng: LatLng): Point
6416 * Projects geographical coordinates into a 2D point.
6417 * Only accepts actual `L.LatLng` instances, not arrays.
6418
6419 * @method unproject(point: Point): LatLng
6420 * The inverse of `project`. Projects a 2D point into a geographical location.
6421 * Only accepts actual `L.Point` instances, not arrays.
6422
6423 * Note that the projection instances do not inherit from Leafet's `Class` object,
6424 * and can't be instantiated. Also, new classes can't inherit from them,
6425 * and methods can't be added to them with the `include` function.
6426
6427 */
6428
6429
6430
6431
6432var index = (Object.freeze || Object)({
6433	LonLat: LonLat,
6434	Mercator: Mercator,
6435	SphericalMercator: SphericalMercator
6436});
6437
6438/*
6439 * @namespace CRS
6440 * @crs L.CRS.EPSG3395
6441 *
6442 * Rarely used by some commercial tile providers. Uses Elliptical Mercator projection.
6443 */
6444var EPSG3395 = extend({}, Earth, {
6445	code: 'EPSG:3395',
6446	projection: Mercator,
6447
6448	transformation: (function () {
6449		var scale = 0.5 / (Math.PI * Mercator.R);
6450		return toTransformation(scale, 0.5, -scale, 0.5);
6451	}())
6452});
6453
6454/*
6455 * @namespace CRS
6456 * @crs L.CRS.EPSG4326
6457 *
6458 * A common CRS among GIS enthusiasts. Uses simple Equirectangular projection.
6459 *
6460 * Leaflet 1.0.x complies with the [TMS coordinate scheme for EPSG:4326](https://wiki.osgeo.org/wiki/Tile_Map_Service_Specification#global-geodetic),
6461 * which is a breaking change from 0.7.x behaviour.  If you are using a `TileLayer`
6462 * with this CRS, ensure that there are two 256x256 pixel tiles covering the
6463 * whole earth at zoom level zero, and that the tile coordinate origin is (-180,+90),
6464 * or (-180,-90) for `TileLayer`s with [the `tms` option](#tilelayer-tms) set.
6465 */
6466
6467var EPSG4326 = extend({}, Earth, {
6468	code: 'EPSG:4326',
6469	projection: LonLat,
6470	transformation: toTransformation(1 / 180, 1, -1 / 180, 0.5)
6471});
6472
6473/*
6474 * @namespace CRS
6475 * @crs L.CRS.Simple
6476 *
6477 * A simple CRS that maps longitude and latitude into `x` and `y` directly.
6478 * May be used for maps of flat surfaces (e.g. game maps). Note that the `y`
6479 * axis should still be inverted (going from bottom to top). `distance()` returns
6480 * simple euclidean distance.
6481 */
6482
6483var Simple = extend({}, CRS, {
6484	projection: LonLat,
6485	transformation: toTransformation(1, 0, -1, 0),
6486
6487	scale: function (zoom) {
6488		return Math.pow(2, zoom);
6489	},
6490
6491	zoom: function (scale) {
6492		return Math.log(scale) / Math.LN2;
6493	},
6494
6495	distance: function (latlng1, latlng2) {
6496		var dx = latlng2.lng - latlng1.lng,
6497		    dy = latlng2.lat - latlng1.lat;
6498
6499		return Math.sqrt(dx * dx + dy * dy);
6500	},
6501
6502	infinite: true
6503});
6504
6505CRS.Earth = Earth;
6506CRS.EPSG3395 = EPSG3395;
6507CRS.EPSG3857 = EPSG3857;
6508CRS.EPSG900913 = EPSG900913;
6509CRS.EPSG4326 = EPSG4326;
6510CRS.Simple = Simple;
6511
6512/*
6513 * @class Layer
6514 * @inherits Evented
6515 * @aka L.Layer
6516 * @aka ILayer
6517 *
6518 * A set of methods from the Layer base class that all Leaflet layers use.
6519 * Inherits all methods, options and events from `L.Evented`.
6520 *
6521 * @example
6522 *
6523 * ```js
6524 * var layer = L.marker(latlng).addTo(map);
6525 * layer.addTo(map);
6526 * layer.remove();
6527 * ```
6528 *
6529 * @event add: Event
6530 * Fired after the layer is added to a map
6531 *
6532 * @event remove: Event
6533 * Fired after the layer is removed from a map
6534 */
6535
6536
6537var Layer = Evented.extend({
6538
6539	// Classes extending `L.Layer` will inherit the following options:
6540	options: {
6541		// @option pane: String = 'overlayPane'
6542		// 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.
6543		pane: 'overlayPane',
6544
6545		// @option attribution: String = null
6546		// 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.
6547		attribution: null,
6548
6549		bubblingMouseEvents: true
6550	},
6551
6552	/* @section
6553	 * Classes extending `L.Layer` will inherit the following methods:
6554	 *
6555	 * @method addTo(map: Map|LayerGroup): this
6556	 * Adds the layer to the given map or layer group.
6557	 */
6558	addTo: function (map) {
6559		map.addLayer(this);
6560		return this;
6561	},
6562
6563	// @method remove: this
6564	// Removes the layer from the map it is currently active on.
6565	remove: function () {
6566		return this.removeFrom(this._map || this._mapToAdd);
6567	},
6568
6569	// @method removeFrom(map: Map): this
6570	// Removes the layer from the given map
6571	removeFrom: function (obj) {
6572		if (obj) {
6573			obj.removeLayer(this);
6574		}
6575		return this;
6576	},
6577
6578	// @method getPane(name? : String): HTMLElement
6579	// Returns the `HTMLElement` representing the named pane on the map. If `name` is omitted, returns the pane for this layer.
6580	getPane: function (name) {
6581		return this._map.getPane(name ? (this.options[name] || name) : this.options.pane);
6582	},
6583
6584	addInteractiveTarget: function (targetEl) {
6585		this._map._targets[stamp(targetEl)] = this;
6586		return this;
6587	},
6588
6589	removeInteractiveTarget: function (targetEl) {
6590		delete this._map._targets[stamp(targetEl)];
6591		return this;
6592	},
6593
6594	// @method getAttribution: String
6595	// Used by the `attribution control`, returns the [attribution option](#gridlayer-attribution).
6596	getAttribution: function () {
6597		return this.options.attribution;
6598	},
6599
6600	_layerAdd: function (e) {
6601		var map = e.target;
6602
6603		// check in case layer gets added and then removed before the map is ready
6604		if (!map.hasLayer(this)) { return; }
6605
6606		this._map = map;
6607		this._zoomAnimated = map._zoomAnimated;
6608
6609		if (this.getEvents) {
6610			var events = this.getEvents();
6611			map.on(events, this);
6612			this.once('remove', function () {
6613				map.off(events, this);
6614			}, this);
6615		}
6616
6617		this.onAdd(map);
6618
6619		if (this.getAttribution && map.attributionControl) {
6620			map.attributionControl.addAttribution(this.getAttribution());
6621		}
6622
6623		this.fire('add');
6624		map.fire('layeradd', {layer: this});
6625	}
6626});
6627
6628/* @section Extension methods
6629 * @uninheritable
6630 *
6631 * Every layer should extend from `L.Layer` and (re-)implement the following methods.
6632 *
6633 * @method onAdd(map: Map): this
6634 * 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).
6635 *
6636 * @method onRemove(map: Map): this
6637 * 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).
6638 *
6639 * @method getEvents(): Object
6640 * 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.
6641 *
6642 * @method getAttribution(): String
6643 * This optional method should return a string containing HTML to be shown on the `Attribution control` whenever the layer is visible.
6644 *
6645 * @method beforeAdd(map: Map): this
6646 * 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.
6647 */
6648
6649
6650/* @namespace Map
6651 * @section Layer events
6652 *
6653 * @event layeradd: LayerEvent
6654 * Fired when a new layer is added to the map.
6655 *
6656 * @event layerremove: LayerEvent
6657 * Fired when some layer is removed from the map
6658 *
6659 * @section Methods for Layers and Controls
6660 */
6661Map.include({
6662	// @method addLayer(layer: Layer): this
6663	// Adds the given layer to the map
6664	addLayer: function (layer) {
6665		if (!layer._layerAdd) {
6666			throw new Error('The provided object is not a Layer.');
6667		}
6668
6669		var id = stamp(layer);
6670		if (this._layers[id]) { return this; }
6671		this._layers[id] = layer;
6672
6673		layer._mapToAdd = this;
6674
6675		if (layer.beforeAdd) {
6676			layer.beforeAdd(this);
6677		}
6678
6679		this.whenReady(layer._layerAdd, layer);
6680
6681		return this;
6682	},
6683
6684	// @method removeLayer(layer: Layer): this
6685	// Removes the given layer from the map.
6686	removeLayer: function (layer) {
6687		var id = stamp(layer);
6688
6689		if (!this._layers[id]) { return this; }
6690
6691		if (this._loaded) {
6692			layer.onRemove(this);
6693		}
6694
6695		if (layer.getAttribution && this.attributionControl) {
6696			this.attributionControl.removeAttribution(layer.getAttribution());
6697		}
6698
6699		delete this._layers[id];
6700
6701		if (this._loaded) {
6702			this.fire('layerremove', {layer: layer});
6703			layer.fire('remove');
6704		}
6705
6706		layer._map = layer._mapToAdd = null;
6707
6708		return this;
6709	},
6710
6711	// @method hasLayer(layer: Layer): Boolean
6712	// Returns `true` if the given layer is currently added to the map
6713	hasLayer: function (layer) {
6714		return !!layer && (stamp(layer) in this._layers);
6715	},
6716
6717	/* @method eachLayer(fn: Function, context?: Object): this
6718	 * Iterates over the layers of the map, optionally specifying context of the iterator function.
6719	 * ```
6720	 * map.eachLayer(function(layer){
6721	 *     layer.bindPopup('Hello');
6722	 * });
6723	 * ```
6724	 */
6725	eachLayer: function (method, context) {
6726		for (var i in this._layers) {
6727			method.call(context, this._layers[i]);
6728		}
6729		return this;
6730	},
6731
6732	_addLayers: function (layers) {
6733		layers = layers ? (isArray(layers) ? layers : [layers]) : [];
6734
6735		for (var i = 0, len = layers.length; i < len; i++) {
6736			this.addLayer(layers[i]);
6737		}
6738	},
6739
6740	_addZoomLimit: function (layer) {
6741		if (isNaN(layer.options.maxZoom) || !isNaN(layer.options.minZoom)) {
6742			this._zoomBoundLayers[stamp(layer)] = layer;
6743			this._updateZoomLevels();
6744		}
6745	},
6746
6747	_removeZoomLimit: function (layer) {
6748		var id = stamp(layer);
6749
6750		if (this._zoomBoundLayers[id]) {
6751			delete this._zoomBoundLayers[id];
6752			this._updateZoomLevels();
6753		}
6754	},
6755
6756	_updateZoomLevels: function () {
6757		var minZoom = Infinity,
6758		    maxZoom = -Infinity,
6759		    oldZoomSpan = this._getZoomSpan();
6760
6761		for (var i in this._zoomBoundLayers) {
6762			var options = this._zoomBoundLayers[i].options;
6763
6764			minZoom = options.minZoom === undefined ? minZoom : Math.min(minZoom, options.minZoom);
6765			maxZoom = options.maxZoom === undefined ? maxZoom : Math.max(maxZoom, options.maxZoom);
6766		}
6767
6768		this._layersMaxZoom = maxZoom === -Infinity ? undefined : maxZoom;
6769		this._layersMinZoom = minZoom === Infinity ? undefined : minZoom;
6770
6771		// @section Map state change events
6772		// @event zoomlevelschange: Event
6773		// Fired when the number of zoomlevels on the map is changed due
6774		// to adding or removing a layer.
6775		if (oldZoomSpan !== this._getZoomSpan()) {
6776			this.fire('zoomlevelschange');
6777		}
6778
6779		if (this.options.maxZoom === undefined && this._layersMaxZoom && this.getZoom() > this._layersMaxZoom) {
6780			this.setZoom(this._layersMaxZoom);
6781		}
6782		if (this.options.minZoom === undefined && this._layersMinZoom && this.getZoom() < this._layersMinZoom) {
6783			this.setZoom(this._layersMinZoom);
6784		}
6785	}
6786});
6787
6788/*
6789 * @class LayerGroup
6790 * @aka L.LayerGroup
6791 * @inherits Layer
6792 *
6793 * Used to group several layers and handle them as one. If you add it to the map,
6794 * any layers added or removed from the group will be added/removed on the map as
6795 * well. Extends `Layer`.
6796 *
6797 * @example
6798 *
6799 * ```js
6800 * L.layerGroup([marker1, marker2])
6801 * 	.addLayer(polyline)
6802 * 	.addTo(map);
6803 * ```
6804 */
6805
6806var LayerGroup = Layer.extend({
6807
6808	initialize: function (layers, options) {
6809		setOptions(this, options);
6810
6811		this._layers = {};
6812
6813		var i, len;
6814
6815		if (layers) {
6816			for (i = 0, len = layers.length; i < len; i++) {
6817				this.addLayer(layers[i]);
6818			}
6819		}
6820	},
6821
6822	// @method addLayer(layer: Layer): this
6823	// Adds the given layer to the group.
6824	addLayer: function (layer) {
6825		var id = this.getLayerId(layer);
6826
6827		this._layers[id] = layer;
6828
6829		if (this._map) {
6830			this._map.addLayer(layer);
6831		}
6832
6833		return this;
6834	},
6835
6836	// @method removeLayer(layer: Layer): this
6837	// Removes the given layer from the group.
6838	// @alternative
6839	// @method removeLayer(id: Number): this
6840	// Removes the layer with the given internal ID from the group.
6841	removeLayer: function (layer) {
6842		var id = layer in this._layers ? layer : this.getLayerId(layer);
6843
6844		if (this._map && this._layers[id]) {
6845			this._map.removeLayer(this._layers[id]);
6846		}
6847
6848		delete this._layers[id];
6849
6850		return this;
6851	},
6852
6853	// @method hasLayer(layer: Layer): Boolean
6854	// Returns `true` if the given layer is currently added to the group.
6855	// @alternative
6856	// @method hasLayer(id: Number): Boolean
6857	// Returns `true` if the given internal ID is currently added to the group.
6858	hasLayer: function (layer) {
6859		return !!layer && (layer in this._layers || this.getLayerId(layer) in this._layers);
6860	},
6861
6862	// @method clearLayers(): this
6863	// Removes all the layers from the group.
6864	clearLayers: function () {
6865		return this.eachLayer(this.removeLayer, this);
6866	},
6867
6868	// @method invoke(methodName: String, …): this
6869	// Calls `methodName` on every layer contained in this group, passing any
6870	// additional parameters. Has no effect if the layers contained do not
6871	// implement `methodName`.
6872	invoke: function (methodName) {
6873		var args = Array.prototype.slice.call(arguments, 1),
6874		    i, layer;
6875
6876		for (i in this._layers) {
6877			layer = this._layers[i];
6878
6879			if (layer[methodName]) {
6880				layer[methodName].apply(layer, args);
6881			}
6882		}
6883
6884		return this;
6885	},
6886
6887	onAdd: function (map) {
6888		this.eachLayer(map.addLayer, map);
6889	},
6890
6891	onRemove: function (map) {
6892		this.eachLayer(map.removeLayer, map);
6893	},
6894
6895	// @method eachLayer(fn: Function, context?: Object): this
6896	// Iterates over the layers of the group, optionally specifying context of the iterator function.
6897	// ```js
6898	// group.eachLayer(function (layer) {
6899	// 	layer.bindPopup('Hello');
6900	// });
6901	// ```
6902	eachLayer: function (method, context) {
6903		for (var i in this._layers) {
6904			method.call(context, this._layers[i]);
6905		}
6906		return this;
6907	},
6908
6909	// @method getLayer(id: Number): Layer
6910	// Returns the layer with the given internal ID.
6911	getLayer: function (id) {
6912		return this._layers[id];
6913	},
6914
6915	// @method getLayers(): Layer[]
6916	// Returns an array of all the layers added to the group.
6917	getLayers: function () {
6918		var layers = [];
6919		this.eachLayer(layers.push, layers);
6920		return layers;
6921	},
6922
6923	// @method setZIndex(zIndex: Number): this
6924	// Calls `setZIndex` on every layer contained in this group, passing the z-index.
6925	setZIndex: function (zIndex) {
6926		return this.invoke('setZIndex', zIndex);
6927	},
6928
6929	// @method getLayerId(layer: Layer): Number
6930	// Returns the internal ID for a layer
6931	getLayerId: function (layer) {
6932		return stamp(layer);
6933	}
6934});
6935
6936
6937// @factory L.layerGroup(layers?: Layer[], options?: Object)
6938// Create a layer group, optionally given an initial set of layers and an `options` object.
6939var layerGroup = function (layers, options) {
6940	return new LayerGroup(layers, options);
6941};
6942
6943/*
6944 * @class FeatureGroup
6945 * @aka L.FeatureGroup
6946 * @inherits LayerGroup
6947 *
6948 * Extended `LayerGroup` that makes it easier to do the same thing to all its member layers:
6949 *  * [`bindPopup`](#layer-bindpopup) binds a popup to all of the layers at once (likewise with [`bindTooltip`](#layer-bindtooltip))
6950 *  * Events are propagated to the `FeatureGroup`, so if the group has an event
6951 * handler, it will handle events from any of the layers. This includes mouse events
6952 * and custom events.
6953 *  * Has `layeradd` and `layerremove` events
6954 *
6955 * @example
6956 *
6957 * ```js
6958 * L.featureGroup([marker1, marker2, polyline])
6959 * 	.bindPopup('Hello world!')
6960 * 	.on('click', function() { alert('Clicked on a member of the group!'); })
6961 * 	.addTo(map);
6962 * ```
6963 */
6964
6965var FeatureGroup = LayerGroup.extend({
6966
6967	addLayer: function (layer) {
6968		if (this.hasLayer(layer)) {
6969			return this;
6970		}
6971
6972		layer.addEventParent(this);
6973
6974		LayerGroup.prototype.addLayer.call(this, layer);
6975
6976		// @event layeradd: LayerEvent
6977		// Fired when a layer is added to this `FeatureGroup`
6978		return this.fire('layeradd', {layer: layer});
6979	},
6980
6981	removeLayer: function (layer) {
6982		if (!this.hasLayer(layer)) {
6983			return this;
6984		}
6985		if (layer in this._layers) {
6986			layer = this._layers[layer];
6987		}
6988
6989		layer.removeEventParent(this);
6990
6991		LayerGroup.prototype.removeLayer.call(this, layer);
6992
6993		// @event layerremove: LayerEvent
6994		// Fired when a layer is removed from this `FeatureGroup`
6995		return this.fire('layerremove', {layer: layer});
6996	},
6997
6998	// @method setStyle(style: Path options): this
6999	// Sets the given path options to each layer of the group that has a `setStyle` method.
7000	setStyle: function (style) {
7001		return this.invoke('setStyle', style);
7002	},
7003
7004	// @method bringToFront(): this
7005	// Brings the layer group to the top of all other layers
7006	bringToFront: function () {
7007		return this.invoke('bringToFront');
7008	},
7009
7010	// @method bringToBack(): this
7011	// Brings the layer group to the back of all other layers
7012	bringToBack: function () {
7013		return this.invoke('bringToBack');
7014	},
7015
7016	// @method getBounds(): LatLngBounds
7017	// Returns the LatLngBounds of the Feature Group (created from bounds and coordinates of its children).
7018	getBounds: function () {
7019		var bounds = new LatLngBounds();
7020
7021		for (var id in this._layers) {
7022			var layer = this._layers[id];
7023			bounds.extend(layer.getBounds ? layer.getBounds() : layer.getLatLng());
7024		}
7025		return bounds;
7026	}
7027});
7028
7029// @factory L.featureGroup(layers: Layer[])
7030// Create a feature group, optionally given an initial set of layers.
7031var featureGroup = function (layers) {
7032	return new FeatureGroup(layers);
7033};
7034
7035/*
7036 * @class Icon
7037 * @aka L.Icon
7038 *
7039 * Represents an icon to provide when creating a marker.
7040 *
7041 * @example
7042 *
7043 * ```js
7044 * var myIcon = L.icon({
7045 *     iconUrl: 'my-icon.png',
7046 *     iconRetinaUrl: '[email protected]',
7047 *     iconSize: [38, 95],
7048 *     iconAnchor: [22, 94],
7049 *     popupAnchor: [-3, -76],
7050 *     shadowUrl: 'my-icon-shadow.png',
7051 *     shadowRetinaUrl: '[email protected]',
7052 *     shadowSize: [68, 95],
7053 *     shadowAnchor: [22, 94]
7054 * });
7055 *
7056 * L.marker([50.505, 30.57], {icon: myIcon}).addTo(map);
7057 * ```
7058 *
7059 * `L.Icon.Default` extends `L.Icon` and is the blue icon Leaflet uses for markers by default.
7060 *
7061 */
7062
7063var Icon = Class.extend({
7064
7065	/* @section
7066	 * @aka Icon options
7067	 *
7068	 * @option iconUrl: String = null
7069	 * **(required)** The URL to the icon image (absolute or relative to your script path).
7070	 *
7071	 * @option iconRetinaUrl: String = null
7072	 * The URL to a retina sized version of the icon image (absolute or relative to your
7073	 * script path). Used for Retina screen devices.
7074	 *
7075	 * @option iconSize: Point = null
7076	 * Size of the icon image in pixels.
7077	 *
7078	 * @option iconAnchor: Point = null
7079	 * The coordinates of the "tip" of the icon (relative to its top left corner). The icon
7080	 * will be aligned so that this point is at the marker's geographical location. Centered
7081	 * by default if size is specified, also can be set in CSS with negative margins.
7082	 *
7083	 * @option popupAnchor: Point = [0, 0]
7084	 * The coordinates of the point from which popups will "open", relative to the icon anchor.
7085	 *
7086	 * @option tooltipAnchor: Point = [0, 0]
7087	 * The coordinates of the point from which tooltips will "open", relative to the icon anchor.
7088	 *
7089	 * @option shadowUrl: String = null
7090	 * The URL to the icon shadow image. If not specified, no shadow image will be created.
7091	 *
7092	 * @option shadowRetinaUrl: String = null
7093	 *
7094	 * @option shadowSize: Point = null
7095	 * Size of the shadow image in pixels.
7096	 *
7097	 * @option shadowAnchor: Point = null
7098	 * The coordinates of the "tip" of the shadow (relative to its top left corner) (the same
7099	 * as iconAnchor if not specified).
7100	 *
7101	 * @option className: String = ''
7102	 * A custom class name to assign to both icon and shadow images. Empty by default.
7103	 */
7104
7105	options: {
7106		popupAnchor: [0, 0],
7107		tooltipAnchor: [0, 0]
7108	},
7109
7110	initialize: function (options) {
7111		setOptions(this, options);
7112	},
7113
7114	// @method createIcon(oldIcon?: HTMLElement): HTMLElement
7115	// Called internally when the icon has to be shown, returns a `<img>` HTML element
7116	// styled according to the options.
7117	createIcon: function (oldIcon) {
7118		return this._createIcon('icon', oldIcon);
7119	},
7120
7121	// @method createShadow(oldIcon?: HTMLElement): HTMLElement
7122	// As `createIcon`, but for the shadow beneath it.
7123	createShadow: function (oldIcon) {
7124		return this._createIcon('shadow', oldIcon);
7125	},
7126
7127	_createIcon: function (name, oldIcon) {
7128		var src = this._getIconUrl(name);
7129
7130		if (!src) {
7131			if (name === 'icon') {
7132				throw new Error('iconUrl not set in Icon options (see the docs).');
7133			}
7134			return null;
7135		}
7136
7137		var img = this._createImg(src, oldIcon && oldIcon.tagName === 'IMG' ? oldIcon : null);
7138		this._setIconStyles(img, name);
7139
7140		return img;
7141	},
7142
7143	_setIconStyles: function (img, name) {
7144		var options = this.options;
7145		var sizeOption = options[name + 'Size'];
7146
7147		if (typeof sizeOption === 'number') {
7148			sizeOption = [sizeOption, sizeOption];
7149		}
7150
7151		var size = toPoint(sizeOption),
7152		    anchor = toPoint(name === 'shadow' && options.shadowAnchor || options.iconAnchor ||
7153		            size && size.divideBy(2, true));
7154
7155		img.className = 'leaflet-marker-' + name + ' ' + (options.className || '');
7156
7157		if (anchor) {
7158			img.style.marginLeft = (-anchor.x) + 'px';
7159			img.style.marginTop  = (-anchor.y) + 'px';
7160		}
7161
7162		if (size) {
7163			img.style.width  = size.x + 'px';
7164			img.style.height = size.y + 'px';
7165		}
7166	},
7167
7168	_createImg: function (src, el) {
7169		el = el || document.createElement('img');
7170		el.src = src;
7171		return el;
7172	},
7173
7174	_getIconUrl: function (name) {
7175		return retina && this.options[name + 'RetinaUrl'] || this.options[name + 'Url'];
7176	}
7177});
7178
7179
7180// @factory L.icon(options: Icon options)
7181// Creates an icon instance with the given options.
7182function icon(options) {
7183	return new Icon(options);
7184}
7185
7186/*
7187 * @miniclass Icon.Default (Icon)
7188 * @aka L.Icon.Default
7189 * @section
7190 *
7191 * A trivial subclass of `Icon`, represents the icon to use in `Marker`s when
7192 * no icon is specified. Points to the blue marker image distributed with Leaflet
7193 * releases.
7194 *
7195 * In order to customize the default icon, just change the properties of `L.Icon.Default.prototype.options`
7196 * (which is a set of `Icon options`).
7197 *
7198 * If you want to _completely_ replace the default icon, override the
7199 * `L.Marker.prototype.options.icon` with your own icon instead.
7200 */
7201
7202var IconDefault = Icon.extend({
7203
7204	options: {
7205		iconUrl:       'marker-icon.png',
7206		iconRetinaUrl: 'marker-icon-2x.png',
7207		shadowUrl:     'marker-shadow.png',
7208		iconSize:    [25, 41],
7209		iconAnchor:  [12, 41],
7210		popupAnchor: [1, -34],
7211		tooltipAnchor: [16, -28],
7212		shadowSize:  [41, 41]
7213	},
7214
7215	_getIconUrl: function (name) {
7216		if (!IconDefault.imagePath) {	// Deprecated, backwards-compatibility only
7217			IconDefault.imagePath = this._detectIconPath();
7218		}
7219
7220		// @option imagePath: String
7221		// `Icon.Default` will try to auto-detect the location of the
7222		// blue icon images. If you are placing these images in a non-standard
7223		// way, set this option to point to the right path.
7224		return (this.options.imagePath || IconDefault.imagePath) + Icon.prototype._getIconUrl.call(this, name);
7225	},
7226
7227	_detectIconPath: function () {
7228		var el = create$1('div',  'leaflet-default-icon-path', document.body);
7229		var path = getStyle(el, 'background-image') ||
7230		           getStyle(el, 'backgroundImage');	// IE8
7231
7232		document.body.removeChild(el);
7233
7234		if (path === null || path.indexOf('url') !== 0) {
7235			path = '';
7236		} else {
7237			path = path.replace(/^url\(["']?/, '').replace(/marker-icon\.png["']?\)$/, '');
7238		}
7239
7240		return path;
7241	}
7242});
7243
7244/*
7245 * L.Handler.MarkerDrag is used internally by L.Marker to make the markers draggable.
7246 */
7247
7248
7249/* @namespace Marker
7250 * @section Interaction handlers
7251 *
7252 * 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:
7253 *
7254 * ```js
7255 * marker.dragging.disable();
7256 * ```
7257 *
7258 * @property dragging: Handler
7259 * Marker dragging handler (by both mouse and touch). Only valid when the marker is on the map (Otherwise set [`marker.options.draggable`](#marker-draggable)).
7260 */
7261
7262var MarkerDrag = Handler.extend({
7263	initialize: function (marker) {
7264		this._marker = marker;
7265	},
7266
7267	addHooks: function () {
7268		var icon = this._marker._icon;
7269
7270		if (!this._draggable) {
7271			this._draggable = new Draggable(icon, icon, true);
7272		}
7273
7274		this._draggable.on({
7275			dragstart: this._onDragStart,
7276			predrag: this._onPreDrag,
7277			drag: this._onDrag,
7278			dragend: this._onDragEnd
7279		}, this).enable();
7280
7281		addClass(icon, 'leaflet-marker-draggable');
7282	},
7283
7284	removeHooks: function () {
7285		this._draggable.off({
7286			dragstart: this._onDragStart,
7287			predrag: this._onPreDrag,
7288			drag: this._onDrag,
7289			dragend: this._onDragEnd
7290		}, this).disable();
7291
7292		if (this._marker._icon) {
7293			removeClass(this._marker._icon, 'leaflet-marker-draggable');
7294		}
7295	},
7296
7297	moved: function () {
7298		return this._draggable && this._draggable._moved;
7299	},
7300
7301	_adjustPan: function (e) {
7302		var marker = this._marker,
7303		    map = marker._map,
7304		    speed = this._marker.options.autoPanSpeed,
7305		    padding = this._marker.options.autoPanPadding,
7306		    iconPos = getPosition(marker._icon),
7307		    bounds = map.getPixelBounds(),
7308		    origin = map.getPixelOrigin();
7309
7310		var panBounds = toBounds(
7311			bounds.min._subtract(origin).add(padding),
7312			bounds.max._subtract(origin).subtract(padding)
7313		);
7314
7315		if (!panBounds.contains(iconPos)) {
7316			// Compute incremental movement
7317			var movement = toPoint(
7318				(Math.max(panBounds.max.x, iconPos.x) - panBounds.max.x) / (bounds.max.x - panBounds.max.x) -
7319				(Math.min(panBounds.min.x, iconPos.x) - panBounds.min.x) / (bounds.min.x - panBounds.min.x),
7320
7321				(Math.max(panBounds.max.y, iconPos.y) - panBounds.max.y) / (bounds.max.y - panBounds.max.y) -
7322				(Math.min(panBounds.min.y, iconPos.y) - panBounds.min.y) / (bounds.min.y - panBounds.min.y)
7323			).multiplyBy(speed);
7324
7325			map.panBy(movement, {animate: false});
7326
7327			this._draggable._newPos._add(movement);
7328			this._draggable._startPos._add(movement);
7329
7330			setPosition(marker._icon, this._draggable._newPos);
7331			this._onDrag(e);
7332
7333			this._panRequest = requestAnimFrame(this._adjustPan.bind(this, e));
7334		}
7335	},
7336
7337	_onDragStart: function () {
7338		// @section Dragging events
7339		// @event dragstart: Event
7340		// Fired when the user starts dragging the marker.
7341
7342		// @event movestart: Event
7343		// Fired when the marker starts moving (because of dragging).
7344
7345		this._oldLatLng = this._marker.getLatLng();
7346		this._marker
7347		    .closePopup()
7348		    .fire('movestart')
7349		    .fire('dragstart');
7350	},
7351
7352	_onPreDrag: function (e) {
7353		if (this._marker.options.autoPan) {
7354			cancelAnimFrame(this._panRequest);
7355			this._panRequest = requestAnimFrame(this._adjustPan.bind(this, e));
7356		}
7357	},
7358
7359	_onDrag: function (e) {
7360		var marker = this._marker,
7361		    shadow = marker._shadow,
7362		    iconPos = getPosition(marker._icon),
7363		    latlng = marker._map.layerPointToLatLng(iconPos);
7364
7365		// update shadow position
7366		if (shadow) {
7367			setPosition(shadow, iconPos);
7368		}
7369
7370		marker._latlng = latlng;
7371		e.latlng = latlng;
7372		e.oldLatLng = this._oldLatLng;
7373
7374		// @event drag: Event
7375		// Fired repeatedly while the user drags the marker.
7376		marker
7377		    .fire('move', e)
7378		    .fire('drag', e);
7379	},
7380
7381	_onDragEnd: function (e) {
7382		// @event dragend: DragEndEvent
7383		// Fired when the user stops dragging the marker.
7384
7385		 cancelAnimFrame(this._panRequest);
7386
7387		// @event moveend: Event
7388		// Fired when the marker stops moving (because of dragging).
7389		delete this._oldLatLng;
7390		this._marker
7391		    .fire('moveend')
7392		    .fire('dragend', e);
7393	}
7394});
7395
7396/*
7397 * @class Marker
7398 * @inherits Interactive layer
7399 * @aka L.Marker
7400 * L.Marker is used to display clickable/draggable icons on the map. Extends `Layer`.
7401 *
7402 * @example
7403 *
7404 * ```js
7405 * L.marker([50.5, 30.5]).addTo(map);
7406 * ```
7407 */
7408
7409var Marker = Layer.extend({
7410
7411	// @section
7412	// @aka Marker options
7413	options: {
7414		// @option icon: Icon = *
7415		// Icon instance to use for rendering the marker.
7416		// See [Icon documentation](#L.Icon) for details on how to customize the marker icon.
7417		// If not specified, a common instance of `L.Icon.Default` is used.
7418		icon: new IconDefault(),
7419
7420		// Option inherited from "Interactive layer" abstract class
7421		interactive: true,
7422
7423		// @option keyboard: Boolean = true
7424		// Whether the marker can be tabbed to with a keyboard and clicked by pressing enter.
7425		keyboard: true,
7426
7427		// @option title: String = ''
7428		// Text for the browser tooltip that appear on marker hover (no tooltip by default).
7429		title: '',
7430
7431		// @option alt: String = ''
7432		// Text for the `alt` attribute of the icon image (useful for accessibility).
7433		alt: '',
7434
7435		// @option zIndexOffset: Number = 0
7436		// 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).
7437		zIndexOffset: 0,
7438
7439		// @option opacity: Number = 1.0
7440		// The opacity of the marker.
7441		opacity: 1,
7442
7443		// @option riseOnHover: Boolean = false
7444		// If `true`, the marker will get on top of others when you hover the mouse over it.
7445		riseOnHover: false,
7446
7447		// @option riseOffset: Number = 250
7448		// The z-index offset used for the `riseOnHover` feature.
7449		riseOffset: 250,
7450
7451		// @option pane: String = 'markerPane'
7452		// `Map pane` where the markers icon will be added.
7453		pane: 'markerPane',
7454
7455		// @option pane: String = 'shadowPane'
7456		// `Map pane` where the markers shadow will be added.
7457		shadowPane: 'shadowPane',
7458
7459		// @option bubblingMouseEvents: Boolean = false
7460		// When `true`, a mouse event on this marker will trigger the same event on the map
7461		// (unless [`L.DomEvent.stopPropagation`](#domevent-stoppropagation) is used).
7462		bubblingMouseEvents: false,
7463
7464		// @section Draggable marker options
7465		// @option draggable: Boolean = false
7466		// Whether the marker is draggable with mouse/touch or not.
7467		draggable: false,
7468
7469		// @option autoPan: Boolean = false
7470		// Whether to pan the map when dragging this marker near its edge or not.
7471		autoPan: false,
7472
7473		// @option autoPanPadding: Point = Point(50, 50)
7474		// Distance (in pixels to the left/right and to the top/bottom) of the
7475		// map edge to start panning the map.
7476		autoPanPadding: [50, 50],
7477
7478		// @option autoPanSpeed: Number = 10
7479		// Number of pixels the map should pan by.
7480		autoPanSpeed: 10
7481	},
7482
7483	/* @section
7484	 *
7485	 * In addition to [shared layer methods](#Layer) like `addTo()` and `remove()` and [popup methods](#Popup) like bindPopup() you can also use the following methods:
7486	 */
7487
7488	initialize: function (latlng, options) {
7489		setOptions(this, options);
7490		this._latlng = toLatLng(latlng);
7491	},
7492
7493	onAdd: function (map) {
7494		this._zoomAnimated = this._zoomAnimated && map.options.markerZoomAnimation;
7495
7496		if (this._zoomAnimated) {
7497			map.on('zoomanim', this._animateZoom, this);
7498		}
7499
7500		this._initIcon();
7501		this.update();
7502	},
7503
7504	onRemove: function (map) {
7505		if (this.dragging && this.dragging.enabled()) {
7506			this.options.draggable = true;
7507			this.dragging.removeHooks();
7508		}
7509		delete this.dragging;
7510
7511		if (this._zoomAnimated) {
7512			map.off('zoomanim', this._animateZoom, this);
7513		}
7514
7515		this._removeIcon();
7516		this._removeShadow();
7517	},
7518
7519	getEvents: function () {
7520		return {
7521			zoom: this.update,
7522			viewreset: this.update
7523		};
7524	},
7525
7526	// @method getLatLng: LatLng
7527	// Returns the current geographical position of the marker.
7528	getLatLng: function () {
7529		return this._latlng;
7530	},
7531
7532	// @method setLatLng(latlng: LatLng): this
7533	// Changes the marker position to the given point.
7534	setLatLng: function (latlng) {
7535		var oldLatLng = this._latlng;
7536		this._latlng = toLatLng(latlng);
7537		this.update();
7538
7539		// @event move: Event
7540		// 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`.
7541		return this.fire('move', {oldLatLng: oldLatLng, latlng: this._latlng});
7542	},
7543
7544	// @method setZIndexOffset(offset: Number): this
7545	// Changes the [zIndex offset](#marker-zindexoffset) of the marker.
7546	setZIndexOffset: function (offset) {
7547		this.options.zIndexOffset = offset;
7548		return this.update();
7549	},
7550
7551	// @method getIcon: Icon
7552	// Returns the current icon used by the marker
7553	getIcon: function () {
7554		return this.options.icon;
7555	},
7556
7557	// @method setIcon(icon: Icon): this
7558	// Changes the marker icon.
7559	setIcon: function (icon) {
7560
7561		this.options.icon = icon;
7562
7563		if (this._map) {
7564			this._initIcon();
7565			this.update();
7566		}
7567
7568		if (this._popup) {
7569			this.bindPopup(this._popup, this._popup.options);
7570		}
7571
7572		return this;
7573	},
7574
7575	getElement: function () {
7576		return this._icon;
7577	},
7578
7579	update: function () {
7580
7581		if (this._icon && this._map) {
7582			var pos = this._map.latLngToLayerPoint(this._latlng).round();
7583			this._setPos(pos);
7584		}
7585
7586		return this;
7587	},
7588
7589	_initIcon: function () {
7590		var options = this.options,
7591		    classToAdd = 'leaflet-zoom-' + (this._zoomAnimated ? 'animated' : 'hide');
7592
7593		var icon = options.icon.createIcon(this._icon),
7594		    addIcon = false;
7595
7596		// if we're not reusing the icon, remove the old one and init new one
7597		if (icon !== this._icon) {
7598			if (this._icon) {
7599				this._removeIcon();
7600			}
7601			addIcon = true;
7602
7603			if (options.title) {
7604				icon.title = options.title;
7605			}
7606
7607			if (icon.tagName === 'IMG') {
7608				icon.alt = options.alt || '';
7609			}
7610		}
7611
7612		addClass(icon, classToAdd);
7613
7614		if (options.keyboard) {
7615			icon.tabIndex = '0';
7616		}
7617
7618		this._icon = icon;
7619
7620		if (options.riseOnHover) {
7621			this.on({
7622				mouseover: this._bringToFront,
7623				mouseout: this._resetZIndex
7624			});
7625		}
7626
7627		var newShadow = options.icon.createShadow(this._shadow),
7628		    addShadow = false;
7629
7630		if (newShadow !== this._shadow) {
7631			this._removeShadow();
7632			addShadow = true;
7633		}
7634
7635		if (newShadow) {
7636			addClass(newShadow, classToAdd);
7637			newShadow.alt = '';
7638		}
7639		this._shadow = newShadow;
7640
7641
7642		if (options.opacity < 1) {
7643			this._updateOpacity();
7644		}
7645
7646
7647		if (addIcon) {
7648			this.getPane().appendChild(this._icon);
7649		}
7650		this._initInteraction();
7651		if (newShadow && addShadow) {
7652			this.getPane(options.shadowPane).appendChild(this._shadow);
7653		}
7654	},
7655
7656	_removeIcon: function () {
7657		if (this.options.riseOnHover) {
7658			this.off({
7659				mouseover: this._bringToFront,
7660				mouseout: this._resetZIndex
7661			});
7662		}
7663
7664		remove(this._icon);
7665		this.removeInteractiveTarget(this._icon);
7666
7667		this._icon = null;
7668	},
7669
7670	_removeShadow: function () {
7671		if (this._shadow) {
7672			remove(this._shadow);
7673		}
7674		this._shadow = null;
7675	},
7676
7677	_setPos: function (pos) {
7678
7679		if (this._icon) {
7680			setPosition(this._icon, pos);
7681		}
7682
7683		if (this._shadow) {
7684			setPosition(this._shadow, pos);
7685		}
7686
7687		this._zIndex = pos.y + this.options.zIndexOffset;
7688
7689		this._resetZIndex();
7690	},
7691
7692	_updateZIndex: function (offset) {
7693		if (this._icon) {
7694			this._icon.style.zIndex = this._zIndex + offset;
7695		}
7696	},
7697
7698	_animateZoom: function (opt) {
7699		var pos = this._map._latLngToNewLayerPoint(this._latlng, opt.zoom, opt.center).round();
7700
7701		this._setPos(pos);
7702	},
7703
7704	_initInteraction: function () {
7705
7706		if (!this.options.interactive) { return; }
7707
7708		addClass(this._icon, 'leaflet-interactive');
7709
7710		this.addInteractiveTarget(this._icon);
7711
7712		if (MarkerDrag) {
7713			var draggable = this.options.draggable;
7714			if (this.dragging) {
7715				draggable = this.dragging.enabled();
7716				this.dragging.disable();
7717			}
7718
7719			this.dragging = new MarkerDrag(this);
7720
7721			if (draggable) {
7722				this.dragging.enable();
7723			}
7724		}
7725	},
7726
7727	// @method setOpacity(opacity: Number): this
7728	// Changes the opacity of the marker.
7729	setOpacity: function (opacity) {
7730		this.options.opacity = opacity;
7731		if (this._map) {
7732			this._updateOpacity();
7733		}
7734
7735		return this;
7736	},
7737
7738	_updateOpacity: function () {
7739		var opacity = this.options.opacity;
7740
7741		if (this._icon) {
7742			setOpacity(this._icon, opacity);
7743		}
7744
7745		if (this._shadow) {
7746			setOpacity(this._shadow, opacity);
7747		}
7748	},
7749
7750	_bringToFront: function () {
7751		this._updateZIndex(this.options.riseOffset);
7752	},
7753
7754	_resetZIndex: function () {
7755		this._updateZIndex(0);
7756	},
7757
7758	_getPopupAnchor: function () {
7759		return this.options.icon.options.popupAnchor;
7760	},
7761
7762	_getTooltipAnchor: function () {
7763		return this.options.icon.options.tooltipAnchor;
7764	}
7765});
7766
7767
7768// factory L.marker(latlng: LatLng, options? : Marker options)
7769
7770// @factory L.marker(latlng: LatLng, options? : Marker options)
7771// Instantiates a Marker object given a geographical point and optionally an options object.
7772function marker(latlng, options) {
7773	return new Marker(latlng, options);
7774}
7775
7776/*
7777 * @class Path
7778 * @aka L.Path
7779 * @inherits Interactive layer
7780 *
7781 * An abstract class that contains options and constants shared between vector
7782 * overlays (Polygon, Polyline, Circle). Do not use it directly. Extends `Layer`.
7783 */
7784
7785var Path = Layer.extend({
7786
7787	// @section
7788	// @aka Path options
7789	options: {
7790		// @option stroke: Boolean = true
7791		// Whether to draw stroke along the path. Set it to `false` to disable borders on polygons or circles.
7792		stroke: true,
7793
7794		// @option color: String = '#3388ff'
7795		// Stroke color
7796		color: '#3388ff',
7797
7798		// @option weight: Number = 3
7799		// Stroke width in pixels
7800		weight: 3,
7801
7802		// @option opacity: Number = 1.0
7803		// Stroke opacity
7804		opacity: 1,
7805
7806		// @option lineCap: String= 'round'
7807		// A string that defines [shape to be used at the end](https://developer.mozilla.org/docs/Web/SVG/Attribute/stroke-linecap) of the stroke.
7808		lineCap: 'round',
7809
7810		// @option lineJoin: String = 'round'
7811		// A string that defines [shape to be used at the corners](https://developer.mozilla.org/docs/Web/SVG/Attribute/stroke-linejoin) of the stroke.
7812		lineJoin: 'round',
7813
7814		// @option dashArray: String = null
7815		// 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).
7816		dashArray: null,
7817
7818		// @option dashOffset: String = null
7819		// 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).
7820		dashOffset: null,
7821
7822		// @option fill: Boolean = depends
7823		// Whether to fill the path with color. Set it to `false` to disable filling on polygons or circles.
7824		fill: false,
7825
7826		// @option fillColor: String = *
7827		// Fill color. Defaults to the value of the [`color`](#path-color) option
7828		fillColor: null,
7829
7830		// @option fillOpacity: Number = 0.2
7831		// Fill opacity.
7832		fillOpacity: 0.2,
7833
7834		// @option fillRule: String = 'evenodd'
7835		// A string that defines [how the inside of a shape](https://developer.mozilla.org/docs/Web/SVG/Attribute/fill-rule) is determined.
7836		fillRule: 'evenodd',
7837
7838		// className: '',
7839
7840		// Option inherited from "Interactive layer" abstract class
7841		interactive: true,
7842
7843		// @option bubblingMouseEvents: Boolean = true
7844		// When `true`, a mouse event on this path will trigger the same event on the map
7845		// (unless [`L.DomEvent.stopPropagation`](#domevent-stoppropagation) is used).
7846		bubblingMouseEvents: true
7847	},
7848
7849	beforeAdd: function (map) {
7850		// Renderer is set here because we need to call renderer.getEvents
7851		// before this.getEvents.
7852		this._renderer = map.getRenderer(this);
7853	},
7854
7855	onAdd: function () {
7856		this._renderer._initPath(this);
7857		this._reset();
7858		this._renderer._addPath(this);
7859	},
7860
7861	onRemove: function () {
7862		this._renderer._removePath(this);
7863	},
7864
7865	// @method redraw(): this
7866	// Redraws the layer. Sometimes useful after you changed the coordinates that the path uses.
7867	redraw: function () {
7868		if (this._map) {
7869			this._renderer._updatePath(this);
7870		}
7871		return this;
7872	},
7873
7874	// @method setStyle(style: Path options): this
7875	// Changes the appearance of a Path based on the options in the `Path options` object.
7876	setStyle: function (style) {
7877		setOptions(this, style);
7878		if (this._renderer) {
7879			this._renderer._updateStyle(this);
7880			if (this.options.stroke && style && style.hasOwnProperty('weight')) {
7881				this._updateBounds();
7882			}
7883		}
7884		return this;
7885	},
7886
7887	// @method bringToFront(): this
7888	// Brings the layer to the top of all path layers.
7889	bringToFront: function () {
7890		if (this._renderer) {
7891			this._renderer._bringToFront(this);
7892		}
7893		return this;
7894	},
7895
7896	// @method bringToBack(): this
7897	// Brings the layer to the bottom of all path layers.
7898	bringToBack: function () {
7899		if (this._renderer) {
7900			this._renderer._bringToBack(this);
7901		}
7902		return this;
7903	},
7904
7905	getElement: function () {
7906		return this._path;
7907	},
7908
7909	_reset: function () {
7910		// defined in child classes
7911		this._project();
7912		this._update();
7913	},
7914
7915	_clickTolerance: function () {
7916		// used when doing hit detection for Canvas layers
7917		return (this.options.stroke ? this.options.weight / 2 : 0) + this._renderer.options.tolerance
vendor: 8,554 bytes, lines 7917-8239
7917;
7918	}
7919});
7920
7921/*
7922 * @class CircleMarker
7923 * @aka L.CircleMarker
7924 * @inherits Path
7925 *
7926 * A circle of a fixed size with radius specified in pixels. Extends `Path`.
7927 */
7928
7929var CircleMarker = Path.extend({
7930
7931	// @section
7932	// @aka CircleMarker options
7933	options: {
7934		fill: true,
7935
7936		// @option radius: Number = 10
7937		// Radius of the circle marker, in pixels
7938		radius: 10
7939	},
7940
7941	initialize: function (latlng, options) {
7942		setOptions(this, options);
7943		this._latlng = toLatLng(latlng);
7944		this._radius = this.options.radius;
7945	},
7946
7947	// @method setLatLng(latLng: LatLng): this
7948	// Sets the position of a circle marker to a new location.
7949	setLatLng: function (latlng) {
7950		var oldLatLng = this._latlng;
7951		this._latlng = toLatLng(latlng);
7952		this.redraw();
7953
7954		// @event move: Event
7955		// Fired when the marker is moved via [`setLatLng`](#circlemarker-setlatlng). Old and new coordinates are included in event arguments as `oldLatLng`, `latlng`.
7956		return this.fire('move', {oldLatLng: oldLatLng, latlng: this._latlng});
7957	},
7958
7959	// @method getLatLng(): LatLng
7960	// Returns the current geographical position of the circle marker
7961	getLatLng: function () {
7962		return this._latlng;
7963	},
7964
7965	// @method setRadius(radius: Number): this
7966	// Sets the radius of a circle marker. Units are in pixels.
7967	setRadius: function (radius) {
7968		this.options.radius = this._radius = radius;
7969		return this.redraw();
7970	},
7971
7972	// @method getRadius(): Number
7973	// Returns the current radius of the circle
7974	getRadius: function () {
7975		return this._radius;
7976	},
7977
7978	setStyle : function (options) {
7979		var radius = options && options.radius || this._radius;
7980		Path.prototype.setStyle.call(this, options);
7981		this.setRadius(radius);
7982		return this;
7983	},
7984
7985	_project: function () {
7986		this._point = this._map.latLngToLayerPoint(this._latlng);
7987		this._updateBounds();
7988	},
7989
7990	_updateBounds: function () {
7991		var r = this._radius,
7992		    r2 = this._radiusY || r,
7993		    w = this._clickTolerance(),
7994		    p = [r + w, r2 + w];
7995		this._pxBounds = new Bounds(this._point.subtract(p), this._point.add(p));
7996	},
7997
7998	_update: function () {
7999		if (this._map) {
8000			this._updatePath();
8001		}
8002	},
8003
8004	_updatePath: function () {
8005		this._renderer._updateCircle(this);
8006	},
8007
8008	_empty: function () {
8009		return this._radius && !this._renderer._bounds.intersects(this._pxBounds);
8010	},
8011
8012	// Needed by the `Canvas` renderer for interactivity
8013	_containsPoint: function (p) {
8014		return p.distanceTo(this._point) <= this._radius + this._clickTolerance();
8015	}
8016});
8017
8018
8019// @factory L.circleMarker(latlng: LatLng, options?: CircleMarker options)
8020// Instantiates a circle marker object given a geographical point, and an optional options object.
8021function circleMarker(latlng, options) {
8022	return new CircleMarker(latlng, options);
8023}
8024
8025/*
8026 * @class Circle
8027 * @aka L.Circle
8028 * @inherits CircleMarker
8029 *
8030 * A class for drawing circle overlays on a map. Extends `CircleMarker`.
8031 *
8032 * It's an approximation and starts to diverge from a real circle closer to poles (due to projection distortion).
8033 *
8034 * @example
8035 *
8036 * ```js
8037 * L.circle([50.5, 30.5], {radius: 200}).addTo(map);
8038 * ```
8039 */
8040
8041var Circle = CircleMarker.extend({
8042
8043	initialize: function (latlng, options, legacyOptions) {
8044		if (typeof options === 'number') {
8045			// Backwards compatibility with 0.7.x factory (latlng, radius, options?)
8046			options = extend({}, legacyOptions, {radius: options});
8047		}
8048		setOptions(this, options);
8049		this._latlng = toLatLng(latlng);
8050
8051		if (isNaN(this.options.radius)) { throw new Error('Circle radius cannot be NaN'); }
8052
8053		// @section
8054		// @aka Circle options
8055		// @option radius: Number; Radius of the circle, in meters.
8056		this._mRadius = this.options.radius;
8057	},
8058
8059	// @method setRadius(radius: Number): this
8060	// Sets the radius of a circle. Units are in meters.
8061	setRadius: function (radius) {
8062		this._mRadius = radius;
8063		return this.redraw();
8064	},
8065
8066	// @method getRadius(): Number
8067	// Returns the current radius of a circle. Units are in meters.
8068	getRadius: function () {
8069		return this._mRadius;
8070	},
8071
8072	// @method getBounds(): LatLngBounds
8073	// Returns the `LatLngBounds` of the path.
8074	getBounds: function () {
8075		var half = [this._radius, this._radiusY || this._radius];
8076
8077		return new LatLngBounds(
8078			this._map.layerPointToLatLng(this._point.subtract(half)),
8079			this._map.layerPointToLatLng(this._point.add(half)));
8080	},
8081
8082	setStyle: Path.prototype.setStyle,
8083
8084	_project: function () {
8085
8086		var lng = this._latlng.lng,
8087		    lat = this._latlng.lat,
8088		    map = this._map,
8089		    crs = map.options.crs;
8090
8091		if (crs.distance === Earth.distance) {
8092			var d = Math.PI / 180,
8093			    latR = (this._mRadius / Earth.R) / d,
8094			    top = map.project([lat + latR, lng]),
8095			    bottom = map.project([lat - latR, lng]),
8096			    p = top.add(bottom).divideBy(2),
8097			    lat2 = map.unproject(p).lat,
8098			    lngR = Math.acos((Math.cos(latR * d) - Math.sin(lat * d) * Math.sin(lat2 * d)) /
8099			            (Math.cos(lat * d) * Math.cos(lat2 * d))) / d;
8100
8101			if (isNaN(lngR) || lngR === 0) {
8102				lngR = latR / Math.cos(Math.PI / 180 * lat); // Fallback for edge case, #2425
8103			}
8104
8105			this._point = p.subtract(map.getPixelOrigin());
8106			this._radius = isNaN(lngR) ? 0 : p.x - map.project([lat2, lng - lngR]).x;
8107			this._radiusY = p.y - top.y;
8108
8109		} else {
8110			var latlng2 = crs.unproject(crs.project(this._latlng).subtract([this._mRadius, 0]));
8111
8112			this._point = map.latLngToLayerPoint(this._latlng);
8113			this._radius = this._point.x - map.latLngToLayerPoint(latlng2).x;
8114		}
8115
8116		this._updateBounds();
8117	}
8118});
8119
8120// @factory L.circle(latlng: LatLng, options?: Circle options)
8121// Instantiates a circle object given a geographical point, and an options object
8122// which contains the circle radius.
8123// @alternative
8124// @factory L.circle(latlng: LatLng, radius: Number, options?: Circle options)
8125// Obsolete way of instantiating a circle, for compatibility with 0.7.x code.
8126// Do not use in new applications or plugins.
8127function circle(latlng, options, legacyOptions) {
8128	return new Circle(latlng, options, legacyOptions);
8129}
8130
8131/*
8132 * @class Polyline
8133 * @aka L.Polyline
8134 * @inherits Path
8135 *
8136 * A class for drawing polyline overlays on a map. Extends `Path`.
8137 *
8138 * @example
8139 *
8140 * ```js
8141 * // create a red polyline from an array of LatLng points
8142 * var latlngs = [
8143 * 	[45.51, -122.68],
8144 * 	[37.77, -122.43],
8145 * 	[34.04, -118.2]
8146 * ];
8147 *
8148 * var polyline = L.polyline(latlngs, {color: 'red'}).addTo(map);
8149 *
8150 * // zoom the map to the polyline
8151 * map.fitBounds(polyline.getBounds());
8152 * ```
8153 *
8154 * You can also pass a multi-dimensional array to represent a `MultiPolyline` shape:
8155 *
8156 * ```js
8157 * // create a red polyline from an array of arrays of LatLng points
8158 * var latlngs = [
8159 * 	[[45.51, -122.68],
8160 * 	 [37.77, -122.43],
8161 * 	 [34.04, -118.2]],
8162 * 	[[40.78, -73.91],
8163 * 	 [41.83, -87.62],
8164 * 	 [32.76, -96.72]]
8165 * ];
8166 * ```
8167 */
8168
8169
8170var Polyline = Path.extend({
8171
8172	// @section
8173	// @aka Polyline options
8174	options: {
8175		// @option smoothFactor: Number = 1.0
8176		// How much to simplify the polyline on each zoom level. More means
8177		// better performance and smoother look, and less means more accurate representation.
8178		smoothFactor: 1.0,
8179
8180		// @option noClip: Boolean = false
8181		// Disable polyline clipping.
8182		noClip: false
8183	},
8184
8185	initialize: function (latlngs, options) {
8186		setOptions(this, options);
8187		this._setLatLngs(latlngs);
8188	},
8189
8190	// @method getLatLngs(): LatLng[]
8191	// Returns an array of the points in the path, or nested arrays of points in case of multi-polyline.
8192	getLatLngs: function () {
8193		return this._latlngs;
8194	},
8195
8196	// @method setLatLngs(latlngs: LatLng[]): this
8197	// Replaces all the points in the polyline with the given array of geographical points.
8198	setLatLngs: function (latlngs) {
8199		this._setLatLngs(latlngs);
8200		return this.redraw();
8201	},
8202
8203	// @method isEmpty(): Boolean
8204	// Returns `true` if the Polyline has no LatLngs.
8205	isEmpty: function () {
8206		return !this._latlngs.length;
8207	},
8208
8209	// @method closestLayerPoint(p: Point): Point
8210	// Returns the point closest to `p` on the Polyline.
8211	closestLayerPoint: function (p) {
8212		var minDistance = Infinity,
8213		    minPoint = null,
8214		    closest = _sqClosestPointOnSegment,
8215		    p1, p2;
8216
8217		for (var j = 0, jLen = this._parts.length; j < jLen; j++) {
8218			var points = this._parts[j];
8219
8220			for (var i = 1, len = points.length; i < len; i++) {
8221				p1 = points[i - 1];
8222				p2 = points[i];
8223
8224				var sqDist = closest(p, p1, p2, true);
8225
8226				if (sqDist < minDistance) {
8227					minDistance = sqDist;
8228					minPoint = closest(p, p1, p2);
8229				}
8230			}
8231		}
8232		if (minPoint) {
8233			minPoint.distance = Math.sqrt(minDistance);
8234		}
8235		return minPoint;
8236	},
8237
8238	// @method getCenter(): LatLng
8239	// Returns the center ([centroid](http
vendor: 11,613 bytes, lines 8239-8672
8239://en.wikipedia.org/wiki/Centroid)) of the polyline.
8240	getCenter: function () {
8241		// throws error when not yet added to map as this center calculation requires projected coordinates
8242		if (!this._map) {
8243			throw new Error('Must add layer to map before using getCenter()');
8244		}
8245
8246		var i, halfDist, segDist, dist, p1, p2, ratio,
8247		    points = this._rings[0],
8248		    len = points.length;
8249
8250		if (!len) { return null; }
8251
8252		// polyline centroid algorithm; only uses the first ring if there are multiple
8253
8254		for (i = 0, halfDist = 0; i < len - 1; i++) {
8255			halfDist += points[i].distanceTo(points[i + 1]) / 2;
8256		}
8257
8258		// The line is so small in the current view that all points are on the same pixel.
8259		if (halfDist === 0) {
8260			return this._map.layerPointToLatLng(points[0]);
8261		}
8262
8263		for (i = 0, dist = 0; i < len - 1; i++) {
8264			p1 = points[i];
8265			p2 = points[i + 1];
8266			segDist = p1.distanceTo(p2);
8267			dist += segDist;
8268
8269			if (dist > halfDist) {
8270				ratio = (dist - halfDist) / segDist;
8271				return this._map.layerPointToLatLng([
8272					p2.x - ratio * (p2.x - p1.x),
8273					p2.y - ratio * (p2.y - p1.y)
8274				]);
8275			}
8276		}
8277	},
8278
8279	// @method getBounds(): LatLngBounds
8280	// Returns the `LatLngBounds` of the path.
8281	getBounds: function () {
8282		return this._bounds;
8283	},
8284
8285	// @method addLatLng(latlng: LatLng, latlngs? LatLng[]): this
8286	// Adds a given point to the polyline. By default, adds to the first ring of
8287	// the polyline in case of a multi-polyline, but can be overridden by passing
8288	// a specific ring as a LatLng array (that you can earlier access with [`getLatLngs`](#polyline-getlatlngs)).
8289	addLatLng: function (latlng, latlngs) {
8290		latlngs = latlngs || this._defaultShape();
8291		latlng = toLatLng(latlng);
8292		latlngs.push(latlng);
8293		this._bounds.extend(latlng);
8294		return this.redraw();
8295	},
8296
8297	_setLatLngs: function (latlngs) {
8298		this._bounds = new LatLngBounds();
8299		this._latlngs = this._convertLatLngs(latlngs);
8300	},
8301
8302	_defaultShape: function () {
8303		return isFlat(this._latlngs) ? this._latlngs : this._latlngs[0];
8304	},
8305
8306	// recursively convert latlngs input into actual LatLng instances; calculate bounds along the way
8307	_convertLatLngs: function (latlngs) {
8308		var result = [],
8309		    flat = isFlat(latlngs);
8310
8311		for (var i = 0, len = latlngs.length; i < len; i++) {
8312			if (flat) {
8313				result[i] = toLatLng(latlngs[i]);
8314				this._bounds.extend(result[i]);
8315			} else {
8316				result[i] = this._convertLatLngs(latlngs[i]);
8317			}
8318		}
8319
8320		return result;
8321	},
8322
8323	_project: function () {
8324		var pxBounds = new Bounds();
8325		this._rings = [];
8326		this._projectLatlngs(this._latlngs, this._rings, pxBounds);
8327
8328		if (this._bounds.isValid() && pxBounds.isValid()) {
8329			this._rawPxBounds = pxBounds;
8330			this._updateBounds();
8331		}
8332	},
8333
8334	_updateBounds: function () {
8335		var w = this._clickTolerance(),
8336		    p = new Point(w, w);
8337		this._pxBounds = new Bounds([
8338			this._rawPxBounds.min.subtract(p),
8339			this._rawPxBounds.max.add(p)
8340		]);
8341	},
8342
8343	// recursively turns latlngs into a set of rings with projected coordinates
8344	_projectLatlngs: function (latlngs, result, projectedBounds) {
8345		var flat = latlngs[0] instanceof LatLng,
8346		    len = latlngs.length,
8347		    i, ring;
8348
8349		if (flat) {
8350			ring = [];
8351			for (i = 0; i < len; i++) {
8352				ring[i] = this._map.latLngToLayerPoint(latlngs[i]);
8353				projectedBounds.extend(ring[i]);
8354			}
8355			result.push(ring);
8356		} else {
8357			for (i = 0; i < len; i++) {
8358				this._projectLatlngs(latlngs[i], result, projectedBounds);
8359			}
8360		}
8361	},
8362
8363	// clip polyline by renderer bounds so that we have less to render for performance
8364	_clipPoints: function () {
8365		var bounds = this._renderer._bounds;
8366
8367		this._parts = [];
8368		if (!this._pxBounds || !this._pxBounds.intersects(bounds)) {
8369			return;
8370		}
8371
8372		if (this.options.noClip) {
8373			this._parts = this._rings;
8374			return;
8375		}
8376
8377		var parts = this._parts,
8378		    i, j, k, len, len2, segment, points;
8379
8380		for (i = 0, k = 0, len = this._rings.length; i < len; i++) {
8381			points = this._rings[i];
8382
8383			for (j = 0, len2 = points.length; j < len2 - 1; j++) {
8384				segment = clipSegment(points[j], points[j + 1], bounds, j, true);
8385
8386				if (!segment) { continue; }
8387
8388				parts[k] = parts[k] || [];
8389				parts[k].push(segment[0]);
8390
8391				// if segment goes out of screen, or it's the last one, it's the end of the line part
8392				if ((segment[1] !== points[j + 1]) || (j === len2 - 2)) {
8393					parts[k].push(segment[1]);
8394					k++;
8395				}
8396			}
8397		}
8398	},
8399
8400	// simplify each clipped part of the polyline for performance
8401	_simplifyPoints: function () {
8402		var parts = this._parts,
8403		    tolerance = this.options.smoothFactor;
8404
8405		for (var i = 0, len = parts.length; i < len; i++) {
8406			parts[i] = simplify(parts[i], tolerance);
8407		}
8408	},
8409
8410	_update: function () {
8411		if (!this._map) { return; }
8412
8413		this._clipPoints();
8414		this._simplifyPoints();
8415		this._updatePath();
8416	},
8417
8418	_updatePath: function () {
8419		this._renderer._updatePoly(this);
8420	},
8421
8422	// Needed by the `Canvas` renderer for interactivity
8423	_containsPoint: function (p, closed) {
8424		var i, j, k, len, len2, part,
8425		    w = this._clickTolerance();
8426
8427		if (!this._pxBounds || !this._pxBounds.contains(p)) { return false; }
8428
8429		// hit detection for polylines
8430		for (i = 0, len = this._parts.length; i < len; i++) {
8431			part = this._parts[i];
8432
8433			for (j = 0, len2 = part.length, k = len2 - 1; j < len2; k = j++) {
8434				if (!closed && (j === 0)) { continue; }
8435
8436				if (pointToSegmentDistance(p, part[k], part[j]) <= w) {
8437					return true;
8438				}
8439			}
8440		}
8441		return false;
8442	}
8443});
8444
8445// @factory L.polyline(latlngs: LatLng[], options?: Polyline options)
8446// Instantiates a polyline object given an array of geographical points and
8447// optionally an options object. You can create a `Polyline` object with
8448// multiple separate lines (`MultiPolyline`) by passing an array of arrays
8449// of geographic points.
8450function polyline(latlngs, options) {
8451	return new Polyline(latlngs, options);
8452}
8453
8454// Retrocompat. Allow plugins to support Leaflet versions before and after 1.1.
8455Polyline._flat = _flat;
8456
8457/*
8458 * @class Polygon
8459 * @aka L.Polygon
8460 * @inherits Polyline
8461 *
8462 * A class for drawing polygon overlays on a map. Extends `Polyline`.
8463 *
8464 * 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.
8465 *
8466 *
8467 * @example
8468 *
8469 * ```js
8470 * // create a red polygon from an array of LatLng points
8471 * var latlngs = [[37, -109.05],[41, -109.03],[41, -102.05],[37, -102.04]];
8472 *
8473 * var polygon = L.polygon(latlngs, {color: 'red'}).addTo(map);
8474 *
8475 * // zoom the map to the polygon
8476 * map.fitBounds(polygon.getBounds());
8477 * ```
8478 *
8479 * 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:
8480 *
8481 * ```js
8482 * var latlngs = [
8483 *   [[37, -109.05],[41, -109.03],[41, -102.05],[37, -102.04]], // outer ring
8484 *   [[37.29, -108.58],[40.71, -108.58],[40.71, -102.50],[37.29, -102.50]] // hole
8485 * ];
8486 * ```
8487 *
8488 * Additionally, you can pass a multi-dimensional array to represent a MultiPolygon shape.
8489 *
8490 * ```js
8491 * var latlngs = [
8492 *   [ // first polygon
8493 *     [[37, -109.05],[41, -109.03],[41, -102.05],[37, -102.04]], // outer ring
8494 *     [[37.29, -108.58],[40.71, -108.58],[40.71, -102.50],[37.29, -102.50]] // hole
8495 *   ],
8496 *   [ // second polygon
8497 *     [[41, -111.03],[45, -111.04],[45, -104.05],[41, -104.05]]
8498 *   ]
8499 * ];
8500 * ```
8501 */
8502
8503var Polygon = Polyline.extend({
8504
8505	options: {
8506		fill: true
8507	},
8508
8509	isEmpty: function () {
8510		return !this._latlngs.length || !this._latlngs[0].length;
8511	},
8512
8513	getCenter: function () {
8514		// throws error when not yet added to map as this center calculation requires projected coordinates
8515		if (!this._map) {
8516			throw new Error('Must add layer to map before using getCenter()');
8517		}
8518
8519		var i, j, p1, p2, f, area, x, y, center,
8520		    points = this._rings[0],
8521		    len = points.length;
8522
8523		if (!len) { return null; }
8524
8525		// polygon centroid algorithm; only uses the first ring if there are multiple
8526
8527		area = x = y = 0;
8528
8529		for (i = 0, j = len - 1; i < len; j = i++) {
8530			p1 = points[i];
8531			p2 = points[j];
8532
8533			f = p1.y * p2.x - p2.y * p1.x;
8534			x += (p1.x + p2.x) * f;
8535			y += (p1.y + p2.y) * f;
8536			area += f * 3;
8537		}
8538
8539		if (area === 0) {
8540			// Polygon is so small that all points are on same pixel.
8541			center = points[0];
8542		} else {
8543			center = [x / area, y / area];
8544		}
8545		return this._map.layerPointToLatLng(center);
8546	},
8547
8548	_convertLatLngs: function (latlngs) {
8549		var result = Polyline.prototype._convertLatLngs.call(this, latlngs),
8550		    len = result.length;
8551
8552		// remove last point if it equals first one
8553		if (len >= 2 && result[0] instanceof LatLng && result[0].equals(result[len - 1])) {
8554			result.pop();
8555		}
8556		return result;
8557	},
8558
8559	_setLatLngs: function (latlngs) {
8560		Polyline.prototype._setLatLngs.call(this, latlngs);
8561		if (isFlat(this._latlngs)) {
8562			this._latlngs = [this._latlngs];
8563		}
8564	},
8565
8566	_defaultShape: function () {
8567		return isFlat(this._latlngs[0]) ? this._latlngs[0] : this._latlngs[0][0];
8568	},
8569
8570	_clipPoints: function () {
8571		// polygons need a different clipping algorithm so we redefine that
8572
8573		var bounds = this._renderer._bounds,
8574		    w = this.options.weight,
8575		    p = new Point(w, w);
8576
8577		// increase clip padding by stroke width to avoid stroke on clip edges
8578		bounds = new Bounds(bounds.min.subtract(p), bounds.max.add(p));
8579
8580		this._parts = [];
8581		if (!this._pxBounds || !this._pxBounds.intersects(bounds)) {
8582			return;
8583		}
8584
8585		if (this.options.noClip) {
8586			this._parts = this._rings;
8587			return;
8588		}
8589
8590		for (var i = 0, len = this._rings.length, clipped; i < len; i++) {
8591			clipped = clipPolygon(this._rings[i], bounds, true);
8592			if (clipped.length) {
8593				this._parts.push(clipped);
8594			}
8595		}
8596	},
8597
8598	_updatePath: function () {
8599		this._renderer._updatePoly(this, true);
8600	},
8601
8602	// Needed by the `Canvas` renderer for interactivity
8603	_containsPoint: function (p) {
8604		var inside = false,
8605		    part, p1, p2, i, j, k, len, len2;
8606
8607		if (!this._pxBounds || !this._pxBounds.contains(p)) { return false; }
8608
8609		// ray casting algorithm for detecting if point is in polygon
8610		for (i = 0, len = this._parts.length; i < len; i++) {
8611			part = this._parts[i];
8612
8613			for (j = 0, len2 = part.length, k = len2 - 1; j < len2; k = j++) {
8614				p1 = part[j];
8615				p2 = part[k];
8616
8617				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)) {
8618					inside = !inside;
8619				}
8620			}
8621		}
8622
8623		// also check if it's on polygon stroke
8624		return inside || Polyline.prototype._containsPoint.call(this, p, true);
8625	}
8626
8627});
8628
8629
8630// @factory L.polygon(latlngs: LatLng[], options?: Polyline options)
8631function polygon(latlngs, options) {
8632	return new Polygon(latlngs, options);
8633}
8634
8635/*
8636 * @class GeoJSON
8637 * @aka L.GeoJSON
8638 * @inherits FeatureGroup
8639 *
8640 * Represents a GeoJSON object or an array of GeoJSON objects. Allows you to parse
8641 * GeoJSON data and display it on the map. Extends `FeatureGroup`.
8642 *
8643 * @example
8644 *
8645 * ```js
8646 * L.geoJSON(data, {
8647 * 	style: function (feature) {
8648 * 		return {color: feature.properties.color};
8649 * 	}
8650 * }).bindPopup(function (layer) {
8651 * 	return layer.feature.properties.description;
8652 * }).addTo(map);
8653 * ```
8654 */
8655
8656var GeoJSON = FeatureGroup.extend({
8657
8658	/* @section
8659	 * @aka GeoJSON options
8660	 *
8661	 * @option pointToLayer: Function = *
8662	 * A `Function` defining how GeoJSON points spawn Leaflet layers. It is internally
8663	 * called when data is added, passing the GeoJSON point feature and its `LatLng`.
8664	 * The default is to spawn a default `Marker`:
8665	 * ```js
8666	 * function(geoJsonPoint, latlng) {
8667	 * 	return L.marker(latlng);
8668	 * }
8669	 * ```
8670	 *
8671	 * @option style: Function = *
8672	 * A `Function` defining the `Path options` for styling GeoJSON lines and polygons,
8673	 * called internally when data is added.
8674	 * The default value is to not override any defaults:
8675	 * ```js
8676	 * function (geoJsonFeature) {
8677	 * 	return {}
8678	 * }
8679	 * ```
8680	 *
8681	 * @option onEachFeature: Function = *
8682	 * A `Function` that will be called once for each created `Feature`, after it has
8683	 * been created and styled. Useful for attaching events and popups to features.
8684	 * The default is to do nothing with the newly created layers:
8685	 * ```js
8686	 * function (feature, layer) {}
8687	 * ```
8688	 *
8689	 * @option filter: Function = *
8690	 * A `Function` that will be used to decide whether to include a feature or not.
8691	 * The default is to include all features:
8692	 * ```js
8693	 * function (geoJsonFeature) {
8694	 * 	return true;
8695	 * }
8696	 * ```
8697	 * Note: dynamically changing the `filter` option will have effect only on newly
8698	 * added data. It will _not_ re-evaluate already included features.
8699	 *
8700	 * @option coordsToLatLng: Function = *
8701	 * A `Function` that will be used for converting GeoJSON coordinates to `LatLng`s.
8702	 * The default is the `coordsToLatLng` static method.
8703	 *
8704	 * @option markersInheritOptions: Boolean = false
8705	 * Whether default Markers for "Point" type Features inherit from group options.
8706	 */
8707
8708	initialize: function (geojson, options) {
8709		setOptions(this, options);
8710
8711		this._layers = {};
8712
8713		if (geojson) {
8714			this.addData(geojson);
8715		}
8716	},
8717
8718	// @method addData( <GeoJSON> data ): this
8719	// Adds a GeoJSON object to the layer.
8720	addData: function (geojson) {
8721		var features = isArray(geojson) ? geojson : geojson.features,
8722		    i, len, feature;
8723
8724		if (features) {
8725			for (i = 0, len = features.length; i < len; i++) {
8726				// only add this if geometry or geometries are set and not null
8727				feature = features[i];
8728				if (feature.geometries || feature.geometry || feature.features || feature.coordinates) {
8729					this.addData(feature);
8730				}
8731			}
8732			return this;
8733		}
8734
8735		var options = this.options;
8736
8737		if (options.filter && !options.filter(geojson)) { return this; }
8738
8739		var layer = geometryToLayer(geojson, options);
8740		if (!layer) {
8741			return this;
8742		}
8743		layer.feature = asFeature(geojson);
8744
8745		layer.defaultOptions = layer.options;
8746		this.resetStyle(layer);
8747
8748		if (options.onEachFeature) {
8749			options.onEachFeature(geojson, layer);
8750		}
8751
8752		return this.addLayer(layer);
8753	},
8754
8755	// @method resetStyle( <Path> layer? ): this
8756	// Resets the given vector layer's style to the original GeoJSON style, useful for resetting style after hover events.
8757	// If `layer` is omitted, the style of all features in the current layer is reset.
8758	resetStyle: function (layer) {
8759		if (layer === undefined) {
8760			return this.eachLayer(this.resetStyle, this);
8761		}
8762		// reset any custom styles
8763		layer.options = extend({}, layer.defaultOptions);
8764		this._setLayerStyle(layer, this.options.style);
8765		return this;
8766	},
8767
8768	// @method setStyle( <Function> style ): this
8769	// Changes styles of GeoJSON vector layers with the given style function.
8770	setStyle: function (style) {
8771		return this.eachLayer(function (layer) {
8772			this._setLayerStyle(layer, style);
8773		}, this);
8774	},
8775
8776	_setLayerStyle: function (layer, style) {
8777		if (layer.setStyle) {
8778			if (typeof style === 'function') {
8779				style = style(layer.feature);
8780			}
8781			layer.setStyle(style);
8782		}
8783	}
8784});
8785
8786// @section
8787// There are several static functions which can be called without instantiating L.GeoJSON:
8788
8789// @function geometryToLayer(featureData: Object, options?: GeoJSON options): Layer
8790// Creates a `Layer` from a given GeoJSON feature. Can use a custom
8791// [`pointToLayer`](#geojson-pointtolayer) and/or [`coordsToLatLng`](#geojson-coordstolatlng)
8792// functions if provided as options.
8793function geometryToLayer(geojson, options) {
8794
8795	var geometry = geojson.type === 'Feature' ? geojson.geometry : geojson,
8796	    coords = geometry ? geometry.coordinates : null,
8797	    layers = [],
8798	    pointToLayer = options && options.pointToLayer,
8799	    _coordsToLatLng = options && options.coordsToLatLng || coordsToLatLng,
8800	    latlng, latlngs, i, len;
8801
8802	if (!coords && !geometry) {
8803		return null;
8804	}
8805
8806	switch (geometry.type) {
8807	case 'Point':
8808		latlng = _coordsToLatLng(coords);
8809		return _pointToLayer(pointToLayer, geojson, latlng, options);
8810
8811	case 'MultiPoint':
vendor: 25,377 bytes, lines 8812-9713
8812		for (i = 0, len = coords.length; i < len; i++) {
8813			latlng = _coordsToLatLng(coords[i]);
8814			layers.push(_pointToLayer(pointToLayer, geojson, latlng, options));
8815		}
8816		return new FeatureGroup(layers);
8817
8818	case 'LineString':
8819	case 'MultiLineString':
8820		latlngs = coordsToLatLngs(coords, geometry.type === 'LineString' ? 0 : 1, _coordsToLatLng);
8821		return new Polyline(latlngs, options);
8822
8823	case 'Polygon':
8824	case 'MultiPolygon':
8825		latlngs = coordsToLatLngs(coords, geometry.type === 'Polygon' ? 1 : 2, _coordsToLatLng);
8826		return new Polygon(latlngs, options);
8827
8828	case 'GeometryCollection':
8829		for (i = 0, len = geometry.geometries.length; i < len; i++) {
8830			var layer = geometryToLayer({
8831				geometry: geometry.geometries[i],
8832				type: 'Feature',
8833				properties: geojson.properties
8834			}, options);
8835
8836			if (layer) {
8837				layers.push(layer);
8838			}
8839		}
8840		return new FeatureGroup(layers);
8841
8842	default:
8843		throw new Error('Invalid GeoJSON object.');
8844	}
8845}
8846
8847function _pointToLayer(pointToLayerFn, geojson, latlng, options) {
8848	return pointToLayerFn ?
8849		pointToLayerFn(geojson, latlng) :
8850		new Marker(latlng, options && options.markersInheritOptions && options);
8851}
8852
8853// @function coordsToLatLng(coords: Array): LatLng
8854// Creates a `LatLng` object from an array of 2 numbers (longitude, latitude)
8855// or 3 numbers (longitude, latitude, altitude) used in GeoJSON for points.
8856function coordsToLatLng(coords) {
8857	return new LatLng(coords[1], coords[0], coords[2]);
8858}
8859
8860// @function coordsToLatLngs(coords: Array, levelsDeep?: Number, coordsToLatLng?: Function): Array
8861// Creates a multidimensional array of `LatLng`s from a GeoJSON coordinates array.
8862// `levelsDeep` specifies the nesting level (0 is for an array of points, 1 for an array of arrays of points, etc., 0 by default).
8863// Can use a custom [`coordsToLatLng`](#geojson-coordstolatlng) function.
8864function coordsToLatLngs(coords, levelsDeep, _coordsToLatLng) {
8865	var latlngs = [];
8866
8867	for (var i = 0, len = coords.length, latlng; i < len; i++) {
8868		latlng = levelsDeep ?
8869			coordsToLatLngs(coords[i], levelsDeep - 1, _coordsToLatLng) :
8870			(_coordsToLatLng || coordsToLatLng)(coords[i]);
8871
8872		latlngs.push(latlng);
8873	}
8874
8875	return latlngs;
8876}
8877
8878// @function latLngToCoords(latlng: LatLng, precision?: Number): Array
8879// Reverse of [`coordsToLatLng`](#geojson-coordstolatlng)
8880function latLngToCoords(latlng, precision) {
8881	precision = typeof precision === 'number' ? precision : 6;
8882	return latlng.alt !== undefined ?
8883		[formatNum(latlng.lng, precision), formatNum(latlng.lat, precision), formatNum(latlng.alt, precision)] :
8884		[formatNum(latlng.lng, precision), formatNum(latlng.lat, precision)];
8885}
8886
8887// @function latLngsToCoords(latlngs: Array, levelsDeep?: Number, closed?: Boolean): Array
8888// Reverse of [`coordsToLatLngs`](#geojson-coordstolatlngs)
8889// `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.
8890function latLngsToCoords(latlngs, levelsDeep, closed, precision) {
8891	var coords = [];
8892
8893	for (var i = 0, len = latlngs.length; i < len; i++) {
8894		coords.push(levelsDeep ?
8895			latLngsToCoords(latlngs[i], levelsDeep - 1, closed, precision) :
8896			latLngToCoords(latlngs[i], precision));
8897	}
8898
8899	if (!levelsDeep && closed) {
8900		coords.push(coords[0]);
8901	}
8902
8903	return coords;
8904}
8905
8906function getFeature(layer, newGeometry) {
8907	return layer.feature ?
8908		extend({}, layer.feature, {geometry: newGeometry}) :
8909		asFeature(newGeometry);
8910}
8911
8912// @function asFeature(geojson: Object): Object
8913// Normalize GeoJSON geometries/features into GeoJSON features.
8914function asFeature(geojson) {
8915	if (geojson.type === 'Feature' || geojson.type === 'FeatureCollection') {
8916		return geojson;
8917	}
8918
8919	return {
8920		type: 'Feature',
8921		properties: {},
8922		geometry: geojson
8923	};
8924}
8925
8926var PointToGeoJSON = {
8927	toGeoJSON: function (precision) {
8928		return getFeature(this, {
8929			type: 'Point',
8930			coordinates: latLngToCoords(this.getLatLng(), precision)
8931		});
8932	}
8933};
8934
8935// @namespace Marker
8936// @section Other methods
8937// @method toGeoJSON(precision?: Number): Object
8938// `precision` is the number of decimal places for coordinates.
8939// The default value is 6 places.
8940// Returns a [`GeoJSON`](http://en.wikipedia.org/wiki/GeoJSON) representation of the marker (as a GeoJSON `Point` Feature).
8941Marker.include(PointToGeoJSON);
8942
8943// @namespace CircleMarker
8944// @method toGeoJSON(precision?: Number): Object
8945// `precision` is the number of decimal places for coordinates.
8946// The default value is 6 places.
8947// Returns a [`GeoJSON`](http://en.wikipedia.org/wiki/GeoJSON) representation of the circle marker (as a GeoJSON `Point` Feature).
8948Circle.include(PointToGeoJSON);
8949CircleMarker.include(PointToGeoJSON);
8950
8951
8952// @namespace Polyline
8953// @method toGeoJSON(precision?: Number): Object
8954// `precision` is the number of decimal places for coordinates.
8955// The default value is 6 places.
8956// Returns a [`GeoJSON`](http://en.wikipedia.org/wiki/GeoJSON) representation of the polyline (as a GeoJSON `LineString` or `MultiLineString` Feature).
8957Polyline.include({
8958	toGeoJSON: function (precision) {
8959		var multi = !isFlat(this._latlngs);
8960
8961		var coords = latLngsToCoords(this._latlngs, multi ? 1 : 0, false, precision);
8962
8963		return getFeature(this, {
8964			type: (multi ? 'Multi' : '') + 'LineString',
8965			coordinates: coords
8966		});
8967	}
8968});
8969
8970// @namespace Polygon
8971// @method toGeoJSON(precision?: Number): Object
8972// `precision` is the number of decimal places for coordinates.
8973// The default value is 6 places.
8974// Returns a [`GeoJSON`](http://en.wikipedia.org/wiki/GeoJSON) representation of the polygon (as a GeoJSON `Polygon` or `MultiPolygon` Feature).
8975Polygon.include({
8976	toGeoJSON: function (precision) {
8977		var holes = !isFlat(this._latlngs),
8978		    multi = holes && !isFlat(this._latlngs[0]);
8979
8980		var coords = latLngsToCoords(this._latlngs, multi ? 2 : holes ? 1 : 0, true, precision);
8981
8982		if (!holes) {
8983			coords = [coords];
8984		}
8985
8986		return getFeature(this, {
8987			type: (multi ? 'Multi' : '') + 'Polygon',
8988			coordinates: coords
8989		});
8990	}
8991});
8992
8993
8994// @namespace LayerGroup
8995LayerGroup.include({
8996	toMultiPoint: function (precision) {
8997		var coords = [];
8998
8999		this.eachLayer(function (layer) {
9000			coords.push(layer.toGeoJSON(precision).geometry.coordinates);
9001		});
9002
9003		return getFeature(this, {
9004			type: 'MultiPoint',
9005			coordinates: coords
9006		});
9007	},
9008
9009	// @method toGeoJSON(precision?: Number): Object
9010	// `precision` is the number of decimal places for coordinates.
9011	// The default value is 6 places.
9012	// Returns a [`GeoJSON`](http://en.wikipedia.org/wiki/GeoJSON) representation of the layer group (as a GeoJSON `FeatureCollection`, `GeometryCollection`, or `MultiPoint`).
9013	toGeoJSON: function (precision) {
9014
9015		var type = this.feature && this.feature.geometry && this.feature.geometry.type;
9016
9017		if (type === 'MultiPoint') {
9018			return this.toMultiPoint(precision);
9019		}
9020
9021		var isGeometryCollection = type === 'GeometryCollection',
9022		    jsons = [];
9023
9024		this.eachLayer(function (layer) {
9025			if (layer.toGeoJSON) {
9026				var json = layer.toGeoJSON(precision);
9027				if (isGeometryCollection) {
9028					jsons.push(json.geometry);
9029				} else {
9030					var feature = asFeature(json);
9031					// Squash nested feature collections
9032					if (feature.type === 'FeatureCollection') {
9033						jsons.push.apply(jsons, feature.features);
9034					} else {
9035						jsons.push(feature);
9036					}
9037				}
9038			}
9039		});
9040
9041		if (isGeometryCollection) {
9042			return getFeature(this, {
9043				geometries: jsons,
9044				type: 'GeometryCollection'
9045			});
9046		}
9047
9048		return {
9049			type: 'FeatureCollection',
9050			features: jsons
9051		};
9052	}
9053});
9054
9055// @namespace GeoJSON
9056// @factory L.geoJSON(geojson?: Object, options?: GeoJSON options)
9057// Creates a GeoJSON layer. Optionally accepts an object in
9058// [GeoJSON format](https://tools.ietf.org/html/rfc7946) to display on the map
9059// (you can alternatively add it later with `addData` method) and an `options` object.
9060function geoJSON(geojson, options) {
9061	return new GeoJSON(geojson, options);
9062}
9063
9064// Backward compatibility.
9065var geoJson = geoJSON;
9066
9067/*
9068 * @class ImageOverlay
9069 * @aka L.ImageOverlay
9070 * @inherits Interactive layer
9071 *
9072 * Used to load and display a single image over specific bounds of the map. Extends `Layer`.
9073 *
9074 * @example
9075 *
9076 * ```js
9077 * var imageUrl = 'http://www.lib.utexas.edu/maps/historical/newark_nj_1922.jpg',
9078 * 	imageBounds = [[40.712216, -74.22655], [40.773941, -74.12544]];
9079 * L.imageOverlay(imageUrl, imageBounds).addTo(map);
9080 * ```
9081 */
9082
9083var ImageOverlay = Layer.extend({
9084
9085	// @section
9086	// @aka ImageOverlay options
9087	options: {
9088		// @option opacity: Number = 1.0
9089		// The opacity of the image overlay.
9090		opacity: 1,
9091
9092		// @option alt: String = ''
9093		// Text for the `alt` attribute of the image (useful for accessibility).
9094		alt: '',
9095
9096		// @option interactive: Boolean = false
9097		// If `true`, the image overlay will emit [mouse events](#interactive-layer) when clicked or hovered.
9098		interactive: false,
9099
9100		// @option crossOrigin: Boolean|String = false
9101		// Whether the crossOrigin attribute will be added to the image.
9102		// 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.
9103		// Refer to [CORS Settings](https://developer.mozilla.org/en-US/docs/Web/HTML/CORS_settings_attributes) for valid String values.
9104		crossOrigin: false,
9105
9106		// @option errorOverlayUrl: String = ''
9107		// URL to the overlay image to show in place of the overlay that failed to load.
9108		errorOverlayUrl: '',
9109
9110		// @option zIndex: Number = 1
9111		// The explicit [zIndex](https://developer.mozilla.org/docs/Web/CSS/CSS_Positioning/Understanding_z_index) of the overlay layer.
9112		zIndex: 1,
9113
9114		// @option className: String = ''
9115		// A custom class name to assign to the image. Empty by default.
9116		className: ''
9117	},
9118
9119	initialize: function (url, bounds, options) { // (String, LatLngBounds, Object)
9120		this._url = url;
9121		this._bounds = toLatLngBounds(bounds);
9122
9123		setOptions(this, options);
9124	},
9125
9126	onAdd: function () {
9127		if (!this._image) {
9128			this._initImage();
9129
9130			if (this.options.opacity < 1) {
9131				this._updateOpacity();
9132			}
9133		}
9134
9135		if (this.options.interactive) {
9136			addClass(this._image, 'leaflet-interactive');
9137			this.addInteractiveTarget(this._image);
9138		}
9139
9140		this.getPane().appendChild(this._image);
9141		this._reset();
9142	},
9143
9144	onRemove: function () {
9145		remove(this._image);
9146		if (this.options.interactive) {
9147			this.removeInteractiveTarget(this._image);
9148		}
9149	},
9150
9151	// @method setOpacity(opacity: Number): this
9152	// Sets the opacity of the overlay.
9153	setOpacity: function (opacity) {
9154		this.options.opacity = opacity;
9155
9156		if (this._image) {
9157			this._updateOpacity();
9158		}
9159		return this;
9160	},
9161
9162	setStyle: function (styleOpts) {
9163		if (styleOpts.opacity) {
9164			this.setOpacity(styleOpts.opacity);
9165		}
9166		return this;
9167	},
9168
9169	// @method bringToFront(): this
9170	// Brings the layer to the top of all overlays.
9171	bringToFront: function () {
9172		if (this._map) {
9173			toFront(this._image);
9174		}
9175		return this;
9176	},
9177
9178	// @method bringToBack(): this
9179	// Brings the layer to the bottom of all overlays.
9180	bringToBack: function () {
9181		if (this._map) {
9182			toBack(this._image);
9183		}
9184		return this;
9185	},
9186
9187	// @method setUrl(url: String): this
9188	// Changes the URL of the image.
9189	setUrl: function (url) {
9190		this._url = url;
9191
9192		if (this._image) {
9193			this._image.src = url;
9194		}
9195		return this;
9196	},
9197
9198	// @method setBounds(bounds: LatLngBounds): this
9199	// Update the bounds that this ImageOverlay covers
9200	setBounds: function (bounds) {
9201		this._bounds = toLatLngBounds(bounds);
9202
9203		if (this._map) {
9204			this._reset();
9205		}
9206		return this;
9207	},
9208
9209	getEvents: function () {
9210		var events = {
9211			zoom: this._reset,
9212			viewreset: this._reset
9213		};
9214
9215		if (this._zoomAnimated) {
9216			events.zoomanim = this._animateZoom;
9217		}
9218
9219		return events;
9220	},
9221
9222	// @method setZIndex(value: Number): this
9223	// Changes the [zIndex](#imageoverlay-zindex) of the image overlay.
9224	setZIndex: function (value) {
9225		this.options.zIndex = value;
9226		this._updateZIndex();
9227		return this;
9228	},
9229
9230	// @method getBounds(): LatLngBounds
9231	// Get the bounds that this ImageOverlay covers
9232	getBounds: function () {
9233		return this._bounds;
9234	},
9235
9236	// @method getElement(): HTMLElement
9237	// Returns the instance of [`HTMLImageElement`](https://developer.mozilla.org/docs/Web/API/HTMLImageElement)
9238	// used by this overlay.
9239	getElement: function () {
9240		return this._image;
9241	},
9242
9243	_initImage: function () {
9244		var wasElementSupplied = this._url.tagName === 'IMG';
9245		var img = this._image = wasElementSupplied ? this._url : create$1('img');
9246
9247		addClass(img, 'leaflet-image-layer');
9248		if (this._zoomAnimated) { addClass(img, 'leaflet-zoom-animated'); }
9249		if (this.options.className) { addClass(img, this.options.className); }
9250
9251		img.onselectstart = falseFn;
9252		img.onmousemove = falseFn;
9253
9254		// @event load: Event
9255		// Fired when the ImageOverlay layer has loaded its image
9256		img.onload = bind(this.fire, this, 'load');
9257		img.onerror = bind(this._overlayOnError, this, 'error');
9258
9259		if (this.options.crossOrigin || this.options.crossOrigin === '') {
9260			img.crossOrigin = this.options.crossOrigin === true ? '' : this.options.crossOrigin;
9261		}
9262
9263		if (this.options.zIndex) {
9264			this._updateZIndex();
9265		}
9266
9267		if (wasElementSupplied) {
9268			this._url = img.src;
9269			return;
9270		}
9271
9272		img.src = this._url;
9273		img.alt = this.options.alt;
9274	},
9275
9276	_animateZoom: function (e) {
9277		var scale = this._map.getZoomScale(e.zoom),
9278		    offset = this._map._latLngBoundsToNewLayerBounds(this._bounds, e.zoom, e.center).min;
9279
9280		setTransform(this._image, offset, scale);
9281	},
9282
9283	_reset: function () {
9284		var image = this._image,
9285		    bounds = new Bounds(
9286		        this._map.latLngToLayerPoint(this._bounds.getNorthWest()),
9287		        this._map.latLngToLayerPoint(this._bounds.getSouthEast())),
9288		    size = bounds.getSize();
9289
9290		setPosition(image, bounds.min);
9291
9292		image.style.width  = size.x + 'px';
9293		image.style.height = size.y + 'px';
9294	},
9295
9296	_updateOpacity: function () {
9297		setOpacity(this._image, this.options.opacity);
9298	},
9299
9300	_updateZIndex: function () {
9301		if (this._image && this.options.zIndex !== undefined && this.options.zIndex !== null) {
9302			this._image.style.zIndex = this.options.zIndex;
9303		}
9304	},
9305
9306	_overlayOnError: function () {
9307		// @event error: Event
9308		// Fired when the ImageOverlay layer fails to load its image
9309		this.fire('error');
9310
9311		var errorUrl = this.options.errorOverlayUrl;
9312		if (errorUrl && this._url !== errorUrl) {
9313			this._url = errorUrl;
9314			this._image.src = errorUrl;
9315		}
9316	}
9317});
9318
9319// @factory L.imageOverlay(imageUrl: String, bounds: LatLngBounds, options?: ImageOverlay options)
9320// Instantiates an image overlay object given the URL of the image and the
9321// geographical bounds it is tied to.
9322var imageOverlay = function (url, bounds, options) {
9323	return new ImageOverlay(url, bounds, options);
9324};
9325
9326/*
9327 * @class VideoOverlay
9328 * @aka L.VideoOverlay
9329 * @inherits ImageOverlay
9330 *
9331 * Used to load and display a video player over specific bounds of the map. Extends `ImageOverlay`.
9332 *
9333 * A video overlay uses the [`<video>`](https://developer.mozilla.org/docs/Web/HTML/Element/video)
9334 * HTML5 element.
9335 *
9336 * @example
9337 *
9338 * ```js
9339 * var videoUrl = 'https://www.mapbox.com/bites/00188/patricia_nasa.webm',
9340 * 	videoBounds = [[ 32, -130], [ 13, -100]];
9341 * L.videoOverlay(videoUrl, videoBounds ).addTo(map);
9342 * ```
9343 */
9344
9345var VideoOverlay = ImageOverlay.extend({
9346
9347	// @section
9348	// @aka VideoOverlay options
9349	options: {
9350		// @option autoplay: Boolean = true
9351		// Whether the video starts playing automatically when loaded.
9352		autoplay: true,
9353
9354		// @option loop: Boolean = true
9355		// Whether the video will loop back to the beginning when played.
9356		loop: true,
9357
9358		// @option keepAspectRatio: Boolean = true
9359		// Whether the video will save aspect ratio after the projection.
9360		// Relevant for supported browsers. Browser compatibility- https://developer.mozilla.org/en-US/docs/Web/CSS/object-fit
9361		keepAspectRatio: true
9362	},
9363
9364	_initImage: function () {
9365		var wasElementSupplied = this._url.tagName === 'VIDEO';
9366		var vid = this._image = wasElementSupplied ? this._url : create$1('video');
9367
9368		addClass(vid, 'leaflet-image-layer');
9369		if (this._zoomAnimated) { addClass(vid, 'leaflet-zoom-animated'); }
9370		if (this.options.className) { addClass(vid, this.options.className); }
9371
9372		vid.onselectstart = falseFn;
9373		vid.onmousemove = falseFn;
9374
9375		// @event load: Event
9376		// Fired when the video has finished loading the first frame
9377		vid.onloadeddata = bind(this.fire, this, 'load');
9378
9379		if (wasElementSupplied) {
9380			var sourceElements = vid.getElementsByTagName('source');
9381			var sources = [];
9382			for (var j = 0; j < sourceElements.length; j++) {
9383				sources.push(sourceElements[j].src);
9384			}
9385
9386			this._url = (sourceElements.length > 0) ? sources : [vid.src];
9387			return;
9388		}
9389
9390		if (!isArray(this._url)) { this._url = [this._url]; }
9391
9392		if (!this.options.keepAspectRatio && vid.style.hasOwnProperty('objectFit')) { vid.style['objectFit'] = 'fill'; }
9393		vid.autoplay = !!this.options.autoplay;
9394		vid.loop = !!this.options.loop;
9395		for (var i = 0; i < this._url.length; i++) {
9396			var source = create$1('source');
9397			source.src = this._url[i];
9398			vid.appendChild(source);
9399		}
9400	}
9401
9402	// @method getElement(): HTMLVideoElement
9403	// Returns the instance of [`HTMLVideoElement`](https://developer.mozilla.org/docs/Web/API/HTMLVideoElement)
9404	// used by this overlay.
9405});
9406
9407
9408// @factory L.videoOverlay(video: String|Array|HTMLVideoElement, bounds: LatLngBounds, options?: VideoOverlay options)
9409// Instantiates an image overlay object given the URL of the video (or array of URLs, or even a video element) and the
9410// geographical bounds it is tied to.
9411
9412function videoOverlay(video, bounds, options) {
9413	return new VideoOverlay(video, bounds, options);
9414}
9415
9416/*
9417 * @class SVGOverlay
9418 * @aka L.SVGOverlay
9419 * @inherits ImageOverlay
9420 *
9421 * Used to load, display and provide DOM access to an SVG file over specific bounds of the map. Extends `ImageOverlay`.
9422 *
9423 * An SVG overlay uses the [`<svg>`](https://developer.mozilla.org/docs/Web/SVG/Element/svg) element.
9424 *
9425 * @example
9426 *
9427 * ```js
9428 * var svgElement = document.createElementNS("http://www.w3.org/2000/svg", "svg");
9429 * svgElement.setAttribute('xmlns', "http://www.w3.org/2000/svg");
9430 * svgElement.setAttribute('viewBox', "0 0 200 200");
9431 * 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"/>';
9432 * var svgElementBounds = [ [ 32, -130 ], [ 13, -100 ] ];
9433 * L.svgOverlay(svgElement, svgElementBounds).addTo(map);
9434 * ```
9435 */
9436
9437var SVGOverlay = ImageOverlay.extend({
9438	_initImage: function () {
9439		var el = this._image = this._url;
9440
9441		addClass(el, 'leaflet-image-layer');
9442		if (this._zoomAnimated) { addClass(el, 'leaflet-zoom-animated'); }
9443		if (this.options.className) { addClass(el, this.options.className); }
9444
9445		el.onselectstart = falseFn;
9446		el.onmousemove = falseFn;
9447	}
9448
9449	// @method getElement(): SVGElement
9450	// Returns the instance of [`SVGElement`](https://developer.mozilla.org/docs/Web/API/SVGElement)
9451	// used by this overlay.
9452});
9453
9454
9455// @factory L.svgOverlay(svg: String|SVGElement, bounds: LatLngBounds, options?: SVGOverlay options)
9456// Instantiates an image overlay object given an SVG element and the geographical bounds it is tied to.
9457// A viewBox attribute is required on the SVG element to zoom in and out properly.
9458
9459function svgOverlay(el, bounds, options) {
9460	return new SVGOverlay(el, bounds, options);
9461}
9462
9463/*
9464 * @class DivOverlay
9465 * @inherits Layer
9466 * @aka L.DivOverlay
9467 * Base model for L.Popup and L.Tooltip. Inherit from it for custom popup like plugins.
9468 */
9469
9470// @namespace DivOverlay
9471var DivOverlay = Layer.extend({
9472
9473	// @section
9474	// @aka DivOverlay options
9475	options: {
9476		// @option offset: Point = Point(0, 7)
9477		// The offset of the popup position. Useful to control the anchor
9478		// of the popup when opening it on some overlays.
9479		offset: [0, 7],
9480
9481		// @option className: String = ''
9482		// A custom CSS class name to assign to the popup.
9483		className: '',
9484
9485		// @option pane: String = 'popupPane'
9486		// `Map pane` where the popup will be added.
9487		pane: 'popupPane'
9488	},
9489
9490	initialize: function (options, source) {
9491		setOptions(this, options);
9492
9493		this._source = source;
9494	},
9495
9496	onAdd: function (map) {
9497		this._zoomAnimated = map._zoomAnimated;
9498
9499		if (!this._container) {
9500			this._initLayout();
9501		}
9502
9503		if (map._fadeAnimated) {
9504			setOpacity(this._container, 0);
9505		}
9506
9507		clearTimeout(this._removeTimeout);
9508		this.getPane().appendChild(this._container);
9509		this.update();
9510
9511		if (map._fadeAnimated) {
9512			setOpacity(this._container, 1);
9513		}
9514
9515		this.bringToFront();
9516	},
9517
9518	onRemove: function (map) {
9519		if (map._fadeAnimated) {
9520			setOpacity(this._container, 0);
9521			this._removeTimeout = setTimeout(bind(remove, undefined, this._container), 200);
9522		} else {
9523			remove(this._container);
9524		}
9525	},
9526
9527	// @namespace Popup
9528	// @method getLatLng: LatLng
9529	// Returns the geographical point of popup.
9530	getLatLng: function () {
9531		return this._latlng;
9532	},
9533
9534	// @method setLatLng(latlng: LatLng): this
9535	// Sets the geographical point where the popup will open.
9536	setLatLng: function (latlng) {
9537		this._latlng = toLatLng(latlng);
9538		if (this._map) {
9539			this._updatePosition();
9540			this._adjustPan();
9541		}
9542		return this;
9543	},
9544
9545	// @method getContent: String|HTMLElement
9546	// Returns the content of the popup.
9547	getContent: function () {
9548		return this._content;
9549	},
9550
9551	// @method setContent(htmlContent: String|HTMLElement|Function): this
9552	// Sets the HTML content of the popup. If a function is passed the source layer will be passed to the function. The function should return a `String` or `HTMLElement` to be used in the popup.
9553	setContent: function (content) {
9554		this._content = content;
9555		this.update();
9556		return this;
9557	},
9558
9559	// @method getElement: String|HTMLElement
9560	// Alias for [getContent()](#popup-getcontent)
9561	getElement: function () {
9562		return this._container;
9563	},
9564
9565	// @method update: null
9566	// Updates the popup content, layout and position. Useful for updating the popup after something inside changed, e.g. image loaded.
9567	update: function () {
9568		if (!this._map) { return; }
9569
9570		this._container.style.visibility = 'hidden';
9571
9572		this._updateContent();
9573		this._updateLayout();
9574		this._updatePosition();
9575
9576		this._container.style.visibility = '';
9577
9578		this._adjustPan();
9579	},
9580
9581	getEvents: function () {
9582		var events = {
9583			zoom: this._updatePosition,
9584			viewreset: this._updatePosition
9585		};
9586
9587		if (this._zoomAnimated) {
9588			events.zoomanim = this._animateZoom;
9589		}
9590		return events;
9591	},
9592
9593	// @method isOpen: Boolean
9594	// Returns `true` when the popup is visible on the map.
9595	isOpen: function () {
9596		return !!this._map && this._map.hasLayer(this);
9597	},
9598
9599	// @method bringToFront: this
9600	// Brings this popup in front of other popups (in the same map pane).
9601	bringToFront: function () {
9602		if (this._map) {
9603			toFront(this._container);
9604		}
9605		return this;
9606	},
9607
9608	// @method bringToBack: this
9609	// Brings this popup to the back of other popups (in the same map pane).
9610	bringToBack: function () {
9611		if (this._map) {
9612			toBack(this._container);
9613		}
9614		return this;
9615	},
9616
9617	_prepareOpen: function (parent, layer, latlng) {
9618		if (!(layer instanceof Layer)) {
9619			latlng = layer;
9620			layer = parent;
9621		}
9622
9623		if (layer instanceof FeatureGroup) {
9624			for (var id in parent._layers) {
9625				layer = parent._layers[id];
9626				break;
9627			}
9628		}
9629
9630		if (!latlng) {
9631			if (layer.getCenter) {
9632				latlng = layer.getCenter();
9633			} else if (layer.getLatLng) {
9634				latlng = layer.getLatLng();
9635			} else {
9636				throw new Error('Unable to get source layer LatLng.');
9637			}
9638		}
9639
9640		// set overlay source to this layer
9641		this._source = layer;
9642
9643		// update the overlay (content, layout, ect...)
9644		this.update();
9645
9646		return latlng;
9647	},
9648
9649	_updateContent: function () {
9650		if (!this._content) { return; }
9651
9652		var node = this._contentNode;
9653		var content = (typeof this._content === 'function') ? this._content(this._source || this) : this._content;
9654
9655		if (typeof content === 'string') {
9656			node.innerHTML = content;
9657		} else {
9658			while (node.hasChildNodes()) {
9659				node.removeChild(node.firstChild);
9660			}
9661			node.appendChild(content);
9662		}
9663		this.fire('contentupdate');
9664	},
9665
9666	_updatePosition: function () {
9667		if (!this._map) { return; }
9668
9669		var pos = this._map.latLngToLayerPoint(this._latlng),
9670		    offset = toPoint(this.options.offset),
9671		    anchor = this._getAnchor();
9672
9673		if (this._zoomAnimated) {
9674			setPosition(this._container, pos.add(anchor));
9675		} else {
9676			offset = offset.add(pos).add(anchor);
9677		}
9678
9679		var bottom = this._containerBottom = -offset.y,
9680		    left = this._containerLeft = -Math.round(this._containerWidth / 2) + offset.x;
9681
9682		// bottom position the popup in case the height of the popup changes (images loading etc)
9683		this._container.style.bottom = bottom + 'px';
9684		this._container.style.left = left + 'px';
9685	},
9686
9687	_getAnchor: function () {
9688		return [0, 0];
9689	}
9690
9691});
9692
9693/*
9694 * @class Popup
9695 * @inherits DivOverlay
9696 * @aka L.Popup
9697 * Used to open popups in certain places of the map. Use [Map.openPopup](#map-openpopup) to
9698 * open popups while making sure that only one popup is open at one time
9699 * (recommended for usability), or use [Map.addLayer](#map-addlayer) to open as many as you want.
9700 *
9701 * @example
9702 *
9703 * If you want to just bind a popup to marker click and then open it, it's really easy:
9704 *
9705 * ```js
9706 * marker.bindPopup(popupContent).openPopup();
9707 * ```
9708 * Path overlays like polylines also have a `bindPopup` method.
9709 * Here's a more complicated way to open a popup on a map:
9710 *
9711 * ```js
9712 * var popup = L.popup()
9713 * 	.setLatLng(latlng)
9714 * 	.setContent('<p>Hello world!<br />This is a nice popup.</p>')
9715 * 	.openOn(map);
9716 * ```
9717 */
9718
9719
9720// @namespace Popup
9721var Popup = DivOverlay.extend({
9722
9723	// @section
9724	// @aka Popup options
9725	options: {
9726		// @option maxWidth: Number = 300
9727		// Max width of the popup, in pixels.
9728		maxWidth: 300,
9729
9730		// @option minWidth: Number = 50
9731		// Min width of the popup, in pixels.
9732		minWidth: 50,
9733
9734		// @option maxHeight: Number = null
9735		// If set, creates a scrollable container of the given height
9736		// inside a popup if its content exceeds it.
9737		maxHeight: null,
9738
9739		// @option autoPan: Boolean = true
9740		// Set it to `false` if you don't want the map to do panning animation
9741		// to fit the opened popup.
9742		autoPan: true,
9743
9744		// @option autoPanPaddingTopLeft: Point = null
9745		// The margin between the popup and the top left corner of the map
9746		// view after autopanning was performed.
9747		autoPanPaddingTopLeft: null,
9748
9749		// @option autoPanPaddingBottomRight: Point = null
9750		// The margin between the popup and the bottom right corner of the map
9751		// view after autopanning was performed.
9752		autoPanPaddingBottomRight: null,
9753
9754		// @option autoPanPadding: Point = Point(5, 5)
9755		// Equivalent of setting both top left and bottom right autopan padding to the same value.
9756		autoPanPadding: [5, 5],
9757
9758		// @option keepInView: Boolean = false
9759		// Set it to `true` if you want to prevent users from panning the popup
9760		// off of the screen while it is open.
9761		keepInView: false,
9762
9763		// @option closeButton: Boolean = true
9764		// Controls the presence of a close button in the popup.
9765		closeButton: true,
9766
9767		// @option autoClose: Boolean = true
9768		// Set it to `false` if you want to override the default behavior of
9769		// the popup closing when another popup is opened.
9770		autoClose: true,
9771
9772		// @option closeOnEscapeKey: Boolean = true
9773		// Set it to `false` if you want to override the default behavior of
9774		// the ESC key for closing of the popup.
9775		closeOnEscapeKey: true,
9776
9777		// @option closeOnClick: Boolean = *
9778		// Set it if you want to override the default behavior of the popup closing when user clicks
9779		// on the map. Defaults to the map's [`closePopupOnClick`](#map-closepopuponclick) option.
9780
9781		// @option className: String = ''
9782		// A custom CSS class name to assign to the popup.
9783		className: ''
9784	},
9785
9786	// @namespace Popup
9787	// @method openOn(map: Map): this
9788	// Adds the popup to the map and closes the previous one. The same as `map.openPopup(popup)`.
9789	openOn: function (map) {
9790		map.openPopup(this);
9791		return this;
9792	},
9793
9794	onAdd: function (map) {
9795		DivOverlay.prototype.onAdd.call(this, map);
9796
9797		// @namespace Map
9798		// @section Popup events
9799		// @event popupopen: PopupEvent
9800		// Fired when a popup is opened in the map
9801		map.fire('popupopen', {popup: this});
9802
9803		if (this._source) {
9804			// @namespace Layer
9805			// @section Popup events
9806			// @event popupopen: PopupEvent
9807			// Fired when a popup bound to this layer is opened
9808			this._source.fire('popupopen', {popup: this}, true);
9809			// For non-path layers, we toggle the popup when clicking
9810			// again the layer, so prevent the map to reopen it.
9811			if (!(this._source instanceof Path)) {
9812				this._source.on('preclick', stopPropagation);
9813			}
9814		}
9815	},
9816
9817	onRemove: function (map) {
9818		DivOverlay.prototype.onRemove.call(this, map);
9819
9820		// @namespace Map
9821		// @section Popup events
9822		// @event popupclose: PopupEvent
9823		// Fired when a popup in the map is closed
9824		map.fire('popupclose', {popup: this});
9825
9826		if (this._source) {
9827			// @namespace Layer
9828			// @section Popup events
9829			// @event popupclose: PopupEvent
9830			// Fired when a popup bound to this layer is closed
9831			this._source.fire('popupclose', {popup: this}, true);
9832			if (!(this._source instanceof Path)) {
9833				this._source.off('preclick', stopPropagation);
9834			}
9835		}
9836	},
9837
9838	getEvents: function () {
9839		var events = DivOverlay.prototype.getEvents.call(this);
9840
9841		if (this.options.closeOnClick !== undefined ? this.options.closeOnClick : this._map.options.closePopupOnClick) {
9842			events.preclick = this._close;
9843		}
9844
9845		if (this.options.keepInView) {
9846			events.moveend = this._adjustPan;
9847		}
9848
9849		return events;
9850	},
9851
9852	_close: function () {
9853		if (this._map) {
9854			this._map.closePopup(this);
9855		}
9856	},
9857
9858	_initLayout: function () {
9859		var prefix = 'leaflet-popup',
9860		    container = this._container = create$1('div',
9861			prefix + ' ' + (this.options.className || '') +
9862			' leaflet-zoom-animated');
9863
9864		var wrapper = this._wrapper = create$1('div', prefix + '-content-wrapper', container);
9865		this._contentNode = create$1('div', prefix + '-content', wrapper);
9866
9867		disableClickPropagation(wrapper);
9868		disableScrollPropagation(this._contentNode);
9869		on(wrapper, 'contextmenu', stopPropagation);
9870
9871		this._tipContainer = create$1('div', prefix + '-tip-container', container);
9872		this._tip = create$1('div', prefix + '-tip', this._tipContainer);
9873
9874		if (this.options.closeButton) {
9875			var closeButton = this._closeButton = create$1('a', prefix + '-close-button', container);
9876			closeButton.href = '#close';
9877			closeButton.innerHTML = '&#215;';
9878
9879			on(closeButton, 'click', this._onCloseButtonClick, this);
9880		}
9881	},
9882
9883	_updateLayout: function () {
9884		var container = this._contentNode,
9885		    style = container.style;
9886
9887		style.width = '';
9888		style.whiteSpace = 'nowrap';
9889
9890		var width = container.offsetWidth;
9891		width = Math.min(width, this.options.maxWidth);
9892		width = Math.max(width, this.options.minWidth);
9893
9894		style.width = (width + 1) + 'px';
9895		style.whiteSpace = '';
9896
9897		style.height = '';
9898
9899		var height = container.offsetHeight,
9900		    maxHeight = this.options.maxHeight,
9901		    scrolledClass = 'leaflet-popup-scrolled';
9902
9903		if (maxHeight && height > maxHeight) {
9904			style.height = maxHeight + 'px';
9905			addClass(container, scrolledClass);
9906		} else {
9907			removeClass(container, scrolledClass);
9908		}
9909
9910		this._containerWidth = this._container.offsetWidth;
9911	},
9912
9913	_animateZoom: function (e) {
9914		var pos = this._map._latLngToNewLayerPoint(this._latlng, e.zoom, e.center),
9915		    anchor = this._getAnchor();
9916		setPosition(this._container, pos.add(anchor));
9917	},
9918
9919	_adjustPan: function () {
9920		if (!this.options.autoPan) { return; }
9921		if (this._map._panAnim) { this._map._panAnim.stop(); }
9922
9923		var map = this._map,
9924		    marginBottom = parseInt(getStyle(this._container, 'marginBottom'), 10) || 0,
9925		    containerHeight = this._container.offsetHeight + marginBottom,
9926		    containerWidth = this._containerWidth,
9927		    layerPos = new Point(this._containerLeft, -containerHeight - this._containerBottom);
9928
9929		layerPos._add(getPosition(this._container));
9930
9931		var containerPos = map.layerPointToContainerPoint(layerPos),
9932		    padding = toPoint(this.options.autoPanPadding),
9933		    paddingTL = toPoint(this.options.autoPanPaddingTopLeft || padding),
9934		    paddingBR = toPoint(this.options.autoPanPaddingBottomRight || padding),
9935		    size = map.getSize(),
9936		    dx = 0,
9937		    dy = 0;
9938
9939		if (containerPos.x + containerWidth + paddingBR.x > size.x) { // right
9940			dx = containerPos.x + containerWidth - size.x + paddingBR.x;
9941		}
9942		if (containerPos.x - dx - paddingTL.x < 0) { // left
9943			dx = containerPos.x - paddingTL.x;
9944		}
9945		if (containerPos.y + containerHeight + paddingBR.y > size.y) { // bottom
9946			dy = containerPos.y + containerHeight - size.y + paddingBR.y;
9947		}
9948		if (containerPos.y - dy - paddingTL.y < 0) { // top
9949			dy = containerPos.y - paddingTL.y;
9950		}
9951
9952		// @namespace Map
9953		// @section Popup events
9954		// @event autopanstart: Event
9955		// Fired when the map starts autopanning when opening a popup.
9956		if (dx || dy) {
9957			map
9958			    .fire('autopanstart')
9959			    .panBy([dx, dy]);
9960		}
9961	},
9962
9963	_onCloseButtonClick: function (e) {
9964		this._close();
9965		stop(e);
9966	},
9967
9968	_getAnchor: function () {
9969		// Where should we anchor the popup on the source layer?
9970		return toPoint(this._source && this._source._getPopupAnchor ? this._source._getPopupAnchor() : [0, 0]);
9971	}
9972
9973});
9974
9975// @namespace Popup
9976// @factory L.popup(options?: Popup options, source?: Layer)
9977// 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.
9978var popup = function (options, source) {
9979	return new Popup(options, source);
9980};
9981
9982
9983/* @namespace Map
9984 * @section Interaction Options
9985 * @option closePopupOnClick: Boolean = true
9986 * Set it to `false` if you don't want popups to close when user clicks the map.
9987 */
9988Map.mergeOptions({
9989	closePopupOnClick: true
9990});
9991
9992
9993// @namespace Map
9994// @section Methods for Layers and Controls
9995Map.include({
9996	// @method openPopup(popup: Popup): this
9997	// Opens the specified popup while closing the previously opened (to make sure only one is opened at one time for usability).
9998	// @alternative
9999	// @method openPopup(content: String|HTMLElement, latlng: LatLng, options?: Popup options): this
10000	// Creates a popup with the specified content and options and opens it in the given point on a map.
10001	openPopup: function (popup, latlng, options) {
10002		if (!(popup instanceof Popup)) {
10003			popup = new Popup(options).setContent(popup);
10004		}
10005
10006		if (latlng) {
10007			popup.setLatLng(latlng);
10008		}
10009
10010		if (this.hasLayer(popup)) {
10011			return this;
10012		}
10013
10014		if (this._popup && this._popup.options.autoClose) {
10015			this.closePopup();
10016		}
10017
10018		this._popup = popup;
10019		return this.addLayer(popup);
10020	},
10021
10022	// @method closePopup(popup?: Popup): this
10023	// Closes the popup previously opened with [openPopup](#map-openpopup) (or the given one).
10024	closePopup: function (popup) {
10025		if (!popup || popup === this._popup) {
10026			popup = this._popup;
10027			this._popup = null;
10028		}
10029		if (popup) {
10030			this.removeLayer(popup);
10031		}
10032		return this;
10033	}
10034});
10035
10036/*
10037 * @namespace Layer
10038 * @section Popup methods example
10039 *
10040 * All layers share a set of methods convenient for binding popups to it.
10041 *
10042 * ```js
10043 * var layer = L.Polygon(latlngs).bindPopup('Hi There!').addTo(map);
10044 * layer.openPopup();
10045 * layer.closePopup();
10046 * ```
10047 *
10048 * 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.
10049 */
10050
10051// @section Popup methods
10052Layer.include({
10053
10054	// @method bindPopup(content: String|HTMLElement|Function|Popup, options?: Popup options): this
10055	// Binds a popup to the layer with the passed `content` and sets up the
10056	// necessary event listeners. If a `Function` is passed it will receive
10057	// the layer as the first argument and should return a `String` or `HTMLElement`.
10058	bindPopup: function (content, options) {
10059
10060		if (content instanceof Popup) {
10061			setOptions(content, options);
10062			this._popup = content;
10063			content._source = this;
10064		} else {
10065			if (!this._popup || options) {
10066				this._popup = new Popup(options, this);
10067			}
10068			this._popup.setContent(content);
10069		}
10070
10071		if (!this._popupHandlersAdded) {
10072			this.on({
10073				click: this._openPopup,
10074				keypress: this._onKeyPress,
10075				remove: this.closePopup,
10076				move: this._movePopup
10077			});
10078			this._popupHandlersAdded = true;
10079		}
10080
10081		return this;
10082	},
10083
10084	// @method unbindPopup(): this
10085	// Removes the popup previously bound with `bindPopup`.
10086	unbindPopup: function () {
10087		if (this._popup) {
10088			this.off({
10089				click: this._openPopup,
10090				keypress: this._onKeyPress,
10091				remove: this.closePopup,
10092				move: this._movePopup
10093			});
10094			this._popupHandlersAdded = false;
10095			this._popup = null;
10096		}
10097		return this;
10098	},
10099
10100	// @method openPopup(latlng?: LatLng): this
10101	// Opens the bound popup at the specified `latlng` or at the default popup anchor if no `latlng` is passed.
10102	openPopup: function (layer, latlng) {
10103		if (this._popup && this._map) {
10104			latlng = this._popup._prepareOpen(this, layer, latlng);
10105
10106			// open the popup on the map
10107			this._map.openPopup(this._popup, latlng);
10108		}
10109
10110		return this;
10111	},
10112
10113	// @method closePopup(): this
10114	// Closes the popup bound to this layer if it is open.
10115	closePopup: function () {
10116		if (this._popup) {
10117			this._popup._close();
10118		}
10119		return this;
10120	},
10121
10122	// @method togglePopup(): this
10123	// Opens or closes the popup bound to this layer depending on its current state.
10124	togglePopup: function (target) {
10125		if (this._popup) {
10126			if (this._popup._map) {
10127				this.closePopup();
10128			} else {
10129				this.openPopup(target);
10130			}
10131		}
10132		return this;
10133	},
10134
10135	// @method isPopupOpen(): boolean
10136	// Returns `true` if the popup bound to this layer is currently open.
10137	isPopupOpen: function () {
10138		return (this._popup ? this._popup.isOpen() : false);
10139	},
10140
10141	// @method setPopupContent(content: String|HTMLElement|Popup): this
10142	// Sets the content of the popup bound to this layer.
10143	setPopupContent: function (content) {
10144		if (this._popup) {
10145			this._popup.setContent(content);
10146		}
10147		return this;
10148	},
10149
10150	// @method getPopup(): Popup
10151	// Returns the popup bound to this layer.
10152	getPopup: function () {
10153		return this._popup;
10154	},
10155
10156	_openPopup: function (e) {
10157		var layer = e.layer || e.target;
10158
10159		if (!this._popup) {
10160			return;
10161		}
10162
10163		if (!this._map) {
10164			return;
10165		}
10166
10167		// prevent map click
10168		stop(e);
10169
10170		// if this inherits from Path its a vector and we can just
10171		// open the popup at the new location
10172		if (layer instanceof Path) {
10173			this.openPopup(e.layer || e.target, e.latlng);
10174			return;
10175		}
10176
10177		// otherwise treat it like a marker and figure out
10178		// if we should toggle it open/closed
10179		if (this._map.hasLayer(this._popup) && this._popup._source === layer) {
10180			this.closePopup();
10181		} else {
10182			this.openPopup(layer, e.latlng);
10183		}
10184	},
10185
10186	_movePopup: function (e) {
10187		this._popup.setLatLng(e.latlng);
10188	},
10189
10190	_onKeyPress: function (e) {
10191		if (e.originalEvent.keyCode === 13) {
10192			this._openPopup(e);
10193		}
10194	}
10195});
10196
10197/*
10198 * @class Tooltip
10199 * @inherits DivOverlay
10200 * @aka L.Tooltip
10201 * Used to display small texts on top of map layers.
10202 *
10203 * @example
10204 *
10205 * ```js
10206 * marker.bindTooltip("my tooltip text").openTooltip();
10207 * ```
10208 * Note about tooltip offset. Leaflet takes two options in consideration
10209 * for computing tooltip offsetting:
10210 * - the `offset` Tooltip option: it defaults to [0, 0], and it's specific to one tooltip.
10211 *   Add a positive x offset to move the tooltip to the right, and a positive y offset to
10212 *   move it to the bottom. Negatives will move to the left and top.
10213 * - the `tooltipAnchor` Icon option: this will only be considered for Marker. You
10214 *   should adapt this value if you use a custom icon.
10215 */
10216
10217
10218// @namespace Tooltip
10219var Tooltip = DivOverlay.extend({
10220
10221	// @section
10222	// @aka Tooltip options
10223	options: {
10224		// @option pane: String = 'tooltipPane'
10225		// `Map pane` where the tooltip will be added.
10226		pane: 'tooltipPane',
10227
10228		// @option offset: Point = Point(0, 0)
10229		// Optional offset of the tooltip position.
10230		offset: [0, 0],
10231
10232		// @option direction: String = 'auto'
10233		// Direction where to open the tooltip. Possible values are: `right`, `left`,
10234		// `top`, `bottom`, `center`, `auto`.
10235		// `auto` will dynamically switch between `right` and `left` according to the tooltip
10236		// position on the map.
10237		direction: 'auto',
10238
10239		// @option permanent: Boolean = false
10240		// Whether to open the tooltip permanently or only on mouseover.
10241		permanent: false,
10242
10243		// @option sticky: Boolean = false
10244		// If true, the tooltip will follow the mouse instead of being fixed at the feature center.
10245		sticky: false,
10246
10247		// @option interactive: Boolean = false
10248		// If true, the tooltip will listen to the feature events.
10249		interactive: false,
10250
10251		// @option opacity: Number = 0.9
10252		// Tooltip container opacity.
10253		opacity: 0.9
10254	},
10255
10256	onAdd: function (map) {
10257		DivOverlay.prototype.onAdd.call(this, map);
10258		this.setOpacity(this.options.opacity);
10259
10260		// @namespace Map
10261		// @section Tooltip events
10262		// @event tooltipopen: TooltipEvent
10263		// Fired when a tooltip is opened in the map.
10264		map.fire('tooltipopen', {tooltip: this});
10265
10266		if (this._source) {
10267			// @namespace Layer
10268			// @section Tooltip events
10269			// @event tooltipopen: TooltipEvent
10270			// Fired when a tooltip bound to this layer is opened.
10271			this._source.fire('tooltipopen', {tooltip: this}, true);
10272		}
10273	},
10274
10275	onRemove: function (map) {
10276		DivOverlay.prototype.onRemove.call(this, map);
10277
10278		// @namespace Map
10279		// @section Tooltip events
10280		// @event tooltipclose: TooltipEvent
10281		// Fired when a tooltip in the map is closed.
10282		map.fire('tooltipclose', {tooltip: this});
10283
10284		if (this._source) {
10285			// @namespace Layer
10286			// @section Tooltip events
10287			// @event tooltipclose: TooltipEvent
10288			// Fired when a tooltip bound to this layer is closed.
10289			this._source.fire('tooltipclose', {tooltip: this}, true);
10290		}
10291	},
10292
10293	getEvents: function () {
10294		var events = DivOverlay.prototype.getEvents.call(this);
10295
10296		if (touch && !this.options.permanent) {
10297			events.preclick = this._close;
10298		}
10299
10300		return events;
10301	},
10302
10303	_close: function () {
10304		if (this._map) {
10305			this._map.closeTooltip(this);
10306		}
10307	},
10308
10309	_initLayout: function () {
10310		var prefix = 'leaflet-tooltip',
10311		    className = prefix + ' ' + (this.options.className || '') + ' leaflet-zoom-' + (this._zoomAnimated ? 'animated' : 'hide');
10312
10313		this._contentNode = this._container = create$1('div', className);
10314	},
10315
10316	_updateLayout: function () {},
10317
10318	_adjustPan: function () {},
10319
10320	_setPosition: function (pos) {
10321		var map = this._map,
10322		    container = this._container,
10323		    centerPoint = map.latLngToContainerPoint(map.getCenter()),
10324		    tooltipPoint = map.layerPointToContainerPoint(pos),
10325		    direction = this.options.direction,
10326		    tooltipWidth = container.offsetWidth,
10327		    tooltipHeight = container.offsetHeight,
10328		    offset = toPoint(this.options.offset),
10329		    anchor = this._getAnchor();
10330
10331		if (direction === 'top') {
10332			pos = pos.add(toPoint(-tooltipWidth / 2 + offset.x, -tooltipHeight + offset.y + anchor.y, true));
10333		} else if (direction === 'bottom') {
10334			pos = pos.subtract(toPoint(tooltipWidth / 2 - offset.x, -offset.y, true));
10335		} else if (direction === 'center') {
10336			pos = pos.subtract(toPoint(tooltipWidth / 2 + offset.x, tooltipHeight / 2 - anchor.y + offset.y, true));
10337		} else if (direction === 'right' || direction === 'auto' && tooltipPoint.x < centerPoint.x) {
10338			direction = 'right';
10339			pos = pos.add(toPoint(offset.x + anchor.x, anchor.y - tooltipHeight / 2 + offset.y, true));
10340		} else {
10341			direction = 'left';
10342			pos = pos.subtract(toPoint(tooltipWidth + anchor.x - offset.x, tooltipHeight / 2 - anchor.y - offset.y, true));
10343		}
10344
10345		removeClass(container, 'leaflet-tooltip-right');
10346		removeClass(container, 'leaflet-tooltip-left');
10347		removeClass(container, 'leaflet-tooltip-top');
10348		removeClass(container, 'leaflet-tooltip-bottom');
10349		addClass(container, 'leaflet-tooltip-' + direction);
10350		setPosition(container, pos);
10351	},
10352
10353	_updatePosition: function () {
10354		var pos = this._map.latLngToLayerPoint(this._latlng);
10355		this._setPosition(pos);
10356	},
10357
10358	setOpacity: function (opacity) {
10359		this.options.opacity = opacity;
10360
10361		if (this._container) {
10362			setOpacity(this._container, opacity);
10363		}
10364	},
10365
10366	_animateZoom: function (e) {
10367		var pos = this._map._latLngToNewLayerPoint(this._latlng, e.zoom, e.center);
10368		this._setPosition(pos);
10369	},
10370
10371	_getAnchor: function () {
10372		// Where should we anchor the tooltip on the source layer?
10373		return toPoint(this._source && this._source._getTooltipAnchor && !this.options.sticky ? this._source._getTooltipAnchor() : [0, 0]);
10374	}
10375
10376});
10377
10378// @namespace Tooltip
10379// @factory L.tooltip(options?: Tooltip options, source?: Layer)
10380// 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.
10381var tooltip = function (options, source) {
10382	return new Tooltip(options, source);
10383};
10384
10385// @namespace Map
10386// @section Methods for Layers and Controls
10387Map.include({
10388
10389	// @method openTooltip(tooltip: Tooltip): this
10390	// Opens the specified tooltip.
10391	// @alternative
10392	// @method openTooltip(content: String|HTMLElement, latlng: LatLng, options?: Tooltip options): this
10393	// Creates a tooltip with the specified content and options and open it.
10394	openTooltip: function (tooltip, latlng, options) {
10395		if (!(tooltip instanceof Tooltip)) {
10396			tooltip = new Tooltip(options).setContent(tooltip);
10397		}
10398
10399		if (latlng) {
10400			tooltip.setLatLng(latlng);
10401		}
10402
10403		if (this.hasLayer(tooltip)) {
10404			return this;
10405		}
10406
10407		return this.addLayer(tooltip);
10408	},
10409
10410	// @method closeTooltip(tooltip?: Tooltip): this
10411	// Closes the tooltip given as parameter.
10412	closeTooltip: function (tooltip) {
10413		if (tooltip) {
10414			this.removeLayer(tooltip);
10415		}
10416		return this;
10417	}
10418
10419});
10420
10421/*
10422 * @namespace Layer
10423 * @section Tooltip methods example
10424 *
10425 * All layers share a set of methods convenient for binding tooltips to it.
10426 *
10427 * ```js
10428 * var layer = L.Polygon(latlngs).bindTooltip('Hi There!').addTo(map);
10429 * layer.openTooltip();
10430 * layer.closeTooltip();
10431 * ```
10432 */
10433
10434// @section Tooltip methods
10435Layer.include({
10436
10437	// @method bindTooltip(content: String|HTMLElement|Function|Tooltip, options?: Tooltip options): this
10438	// Binds a tooltip to the layer with the passed `content` and sets up the
10439	// necessary event listeners. If a `Function` is passed it will receive
10440	// the layer as the first argument and should return a `String` or `HTMLElement`.
10441	bindTooltip: function (content, options) {
10442
10443		if (content instanceof Tooltip) {
10444			setOptions(content, options);
10445			this._tooltip = content;
10446			content._source = this;
10447		} else {
10448			if (!this._tooltip || options) {
10449				this._tooltip = new Tooltip(options, this);
10450			}
10451			this._tooltip.setContent(content);
10452
10453		}
10454
10455		this._initTooltipInteractions();
10456
10457		if (this._tooltip.options.permanent && this._map && this._map.hasLayer(this)) {
10458			this.openTooltip();
10459		}
10460
10461		return this;
10462	},
10463
10464	// @method unbindTooltip(): this
10465	// Removes the tooltip previously bound with `bindTooltip`.
10466	unbindTooltip: function () {
10467		if (this._tooltip) {
10468			this._initTooltipInteractions(true);
10469			this.closeTooltip();
10470			this._tooltip = null;
10471		}
10472		return this;
10473	},
10474
10475	_initTooltipInteractions: function (remove$$1) {
10476		if (!remove$$1 && this._tooltipHandlersAdded) { return; }
10477		var onOff = remove$$1 ? 'off' : 'on',
10478		    events = {
10479			remove: this.closeTooltip,
10480			move: this._moveTooltip
10481		    };
10482		if (!this._tooltip.options.permanent) {
10483			events.mouseover = this._openTooltip;
10484			events.mouseout = this.closeTooltip;
10485			if (this._tooltip.options.sticky) {
10486				events.mousemove = this._moveTooltip;
10487			}
10488			if (touch) {
10489				events.click = this._openTooltip;
10490			}
10491		} else {
10492			events.add = this._openTooltip;
10493		}
10494		this[onOff](events);
10495		this._tooltipHandlersAdded = !remove$$1;
10496	},
10497
10498	// @method openTooltip(latlng?: LatLng): this
10499	// Opens the bound tooltip at the specified `latlng` or at the default tooltip anchor if no `latlng` is passed.
10500	openTooltip: function (layer, latlng) {
10501		if (this._tooltip && this._map) {
10502			latlng = this._tooltip._prepareOpen(this, layer, latlng);
10503
10504			// open the tooltip on the map
10505			this._map.openTooltip(this._tooltip, latlng);
10506
10507			// Tooltip container may not be defined if not permanent and never
10508			// opened.
10509			if (this._tooltip.options.interactive && this._tooltip._container) {
10510				addClass(this._tooltip._container, 'leaflet-clickable');
10511				this.addInteractiveTarget(this._tooltip._container);
10512			}
10513		}
10514
10515		return this;
10516	},
10517
10518	// @method closeTooltip(): this
10519	// Closes the tooltip bound to this layer if it is open.
10520	closeTooltip: function () {
10521		if (this._tooltip) {
10522			this._tooltip._close();
10523			if (this._tooltip.options.interactive && this._tooltip._container) {
10524				removeClass(this._tooltip._container, 'leaflet-clickable');
10525				this.removeInteractiveTarget(this._tooltip._container);
10526			}
10527		}
10528		return this;
10529	},
10530
10531	// @method toggleTooltip(): this
10532	// Opens or closes the tooltip bound to this layer depending on its current state.
10533	toggleTooltip: function (target) {
10534		if (this._tooltip) {
10535			if (this._tooltip._map) {
10536				this.closeTooltip();
10537			} else {
10538				this.openTooltip(target);
10539			}
10540		}
10541		return this;
10542	},
10543
10544	// @method isTooltipOpen(): boolean
10545	// Returns `true` if the tooltip bound to this layer is currently open.
10546	isTooltipOpen: function () {
10547		return this._tooltip.isOpen();
10548	},
10549
10550	// @method setTooltipContent(content: String|HTMLElement|Tooltip): this
vendor: 4,153 bytes, lines 10551-10695
10551	// Sets the content of the tooltip bound to this layer.
10552	setTooltipContent: function (content) {
10553		if (this._tooltip) {
10554			this._tooltip.setContent(content);
10555		}
10556		return this;
10557	},
10558
10559	// @method getTooltip(): Tooltip
10560	// Returns the tooltip bound to this layer.
10561	getTooltip: function () {
10562		return this._tooltip;
10563	},
10564
10565	_openTooltip: function (e) {
10566		var layer = e.layer || e.target;
10567
10568		if (!this._tooltip || !this._map) {
10569			return;
10570		}
10571		this.openTooltip(layer, this._tooltip.options.sticky ? e.latlng : undefined);
10572	},
10573
10574	_moveTooltip: function (e) {
10575		var latlng = e.latlng, containerPoint, layerPoint;
10576		if (this._tooltip.options.sticky && e.originalEvent) {
10577			containerPoint = this._map.mouseEventToContainerPoint(e.originalEvent);
10578			layerPoint = this._map.containerPointToLayerPoint(containerPoint);
10579			latlng = this._map.layerPointToLatLng(layerPoint);
10580		}
10581		this._tooltip.setLatLng(latlng);
10582	}
10583});
10584
10585/*
10586 * @class DivIcon
10587 * @aka L.DivIcon
10588 * @inherits Icon
10589 *
10590 * Represents a lightweight icon for markers that uses a simple `<div>`
10591 * element instead of an image. Inherits from `Icon` but ignores the `iconUrl` and shadow options.
10592 *
10593 * @example
10594 * ```js
10595 * var myIcon = L.divIcon({className: 'my-div-icon'});
10596 * // you can set .my-div-icon styles in CSS
10597 *
10598 * L.marker([50.505, 30.57], {icon: myIcon}).addTo(map);
10599 * ```
10600 *
10601 * By default, it has a 'leaflet-div-icon' CSS class and is styled as a little white square with a shadow.
10602 */
10603
10604var DivIcon = Icon.extend({
10605	options: {
10606		// @section
10607		// @aka DivIcon options
10608		iconSize: [12, 12], // also can be set through CSS
10609
10610		// iconAnchor: (Point),
10611		// popupAnchor: (Point),
10612
10613		// @option html: String|HTMLElement = ''
10614		// Custom HTML code to put inside the div element, empty by default. Alternatively,
10615		// an instance of `HTMLElement`.
10616		html: false,
10617
10618		// @option bgPos: Point = [0, 0]
10619		// Optional relative position of the background, in pixels
10620		bgPos: null,
10621
10622		className: 'leaflet-div-icon'
10623	},
10624
10625	createIcon: function (oldIcon) {
10626		var div = (oldIcon && oldIcon.tagName === 'DIV') ? oldIcon : document.createElement('div'),
10627		    options = this.options;
10628
10629		if (options.html instanceof Element) {
10630			empty(div);
10631			div.appendChild(options.html);
10632		} else {
10633			div.innerHTML = options.html !== false ? options.html : '';
10634		}
10635
10636		if (options.bgPos) {
10637			var bgPos = toPoint(options.bgPos);
10638			div.style.backgroundPosition = (-bgPos.x) + 'px ' + (-bgPos.y) + 'px';
10639		}
10640		this._setIconStyles(div, 'icon');
10641
10642		return div;
10643	},
10644
10645	createShadow: function () {
10646		return null;
10647	}
10648});
10649
10650// @factory L.divIcon(options: DivIcon options)
10651// Creates a `DivIcon` instance with the given options.
10652function divIcon(options) {
10653	return new DivIcon(options);
10654}
10655
10656Icon.Default = IconDefault;
10657
10658/*
10659 * @class GridLayer
10660 * @inherits Layer
10661 * @aka L.GridLayer
10662 *
10663 * Generic class for handling a tiled grid of HTML elements. This is the base class for all tile layers and replaces `TileLayer.Canvas`.
10664 * 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.
10665 *
10666 *
10667 * @section Synchronous usage
10668 * @example
10669 *
10670 * 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.
10671 *
10672 * ```js
10673 * var CanvasLayer = L.GridLayer.extend({
10674 *     createTile: function(coords){
10675 *         // create a <canvas> element for drawing
10676 *         var tile = L.DomUtil.create('canvas', 'leaflet-tile');
10677 *
10678 *         // setup tile width and height according to the options
10679 *         var size = this.getTileSize();
10680 *         tile.width = size.x;
10681 *         tile.height = size.y;
10682 *
10683 *         // get a canvas context and draw something on it using coords.x, coords.y and coords.z
10684 *         var ctx = tile.getContext('2d');
10685 *
10686 *         // return the tile so it can be rendered on screen
10687 *         return tile;
10688 *     }
10689 * });
10690 * ```
10691 *
10692 * @section Asynchronous usage
10693 * @example
10694 *
10695 * Tile creation can also be asynchronous, this is useful when using a third-party drawing library. O
10695nce the tile is finished drawing it can be passed to the `done()` callback.
10696 *
10697 * ```js
10698 * var CanvasLayer = L.GridLayer.extend({
10699 *     createTile: function(coords, done){
10700 *         var error;
10701 *
10702 *         // create a <canvas> element for drawing
10703 *         var tile = L.DomUtil.create('canvas', 'leaflet-tile');
10704 *
10705 *         // setup tile width and height according to the options
10706 *         var size = this.getTileSize();
10707 *         tile.width = size.x;
10708 *         tile.height = size.y;
10709 *
10710 *         // draw something asynchronously and pass the tile to the done() callback
10711 *         setTimeout(function() {
10712 *             done(error, tile);
10713 *         }, 1000);
10714 *
10715 *         return tile;
10716 *     }
10717 * });
10718 * ```
10719 *
10720 * @section
10721 */
10722
10723
10724var GridLayer = Layer.extend({
10725
10726	// @section
10727	// @aka GridLayer options
10728	options: {
10729		// @option tileSize: Number|Point = 256
10730		// Width and height of tiles in the grid. Use a number if width and height are equal, or `L.point(width, height)` otherwise.
10731		tileSize: 256,
10732
10733		// @option opacity: Number = 1.0
10734		// Opacity of the tiles. Can be used in the `createTile()` function.
10735		opacity: 1,
10736
10737		// @option updateWhenIdle: Boolean = (depends)
10738		// Load new tiles only when panning ends.
10739		// `true` by default on mobile browsers, in order to avoid too many requests and keep smooth navigation.
10740		// `false` otherwise in order to display new tiles _during_ panning, since it is easy to pan outside the
10741		// [`keepBuffer`](#gridlayer-keepbuffer) option in desktop browsers.
10742		updateWhenIdle: mobile,
10743
10744		// @option updateWhenZooming: Boolean = true
10745		// 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.
10746		updateWhenZooming: true,
10747
10748		// @option updateInterval: Number = 200
10749		// Tiles will not update more than once every `updateInterval` milliseconds when panning.
10750		updateInterval: 200,
10751
10752		// @option zIndex: Number = 1
10753		// The explicit zIndex of the tile layer.
10754		zIndex: 1,
10755
10756		// @option bounds: LatLngBounds = undefined
10757		// If set, tiles will only be loaded inside the set `LatLngBounds`.
10758		bounds: null,
10759
10760		// @option minZoom: Number = 0
10761		// The minimum zoom level down to which this layer will be displayed (inclusive).
10762		minZoom: 0,
10763
10764		// @option maxZoom: Number = undefined
10765		// The maximum zoom level up to which this layer will be displayed (inclusive).
10766		maxZoom: undefined,
10767
10768		// @option maxNativeZoom: Number = undefined
10769		// Maximum zoom number the tile source has available. If it is specified,
10770		// the tiles on all zoom levels higher than `maxNativeZoom` will be loaded
10771		// from `maxNativeZoom` level and auto-scaled.
10772		maxNativeZoom: undefined,
10773
10774		// @option minNativeZoom: Number = undefined
10775		// Minimum zoom number the tile source has available. If it is specified,
10776		// the tiles on all zoom levels lower than `minNativeZoom` will be loaded
10777		// from `minNativeZoom` level and auto-scaled.
10778		minNativeZoom: undefined,
10779
10780		// @option noWrap: Boolean = false
10781		// Whether the layer is wrapped around the antimeridian. If `true`, the
10782		// GridLayer will only be displayed once at low zoom levels. Has no
10783		// effect when the [map CRS](#map-crs) doesn't wrap around. Can be used
10784		// in combination with [`bounds`](#gridlayer-bounds) to prevent requesting
10785		// tiles outside the CRS limits.
10786		noWrap: false,
10787
10788		// @option pane: String = 'tilePane'
10789		// `Map pane` where the grid layer will be added.
10790		pane: 'tilePane',
10791
10792		// @option className: String = ''
10793		// A custom class name to assign to the tile layer. Empty by default.
10794		className: '',
10795
10796		// @option keepBuffer: Number = 2
10797		// When panning the map, keep this many rows and columns of tiles before unloading them.
10798		keepBuffer: 2
10799	},
10800
10801	initialize: function (options) {
10802		setOptions(this, options);
10803	},
10804
10805	onAdd: function () {
10806		this._initContainer();
10807
10808		this._levels = {};
10809		this._tiles = {};
10810
10811		this._resetView();
10812		this._update();
10813	},
10814
10815	beforeAdd: function (map) {
vendor: 21,432 bytes, lines 10816-11618
10816		map._addZoomLimit(this);
10817	},
10818
10819	onRemove: function (map) {
10820		this._removeAllTiles();
10821		remove(this._container);
10822		map._removeZoomLimit(this);
10823		this._container = null;
10824		this._tileZoom = undefined;
10825	},
10826
10827	// @method bringToFront: this
10828	// Brings the tile layer to the top of all tile layers.
10829	bringToFront: function () {
10830		if (this._map) {
10831			toFront(this._container);
10832			this._setAutoZIndex(Math.max);
10833		}
10834		return this;
10835	},
10836
10837	// @method bringToBack: this
10838	// Brings the tile layer to the bottom of all tile layers.
10839	bringToBack: function () {
10840		if (this._map) {
10841			toBack(this._container);
10842			this._setAutoZIndex(Math.min);
10843		}
10844		return this;
10845	},
10846
10847	// @method getContainer: HTMLElement
10848	// Returns the HTML element that contains the tiles for this layer.
10849	getContainer: function () {
10850		return this._container;
10851	},
10852
10853	// @method setOpacity(opacity: Number): this
10854	// Changes the [opacity](#gridlayer-opacity) of the grid layer.
10855	setOpacity: function (opacity) {
10856		this.options.opacity = opacity;
10857		this._updateOpacity();
10858		return this;
10859	},
10860
10861	// @method setZIndex(zIndex: Number): this
10862	// Changes the [zIndex](#gridlayer-zindex) of the grid layer.
10863	setZIndex: function (zIndex) {
10864		this.options.zIndex = zIndex;
10865		this._updateZIndex();
10866
10867		return this;
10868	},
10869
10870	// @method isLoading: Boolean
10871	// Returns `true` if any tile in the grid layer has not finished loading.
10872	isLoading: function () {
10873		return this._loading;
10874	},
10875
10876	// @method redraw: this
10877	// Causes the layer to clear all the tiles and request them again.
10878	redraw: function () {
10879		if (this._map) {
10880			this._removeAllTiles();
10881			this._update();
10882		}
10883		return this;
10884	},
10885
10886	getEvents: function () {
10887		var events = {
10888			viewprereset: this._invalidateAll,
10889			viewreset: this._resetView,
10890			zoom: this._resetView,
10891			moveend: this._onMoveEnd
10892		};
10893
10894		if (!this.options.updateWhenIdle) {
10895			// update tiles on move, but not more often than once per given interval
10896			if (!this._onMove) {
10897				this._onMove = throttle(this._onMoveEnd, this.options.updateInterval, this);
10898			}
10899
10900			events.move = this._onMove;
10901		}
10902
10903		if (this._zoomAnimated) {
10904			events.zoomanim = this._animateZoom;
10905		}
10906
10907		return events;
10908	},
10909
10910	// @section Extension methods
10911	// Layers extending `GridLayer` shall reimplement the following method.
10912	// @method createTile(coords: Object, done?: Function): HTMLElement
10913	// Called only internally, must be overridden by classes extending `GridLayer`.
10914	// Returns the `HTMLElement` corresponding to the given `coords`. If the `done` callback
10915	// is specified, it must be called when the tile has finished loading and drawing.
10916	createTile: function () {
10917		return document.createElement('div');
10918	},
10919
10920	// @section
10921	// @method getTileSize: Point
10922	// Normalizes the [tileSize option](#gridlayer-tilesize) into a point. Used by the `createTile()` method.
10923	getTileSize: function () {
10924		var s = this.options.tileSize;
10925		return s instanceof Point ? s : new Point(s, s);
10926	},
10927
10928	_updateZIndex: function () {
10929		if (this._container && this.options.zIndex !== undefined && this.options.zIndex !== null) {
10930			this._container.style.zIndex = this.options.zIndex;
10931		}
10932	},
10933
10934	_setAutoZIndex: function (compare) {
10935		// go through all other layers of the same pane, set zIndex to max + 1 (front) or min - 1 (back)
10936
10937		var layers = this.getPane().children,
10938		    edgeZIndex = -compare(-Infinity, Infinity); // -Infinity for max, Infinity for min
10939
10940		for (var i = 0, len = layers.length, zIndex; i < len; i++) {
10941
10942			zIndex = layers[i].style.zIndex;
10943
10944			if (layers[i] !== this._container && zIndex) {
10945				edgeZIndex = compare(edgeZIndex, +zIndex);
10946			}
10947		}
10948
10949		if (isFinite(edgeZIndex)) {
10950			this.options.zIndex = edgeZIndex + compare(-1, 1);
10951			this._updateZIndex();
10952		}
10953	},
10954
10955	_updateOpacity: function () {
10956		if (!this._map) { return; }
10957
10958		// IE doesn't inherit filter opacity properly, so we're forced to set it on tiles
10959		if (ielt9) { return; }
10960
10961		setOpacity(this._container, this.options.opacity);
10962
10963		var now = +new Date(),
10964		    nextFrame = false,
10965		    willPrune = false;
10966
10967		for (var key in this._tiles) {
10968			var tile = this._tiles[key];
10969			if (!tile.current || !tile.loaded) { continue; }
10970
10971			var fade = Math.min(1, (now - tile.loaded) / 200);
10972
10973			setOpacity(tile.el, fade);
10974			if (fade < 1) {
10975				nextFrame = true;
10976			} else {
10977				if (tile.active) {
10978					willPrune = true;
10979				} else {
10980					this._onOpaqueTile(tile);
10981				}
10982				tile.active = true;
10983			}
10984		}
10985
10986		if (willPrune && !this._noPrune) { this._pruneTiles(); }
10987
10988		if (nextFrame) {
10989			cancelAnimFrame(this._fadeFrame);
10990			this._fadeFrame = requestAnimFrame(this._updateOpacity, this);
10991		}
10992	},
10993
10994	_onOpaqueTile: falseFn,
10995
10996	_initContainer: function () {
10997		if (this._container) { return; }
10998
10999		this._container = create$1('div', 'leaflet-layer ' + (this.options.className || ''));
11000		this._updateZIndex();
11001
11002		if (this.options.opacity < 1) {
11003			this._updateOpacity();
11004		}
11005
11006		this.getPane().appendChild(this._container);
11007	},
11008
11009	_updateLevels: function () {
11010
11011		var zoom = this._tileZoom,
11012		    maxZoom = this.options.maxZoom;
11013
11014		if (zoom === undefined) { return undefined; }
11015
11016		for (var z in this._levels) {
11017			if (this._levels[z].el.children.length || z === zoom) {
11018				this._levels[z].el.style.zIndex = maxZoom - Math.abs(zoom - z);
11019				this._onUpdateLevel(z);
11020			} else {
11021				remove(this._levels[z].el);
11022				this._removeTilesAtZoom(z);
11023				this._onRemoveLevel(z);
11024				delete this._levels[z];
11025			}
11026		}
11027
11028		var level = this._levels[zoom],
11029		    map = this._map;
11030
11031		if (!level) {
11032			level = this._levels[zoom] = {};
11033
11034			level.el = create$1('div', 'leaflet-tile-container leaflet-zoom-animated', this._container);
11035			level.el.style.zIndex = maxZoom;
11036
11037			level.origin = map.project(map.unproject(map.getPixelOrigin()), zoom).round();
11038			level.zoom = zoom;
11039
11040			this._setZoomTransform(level, map.getCenter(), map.getZoom());
11041
11042			// force the browser to consider the newly added element for transition
11043			falseFn(level.el.offsetWidth);
11044
11045			this._onCreateLevel(level);
11046		}
11047
11048		this._level = level;
11049
11050		return level;
11051	},
11052
11053	_onUpdateLevel: falseFn,
11054
11055	_onRemoveLevel: falseFn,
11056
11057	_onCreateLevel: falseFn,
11058
11059	_pruneTiles: function () {
11060		if (!this._map) {
11061			return;
11062		}
11063
11064		var key, tile;
11065
11066		var zoom = this._map.getZoom();
11067		if (zoom > this.options.maxZoom ||
11068			zoom < this.options.minZoom) {
11069			this._removeAllTiles();
11070			return;
11071		}
11072
11073		for (key in this._tiles) {
11074			tile = this._tiles[key];
11075			tile.retain = tile.current;
11076		}
11077
11078		for (key in this._tiles) {
11079			tile = this._tiles[key];
11080			if (tile.current && !tile.active) {
11081				var coords = tile.coords;
11082				if (!this._retainParent(coords.x, coords.y, coords.z, coords.z - 5)) {
11083					this._retainChildren(coords.x, coords.y, coords.z, coords.z + 2);
11084				}
11085			}
11086		}
11087
11088		for (key in this._tiles) {
11089			if (!this._tiles[key].retain) {
11090				this._removeTile(key);
11091			}
11092		}
11093	},
11094
11095	_removeTilesAtZoom: function (zoom) {
11096		for (var key in this._tiles) {
11097			if (this._tiles[key].coords.z !== zoom) {
11098				continue;
11099			}
11100			this._removeTile(key);
11101		}
11102	},
11103
11104	_removeAllTiles: function () {
11105		for (var key in this._tiles) {
11106			this._removeTile(key);
11107		}
11108	},
11109
11110	_invalidateAll: function () {
11111		for (var z in this._levels) {
11112			remove(this._levels[z].el);
11113			this._onRemoveLevel(z);
11114			delete this._levels[z];
11115		}
11116		this._removeAllTiles();
11117
11118		this._tileZoom = undefined;
11119	},
11120
11121	_retainParent: function (x, y, z, minZoom) {
11122		var x2 = Math.floor(x / 2),
11123		    y2 = Math.floor(y / 2),
11124		    z2 = z - 1,
11125		    coords2 = new Point(+x2, +y2);
11126		coords2.z = +z2;
11127
11128		var key = this._tileCoordsToKey(coords2),
11129		    tile = this._tiles[key];
11130
11131		if (tile && tile.active) {
11132			tile.retain = true;
11133			return true;
11134
11135		} else if (tile && tile.loaded) {
11136			tile.retain = true;
11137		}
11138
11139		if (z2 > minZoom) {
11140			return this._retainParent(x2, y2, z2, minZoom);
11141		}
11142
11143		return false;
11144	},
11145
11146	_retainChildren: function (x, y, z, maxZoom) {
11147
11148		for (var i = 2 * x; i < 2 * x + 2; i++) {
11149			for (var j = 2 * y; j < 2 * y + 2; j++) {
11150
11151				var coords = new Point(i, j);
11152				coords.z = z + 1;
11153
11154				var key = this._tileCoordsToKey(coords),
11155				    tile = this._tiles[key];
11156
11157				if (tile && tile.active) {
11158					tile.retain = true;
11159					continue;
11160
11161				} else if (tile && tile.loaded) {
11162					tile.retain = true;
11163				}
11164
11165				if (z + 1 < maxZoom) {
11166					this._retainChildren(i, j, z + 1, maxZoom);
11167				}
11168			}
11169		}
11170	},
11171
11172	_resetView: function (e) {
11173		var animating = e && (e.pinch || e.flyTo);
11174		this._setView(this._map.getCenter(), this._map.getZoom(), animating, animating);
11175	},
11176
11177	_animateZoom: function (e) {
11178		this._setView(e.center, e.zoom, true, e.noUpdate);
11179	},
11180
11181	_clampZoom: function (zoom) {
11182		var options = this.options;
11183
11184		if (undefined !== options.minNativeZoom && zoom < options.minNativeZoom) {
11185			return options.minNativeZoom;
11186		}
11187
11188		if (undefined !== options.maxNativeZoom && options.maxNativeZoom < zoom) {
11189			return options.maxNativeZoom;
11190		}
11191
11192		return zoom;
11193	},
11194
11195	_setView: function (center, zoom, noPrune, noUpdate) {
11196		var tileZoom = this._clampZoom(Math.round(zoom));
11197		if ((this.options.maxZoom !== undefined && tileZoom > this.options.maxZoom) ||
11198		    (this.options.minZoom !== undefined && tileZoom < this.options.minZoom)) {
11199			tileZoom = undefined;
11200		}
11201
11202		var tileZoomChanged = this.options.updateWhenZooming && (tileZoom !== this._tileZoom);
11203
11204		if (!noUpdate || tileZoomChanged) {
11205
11206			this._tileZoom = tileZoom;
11207
11208			if (this._abortLoading) {
11209				this._abortLoading();
11210			}
11211
11212			this._updateLevels();
11213			this._resetGrid();
11214
11215			if (tileZoom !== undefined) {
11216				this._update(center);
11217			}
11218
11219			if (!noPrune) {
11220				this._pruneTiles();
11221			}
11222
11223			// Flag to prevent _updateOpacity from pruning tiles during
11224			// a zoom anim or a pinch gesture
11225			this._noPrune = !!noPrune;
11226		}
11227
11228		this._setZoomTransforms(center, zoom);
11229	},
11230
11231	_setZoomTransforms: function (center, zoom) {
11232		for (var i in this._levels) {
11233			this._setZoomTransform(this._levels[i], center, zoom);
11234		}
11235	},
11236
11237	_setZoomTransform: function (level, center, zoom) {
11238		var scale = this._map.getZoomScale(zoom, level.zoom),
11239		    translate = level.origin.multiplyBy(scale)
11240		        .subtract(this._map._getNewPixelOrigin(center, zoom)).round();
11241
11242		if (any3d) {
11243			setTransform(level.el, translate, scale);
11244		} else {
11245			setPosition(level.el, translate);
11246		}
11247	},
11248
11249	_resetGrid: function () {
11250		var map = this._map,
11251		    crs = map.options.crs,
11252		    tileSize = this._tileSize = this.getTileSize(),
11253		    tileZoom = this._tileZoom;
11254
11255		var bounds = this._map.getPixelWorldBounds(this._tileZoom);
11256		if (bounds) {
11257			this._globalTileRange = this._pxBoundsToTileRange(bounds);
11258		}
11259
11260		this._wrapX = crs.wrapLng && !this.options.noWrap && [
11261			Math.floor(map.project([0, crs.wrapLng[0]], tileZoom).x / tileSize.x),
11262			Math.ceil(map.project([0, crs.wrapLng[1]], tileZoom).x / tileSize.y)
11263		];
11264		this._wrapY = crs.wrapLat && !this.options.noWrap && [
11265			Math.floor(map.project([crs.wrapLat[0], 0], tileZoom).y / tileSize.x),
11266			Math.ceil(map.project([crs.wrapLat[1], 0], tileZoom).y / tileSize.y)
11267		];
11268	},
11269
11270	_onMoveEnd: function () {
11271		if (!this._map || this._map._animatingZoom) { return; }
11272
11273		this._update();
11274	},
11275
11276	_getTiledPixelBounds: function (center) {
11277		var map = this._map,
11278		    mapZoom = map._animatingZoom ? Math.max(map._animateToZoom, map.getZoom()) : map.getZoom(),
11279		    scale = map.getZoomScale(mapZoom, this._tileZoom),
11280		    pixelCenter = map.project(center, this._tileZoom).floor(),
11281		    halfSize = map.getSize().divideBy(scale * 2);
11282
11283		return new Bounds(pixelCenter.subtract(halfSize), pixelCenter.add(halfSize));
11284	},
11285
11286	// Private method to load tiles in the grid's active zoom level according to map bounds
11287	_update: function (center) {
11288		var map = this._map;
11289		if (!map) { return; }
11290		var zoom = this._clampZoom(map.getZoom());
11291
11292		if (center === undefined) { center = map.getCenter(); }
11293		if (this._tileZoom === undefined) { return; }	// if out of minzoom/maxzoom
11294
11295		var pixelBounds = this._getTiledPixelBounds(center),
11296		    tileRange = this._pxBoundsToTileRange(pixelBounds),
11297		    tileCenter = tileRange.getCenter(),
11298		    queue = [],
11299		    margin = this.options.keepBuffer,
11300		    noPruneRange = new Bounds(tileRange.getBottomLeft().subtract([margin, -margin]),
11301		                              tileRange.getTopRight().add([margin, -margin]));
11302
11303		// Sanity check: panic if the tile range contains Infinity somewhere.
11304		if (!(isFinite(tileRange.min.x) &&
11305		      isFinite(tileRange.min.y) &&
11306		      isFinite(tileRange.max.x) &&
11307		      isFinite(tileRange.max.y))) { throw new Error('Attempted to load an infinite number of tiles'); }
11308
11309		for (var key in this._tiles) {
11310			var c = this._tiles[key].coords;
11311			if (c.z !== this._tileZoom || !noPruneRange.contains(new Point(c.x, c.y))) {
11312				this._tiles[key].current = false;
11313			}
11314		}
11315
11316		// _update just loads more tiles. If the tile zoom level differs too much
11317		// from the map's, let _setView reset levels and prune old tiles.
11318		if (Math.abs(zoom - this._tileZoom) > 1) { this._setView(center, zoom); return; }
11319
11320		// create a queue of coordinates to load tiles from
11321		for (var j = tileRange.min.y; j <= tileRange.max.y; j++) {
11322			for (var i = tileRange.min.x; i <= tileRange.max.x; i++) {
11323				var coords = new Point(i, j);
11324				coords.z = this._tileZoom;
11325
11326				if (!this._isValidTile(coords)) { continue; }
11327
11328				var tile = this._tiles[this._tileCoordsToKey(coords)];
11329				if (tile) {
11330					tile.current = true;
11331				} else {
11332					queue.push(coords);
11333				}
11334			}
11335		}
11336
11337		// sort tile queue to load tiles in order of their distance to center
11338		queue.sort(function (a, b) {
11339			return a.distanceTo(tileCenter) - b.distanceTo(tileCenter);
11340		});
11341
11342		if (queue.length !== 0) {
11343			// if it's the first batch of tiles to load
11344			if (!this._loading) {
11345				this._loading = true;
11346				// @event loading: Event
11347				// Fired when the grid layer starts loading tiles.
11348				this.fire('loading');
11349			}
11350
11351			// create DOM fragment to append tiles in one batch
11352			var fragment = document.createDocumentFragment();
11353
11354			for (i = 0; i < queue.length; i++) {
11355				this._addTile(queue[i], fragment);
11356			}
11357
11358			this._level.el.appendChild(fragment);
11359		}
11360	},
11361
11362	_isValidTile: function (coords) {
11363		var crs = this._map.options.crs;
11364
11365		if (!crs.infinite) {
11366			// don't load tile if it's out of bounds and not wrapped
11367			var bounds = this._globalTileRange;
11368			if ((!crs.wrapLng && (coords.x < bounds.min.x || coords.x > bounds.max.x)) ||
11369			    (!crs.wrapLat && (coords.y < bounds.min.y || coords.y > bounds.max.y))) { return false; }
11370		}
11371
11372		if (!this.options.bounds) { return true; }
11373
11374		// don't load tile if it doesn't intersect the bounds in options
11375		var tileBounds = this._tileCoordsToBounds(coords);
11376		return toLatLngBounds(this.options.bounds).overlaps(tileBounds);
11377	},
11378
11379	_keyToBounds: function (key) {
11380		return this._tileCoordsToBounds(this._keyToTileCoords(key));
11381	},
11382
11383	_tileCoordsToNwSe: function (coords) {
11384		var map = this._map,
11385		    tileSize = this.getTileSize(),
11386		    nwPoint = coords.scaleBy(tileSize),
11387		    sePoint = nwPoint.add(tileSize),
11388		    nw = map.unproject(nwPoint, coords.z),
11389		    se = map.unproject(sePoint, coords.z);
11390		return [nw, se];
11391	},
11392
11393	// converts tile coordinates to its geographical bounds
11394	_tileCoordsToBounds: function (coords) {
11395		var bp = this._tileCoordsToNwSe(coords),
11396		    bounds = new LatLngBounds(bp[0], bp[1]);
11397
11398		if (!this.options.noWrap) {
11399			bounds = this._map.wrapLatLngBounds(bounds);
11400		}
11401		return bounds;
11402	},
11403	// converts tile coordinates to key for the tile cache
11404	_tileCoordsToKey: function (coords) {
11405		return coords.x + ':' + coords.y + ':' + coords.z;
11406	},
11407
11408	// converts tile cache key to coordinates
11409	_keyToTileCoords: function (key) {
11410		var k = key.split(':'),
11411		    coords = new Point(+k[0], +k[1]);
11412		coords.z = +k[2];
11413		return coords;
11414	},
11415
11416	_removeTile: function (key) {
11417		var tile = this._tiles[key];
11418		if (!tile) { return; }
11419
11420		remove(tile.el);
11421
11422		delete this._tiles[key];
11423
11424		// @event tileunload: TileEvent
11425		// Fired when a tile is removed (e.g. when a tile goes off the screen).
11426		this.fire('tileunload', {
11427			tile: tile.el,
11428			coords: this._keyToTileCoords(key)
11429		});
11430	},
11431
11432	_initTile: function (tile) {
11433		addClass(tile, 'leaflet-tile');
11434
11435		var tileSize = this.getTileSize();
11436		tile.style.width = tileSize.x + 'px';
11437		tile.style.height = tileSize.y + 'px';
11438
11439		tile.onselectstart = falseFn;
11440		tile.onmousemove = falseFn;
11441
11442		// update opacity on tiles in IE7-8 because of filter inheritance problems
11443		if (ielt9 && this.options.opacity < 1) {
11444			setOpacity(tile, this.options.opacity);
11445		}
11446
11447		// without this hack, tiles disappear after zoom on Chrome for Android
11448		// https://github.com/Leaflet/Leaflet/issues/2078
11449		if (android && !android23) {
11450			tile.style.WebkitBackfaceVisibility = 'hidden';
11451		}
11452	},
11453
11454	_addTile: function (coords, container) {
11455		var tilePos = this._getTilePos(coords),
11456		    key = this._tileCoordsToKey(coords);
11457
11458		var tile = this.createTile(this._wrapCoords(coords), bind(this._tileReady, this, coords));
11459
11460		this._initTile(tile);
11461
11462		// if createTile is defined with a second argument ("done" callback),
11463		// we know that tile is async and will be ready later; otherwise
11464		if (this.createTile.length < 2) {
11465			// mark tile as ready, but delay one frame for opacity animation to happen
11466			requestAnimFrame(bind(this._tileReady, this, coords, null, tile));
11467		}
11468
11469		setPosition(tile, tilePos);
11470
11471		// save tile in cache
11472		this._tiles[key] = {
11473			el: tile,
11474			coords: coords,
11475			current: true
11476		};
11477
11478		container.appendChild(tile);
11479		// @event tileloadstart: TileEvent
11480		// Fired when a tile is requested and starts loading.
11481		this.fire('tileloadstart', {
11482			tile: tile,
11483			coords: coords
11484		});
11485	},
11486
11487	_tileReady: function (coords, err, tile) {
11488		if (err) {
11489			// @event tileerror: TileErrorEvent
11490			// Fired when there is an error loading a tile.
11491			this.fire('tileerror', {
11492				error: err,
11493				tile: tile,
11494				coords: coords
11495			});
11496		}
11497
11498		var key = this._tileCoordsToKey(coords);
11499
11500		tile = this._tiles[key];
11501		if (!tile) { return; }
11502
11503		tile.loaded = +new Date();
11504		if (this._map._fadeAnimated) {
11505			setOpacity(tile.el, 0);
11506			cancelAnimFrame(this._fadeFrame);
11507			this._fadeFrame = requestAnimFrame(this._updateOpacity, this);
11508		} else {
11509			tile.active = true;
11510			this._pruneTiles();
11511		}
11512
11513		if (!err) {
11514			addClass(tile.el, 'leaflet-tile-loaded');
11515
11516			// @event tileload: TileEvent
11517			// Fired when a tile loads.
11518			this.fire('tileload', {
11519				tile: tile.el,
11520				coords: coords
11521			});
11522		}
11523
11524		if (this._noTilesToLoad()) {
11525			this._loading = false;
11526			// @event load: Event
11527			// Fired when the grid layer loaded all visible tiles.
11528			this.fire('load');
11529
11530			if (ielt9 || !this._map._fadeAnimated) {
11531				requestAnimFrame(this._pruneTiles, this);
11532			} else {
11533				// Wait a bit more than 0.2 secs (the duration of the tile fade-in)
11534				// to trigger a pruning.
11535				setTimeout(bind(this._pruneTiles, this), 250);
11536			}
11537		}
11538	},
11539
11540	_getTilePos: function (coords) {
11541		return coords.scaleBy(this.getTileSize()).subtract(this._level.origin);
11542	},
11543
11544	_wrapCoords: function (coords) {
11545		var newCoords = new Point(
11546			this._wrapX ? wrapNum(coords.x, this._wrapX) : coords.x,
11547			this._wrapY ? wrapNum(coords.y, this._wrapY) : coords.y);
11548		newCoords.z = coords.z;
11549		return newCoords;
11550	},
11551
11552	_pxBoundsToTileRange: function (bounds) {
11553		var tileSize = this.getTileSize();
11554		return new Bounds(
11555			bounds.min.unscaleBy(tileSize).floor(),
11556			bounds.max.unscaleBy(tileSize).ceil().subtract([1, 1]));
11557	},
11558
11559	_noTilesToLoad: function () {
11560		for (var key in this._tiles) {
11561			if (!this._tiles[key].loaded) { return false; }
11562		}
11563		return true;
11564	}
11565});
11566
11567// @factory L.gridLayer(options?: GridLayer options)
11568// Creates a new instance of GridLayer with the supplied options.
11569function gridLayer(options) {
11570	return new GridLayer(options);
11571}
11572
11573/*
11574 * @class TileLayer
11575 * @inherits GridLayer
11576 * @aka L.TileLayer
11577 * 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`.
11578 *
11579 * @example
11580 *
11581 * ```js
11582 * L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png?{foo}', {foo: 'bar', attribution: 'Map data &copy; <a href="https://www.openstreetmap.org/">OpenStreetMap</a> contributors, <a href="https://creativecommons.org/licenses/by-sa/2.0/">CC-BY-SA</a>'}).addTo(map);
11583 * ```
11584 *
11585 * @section URL template
11586 * @example
11587 *
11588 * A string of the following form:
11589 *
11590 * ```
11591 * 'http://{s}.somedomain.com/blabla/{z}/{x}/{y}{r}.png'
11592 * ```
11593 *
11594 * `{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.
11595 *
11596 * You can use custom keys in the template, which will be [evaluated](#util-template) from TileLayer options, like this:
11597 *
11598 * ```
11599 * L.tileLayer('http://{s}.somedomain.com/{foo}/{z}/{x}/{y}.png', {foo: 'bar'});
11600 * ```
11601 */
11602
11603
11604var TileLayer = GridLayer.extend({
11605
11606	// @section
11607	// @aka TileLayer options
11608	options: {
11609		// @option minZoom: Number = 0
11610		// The minimum zoom level down to which this layer will be displayed (inclusive).
11611		minZoom: 0,
11612
11613		// @option maxZoom: Number = 18
11614		// The maximum zoom level up to which this layer will be displayed (inclusive).
11615		maxZoom: 18,
11616
11617		// @option subdomains: String|String[] = 'abc'
11618		// 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
11618ings.
11619		subdomains: 'abc',
11620
11621		// @option errorTileUrl: String = ''
11622		// URL to the tile image to show in place of the tile that failed to load.
11623		errorTileUrl: '',
11624
11625		// @option zoomOffset: Number = 0
11626		// The zoom number used in tile URLs will be offset with this value.
11627		zoomOffset: 0,
11628
11629		// @option tms: Boolean = false
11630		// If `true`, inverses Y axis numbering for tiles (turn this on for [TMS](https://en.wikipedia.org/wiki/Tile_Map_Service) services).
11631		tms: false,
11632
11633		// @option zoomReverse: Boolean = false
11634		// If set to true, the zoom number used in tile URLs will be reversed (`maxZoom - zoom` instead of `zoom`)
11635		zoomReverse: false,
11636
11637		// @option detectRetina: Boolean = false
11638		// 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.
11639		detectRetina: false,
11640
11641		// @option crossOrigin: Boolean|String = false
11642		// Whether the crossOrigin attribute will be added to the tiles.
11643		// 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.
11644		// Refer to [CORS Settings](https://developer.mozilla.org/en-US/docs/Web/HTML/CORS_settings_attributes) for valid String values.
11645		crossOrigin: false
11646	},
11647
11648	initialize: function (url, options) {
11649
11650		this._url = url;
11651
11652		options = setOptions(this, options);
11653
11654		// detecting retina displays, adjusting tileSize and zoom levels
11655		if (options.detectRetina && retina && options.maxZoom > 0) {
11656
11657			options.tileSize = Math.floor(options.tileSize / 2);
11658
11659			if (!options.zoomReverse) {
11660				options.zoomOffset++;
11661				options.maxZoom--;
11662			} else {
11663				options.zoomOffset--;
11664				options.minZoom++;
11665			}
11666
11667			options.minZoom = Math.max(0, options.minZoom);
11668		}
11669
11670		if (typeof options.subdomains === 'string') {
11671			options.subdomains = options.subdomains.split('');
11672		}
11673
11674		// for https://github.com/Leaflet/Leaflet/issues/137
11675		if (!android) {
11676			this.on('tileunload', this._onTileRemove);
11677		}
11678	},
11679
11680	// @method setUrl(url: String, noRedraw?: Boolean): this
11681	// Updates the layer's URL template and redraws it (unless `noRedraw` is set to `true`).
11682	// If the URL does not change, the layer will not be redrawn unless
11683	// the noRedraw parameter is set to false.
11684	setUrl: function (url, noRedraw) {
11685		if (this._url === url && noRedraw === undefined) {
11686			noRedraw = true;
11687		}
11688
11689		this._url = url;
11690
11691		if (!noRedraw) {
11692			this.redraw();
11693		}
11694		return this;
11695	},
11696
11697	// @method createTile(coords: Object, done?: Function): HTMLElement
11698	// Called only internally, overrides GridLayer's [`createTile()`](#gridlayer-createtile)
11699	// to return an `<img>` HTML element with the appropriate image URL given `coords`. The `done`
11700	// callback is called when the tile has been loaded.
11701	createTile: function (coords, done) {
11702		var tile = document.createElement('img');
11703
11704		on(tile, 'load', bind(this._tileOnLoad, this, done, tile));
11705		on(tile, 'error', bind(this._tileOnError, this, done, tile));
11706
11707		if (this.options.crossOrigin || this.options.crossOrigin === '') {
11708			tile.crossOrigin = this.options.crossOrigin === true ? '' : this.options.crossOrigin;
11709		}
11710
11711		/*
11712		 Alt tag is set to empty string to keep screen readers from reading URL and for compliance reasons
11713		 http://www.w3.org/TR/WCAG20-TECHS/H67
11714		*/
11715		tile.alt = '';
11716
11717		/*
11718		 Set role="presentation" to force screen readers to ignore this
11719		 https://www.w3.org/TR/wai-aria/roles#textalternativecomputation
11720		*/
11721		tile.setAttribute('role', 'presentation');
11722
11723		tile.src = this.getTileUrl(coords);
11724
11725		return tile;
11726	},
11727
11728	// @section Extension methods
11729	// @uninheritable
11730	// Layers extending `TileLayer` might reimplement the following method.
11731	// @method getTileUrl(coords: Object): String
11732	// Called only internally, returns the URL for a tile given its coordinates.
11733	// Classes extending `TileLayer` can override this function to provide custom tile URL naming schemes.
11734	getTileUrl: function (coords) {
11735		var data = {
11736			r: retina ? '@2x' : '',
11737			s: this._getSubdomain(coords),
11738			x: coords.x,
11739			y: coords.y,
11740			z: this._getZoomForUrl()
11741		};
11742		if (this._map && !this._map.options.crs.infinite) {
11743			var invertedY = this._globalTileRange.max.y - coords.y;
11744			if (this.options.tms) {
11745				data['y'] = invertedY;
11746			}
vendor: 48,575 bytes, lines 11747-13639
11747			data['-y'] = invertedY;
11748		}
11749
11750		return template(this._url, extend(data, this.options));
11751	},
11752
11753	_tileOnLoad: function (done, tile) {
11754		// For https://github.com/Leaflet/Leaflet/issues/3332
11755		if (ielt9) {
11756			setTimeout(bind(done, this, null, tile), 0);
11757		} else {
11758			done(null, tile);
11759		}
11760	},
11761
11762	_tileOnError: function (done, tile, e) {
11763		var errorUrl = this.options.errorTileUrl;
11764		if (errorUrl && tile.getAttribute('src') !== errorUrl) {
11765			tile.src = errorUrl;
11766		}
11767		done(e, tile);
11768	},
11769
11770	_onTileRemove: function (e) {
11771		e.tile.onload = null;
11772	},
11773
11774	_getZoomForUrl: function () {
11775		var zoom = this._tileZoom,
11776		maxZoom = this.options.maxZoom,
11777		zoomReverse = this.options.zoomReverse,
11778		zoomOffset = this.options.zoomOffset;
11779
11780		if (zoomReverse) {
11781			zoom = maxZoom - zoom;
11782		}
11783
11784		return zoom + zoomOffset;
11785	},
11786
11787	_getSubdomain: function (tilePoint) {
11788		var index = Math.abs(tilePoint.x + tilePoint.y) % this.options.subdomains.length;
11789		return this.options.subdomains[index];
11790	},
11791
11792	// stops loading all tiles in the background layer
11793	_abortLoading: function () {
11794		var i, tile;
11795		for (i in this._tiles) {
11796			if (this._tiles[i].coords.z !== this._tileZoom) {
11797				tile = this._tiles[i].el;
11798
11799				tile.onload = falseFn;
11800				tile.onerror = falseFn;
11801
11802				if (!tile.complete) {
11803					tile.src = emptyImageUrl;
11804					remove(tile);
11805					delete this._tiles[i];
11806				}
11807			}
11808		}
11809	},
11810
11811	_removeTile: function (key) {
11812		var tile = this._tiles[key];
11813		if (!tile) { return; }
11814
11815		// Cancels any pending http requests associated with the tile
11816		// unless we're on Android's stock browser,
11817		// see https://github.com/Leaflet/Leaflet/issues/137
11818		if (!androidStock) {
11819			tile.el.setAttribute('src', emptyImageUrl);
11820		}
11821
11822		return GridLayer.prototype._removeTile.call(this, key);
11823	},
11824
11825	_tileReady: function (coords, err, tile) {
11826		if (!this._map || (tile && tile.getAttribute('src') === emptyImageUrl)) {
11827			return;
11828		}
11829
11830		return GridLayer.prototype._tileReady.call(this, coords, err, tile);
11831	}
11832});
11833
11834
11835// @factory L.tilelayer(urlTemplate: String, options?: TileLayer options)
11836// Instantiates a tile layer object given a `URL template` and optionally an options object.
11837
11838function tileLayer(url, options) {
11839	return new TileLayer(url, options);
11840}
11841
11842/*
11843 * @class TileLayer.WMS
11844 * @inherits TileLayer
11845 * @aka L.TileLayer.WMS
11846 * Used to display [WMS](https://en.wikipedia.org/wiki/Web_Map_Service) services as tile layers on the map. Extends `TileLayer`.
11847 *
11848 * @example
11849 *
11850 * ```js
11851 * var nexrad = L.tileLayer.wms("http://mesonet.agron.iastate.edu/cgi-bin/wms/nexrad/n0r.cgi", {
11852 * 	layers: 'nexrad-n0r-900913',
11853 * 	format: 'image/png',
11854 * 	transparent: true,
11855 * 	attribution: "Weather data © 2012 IEM Nexrad"
11856 * });
11857 * ```
11858 */
11859
11860var TileLayerWMS = TileLayer.extend({
11861
11862	// @section
11863	// @aka TileLayer.WMS options
11864	// If any custom options not documented here are used, they will be sent to the
11865	// WMS server as extra parameters in each request URL. This can be useful for
11866	// [non-standard vendor WMS parameters](http://docs.geoserver.org/stable/en/user/services/wms/vendor.html).
11867	defaultWmsParams: {
11868		service: 'WMS',
11869		request: 'GetMap',
11870
11871		// @option layers: String = ''
11872		// **(required)** Comma-separated list of WMS layers to show.
11873		layers: '',
11874
11875		// @option styles: String = ''
11876		// Comma-separated list of WMS styles.
11877		styles: '',
11878
11879		// @option format: String = 'image/jpeg'
11880		// WMS image format (use `'image/png'` for layers with transparency).
11881		format: 'image/jpeg',
11882
11883		// @option transparent: Boolean = false
11884		// If `true`, the WMS service will return images with transparency.
11885		transparent: false,
11886
11887		// @option version: String = '1.1.1'
11888		// Version of the WMS service to use
11889		version: '1.1.1'
11890	},
11891
11892	options: {
11893		// @option crs: CRS = null
11894		// Coordinate Reference System to use for the WMS requests, defaults to
11895		// map CRS. Don't change this if you're not sure what it means.
11896		crs: null,
11897
11898		// @option uppercase: Boolean = false
11899		// If `true`, WMS request parameter keys will be uppercase.
11900		uppercase: false
11901	},
11902
11903	initialize: function (url, options) {
11904
11905		this._url = url;
11906
11907		var wmsParams = extend({}, this.defaultWmsParams);
11908
11909		// all keys that are not TileLayer options go to WMS params
11910		for (var i in options) {
11911			if (!(i in this.options)) {
11912				wmsParams[i] = options[i];
11913			}
11914		}
11915
11916		options = setOptions(this, options);
11917
11918		var realRetina = options.detectRetina && retina ? 2 : 1;
11919		var tileSize = this.getTileSize();
11920		wmsParams.width = tileSize.x * realRetina;
11921		wmsParams.height = tileSize.y * realRetina;
11922
11923		this.wmsParams = wmsParams;
11924	},
11925
11926	onAdd: function (map) {
11927
11928		this._crs = this.options.crs || map.options.crs;
11929		this._wmsVersion = parseFloat(this.wmsParams.version);
11930
11931		var projectionKey = this._wmsVersion >= 1.3 ? 'crs' : 'srs';
11932		this.wmsParams[projectionKey] = this._crs.code;
11933
11934		TileLayer.prototype.onAdd.call(this, map);
11935	},
11936
11937	getTileUrl: function (coords) {
11938
11939		var tileBounds = this._tileCoordsToNwSe(coords),
11940		    crs = this._crs,
11941		    bounds = toBounds(crs.project(tileBounds[0]), crs.project(tileBounds[1])),
11942		    min = bounds.min,
11943		    max = bounds.max,
11944		    bbox = (this._wmsVersion >= 1.3 && this._crs === EPSG4326 ?
11945		    [min.y, min.x, max.y, max.x] :
11946		    [min.x, min.y, max.x, max.y]).join(','),
11947		    url = TileLayer.prototype.getTileUrl.call(this, coords);
11948		return url +
11949			getParamString(this.wmsParams, url, this.options.uppercase) +
11950			(this.options.uppercase ? '&BBOX=' : '&bbox=') + bbox;
11951	},
11952
11953	// @method setParams(params: Object, noRedraw?: Boolean): this
11954	// Merges an object with the new parameters and re-requests tiles on the current screen (unless `noRedraw` was set to true).
11955	setParams: function (params, noRedraw) {
11956
11957		extend(this.wmsParams, params);
11958
11959		if (!noRedraw) {
11960			this.redraw();
11961		}
11962
11963		return this;
11964	}
11965});
11966
11967
11968// @factory L.tileLayer.wms(baseUrl: String, options: TileLayer.WMS options)
11969// Instantiates a WMS tile layer object given a base URL of the WMS service and a WMS parameters/options object.
11970function tileLayerWMS(url, options) {
11971	return new TileLayerWMS(url, options);
11972}
11973
11974TileLayer.WMS = TileLayerWMS;
11975tileLayer.wms = tileLayerWMS;
11976
11977/*
11978 * @class Renderer
11979 * @inherits Layer
11980 * @aka L.Renderer
11981 *
11982 * Base class for vector renderer implementations (`SVG`, `Canvas`). Handles the
11983 * DOM container of the renderer, its bounds, and its zoom animation.
11984 *
11985 * A `Renderer` works as an implicit layer group for all `Path`s - the renderer
11986 * itself can be added or removed to the map. All paths use a renderer, which can
11987 * be implicit (the map will decide the type of renderer and use it automatically)
11988 * or explicit (using the [`renderer`](#path-renderer) option of the path).
11989 *
11990 * Do not use this class directly, use `SVG` and `Canvas` instead.
11991 *
11992 * @event update: Event
11993 * Fired when the renderer updates its bounds, center and zoom, for example when
11994 * its map has moved
11995 */
11996
11997var Renderer = Layer.extend({
11998
11999	// @section
12000	// @aka Renderer options
12001	options: {
12002		// @option padding: Number = 0.1
12003		// How much to extend the clip area around the map view (relative to its size)
12004		// e.g. 0.1 would be 10% of map view in each direction
12005		padding: 0.1,
12006
12007		// @option tolerance: Number = 0
12008		// How much to extend click tolerance round a path/object on the map
12009		tolerance : 0
12010	},
12011
12012	initialize: function (options) {
12013		setOptions(this, options);
12014		stamp(this);
12015		this._layers = this._layers || {};
12016	},
12017
12018	onAdd: function () {
12019		if (!this._container) {
12020			this._initContainer(); // defined by renderer implementations
12021
12022			if (this._zoomAnimated) {
12023				addClass(this._container, 'leaflet-zoom-animated');
12024			}
12025		}
12026
12027		this.getPane().appendChild(this._container);
12028		this._update();
12029		this.on('update', this._updatePaths, this);
12030	},
12031
12032	onRemove: function () {
12033		this.off('update', this._updatePaths, this);
12034		this._destroyContainer();
12035	},
12036
12037	getEvents: function () {
12038		var events = {
12039			viewreset: this._reset,
12040			zoom: this._onZoom,
12041			moveend: this._update,
12042			zoomend: this._onZoomEnd
12043		};
12044		if (this._zoomAnimated) {
12045			events.zoomanim = this._onAnimZoom;
12046		}
12047		return events;
12048	},
12049
12050	_onAnimZoom: function (ev) {
12051		this._updateTransform(ev.center, ev.zoom);
12052	},
12053
12054	_onZoom: function () {
12055		this._updateTransform(this._map.getCenter(), this._map.getZoom());
12056	},
12057
12058	_updateTransform: function (center, zoom) {
12059		var scale = this._map.getZoomScale(zoom, this._zoom),
12060		    position = getPosition(this._container),
12061		    viewHalf = this._map.getSize().multiplyBy(0.5 + this.options.padding),
12062		    currentCenterPoint = this._map.project(this._center, zoom),
12063		    destCenterPoint = this._map.project(center, zoom),
12064		    centerOffset = destCenterPoint.subtract(currentCenterPoint),
12065
12066		    topLeftOffset = viewHalf.multiplyBy(-scale).add(position).add(viewHalf).subtract(centerOffset);
12067
12068		if (any3d) {
12069			setTransform(this._container, topLeftOffset, scale);
12070		} else {
12071			setPosition(this._container, topLeftOffset);
12072		}
12073	},
12074
12075	_reset: function () {
12076		this._update();
12077		this._updateTransform(this._center, this._zoom);
12078
12079		for (var id in this._layers) {
12080			this._layers[id]._reset();
12081		}
12082	},
12083
12084	_onZoomEnd: function () {
12085		for (var id in this._layers) {
12086			this._layers[id]._project();
12087		}
12088	},
12089
12090	_updatePaths: function () {
12091		for (var id in this._layers) {
12092			this._layers[id]._update();
12093		}
12094	},
12095
12096	_update: function () {
12097		// Update pixel bounds of renderer container (for positioning/sizing/clipping later)
12098		// Subclasses are responsible of firing the 'update' event.
12099		var p = this.options.padding,
12100		    size = this._map.getSize(),
12101		    min = this._map.containerPointToLayerPoint(size.multiplyBy(-p)).round();
12102
12103		this._bounds = new Bounds(min, min.add(size.multiplyBy(1 + p * 2)).round());
12104
12105		this._center = this._map.getCenter();
12106		this._zoom = this._map.getZoom();
12107	}
12108});
12109
12110/*
12111 * @class Canvas
12112 * @inherits Renderer
12113 * @aka L.Canvas
12114 *
12115 * Allows vector layers to be displayed with [`<canvas>`](https://developer.mozilla.org/docs/Web/API/Canvas_API).
12116 * Inherits `Renderer`.
12117 *
12118 * Due to [technical limitations](http://caniuse.com/#search=canvas), Canvas is not
12119 * available in all web browsers, notably IE8, and overlapping geometries might
12120 * not display properly in some edge cases.
12121 *
12122 * @example
12123 *
12124 * Use Canvas by default for all paths in the map:
12125 *
12126 * ```js
12127 * var map = L.map('map', {
12128 * 	renderer: L.canvas()
12129 * });
12130 * ```
12131 *
12132 * Use a Canvas renderer with extra padding for specific vector geometries:
12133 *
12134 * ```js
12135 * var map = L.map('map');
12136 * var myRenderer = L.canvas({ padding: 0.5 });
12137 * var line = L.polyline( coordinates, { renderer: myRenderer } );
12138 * var circle = L.circle( center, { renderer: myRenderer } );
12139 * ```
12140 */
12141
12142var Canvas = Renderer.extend({
12143	getEvents: function () {
12144		var events = Renderer.prototype.getEvents.call(this);
12145		events.viewprereset = this._onViewPreReset;
12146		return events;
12147	},
12148
12149	_onViewPreReset: function () {
12150		// Set a flag so that a viewprereset+moveend+viewreset only updates&redraws once
12151		this._postponeUpdatePaths = true;
12152	},
12153
12154	onAdd: function () {
12155		Renderer.prototype.onAdd.call(this);
12156
12157		// Redraw vectors since canvas is cleared upon removal,
12158		// in case of removing the renderer itself from the map.
12159		this._draw();
12160	},
12161
12162	_initContainer: function () {
12163		var container = this._container = document.createElement('canvas');
12164
12165		on(container, 'mousemove', this._onMouseMove, this);
12166		on(container, 'click dblclick mousedown mouseup contextmenu', this._onClick, this);
12167		on(container, 'mouseout', this._handleMouseOut, this);
12168
12169		this._ctx = container.getContext('2d');
12170	},
12171
12172	_destroyContainer: function () {
12173		cancelAnimFrame(this._redrawRequest);
12174		delete this._ctx;
12175		remove(this._container);
12176		off(this._container);
12177		delete this._container;
12178	},
12179
12180	_updatePaths: function () {
12181		if (this._postponeUpdatePaths) { return; }
12182
12183		var layer;
12184		this._redrawBounds = null;
12185		for (var id in this._layers) {
12186			layer = this._layers[id];
12187			layer._update();
12188		}
12189		this._redraw();
12190	},
12191
12192	_update: function () {
12193		if (this._map._animatingZoom && this._bounds) { return; }
12194
12195		Renderer.prototype._update.call(this);
12196
12197		var b = this._bounds,
12198		    container = this._container,
12199		    size = b.getSize(),
12200		    m = retina ? 2 : 1;
12201
12202		setPosition(container, b.min);
12203
12204		// set canvas size (also clearing it); use double size on retina
12205		container.width = m * size.x;
12206		container.height = m * size.y;
12207		container.style.width = size.x + 'px';
12208		container.style.height = size.y + 'px';
12209
12210		if (retina) {
12211			this._ctx.scale(2, 2);
12212		}
12213
12214		// translate so we use the same path coordinates after canvas element moves
12215		this._ctx.translate(-b.min.x, -b.min.y);
12216
12217		// Tell paths to redraw themselves
12218		this.fire('update');
12219	},
12220
12221	_reset: function () {
12222		Renderer.prototype._reset.call(this);
12223
12224		if (this._postponeUpdatePaths) {
12225			this._postponeUpdatePaths = false;
12226			this._updatePaths();
12227		}
12228	},
12229
12230	_initPath: function (layer) {
12231		this._updateDashArray(layer);
12232		this._layers[stamp(layer)] = layer;
12233
12234		var order = layer._order = {
12235			layer: layer,
12236			prev: this._drawLast,
12237			next: null
12238		};
12239		if (this._drawLast) { this._drawLast.next = order; }
12240		this._drawLast = order;
12241		this._drawFirst = this._drawFirst || this._drawLast;
12242	},
12243
12244	_addPath: function (layer) {
12245		this._requestRedraw(layer);
12246	},
12247
12248	_removePath: function (layer) {
12249		var order = layer._order;
12250		var next = order.next;
12251		var prev = order.prev;
12252
12253		if (next) {
12254			next.prev = prev;
12255		} else {
12256			this._drawLast = prev;
12257		}
12258		if (prev) {
12259			prev.next = next;
12260		} else {
12261			this._drawFirst = next;
12262		}
12263
12264		delete layer._order;
12265
12266		delete this._layers[stamp(layer)];
12267
12268		this._requestRedraw(layer);
12269	},
12270
12271	_updatePath: function (layer) {
12272		// Redraw the union of the layer's old pixel
12273		// bounds and the new pixel bounds.
12274		this._extendRedrawBounds(layer);
12275		layer._project();
12276		layer._update();
12277		// The redraw will extend the redraw bounds
12278		// with the new pixel bounds.
12279		this._requestRedraw(layer);
12280	},
12281
12282	_updateStyle: function (layer) {
12283		this._updateDashArray(layer);
12284		this._requestRedraw(layer);
12285	},
12286
12287	_updateDashArray: function (layer) {
12288		if (typeof layer.options.dashArray === 'string') {
12289			var parts = layer.options.dashArray.split(/[, ]+/),
12290			    dashArray = [],
12291			    dashValue,
12292			    i;
12293			for (i = 0; i < parts.length; i++) {
12294				dashValue = Number(parts[i]);
12295				// Ignore dash array containing invalid lengths
12296				if (isNaN(dashValue)) { return; }
12297				dashArray.push(dashValue);
12298			}
12299			layer.options._dashArray = dashArray;
12300		} else {
12301			layer.options._dashArray = layer.options.dashArray;
12302		}
12303	},
12304
12305	_requestRedraw: function (layer) {
12306		if (!this._map) { return; }
12307
12308		this._extendRedrawBounds(layer);
12309		this._redrawRequest = this._redrawRequest || requestAnimFrame(this._redraw, this);
12310	},
12311
12312	_extendRedrawBounds: function (layer) {
12313		if (layer._pxBounds) {
12314			var padding = (layer.options.weight || 0) + 1;
12315			this._redrawBounds = this._redrawBounds || new Bounds();
12316			this._redrawBounds.extend(layer._pxBounds.min.subtract([padding, padding]));
12317			this._redrawBounds.extend(layer._pxBounds.max.add([padding, padding]));
12318		}
12319	},
12320
12321	_redraw: function () {
12322		this._redrawRequest = null;
12323
12324		if (this._redrawBounds) {
12325			this._redrawBounds.min._floor();
12326			this._redrawBounds.max._ceil();
12327		}
12328
12329		this._clear(); // clear layers in redraw bounds
12330		this._draw(); // draw layers
12331
12332		this._redrawBounds = null;
12333	},
12334
12335	_clear: function () {
12336		var bounds = this._redrawBounds;
12337		if (bounds) {
12338			var size = bounds.getSize();
12339			this._ctx.clearRect(bounds.min.x, bounds.min.y, size.x, size.y);
12340		} else {
12341			this._ctx.clearRect(0, 0, this._container.width, this._container.height);
12342		}
12343	},
12344
12345	_draw: function () {
12346		var layer, bounds = this._redrawBounds;
12347		this._ctx.save();
12348		if (bounds) {
12349			var size = bounds.getSize();
12350			this._ctx.beginPath();
12351			this._ctx.rect(bounds.min.x, bounds.min.y, size.x, size.y);
12352			this._ctx.clip();
12353		}
12354
12355		this._drawing = true;
12356
12357		for (var order = this._drawFirst; order; order = order.next) {
12358			layer = order.layer;
12359			if (!bounds || (layer._pxBounds && layer._pxBounds.intersects(bounds))) {
12360				layer._updatePath();
12361			}
12362		}
12363
12364		this._drawing = false;
12365
12366		this._ctx.restore();  // Restore state before clipping.
12367	},
12368
12369	_updatePoly: function (layer, closed) {
12370		if (!this._drawing) { return; }
12371
12372		var i, j, len2, p,
12373		    parts = layer._parts,
12374		    len = parts.length,
12375		    ctx = this._ctx;
12376
12377		if (!len) { return; }
12378
12379		ctx.beginPath();
12380
12381		for (i = 0; i < len; i++) {
12382			for (j = 0, len2 = parts[i].length; j < len2; j++) {
12383				p = parts[i][j];
12384				ctx[j ? 'lineTo' : 'moveTo'](p.x, p.y);
12385			}
12386			if (closed) {
12387				ctx.closePath();
12388			}
12389		}
12390
12391		this._fillStroke(ctx, layer);
12392
12393		// TODO optimization: 1 fill/stroke for all features with equal style instead of 1 for each feature
12394	},
12395
12396	_updateCircle: function (layer) {
12397
12398		if (!this._drawing || layer._empty()) { return; }
12399
12400		var p = layer._point,
12401		    ctx = this._ctx,
12402		    r = Math.max(Math.round(layer._radius), 1),
12403		    s = (Math.max(Math.round(layer._radiusY), 1) || r) / r;
12404
12405		if (s !== 1) {
12406			ctx.save();
12407			ctx.scale(1, s);
12408		}
12409
12410		ctx.beginPath();
12411		ctx.arc(p.x, p.y / s, r, 0, Math.PI * 2, false);
12412
12413		if (s !== 1) {
12414			ctx.restore();
12415		}
12416
12417		this._fillStroke(ctx, layer);
12418	},
12419
12420	_fillStroke: function (ctx, layer) {
12421		var options = layer.options;
12422
12423		if (options.fill) {
12424			ctx.globalAlpha = options.fillOpacity;
12425			ctx.fillStyle = options.fillColor || options.color;
12426			ctx.fill(options.fillRule || 'evenodd');
12427		}
12428
12429		if (options.stroke && options.weight !== 0) {
12430			if (ctx.setLineDash) {
12431				ctx.setLineDash(layer.options && layer.options._dashArray || []);
12432			}
12433			ctx.globalAlpha = options.opacity;
12434			ctx.lineWidth = options.weight;
12435			ctx.strokeStyle = options.color;
12436			ctx.lineCap = options.lineCap;
12437			ctx.lineJoin = options.lineJoin;
12438			ctx.stroke();
12439		}
12440	},
12441
12442	// Canvas obviously doesn't have mouse events for individual drawn objects,
12443	// so we emulate that by calculating what's under the mouse on mousemove/click manually
12444
12445	_onClick: function (e) {
12446		var point = this._map.mouseEventToLayerPoint(e), layer, clickedLayer;
12447
12448		for (var order = this._drawFirst; order; order = order.next) {
12449			layer = order.layer;
12450			if (layer.options.interactive && layer._containsPoint(point) && !this._map._draggableMoved(layer)) {
12451				clickedLayer = layer;
12452			}
12453		}
12454		if (clickedLayer)  {
12455			fakeStop(e);
12456			this._fireEvent([clickedLayer], e);
12457		}
12458	},
12459
12460	_onMouseMove: function (e) {
12461		if (!this._map || this._map.dragging.moving() || this._map._animatingZoom) { return; }
12462
12463		var point = this._map.mouseEventToLayerPoint(e);
12464		this._handleMouseHover(e, point);
12465	},
12466
12467
12468	_handleMouseOut: function (e) {
12469		var layer = this._hoveredLayer;
12470		if (layer) {
12471			// if we're leaving the layer, fire mouseout
12472			removeClass(this._container, 'leaflet-interactive');
12473			this._fireEvent([layer], e, 'mouseout');
12474			this._hoveredLayer = null;
12475			this._mouseHoverThrottled = false;
12476		}
12477	},
12478
12479	_handleMouseHover: function (e, point) {
12480		if (this._mouseHoverThrottled) {
12481			return;
12482		}
12483
12484		var layer, candidateHoveredLayer;
12485
12486		for (var order = this._drawFirst; order; order = order.next) {
12487			layer = order.layer;
12488			if (layer.options.interactive && layer._containsPoint(point)) {
12489				candidateHoveredLayer = layer;
12490			}
12491		}
12492
12493		if (candidateHoveredLayer !== this._hoveredLayer) {
12494			this._handleMouseOut(e);
12495
12496			if (candidateHoveredLayer) {
12497				addClass(this._container, 'leaflet-interactive'); // change cursor
12498				this._fireEvent([candidateHoveredLayer], e, 'mouseover');
12499				this._hoveredLayer = candidateHoveredLayer;
12500			}
12501		}
12502
12503		if (this._hoveredLayer) {
12504			this._fireEvent([this._hoveredLayer], e);
12505		}
12506
12507		this._mouseHoverThrottled = true;
12508		setTimeout(L.bind(function () {
12509			this._mouseHoverThrottled = false;
12510		}, this), 32);
12511	},
12512
12513	_fireEvent: function (layers, e, type) {
12514		this._map._fireDOMEvent(e, type || e.type, layers);
12515	},
12516
12517	_bringToFront: function (layer) {
12518		var order = layer._order;
12519
12520		if (!order) { return; }
12521
12522		var next = order.next;
12523		var prev = order.prev;
12524
12525		if (next) {
12526			next.prev = prev;
12527		} else {
12528			// Already last
12529			return;
12530		}
12531		if (prev) {
12532			prev.next = next;
12533		} else if (next) {
12534			// Update first entry unless this is the
12535			// single entry
12536			this._drawFirst = next;
12537		}
12538
12539		order.prev = this._drawLast;
12540		this._drawLast.next = order;
12541
12542		order.next = null;
12543		this._drawLast = order;
12544
12545		this._requestRedraw(layer);
12546	},
12547
12548	_bringToBack: function (layer) {
12549		var order = layer._order;
12550
12551		if (!order) { return; }
12552
12553		var next = order.next;
12554		var prev = order.prev;
12555
12556		if (prev) {
12557			prev.next = next;
12558		} else {
12559			// Already first
12560			return;
12561		}
12562		if (next) {
12563			next.prev = prev;
12564		} else if (prev) {
12565			// Update last entry unless this is the
12566			// single entry
12567			this._drawLast = prev;
12568		}
12569
12570		order.prev = null;
12571
12572		order.next = this._drawFirst;
12573		this._drawFirst.prev = order;
12574		this._drawFirst = order;
12575
12576		this._requestRedraw(layer);
12577	}
12578});
12579
12580// @factory L.canvas(options?: Renderer options)
12581// Creates a Canvas renderer with the given options.
12582function canvas$1(options) {
12583	return canvas ? new Canvas(options) : null;
12584}
12585
12586/*
12587 * Thanks to Dmitry Baranovsky and his Raphael library for inspiration!
12588 */
12589
12590
12591var vmlCreate = (function () {
12592	try {
12593		document.namespaces.add('lvml', 'urn:schemas-microsoft-com:vml');
12594		return function (name) {
12595			return document.createElement('<lvml:' + name + ' class="lvml">');
12596		};
12597	} catch (e) {
12598		return function (name) {
12599			return document.createElement('<' + name + ' xmlns="urn:schemas-microsoft.com:vml" class="lvml">');
12600		};
12601	}
12602})();
12603
12604
12605/*
12606 * @class SVG
12607 *
12608 *
12609 * VML was deprecated in 2012, which means VML functionality exists only for backwards compatibility
12610 * with old versions of Internet Explorer.
12611 */
12612
12613// mixin to redefine some SVG methods to handle VML syntax which is similar but with some differences
12614var vmlMixin = {
12615
12616	_initContainer: function () {
12617		this._container = create$1('div', 'leaflet-vml-container');
12618	},
12619
12620	_update: function () {
12621		if (this._map._animatingZoom) { return; }
12622		Renderer.prototype._update.call(this);
12623		this.fire('update');
12624	},
12625
12626	_initPath: function (layer) {
12627		var container = layer._container = vmlCreate('shape');
12628
12629		addClass(container, 'leaflet-vml-shape ' + (this.options.className || ''));
12630
12631		container.coordsize = '1 1';
12632
12633		layer._path = vmlCreate('path');
12634		container.appendChild(layer._path);
12635
12636		this._updateStyle(layer);
12637		this._layers[stamp(layer)] = layer;
12638	},
12639
12640	_addPath: function (layer) {
12641		var container = layer._container;
12642		this._container.appendChild(container);
12643
12644		if (layer.options.interactive) {
12645			layer.addInteractiveTarget(container);
12646		}
12647	},
12648
12649	_removePath: function (layer) {
12650		var container = layer._container;
12651		remove(container);
12652		layer.removeInteractiveTarget(container);
12653		delete this._layers[stamp(layer)];
12654	},
12655
12656	_updateStyle: function (layer) {
12657		var stroke = layer._stroke,
12658		    fill = layer._fill,
12659		    options = layer.options,
12660		    container = layer._container;
12661
12662		container.stroked = !!options.stroke;
12663		container.filled = !!options.fill;
12664
12665		if (options.stroke) {
12666			if (!stroke) {
12667				stroke = layer._stroke = vmlCreate('stroke');
12668			}
12669			container.appendChild(stroke);
12670			stroke.weight = options.weight + 'px';
12671			stroke.color = options.color;
12672			stroke.opacity = options.opacity;
12673
12674			if (options.dashArray) {
12675				stroke.dashStyle = isArray(options.dashArray) ?
12676				    options.dashArray.join(' ') :
12677				    options.dashArray.replace(/( *, *)/g, ' ');
12678			} else {
12679				stroke.dashStyle = '';
12680			}
12681			stroke.endcap = options.lineCap.replace('butt', 'flat');
12682			stroke.joinstyle = options.lineJoin;
12683
12684		} else if (stroke) {
12685			container.removeChild(stroke);
12686			layer._stroke = null;
12687		}
12688
12689		if (options.fill) {
12690			if (!fill) {
12691				fill = layer._fill = vmlCreate('fill');
12692			}
12693			container.appendChild(fill);
12694			fill.color = options.fillColor || options.color;
12695			fill.opacity = options.fillOpacity;
12696
12697		} else if (fill) {
12698			container.removeChild(fill);
12699			layer._fill = null;
12700		}
12701	},
12702
12703	_updateCircle: function (layer) {
12704		var p = layer._point.round(),
12705		    r = Math.round(layer._radius),
12706		    r2 = Math.round(layer._radiusY || r);
12707
12708		this._setPath(layer, layer._empty() ? 'M0 0' :
12709			'AL ' + p.x + ',' + p.y + ' ' + r + ',' + r2 + ' 0,' + (65535 * 360));
12710	},
12711
12712	_setPath: function (layer, path) {
12713		layer._path.v = path;
12714	},
12715
12716	_bringToFront: function (layer) {
12717		toFront(layer._container);
12718	},
12719
12720	_bringToBack: function (layer) {
12721		toBack(layer._container);
12722	}
12723};
12724
12725var create$2 = vml ? vmlCreate : svgCreate;
12726
12727/*
12728 * @class SVG
12729 * @inherits Renderer
12730 * @aka L.SVG
12731 *
12732 * Allows vector layers to be displayed with [SVG](https://developer.mozilla.org/docs/Web/SVG).
12733 * Inherits `Renderer`.
12734 *
12735 * Due to [technical limitations](http://caniuse.com/#search=svg), SVG is not
12736 * available in all web browsers, notably Android 2.x and 3.x.
12737 *
12738 * Although SVG is not available on IE7 and IE8, these browsers support
12739 * [VML](https://en.wikipedia.org/wiki/Vector_Markup_Language)
12740 * (a now deprecated technology), and the SVG renderer will fall back to VML in
12741 * this case.
12742 *
12743 * @example
12744 *
12745 * Use SVG by default for all paths in the map:
12746 *
12747 * ```js
12748 * var map = L.map('map', {
12749 * 	renderer: L.svg()
12750 * });
12751 * ```
12752 *
12753 * Use a SVG renderer with extra padding for specific vector geometries:
12754 *
12755 * ```js
12756 * var map = L.map('map');
12757 * var myRenderer = L.svg({ padding: 0.5 });
12758 * var line = L.polyline( coordinates, { renderer: myRenderer } );
12759 * var circle = L.circle( center, { renderer: myRenderer } );
12760 * ```
12761 */
12762
12763var SVG = Renderer.extend({
12764
12765	getEvents: function () {
12766		var events = Renderer.prototype.getEvents.call(this);
12767		events.zoomstart = this._onZoomStart;
12768		return events;
12769	},
12770
12771	_initContainer: function () {
12772		this._container = create$2('svg');
12773
12774		// makes it possible to click through svg root; we'll reset it back in individual paths
12775		this._container.setAttribute('pointer-events', 'none');
12776
12777		this._rootGroup = create$2('g');
12778		this._container.appendChild(this._rootGroup);
12779	},
12780
12781	_destroyContainer: function () {
12782		remove(this._container);
12783		off(this._container);
12784		delete this._container;
12785		delete this._rootGroup;
12786		delete this._svgSize;
12787	},
12788
12789	_onZoomStart: function () {
12790		// Drag-then-pinch interactions might mess up the center and zoom.
12791		// In this case, the easiest way to prevent this is re-do the renderer
12792		//   bounds and padding when the zooming starts.
12793		this._update();
12794	},
12795
12796	_update: function () {
12797		if (this._map._animatingZoom && this._bounds) { return; }
12798
12799		Renderer.prototype._update.call(this);
12800
12801		var b = this._bounds,
12802		    size = b.getSize(),
12803		    container = this._container;
12804
12805		// set size of svg-container if changed
12806		if (!this._svgSize || !this._svgSize.equals(size)) {
12807			this._svgSize = size;
12808			container.setAttribute('width', size.x);
12809			container.setAttribute('height', size.y);
12810		}
12811
12812		// movement: update container viewBox so that we don't have to change coordinates of individual layers
12813		setPosition(container, b.min);
12814		container.setAttribute('viewBox', [b.min.x, b.min.y, size.x, size.y].join(' '));
12815
12816		this.fire('update');
12817	},
12818
12819	// methods below are called by vector layers implementations
12820
12821	_initPath: function (layer) {
12822		var path = layer._path = create$2('path');
12823
12824		// @namespace Path
12825		// @option className: String = null
12826		// Custom class name set on an element. Only for SVG renderer.
12827		if (layer.options.className) {
12828			addClass(path, layer.options.className);
12829		}
12830
12831		if (layer.options.interactive) {
12832			addClass(path, 'leaflet-interactive');
12833		}
12834
12835		this._updateStyle(layer);
12836		this._layers[stamp(layer)] = layer;
12837	},
12838
12839	_addPath: function (layer) {
12840		if (!this._rootGroup) { this._initContainer(); }
12841		this._rootGroup.appendChild(layer._path);
12842		layer.addInteractiveTarget(layer._path);
12843	},
12844
12845	_removePath: function (layer) {
12846		remove(layer._path);
12847		layer.removeInteractiveTarget(layer._path);
12848		delete this._layers[stamp(layer)];
12849	},
12850
12851	_updatePath: function (layer) {
12852		layer._project();
12853		layer._update();
12854	},
12855
12856	_updateStyle: function (layer) {
12857		var path = layer._path,
12858		    options = layer.options;
12859
12860		if (!path) { return; }
12861
12862		if (options.stroke) {
12863			path.setAttribute('stroke', options.color);
12864			path.setAttribute('stroke-opacity', options.opacity);
12865			path.setAttribute('stroke-width', options.weight);
12866			path.setAttribute('stroke-linecap', options.lineCap);
12867			path.setAttribute('stroke-linejoin', options.lineJoin);
12868
12869			if (options.dashArray) {
12870				path.setAttribute('stroke-dasharray', options.dashArray);
12871			} else {
12872				path.removeAttribute('stroke-dasharray');
12873			}
12874
12875			if (options.dashOffset) {
12876				path.setAttribute('stroke-dashoffset', options.dashOffset);
12877			} else {
12878				path.removeAttribute('stroke-dashoffset');
12879			}
12880		} else {
12881			path.setAttribute('stroke', 'none');
12882		}
12883
12884		if (options.fill) {
12885			path.setAttribute('fill', options.fillColor || options.color);
12886			path.setAttribute('fill-opacity', options.fillOpacity);
12887			path.setAttribute('fill-rule', options.fillRule || 'evenodd');
12888		} else {
12889			path.setAttribute('fill', 'none');
12890		}
12891	},
12892
12893	_updatePoly: function (layer, closed) {
12894		this._setPath(layer, pointsToPath(layer._parts, closed));
12895	},
12896
12897	_updateCircle: function (layer) {
12898		var p = layer._point,
12899		    r = Math.max(Math.round(layer._radius), 1),
12900		    r2 = Math.max(Math.round(layer._radiusY), 1) || r,
12901		    arc = 'a' + r + ',' + r2 + ' 0 1,0 ';
12902
12903		// drawing a circle with two half-arcs
12904		var d = layer._empty() ? 'M0 0' :
12905			'M' + (p.x - r) + ',' + p.y +
12906			arc + (r * 2) + ',0 ' +
12907			arc + (-r * 2) + ',0 ';
12908
12909		this._setPath(layer, d);
12910	},
12911
12912	_setPath: function (layer, path) {
12913		layer._path.setAttribute('d', path);
12914	},
12915
12916	// SVG does not have the concept of zIndex so we resort to changing the DOM order of elements
12917	_bringToFront: function (layer) {
12918		toFront(layer._path);
12919	},
12920
12921	_bringToBack: function (layer) {
12922		toBack(layer._path);
12923	}
12924});
12925
12926if (vml) {
12927	SVG.include(vmlMixin);
12928}
12929
12930// @namespace SVG
12931// @factory L.svg(options?: Renderer options)
12932// Creates a SVG renderer with the given options.
12933function svg$1(options) {
12934	return svg || vml ? new SVG(options) : null;
12935}
12936
12937Map.include({
12938	// @namespace Map; @method getRenderer(layer: Path): Renderer
12939	// Returns the instance of `Renderer` that should be used to render the given
12940	// `Path`. It will ensure that the `renderer` options of the map and paths
12941	// are respected, and that the renderers do exist on the map.
12942	getRenderer: function (layer) {
12943		// @namespace Path; @option renderer: Renderer
12944		// Use this specific instance of `Renderer` for this path. Takes
12945		// precedence over the map's [default renderer](#map-renderer).
12946		var renderer = layer.options.renderer || this._getPaneRenderer(layer.options.pane) || this.options.renderer || this._renderer;
12947
12948		if (!renderer) {
12949			renderer = this._renderer = this._createRenderer();
12950		}
12951
12952		if (!this.hasLayer(renderer)) {
12953			this.addLayer(renderer);
12954		}
12955		return renderer;
12956	},
12957
12958	_getPaneRenderer: function (name) {
12959		if (name === 'overlayPane' || name === undefined) {
12960			return false;
12961		}
12962
12963		var renderer = this._paneRenderers[name];
12964		if (renderer === undefined) {
12965			renderer = this._createRenderer({pane: name});
12966			this._paneRenderers[name] = renderer;
12967		}
12968		return renderer;
12969	},
12970
12971	_createRenderer: function (options) {
12972		// @namespace Map; @option preferCanvas: Boolean = false
12973		// Whether `Path`s should be rendered on a `Canvas` renderer.
12974		// By default, all `Path`s are rendered in a `SVG` renderer.
12975		return (this.options.preferCanvas && canvas$1(options)) || svg$1(options);
12976	}
12977});
12978
12979/*
12980 * L.Rectangle extends Polygon and creates a rectangle when passed a LatLngBounds object.
12981 */
12982
12983/*
12984 * @class Rectangle
12985 * @aka L.Rectangle
12986 * @inherits Polygon
12987 *
12988 * A class for drawing rectangle overlays on a map. Extends `Polygon`.
12989 *
12990 * @example
12991 *
12992 * ```js
12993 * // define rectangle geographical bounds
12994 * var bounds = [[54.559322, -5.767822], [56.1210604, -3.021240]];
12995 *
12996 * // create an orange rectangle
12997 * L.rectangle(bounds, {color: "#ff7800", weight: 1}).addTo(map);
12998 *
12999 * // zoom the map to the rectangle bounds
13000 * map.fitBounds(bounds);
13001 * ```
13002 *
13003 */
13004
13005
13006var Rectangle = Polygon.extend({
13007	initialize: function (latLngBounds, options) {
13008		Polygon.prototype.initialize.call(this, this._boundsToLatLngs(latLngBounds), options);
13009	},
13010
13011	// @method setBounds(latLngBounds: LatLngBounds): this
13012	// Redraws the rectangle with the passed bounds.
13013	setBounds: function (latLngBounds) {
13014		return this.setLatLngs(this._boundsToLatLngs(latLngBounds));
13015	},
13016
13017	_boundsToLatLngs: function (latLngBounds) {
13018		latLngBounds = toLatLngBounds(latLngBounds);
13019		return [
13020			latLngBounds.getSouthWest(),
13021			latLngBounds.getNorthWest(),
13022			latLngBounds.getNorthEast(),
13023			latLngBounds.getSouthEast()
13024		];
13025	}
13026});
13027
13028
13029// @factory L.rectangle(latLngBounds: LatLngBounds, options?: Polyline options)
13030function rectangle(latLngBounds, options) {
13031	return new Rectangle(latLngBounds, options);
13032}
13033
13034SVG.create = create$2;
13035SVG.pointsToPath = pointsToPath;
13036
13037GeoJSON.geometryToLayer = geometryToLayer;
13038GeoJSON.coordsToLatLng = coordsToLatLng;
13039GeoJSON.coordsToLatLngs = coordsToLatLngs;
13040GeoJSON.latLngToCoords = latLngToCoords;
13041GeoJSON.latLngsToCoords = latLngsToCoords;
13042GeoJSON.getFeature = getFeature;
13043GeoJSON.asFeature = asFeature;
13044
13045/*
13046 * L.Handler.BoxZoom is used to add shift-drag zoom interaction to the map
13047 * (zoom to a selected bounding box), enabled by default.
13048 */
13049
13050// @namespace Map
13051// @section Interaction Options
13052Map.mergeOptions({
13053	// @option boxZoom: Boolean = true
13054	// Whether the map can be zoomed to a rectangular area specified by
13055	// dragging the mouse while pressing the shift key.
13056	boxZoom: true
13057});
13058
13059var BoxZoom = Handler.extend({
13060	initialize: function (map) {
13061		this._map = map;
13062		this._container = map._container;
13063		this._pane = map._panes.overlayPane;
13064		this._resetStateTimeout = 0;
13065		map.on('unload', this._destroy, this);
13066	},
13067
13068	addHooks: function () {
13069		on(this._container, 'mousedown', this._onMouseDown, this);
13070	},
13071
13072	removeHooks: function () {
13073		off(this._container, 'mousedown', this._onMouseDown, this);
13074	},
13075
13076	moved: function () {
13077		return this._moved;
13078	},
13079
13080	_destroy: function () {
13081		remove(this._pane);
13082		delete this._pane;
13083	},
13084
13085	_resetState: function () {
13086		this._resetStateTimeout = 0;
13087		this._moved = false;
13088	},
13089
13090	_clearDeferredResetState: function () {
13091		if (this._resetStateTimeout !== 0) {
13092			clearTimeout(this._resetStateTimeout);
13093			this._resetStateTimeout = 0;
13094		}
13095	},
13096
13097	_onMouseDown: function (e) {
13098		if (!e.shiftKey || ((e.which !== 1) && (e.button !== 1))) { return false; }
13099
13100		// Clear the deferred resetState if it hasn't executed yet, otherwise it
13101		// will interrupt the interaction and orphan a box element in the container.
13102		this._clearDeferredResetState();
13103		this._resetState();
13104
13105		disableTextSelection();
13106		disableImageDrag();
13107
13108		this._startPoint = this._map.mouseEventToContainerPoint(e);
13109
13110		on(document, {
13111			contextmenu: stop,
13112			mousemove: this._onMouseMove,
13113			mouseup: this._onMouseUp,
13114			keydown: this._onKeyDown
13115		}, this);
13116	},
13117
13118	_onMouseMove: function (e) {
13119		if (!this._moved) {
13120			this._moved = true;
13121
13122			this._box = create$1('div', 'leaflet-zoom-box', this._container);
13123			addClass(this._container, 'leaflet-crosshair');
13124
13125			this._map.fire('boxzoomstart');
13126		}
13127
13128		this._point = this._map.mouseEventToContainerPoint(e);
13129
13130		var bounds = new Bounds(this._point, this._startPoint),
13131		    size = bounds.getSize();
13132
13133		setPosition(this._box, bounds.min);
13134
13135		this._box.style.width  = size.x + 'px';
13136		this._box.style.height = size.y + 'px';
13137	},
13138
13139	_finish: function () {
13140		if (this._moved) {
13141			remove(this._box);
13142			removeClass(this._container, 'leaflet-crosshair');
13143		}
13144
13145		enableTextSelection();
13146		enableImageDrag();
13147
13148		off(document, {
13149			contextmenu: stop,
13150			mousemove: this._onMouseMove,
13151			mouseup: this._onMouseUp,
13152			keydown: this._onKeyDown
13153		}, this);
13154	},
13155
13156	_onMouseUp: function (e) {
13157		if ((e.which !== 1) && (e.button !== 1)) { return; }
13158
13159		this._finish();
13160
13161		if (!this._moved) { return; }
13162		// Postpone to next JS tick so internal click event handling
13163		// still see it as "moved".
13164		this._clearDeferredResetState();
13165		this._resetStateTimeout = setTimeout(bind(this._resetState, this), 0);
13166
13167		var bounds = new LatLngBounds(
13168		        this._map.containerPointToLatLng(this._startPoint),
13169		        this._map.containerPointToLatLng(this._point));
13170
13171		this._map
13172			.fitBounds(bounds)
13173			.fire('boxzoomend', {boxZoomBounds: bounds});
13174	},
13175
13176	_onKeyDown: function (e) {
13177		if (e.keyCode === 27) {
13178			this._finish();
13179		}
13180	}
13181});
13182
13183// @section Handlers
13184// @property boxZoom: Handler
13185// Box (shift-drag with mouse) zoom handler.
13186Map.addInitHook('addHandler', 'boxZoom', BoxZoom);
13187
13188/*
13189 * L.Handler.DoubleClickZoom is used to handle double-click zoom on the map, enabled by default.
13190 */
13191
13192// @namespace Map
13193// @section Interaction Options
13194
13195Map.mergeOptions({
13196	// @option doubleClickZoom: Boolean|String = true
13197	// Whether the map can be zoomed in by double clicking on it and
13198	// zoomed out by double clicking while holding shift. If passed
13199	// `'center'`, double-click zoom will zoom to the center of the
13200	//  view regardless of where the mouse was.
13201	doubleClickZoom: true
13202});
13203
13204var DoubleClickZoom = Handler.extend({
13205	addHooks: function () {
13206		this._map.on('dblclick', this._onDoubleClick, this);
13207	},
13208
13209	removeHooks: function () {
13210		this._map.off('dblclick', this._onDoubleClick, this);
13211	},
13212
13213	_onDoubleClick: function (e) {
13214		var map = this._map,
13215		    oldZoom = map.getZoom(),
13216		    delta = map.options.zoomDelta,
13217		    zoom = e.originalEvent.shiftKey ? oldZoom - delta : oldZoom + delta;
13218
13219		if (map.options.doubleClickZoom === 'center') {
13220			map.setZoom(zoom);
13221		} else {
13222			map.setZoomAround(e.containerPoint, zoom);
13223		}
13224	}
13225});
13226
13227// @section Handlers
13228//
13229// Map properties include interaction handlers that allow you to control
13230// interaction behavior in runtime, enabling or disabling certain features such
13231// as dragging or touch zoom (see `Handler` methods). For example:
13232//
13233// ```js
13234// map.doubleClickZoom.disable();
13235// ```
13236//
13237// @property doubleClickZoom: Handler
13238// Double click zoom handler.
13239Map.addInitHook('addHandler', 'doubleClickZoom', DoubleClickZoom);
13240
13241/*
13242 * L.Handler.MapDrag is used to make the map draggable (with panning inertia), enabled by default.
13243 */
13244
13245// @namespace Map
13246// @section Interaction Options
13247Map.mergeOptions({
13248	// @option dragging: Boolean = true
13249	// Whether the map be draggable with mouse/touch or not.
13250	dragging: true,
13251
13252	// @section Panning Inertia Options
13253	// @option inertia: Boolean = *
13254	// If enabled, panning of the map will have an inertia effect where
13255	// the map builds momentum while dragging and continues moving in
13256	// the same direction for some time. Feels especially nice on touch
13257	// devices. Enabled by default unless running on old Android devices.
13258	inertia: !android23,
13259
13260	// @option inertiaDeceleration: Number = 3000
13261	// The rate with which the inertial movement slows down, in pixels/second².
13262	inertiaDeceleration: 3400, // px/s^2
13263
13264	// @option inertiaMaxSpeed: Number = Infinity
13265	// Max speed of the inertial movement, in pixels/second.
13266	inertiaMaxSpeed: Infinity, // px/s
13267
13268	// @option easeLinearity: Number = 0.2
13269	easeLinearity: 0.2,
13270
13271	// TODO refactor, move to CRS
13272	// @option worldCopyJump: Boolean = false
13273	// With this option enabled, the map tracks when you pan to another "copy"
13274	// of the world and seamlessly jumps to the original one so that all overlays
13275	// like markers and vector layers are still visible.
13276	worldCopyJump: false,
13277
13278	// @option maxBoundsViscosity: Number = 0.0
13279	// If `maxBounds` is set, this option will control how solid the bounds
13280	// are when dragging the map around. The default value of `0.0` allows the
13281	// user to drag outside the bounds at normal speed, higher values will
13282	// slow down map dragging outside bounds, and `1.0` makes the bounds fully
13283	// solid, preventing the user from dragging outside the bounds.
13284	maxBoundsViscosity: 0.0
13285});
13286
13287var Drag = Handler.extend({
13288	addHooks: function () {
13289		if (!this._draggable) {
13290			var map = this._map;
13291
13292			this._draggable = new Draggable(map._mapPane, map._container);
13293
13294			this._draggable.on({
13295				dragstart: this._onDragStart,
13296				drag: this._onDrag,
13297				dragend: this._onDragEnd
13298			}, this);
13299
13300			this._draggable.on('predrag', this._onPreDragLimit, this);
13301			if (map.options.worldCopyJump) {
13302				this._draggable.on('predrag', this._onPreDragWrap, this);
13303				map.on('zoomend', this._onZoomEnd, this);
13304
13305				map.whenReady(this._onZoomEnd, this);
13306			}
13307		}
13308		addClass(this._map._container, 'leaflet-grab leaflet-touch-drag');
13309		this._draggable.enable();
13310		this._positions = [];
13311		this._times = [];
13312	},
13313
13314	removeHooks: function () {
13315		removeClass(this._map._container, 'leaflet-grab');
13316		removeClass(this._map._container, 'leaflet-touch-drag');
13317		this._draggable.disable();
13318	},
13319
13320	moved: function () {
13321		return this._draggable && this._draggable._moved;
13322	},
13323
13324	moving: function () {
13325		return this._draggable && this._draggable._moving;
13326	},
13327
13328	_onDragStart: function () {
13329		var map = this._map;
13330
13331		map._stop();
13332		if (this._map.options.maxBounds && this._map.options.maxBoundsViscosity) {
13333			var bounds = toLatLngBounds(this._map.options.maxBounds);
13334
13335			this._offsetLimit = toBounds(
13336				this._map.latLngToContainerPoint(bounds.getNorthWest()).multiplyBy(-1),
13337				this._map.latLngToContainerPoint(bounds.getSouthEast()).multiplyBy(-1)
13338					.add(this._map.getSize()));
13339
13340			this._viscosity = Math.min(1.0, Math.max(0.0, this._map.options.maxBoundsViscosity));
13341		} else {
13342			this._offsetLimit = null;
13343		}
13344
13345		map
13346		    .fire('movestart')
13347		    .fire('dragstart');
13348
13349		if (map.options.inertia) {
13350			this._positions = [];
13351			this._times = [];
13352		}
13353	},
13354
13355	_onDrag: function (e) {
13356		if (this._map.options.inertia) {
13357			var time = this._lastTime = +new Date(),
13358			    pos = this._lastPos = this._draggable._absPos || this._draggable._newPos;
13359
13360			this._positions.push(pos);
13361			this._times.push(time);
13362
13363			this._prunePositions(time);
13364		}
13365
13366		this._map
13367		    .fire('move', e)
13368		    .fire('drag', e);
13369	},
13370
13371	_prunePositions: function (time) {
13372		while (this._positions.length > 1 && time - this._times[0] > 50) {
13373			this._positions.shift();
13374			this._times.shift();
13375		}
13376	},
13377
13378	_onZoomEnd: function () {
13379		var pxCenter = this._map.getSize().divideBy(2),
13380		    pxWorldCenter = this._map.latLngToLayerPoint([0, 0]);
13381
13382		this._initialWorldOffset = pxWorldCenter.subtract(pxCenter).x;
13383		this._worldWidth = this._map.getPixelWorldBounds().getSize().x;
13384	},
13385
13386	_viscousLimit: function (value, threshold) {
13387		return value - (value - threshold) * this._viscosity;
13388	},
13389
13390	_onPreDragLimit: function () {
13391		if (!this._viscosity || !this._offsetLimit) { return; }
13392
13393		var offset = this._draggable._newPos.subtract(this._draggable._startPos);
13394
13395		var limit = this._offsetLimit;
13396		if (offset.x < limit.min.x) { offset.x = this._viscousLimit(offset.x, limit.min.x); }
13397		if (offset.y < limit.min.y) { offset.y = this._viscousLimit(offset.y, limit.min.y); }
13398		if (offset.x > limit.max.x) { offset.x = this._viscousLimit(offset.x, limit.max.x); }
13399		if (offset.y > limit.max.y) { offset.y = this._viscousLimit(offset.y, limit.max.y); }
13400
13401		this._draggable._newPos = this._draggable._startPos.add(offset);
13402	},
13403
13404	_onPreDragWrap: function () {
13405		// TODO refactor to be able to adjust map pane position after zoom
13406		var worldWidth = this._worldWidth,
13407		    halfWidth = Math.round(worldWidth / 2),
13408		    dx = this._initialWorldOffset,
13409		    x = this._draggable._newPos.x,
13410		    newX1 = (x - halfWidth + dx) % worldWidth + halfWidth - dx,
13411		    newX2 = (x + halfWidth + dx) % worldWidth - halfWidth - dx,
13412		    newX = Math.abs(newX1 + dx) < Math.abs(newX2 + dx) ? newX1 : newX2;
13413
13414		this._draggable._absPos = this._draggable._newPos.clone();
13415		this._draggable._newPos.x = newX;
13416	},
13417
13418	_onDragEnd: function (e) {
13419		var map = this._map,
13420		    options = map.options,
13421
13422		    noInertia = !options.inertia || this._times.length < 2;
13423
13424		map.fire('dragend', e);
13425
13426		if (noInertia) {
13427			map.fire('moveend');
13428
13429		} else {
13430			this._prunePositions(+new Date());
13431
13432			var direction = this._lastPos.subtract(this._positions[0]),
13433			    duration = (this._lastTime - this._times[0]) / 1000,
13434			    ease = options.easeLinearity,
13435
13436			    speedVector = direction.multiplyBy(ease / duration),
13437			    speed = speedVector.distanceTo([0, 0]),
13438
13439			    limitedSpeed = Math.min(options.inertiaMaxSpeed, speed),
13440			    limitedSpeedVector = speedVector.multiplyBy(limitedSpeed / speed),
13441
13442			    decelerationDuration = limitedSpeed / (options.inertiaDeceleration * ease),
13443			    offset = limitedSpeedVector.multiplyBy(-decelerationDuration / 2).round();
13444
13445			if (!offset.x && !offset.y) {
13446				map.fire('moveend');
13447
13448			} else {
13449				offset = map._limitOffset(offset, map.options.maxBounds);
13450
13451				requestAnimFrame(function () {
13452					map.panBy(offset, {
13453						duration: decelerationDuration,
13454						easeLinearity: ease,
13455						noMoveStart: true,
13456						animate: true
13457					});
13458				});
13459			}
13460		}
13461	}
13462});
13463
13464// @section Handlers
13465// @property dragging: Handler
13466// Map dragging handler (by both mouse and touch).
13467Map.addInitHook('addHandler', 'dragging', Drag);
13468
13469/*
13470 * L.Map.Keyboard is handling keyboard interaction with the map, enabled by default.
13471 */
13472
13473// @namespace Map
13474// @section Keyboard Navigation Options
13475Map.mergeOptions({
13476	// @option keyboard: Boolean = true
13477	// Makes the map focusable and allows users to navigate the map with keyboard
13478	// arrows and `+`/`-` keys.
13479	keyboard: true,
13480
13481	// @option keyboardPanDelta: Number = 80
13482	// Amount of pixels to pan when pressing an arrow key.
13483	keyboardPanDelta: 80
13484});
13485
13486var Keyboard = Handler.extend({
13487
13488	keyCodes: {
13489		left:    [37],
13490		right:   [39],
13491		down:    [40],
13492		up:      [38],
13493		zoomIn:  [187, 107, 61, 171],
13494		zoomOut: [189, 109, 54, 173]
13495	},
13496
13497	initialize: function (map) {
13498		this._map = map;
13499
13500		this._setPanDelta(map.options.keyboardPanDelta);
13501		this._setZoomDelta(map.options.zoomDelta);
13502	},
13503
13504	addHooks: function () {
13505		var container = this._map._container;
13506
13507		// make the container focusable by tabbing
13508		if (container.tabIndex <= 0) {
13509			container.tabIndex = '0';
13510		}
13511
13512		on(container, {
13513			focus: this._onFocus,
13514			blur: this._onBlur,
13515			mousedown: this._onMouseDown
13516		}, this);
13517
13518		this._map.on({
13519			focus: this._addHooks,
13520			blur: this._removeHooks
13521		}, this);
13522	},
13523
13524	removeHooks: function () {
13525		this._removeHooks();
13526
13527		off(this._map._container, {
13528			focus: this._onFocus,
13529			blur: this._onBlur,
13530			mousedown: this._onMouseDown
13531		}, this);
13532
13533		this._map.off({
13534			focus: this._addHooks,
13535			blur: this._removeHooks
13536		}, this);
13537	},
13538
13539	_onMouseDown: function () {
13540		if (this._focused) { return; }
13541
13542		var body = document.body,
13543		    docEl = document.documentElement,
13544		    top = body.scrollTop || docEl.scrollTop,
13545		    left = body.scrollLeft || docEl.scrollLeft;
13546
13547		this._map._container.focus();
13548
13549		window.scrollTo(left, top);
13550	},
13551
13552	_onFocus: function () {
13553		this._focused = true;
13554		this._map.fire('focus');
13555	},
13556
13557	_onBlur: function () {
13558		this._focused = false;
13559		this._map.fire('blur');
13560	},
13561
13562	_setPanDelta: function (panDelta) {
13563		var keys = this._panKeys = {},
13564		    codes = this.keyCodes,
13565		    i, len;
13566
13567		for (i = 0, len = codes.left.length; i < len; i++) {
13568			keys[codes.left[i]] = [-1 * panDelta, 0];
13569		}
13570		for (i = 0, len = codes.right.length; i < len; i++) {
13571			keys[codes.right[i]] = [panDelta, 0];
13572		}
13573		for (i = 0, len = codes.down.length; i < len; i++) {
13574			keys[codes.down[i]] = [0, panDelta];
13575		}
13576		for (i = 0, len = codes.up.length; i < len; i++) {
13577			keys[codes.up[i]] = [0, -1 * panDelta];
13578		}
13579	},
13580
13581	_setZoomDelta: function (zoomDelta) {
13582		var keys = this._zoomKeys = {},
13583		    codes = this.keyCodes,
13584		    i, len;
13585
13586		for (i = 0, len = codes.zoomIn.length; i < len; i++) {
13587			keys[codes.zoomIn[i]] = zoomDelta;
13588		}
13589		for (i = 0, len = codes.zoomOut.length; i < len; i++) {
13590			keys[codes.zoomOut[i]] = -zoomDelta;
13591		}
13592	},
13593
13594	_addHooks: function () {
13595		on(document, 'keydown', this._onKeyDown, this);
13596	},
13597
13598	_removeHooks: function () {
13599		off(document, 'keydown', this._onKeyDown, this);
13600	},
13601
13602	_onKeyDown: function (e) {
13603		if (e.altKey || e.ctrlKey || e.metaKey) { return; }
13604
13605		var key = e.keyCode,
13606		    map = this._map,
13607		    offset;
13608
13609		if (key in this._panKeys) {
13610			if (!map._panAnim || !map._panAnim._inProgress) {
13611				offset = this._panKeys[key];
13612				if (e.shiftKey) {
13613					offset = toPoint(offset).multiplyBy(3);
13614				}
13615
13616				map.panBy(offset);
13617
13618				if (map.options.maxBounds) {
13619					map.panInsideBounds(map.options.maxBounds);
13620				}
13621			}
13622		} else if (key in this._zoomKeys) {
13623			map.setZoom(map.getZoom() + (e.shiftKey ? 3 : 1) * this._zoomKeys[key]);
13624
13625		} else if (key === 27 && map._popup && map._popup.options.closeOnEscapeKey) {
13626			map.closePopup();
13627
13628		} else {
13629			return;
13630		}
13631
13632		stop(e);
13633	}
13634});
13635
13636// @section Handlers
13637// @section Handlers
13638// @property keyboard: Handler
13639// Keyboard navigation handler.
13640Map.addInitHook('addHandler', 'keyboard', Keyboard);
13641
13642/*
13643 * L.Handler.ScrollWheelZoom is used by L.Map to enable mouse scroll wheel zoom on the map.
13644 */
13645
13646// @namespace Map
13647// @section Interaction Options
13648Map.mergeOptions({
13649	// @section Mousewheel options
13650	// @option scrollWheelZoom: Boolean|String = true
13651	// Whether the map can be zoomed by using the mouse wheel. If passed `'center'`,
13652	// it will zoom to the center of the view regardless of where the mouse was.
13653	scrollWheelZoom: true,
13654
13655	// @option wheelDebounceTime: Number = 40
13656	// Limits the rate at which a wheel can fire (in milliseconds). By default
13657	// user can't zoom via wheel more often than once per 40 ms.
13658	wheelDebounceTime: 40,
13659
13660	// @option wheelPxPerZoomLevel: Number = 60
13661	// How many scroll pixels (as reported by [L.DomEvent.getWheelDelta](#domevent-getwheeldelta))
13662	// mean a change of one full zoom level. Smaller values will make wheel-zooming
13663	// faster (and vice versa).
13664	wheelPxPerZoomLevel: 60
13665});
13666
13667var ScrollWheelZoom = Handler.extend({
13668	addHooks: function () {
13669		on(this._map._container, 'mousewheel', this._onWheelScroll, this);
13670
13671		this._delta = 0;
13672	},
13673
13674	removeHooks: function () {
13675		off(this._map._container, 'mousewheel', this._onWheelScroll, this);
13676	},
13677
13678	_onWheelScroll: function (e) {
13679		var delta = getWheelDelta(e);
13680
13681		var debounce = this._map.options.wheelDebounceTime;
13682
13683		this._delta += delta;
13684		this._lastMousePos = this._map.mouseEventToContainerPoint(e);
13685
13686		if (!this._startTime) {
13687			this._startTime = +new Date();
13688		}
13689
13690		var left = Math.max(debounce - (+new Date() - this._startTime), 0);
13691
13692		clearTimeout(this._timer);
13693		this._timer = setTimeout(bind(this._performZoom, this), left);
13694
13695		stop(e);
13696	},
13697
13698	_performZoom: function () {
13699		var map = this._map,
13700		    zoom = map.getZoom(),
13701		    snap = this._map.options.zoomSnap || 0;
13702
13703		map._stop(); // stop panning and fly animations if any
13704
13705		// map the delta with a sigmoid function to -4..4 range leaning on -1..1
13706		var d2 = this._delta / (this._map.options.wheelPxPerZoomLevel * 4),
13707		    d3 = 4 * Math.log(2 / (1 + Math.exp(-Math.abs(d2)))) / Math.LN2,
13708		    d4 = snap ? Math.ceil(d3 / snap) * snap : d3,
13709		    delta = map._limitZoom(zoom + (this._delta > 0 ? d4 : -d4)) - zoom;
13710
13711		this._delta = 0;
13712		this._startTime = null;
13713
13714		if (!delta) { return; }
13715
13716		if (map.options.scrollWheelZoom === 'center') {
13717			map.setZoom(zoom + delta);
13718		} else {
13719			map.setZoomAround(this._lastMousePos, zoom + delta);
13720		}
13721	}
13722});
13723
13724// @section Handlers
13725// @property scrollWheelZoom: Handler
13726// Scroll wheel zoom handler.
13727Map.addInitHook('addHandler', 'scrollWheelZoom', ScrollWheelZoom);
13728
13729/*
13730 * L.Map.Tap is used to enable mobile hacks like quick taps and long hold.
13731 */
13732
13733// @namespace Map
13734// @section Interaction Options
13735Map.mergeOptions({
13736	// @section Touch interaction options
13737	// @option tap: Boolean = true
13738	// Enables mobile hacks for supporting instant taps (fixing 200ms click
13739	// delay on iOS/Android) and touch holds (fired as `contextmenu` events).
13740	tap: true,
13741
13742	// @option tapTolerance: Number = 15
13743	// The max number of pixels a user can shift his finger during touch
13744	// for it to be considered a valid tap.
13745	tapTolerance: 15
13746});
13747
13748var Tap = Handler.extend({
13749	addHooks: function () {
13750		on(this._map._container, 'touchstart', this._onDown, this);
13751	},
13752
13753	removeHooks: function () {
13754		off(this._map._container, 'touchstart', this._onDown, this);
13755	},
13756
13757	_onDown: function (e) {
13758		if (!e.touches) { return; }
13759
13760		preventDefault(e);
13761
13762		this._fireClick = true;
13763
13764		// don't simulate click or track longpress if more than 1 touch
13765		if (e.touches.length > 1) {
13766			this._fireClick = false;
13767			clearTimeout(this._holdTimeout);
13768			return;
13769		}
13770
13771		var first = e.touches[0],
13772		    el = first.target;
13773
13774		this._startPos = this._newPos = new Point(first.clientX, first.clientY);
13775
13776		// if touching a link, highlight it
13777		if (el.tagName && el.tagName.toLowerCase() === 'a') {
13778			addClass(el, 'leaflet-active');
13779		}
13780
13781		// simulate long hold but setting a timeout
13782		this._holdTimeout = setTimeout(bind(function () {
13783			if (this._isTapValid()) {
13784				this._fireClick = false;
13785				this._onUp();
13786				this._simulateEvent('contextmenu', first);
13787			}
13788		}, this), 1000);
13789
13790		this._simulateEvent('mousedown', first);
13791
13792		on(document, {
13793			touchmove: this._onMove,
13794			touchend: this._onUp
13795		}, this);
13796	},
13797
13798	_onUp: function (e) {
13799		clearTimeout(this._holdTimeout);
13800
13801		off(document, {
13802			touchmove: this._onMove,
13803			touchend: this._onUp
13804		}, this);
13805
13806		if (this._fireClick && e && e.changedTouches) {
13807
13808			var first = e.changedTouches[0],
13809			    el = first.target;
13810
13811			if (el && el.tagName && el.tagName.toLowerCase() === 'a') {
13812				removeClass(el, 'leaflet-active');
13813			}
13814
13815			this._simulateEvent('mouseup', first);
13816
13817			// simulate click if the touch didn't move too much
13818			if (this._isTapValid()) {
13819				this._simulateEvent('click', first);
13820			}
13821		}
13822	},
13823
13824	_isTapValid: function () {
13825		return this._newPos.distanceTo(this._startPos) <= this._map.options.tapTolerance;
13826	},
13827
13828	_onMove: function (e) {
13829		var first = e.touches[0];
13830		this._newPos = new Point(first.clientX, first.clientY);
13831		this._simulateEvent('mousemove', first);
13832	},
13833
13834	_simulateEvent: function (type, e) {
13835		var simulatedEvent = document.createEvent('MouseEvents');
13836
13837		simulatedEvent._simulated = true;
13838		e.target._simulatedClick = true;
13839
13840		simulatedEvent.initMouseEvent(
13841		        type, true, true, window, 1,
13842		        e.screenX, e.screenY,
13843		        e.clientX, e.clientY,
13844		        false, false, false, false, 0, null);
13845
13846		e.target.dispatchEvent(simulatedEvent);
13847	}
13848});
13849
13850// @section Handlers
13851// @property tap: Handler
13852// Mobile touch hacks (quick tap and touch hold) handler.
13853if (touch && !pointer) {
13854	Map.addInitHook('addHandler', 'tap', Tap);
13855}
13856
13857/*
13858 * L.Handler.TouchZoom is used by L.Map to add pinch zoom on supp
vendor: 4,276 bytes, lines 13858-13998
13858orted mobile browsers.
13859 */
13860
13861// @namespace Map
13862// @section Interaction Options
13863Map.mergeOptions({
13864	// @section Touch interaction options
13865	// @option touchZoom: Boolean|String = *
13866	// Whether the map can be zoomed by touch-dragging with two fingers. If
13867	// passed `'center'`, it will zoom to the center of the view regardless of
13868	// where the touch events (fingers) were. Enabled for touch-capable web
13869	// browsers except for old Androids.
13870	touchZoom: touch && !android23,
13871
13872	// @option bounceAtZoomLimits: Boolean = true
13873	// Set it to false if you don't want the map to zoom beyond min/max zoom
13874	// and then bounce back when pinch-zooming.
13875	bounceAtZoomLimits: true
13876});
13877
13878var TouchZoom = Handler.extend({
13879	addHooks: function () {
13880		addClass(this._map._container, 'leaflet-touch-zoom');
13881		on(this._map._container, 'touchstart', this._onTouchStart, this);
13882	},
13883
13884	removeHooks: function () {
13885		removeClass(this._map._container, 'leaflet-touch-zoom');
13886		off(this._map._container, 'touchstart', this._onTouchStart, this);
13887	},
13888
13889	_onTouchStart: function (e) {
13890		var map = this._map;
13891		if (!e.touches || e.touches.length !== 2 || map._animatingZoom || this._zooming) { return; }
13892
13893		var p1 = map.mouseEventToContainerPoint(e.touches[0]),
13894		    p2 = map.mouseEventToContainerPoint(e.touches[1]);
13895
13896		this._centerPoint = map.getSize()._divideBy(2);
13897		this._startLatLng = map.containerPointToLatLng(this._centerPoint);
13898		if (map.options.touchZoom !== 'center') {
13899			this._pinchStartLatLng = map.containerPointToLatLng(p1.add(p2)._divideBy(2));
13900		}
13901
13902		this._startDist = p1.distanceTo(p2);
13903		this._startZoom = map.getZoom();
13904
13905		this._moved = false;
13906		this._zooming = true;
13907
13908		map._stop();
13909
13910		on(document, 'touchmove', this._onTouchMove, this);
13911		on(document, 'touchend', this._onTouchEnd, this);
13912
13913		preventDefault(e);
13914	},
13915
13916	_onTouchMove: function (e) {
13917		if (!e.touches || e.touches.length !== 2 || !this._zooming) { return; }
13918
13919		var map = this._map,
13920		    p1 = map.mouseEventToContainerPoint(e.touches[0]),
13921		    p2 = map.mouseEventToContainerPoint(e.touches[1]),
13922		    scale = p1.distanceTo(p2) / this._startDist;
13923
13924		this._zoom = map.getScaleZoom(scale, this._startZoom);
13925
13926		if (!map.options.bounceAtZoomLimits && (
13927			(this._zoom < map.getMinZoom() && scale < 1) ||
13928			(this._zoom > map.getMaxZoom() && scale > 1))) {
13929			this._zoom = map._limitZoom(this._zoom);
13930		}
13931
13932		if (map.options.touchZoom === 'center') {
13933			this._center = this._startLatLng;
13934			if (scale === 1) { return; }
13935		} else {
13936			// Get delta from pinch to center, so centerLatLng is delta applied to initial pinchLatLng
13937			var delta = p1._add(p2)._divideBy(2)._subtract(this._centerPoint);
13938			if (scale === 1 && delta.x === 0 && delta.y === 0) { return; }
13939			this._center = map.unproject(map.project(this._pinchStartLatLng, this._zoom).subtract(delta), this._zoom);
13940		}
13941
13942		if (!this._moved) {
13943			map._moveStart(true, false);
13944			this._moved = true;
13945		}
13946
13947		cancelAnimFrame(this._animRequest);
13948
13949		var moveFn = bind(map._move, map, this._center, this._zoom, {pinch: true, round: false});
13950		this._animRequest = requestAnimFrame(moveFn, this, true);
13951
13952		preventDefault(e);
13953	},
13954
13955	_onTouchEnd: function () {
13956		if (!this._moved || !this._zooming) {
13957			this._zooming = false;
13958			return;
13959		}
13960
13961		this._zooming = false;
13962		cancelAnimFrame(this._animRequest);
13963
13964		off(document, 'touchmove', this._onTouchMove);
13965		off(document, 'touchend', this._onTouchEnd);
13966
13967		// Pinch updates GridLayers' levels only when zoomSnap is off, so zoomSnap becomes noUpdate.
13968		if (this._map.options.zoomAnimation) {
13969			this._map._animateZoom(this._center, this._map._limitZoom(this._zoom), true, this._map.options.zoomSnap);
13970		} else {
13971			this._map._resetView(this._center, this._map._limitZoom(this._zoom));
13972		}
13973	}
13974});
13975
13976// @section Handlers
13977// @property touchZoom: Handler
13978// Touch zoom handler.
13979Map.addInitHook('addHandler', 'touchZoom', TouchZoom);
13980
13981Map.BoxZoom = BoxZoom;
13982Map.DoubleClickZoom = DoubleClickZoom;
13983Map.Drag = Drag;
13984Map.Keyboard = Keyboard;
13985Map.ScrollWheelZoom = ScrollWheelZoom;
13986Map.Tap = Tap;
13987Map.TouchZoom = TouchZoom;
13988
13989Object.freeze = freeze;
13990
13991exports.version = version;
13992exports.Control = Control;
13993exports.control = control;
13994exports.Browser = Browser;
13995exports.Evented = Evented;
13996exports.Mixin = Mixin;
13997exports.Util = Util;
13998exports.Class = Class;
13999exports.Handler = Handler;
14000exports.extend = extend;
14001exports.bind = bind;
14002exports.stamp = stamp;
14003exports.setOptions = setOptions;
14004exports.DomEvent = DomEvent;
14005exports.DomUtil = DomUtil;
14006exports.PosAnimation = PosAnimation;
14007exports.Draggable = Draggable;
14008exports.LineUtil = LineUtil;
14009exports.PolyUtil = PolyUtil;
14010exports.Point = Point;
14011exports.point = toPoint;
14012exports.Bounds = Bounds;
14013exports.bounds = toBounds;
14014exports.Transformation = Transformation;
14015exports.transformation = toTransformation;
14016exports.Projection = index;
14017exports.LatLng = LatLng;
14018exports.latLng = toLatLng;
14019exports.LatLngBounds = LatLngBounds;
14020exports.latLngBounds = toLatLngBounds;
14021exports.CRS = CRS;
14022exports.GeoJSON = GeoJSON;
14023exports.geoJSON = geoJSON;
14024exports.geoJson = geoJson;
14025exports.Layer = Layer;
14026exports.LayerGroup = LayerGroup;
14027exports.layerGroup = layerGroup;
14028exports.FeatureGroup = FeatureGroup;
14029exports.featureGroup = featureGroup;
14030exports.ImageOverlay = ImageOverlay;
14031exports.imageOverlay = imageOverlay;
14032exports.VideoOverlay = VideoOverlay;
14033exports.videoOverlay = videoOverlay;
14034exports.SVGOverlay = SVGOverlay;
14035exports.svgOverlay = svgOverlay;
14036exports.DivOverlay = DivOverlay;
14037exports.Popup = Popup;
14038exports.popup = popup;
14039exports.Tooltip = Tooltip;
14040exports.tooltip = tooltip;
14041exports.Icon = Icon;
14042exports.icon = icon;
14043exports.DivIcon = DivIcon;
14044exports.divIcon = divIcon;
14045exports.Marker = Marker;
14046exports.marker = marker;
14047exports.TileLayer = TileLayer;
14048exports.tileLayer = tileLayer;
14049exports.GridLayer = GridLayer;
14050exports.gridLayer = gridLayer;
14051exports.SVG = SVG;
14052exports.svg = svg$1;
14053exports.Renderer = Renderer;
14054exports.Canvas = Canvas;
14055exports.canvas = canvas$1;
14056exports.Path = Path;
14057exports.CircleMarker = CircleMarker;
14058exports.circleMarker = circleMarker;
14059exports.Circle = Circle;
14060exports.circle = circle;
14061exports.Polyline = Polyline;
14062exports.polyline = polyline;
14063exports.Polygon = Polygon;
14064exports.polygon = polygon;
14065exports.Rectangle = Rectangle;
14066exports.rectangle = rectangle;
14067exports.Map = Map;
14068exports.map = createMap;
14069
14070var oldL = window.L;
14071exports.noConflict = function() {
14072	window.L = oldL;
14073	return this;
14074}
14075
14076// Always export us to window global (see #2364)
14077window.L = exports;
14078
14079})));
14080//# sourceMappingURL=leaflet-src.js.map

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