PageSourceSearch

https://villemoirieu.fr/lib/leaflet/leaflet-src.js

js villemoirieu.fr collected 2026-10-02 20:56:22 UTC 392,037 bytes, 13,870 lines download raw bytes

1/* @preserve
2 * Leaflet 1.3.4+Detached: 0e566b2ad5e696ba9f79a9d48a7e51c8f4892441.0e566b2, a JS library for interactive maps. http://leafletjs.com
3 * (c) 2010-2018 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.3.4+HEAD.0e566b2";
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.
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,558 bytes, lines 1602-1878
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 SphericalMercator = {
1652
1653	R: 6378137,
1654	MAX_LATITUDE: 85.0511287798,
1655
1656	project: function (latlng) {
1657		var d = Math.PI / 180,
1658		    max = this.MAX_LATITUDE,
1659		    lat = Math.max(Math.min(max, latlng.lat), -max),
1660		    sin = Math.sin(lat * d);
1661
1662		return new Point(
1663			this.R * latlng.lng * d,
1664			this.R * Math.log((1 + sin) / (1 - sin)) / 2);
1665	},
1666
1667	unproject: function (point) {
1668		var d = 180 / Math.PI;
1669
1670		return new LatLng(
1671			(2 * Math.atan(Math.exp(point.y / this.R)) - (Math.PI / 2)) * d,
1672			point.x * d / this.R);
1673	},
1674
1675	bounds: (function () {
1676		var d = 6378137 * Math.PI;
1677		return new Bounds([-d, -d], [d, d]);
1678	})()
1679};
1680
1681/*
1682 * @class Transformation
1683 * @aka L.Transformation
1684 *
1685 * Represents an affine transformation: a set of coefficients `a`, `b`, `c`, `d`
1686 * for transforming a point of a form `(x, y)` into `(a*x + b, c*y + d)` and doing
1687 * the reverse. Used by Leaflet in its projections code.
1688 *
1689 * @example
1690 *
1691 * ```js
1692 * var transformation = L.transformation(2, 5, -1, 10),
1693 * 	p = L.point(1, 2),
1694 * 	p2 = transformation.transform(p), //  L.point(7, 8)
1695 * 	p3 = transformation.untransform(p2); //  L.point(1, 2)
1696 * ```
1697 */
1698
1699
1700// factory new L.Transformation(a: Number, b: Number, c: Number, d: Number)
1701// Creates a `Transformation` object with the given coefficients.
1702function Transformation(a, b, c, d) {
1703	if (isArray(a)) {
1704		// use array properties
1705		this._a = a[0];
1706		this._b = a[1];
1707		this._c = a[2];
1708		this._d = a[3];
1709		return;
1710	}
1711	this._a = a;
1712	this._b = b;
1713	this._c = c;
1714	this._d = d;
1715}
1716
1717Transformation.prototype = {
1718	// @method transform(point: Point, scale?: Number): Point
1719	// Returns a transformed point, optionally multiplied by the given scale.
1720	// Only accepts actual `L.Point` instances, not arrays.
1721	transform: function (point, scale) { // (Point, Number) -> Point
1722		return this._transform(point.clone(), scale);
1723	},
1724
1725	// destructive transform (faster)
1726	_transform: function (point, scale) {
1727		scale = scale || 1;
1728		point.x = scale * (this._a * point.x + this._b);
1729		point.y = scale * (this._c * point.y + this._d);
1730		return point;
1731	},
1732
1733	// @method untransform(point: Point, scale?: Number): Point
1734	// Returns the reverse transformation of the given point, optionally divided
1735	// by the given scale. Only accepts actual `L.Point` instances, not arrays.
1736	untransform: function (point, scale) {
1737		scale = scale || 1;
1738		return new Point(
1739		        (point.x / scale - this._b) / this._a,
1740		        (point.y / scale - this._d) / this._c);
1741	}
1742};
1743
1744// factory L.transformation(a: Number, b: Number, c: Number, d: Number)
1745
1746// @factory L.transformation(a: Number, b: Number, c: Number, d: Number)
1747// Instantiates a Transformation object with the given coefficients.
1748
1749// @alternative
1750// @factory L.transformation(coefficients: Array): Transformation
1751// Expects an coefficients array of the form
1752// `[a: Number, b: Number, c: Number, d: Number]`.
1753
1754function toTransformation(a, b, c, d) {
1755	return new Transformation(a, b, c, d);
1756}
1757
1758/*
1759 * @namespace CRS
1760 * @crs L.CRS.EPSG3857
1761 *
1762 * The most common CRS for online maps, used by almost all free and commercial
1763 * tile providers. Uses Spherical Mercator projection. Set in by default in
1764 * Map's `crs` option.
1765 */
1766
1767var EPSG3857 = extend({}, Earth, {
1768	code: 'EPSG:3857',
1769	projection: SphericalMercator,
1770
1771	transformation: (function () {
1772		var scale = 0.5 / (Math.PI * SphericalMercator.R);
1773		return toTransformation(scale, 0.5, -scale, 0.5);
1774	}())
1775});
1776
1777var EPSG900913 = extend({}, EPSG3857, {
1778	code: 'EPSG:900913'
1779});
1780
1781// @namespace SVG; @section
1782// There are several static functions which can be called without instantiating L.SVG:
1783
1784// @function create(name: String): SVGElement
1785// Returns a instance of [SVGElement](https://developer.mozilla.org/docs/Web/API/SVGElement),
1786// corresponding to the class name passed. For example, using 'line' will return
1787// an instance of [SVGLineElement](https://developer.mozilla.org/docs/Web/API/SVGLineElement).
1788function svgCreate(name) {
1789	return document.createElementNS('http://www.w3.org/2000/svg', name);
1790}
1791
1792// @function pointsToPath(rings: Point[], closed: Boolean): String
1793// Generates a SVG path string for multiple rings, with each ring turning
1794// into "M..L..L.." instructions
1795function pointsToPath(rings, closed) {
1796	var str = '',
1797	i, j, len, len2, points, p;
1798
1799	for (i = 0, len = rings.length; i < len; i++) {
1800		points = rings[i];
1801
1802		for (j = 0, len2 = points.length; j < len2; j++) {
1803			p = points[j];
1804			str += (j ? 'L' : 'M') + p.x + ' ' + p.y;
1805		}
1806
1807		// closes the ring for polygons; "x" is VML syntax
1808		str += closed ? (svg ? 'z' : 'x') : '';
1809	}
1810
1811	// SVG complains about empty path strings
1812	return str || 'M0 0';
1813}
1814
1815/*
1816 * @namespace Browser
1817 * @aka L.Browser
1818 *
1819 * A namespace with static properties for browser/feature detection used by Leaflet internally.
1820 *
1821 * @example
1822 *
1823 * ```js
1824 * if (L.Browser.ielt9) {
1825 *   alert('Upgrade your browser, dude!');
1826 * }
1827 * ```
1828 */
1829
1830var style$1 = document.documentElement.style;
1831
1832// @property ie: Boolean; `true` for all Internet Explorer versions (not Edge).
1833var ie = 'ActiveXObject' in window;
1834
1835// @property ielt9: Boolean; `true` for Internet Explorer versions less than 9.
1836var ielt9 = ie && !document.addEventListener;
1837
1838// @property edge: Boolean; `true` for the Edge web browser.
1839var edge = 'msLaunchUri' in navigator && !('documentMode' in document);
1840
1841// @property webkit: Boolean;
1842// `true` for webkit-based browsers like Chrome and Safari (including mobile versions).
1843var webkit = userAgentContains('webkit');
1844
1845// @property android: Boolean
1846// `true` for any browser running on an Android platform.
1847var android = userAgentContains('android');
1848
1849// @property android23: Boolean; `true` for browsers running on Android 2 or Android 3.
1850var android23 = userAgentContains('android 2') || userAgentContains('android 3');
1851
1852/* See https://stackoverflow.com/a/17961266 for details on detecting stock Android */
1853var webkitVer = parseInt(/WebKit\/([0-9]+)|$/.exec(navigator.userAgent)[1], 10); // also matches AppleWebKit
1854// @property androidStock: Boolean; `true` for the Android stock browser (i.e. not Chrome)
1855var androidStock = android && userAgentContains('Google') && webkitVer < 537 && !('AudioNode' in window);
1856
1857// @property opera: Boolean; `true` for the Opera browser
1858var opera = !!window.opera;
1859
1860// @property chrome: Boolean; `true` for the Chrome browser.
1861var chrome = userAgentContains('chrome');
1862
1863// @property gecko: Boolean; `true` for gecko-based browsers like Firefox.
1864var gecko = userAgentContains('gecko') && !webkit && !opera && !ie;
1865
1866// @property safari: Boolean; `true` for the Safari browser.
1867var safari = !chrome && userAgentContains('safari');
1868
1869var phantom = userAgentContains('phantom');
1870
1871// @property opera12: Boolean
1872// `true` for the Opera browser supporting CSS transforms (version 12 or later).
1873var opera12 = 'OTransition' in style$1;
1874
1875// @property win: Boolean; `true` when the browser is running in a Windows platform
1876var win = navigator.platform.indexOf('Win') === 0;
1877
1878// @property ie3d: Boolean;
1878 `true` for all Internet Explorer versions supporting CSS transforms.
1879var ie3d = ie && ('transition' in style$1);
1880
1881// @property webkit3d: Boolean; `true` for webkit-based browsers supporting CSS transforms.
1882var webkit3d = ('WebKitCSSMatrix' in window) && ('m11' in new window.WebKitCSSMatrix()) && !android23;
1883
1884// @property gecko3d: Boolean; `true` for gecko-based browsers supporting CSS transforms.
1885var gecko3d = 'MozPerspective' in style$1;
1886
1887// @property any3d: Boolean
1888// `true` for all browsers supporting CSS transforms.
1889var any3d = !window.L_DISABLE_3D && (ie3d || webkit3d || gecko3d) && !opera12 && !phantom;
1890
1891// @property mobile: Boolean; `true` for all browsers running in a mobile device.
1892var mobile = typeof orientation !== 'undefined' || userAgentContains('mobile');
1893
1894// @property mobileWebkit: Boolean; `true` for all webkit-based browsers in a mobile device.
1895var mobileWebkit = mobile && webkit;
1896
1897// @property mobileWebkit3d: Boolean
1898// `true` for all webkit-based browsers in a mobile device supporting CSS transforms.
1899var mobileWebkit3d = mobile && webkit3d;
1900
1901// @property msPointer: Boolean
1902// `true` for browsers implementing the Microsoft touch events model (notably IE10).
1903var msPointer = !window.PointerEvent && window.MSPointerEvent;
1904
1905// @property pointer: Boolean
1906// `true` for all browsers supporting [pointer events](https://msdn.microsoft.com/en-us/library/dn433244%28v=vs.85%29.aspx).
1907var pointer = !!(window.PointerEvent || msPointer);
1908
1909// @property touch: Boolean
1910// `true` for all browsers supporting [touch events](https://developer.mozilla.org/docs/Web/API/Touch_events).
1911// This does not necessarily mean that the browser is running in a computer with
1912// a touchscreen, it only means that the browser is capable of understanding
1913// touch events.
1914var touch = !window.L_NO_TOUCH && (pointer || 'ontouchstart' in window ||
1915		(window.DocumentTouch && document instanceof window.DocumentTouch));
1916
1917// @property mobileOpera: Boolean; `true` for the Opera browser in a mobile device.
1918var mobileOpera = mobile && opera;
1919
1920// @property mobileGecko: Boolean
1921// `true` for gecko-based browsers running in a mobile device.
1922var mobileGecko = mobile && gecko;
1923
1924// @property retina: Boolean
1925// `true` for browsers on a high-resolution "retina" screen or on any screen when browser's display zoom is more than 100%.
1926var retina = (window.devicePixelRatio || (window.screen.deviceXDPI / window.screen.logicalXDPI)) > 1;
1927
1928
1929// @property canvas: Boolean
1930// `true` when the browser supports [`<canvas>`](https://developer.mozilla.org/docs/Web/API/Canvas_API).
1931var canvas = (function () {
1932	return !!document.createElement('canvas').getContext;
1933}());
1934
1935// @property svg: Boolean
1936// `true` when the browser supports [SVG](https://developer.mozilla.org/docs/Web/SVG).
1937var svg = !!(document.createElementNS && svgCreate('svg').createSVGRect);
1938
1939// @property vml: Boolean
1940// `true` if the browser supports [VML](https://en.wikipedia.org/wiki/Vector_Markup_Language).
1941var vml = !svg && (function () {
1942	try {
1943		var div = document.createElement('div');
1944		div.innerHTML = '<v:shape adj="1"/>';
1945
1946		var shape = div.firstChild;
1947		shape.style.behavior = 'url(#default#VML)';
1948
1949		return shape && (typeof shape.adj === 'object');
1950
1951	} catch (e) {
1952		return false;
1953	}
1954}());
1955
1956
1957function userAgentContains(str) {
1958	return navigator.userAgent.toLowerCase().indexOf(str) >= 0;
1959}
1960
1961
1962var Browser = (Object.freeze || Object)({
1963	ie: ie,
1964	ielt9: ielt9,
1965	edge: edge,
1966	webkit: webkit,
1967	android: android,
1968	android23: android23,
1969	androidStock: androidStock,
1970	opera: opera,
1971	chrome: chrome,
1972	gecko: gecko,
1973	safari: safari,
1974	phantom: phantom,
1975	opera12: opera12,
1976	win: win,
1977	ie3d: ie3d,
1978	webkit3d: webkit3d,
1979	gecko3d: gecko3d,
1980	any3d: any3d,
1981	mobile: mobile,
1982	mobileWebkit: mobileWebkit,
1983	mobileWebkit3d: mobileWebkit3d,
1984	msPointer: msPointer,
1985	pointer: pointer,
1986	touch: touch,
1987	mobileOpera: mobileOpera,
1988	mobileGecko: mobileGecko,
1989	retina: retina,
1990	canvas: canvas,
1991	svg: svg,
1992	vml: vml
1993});
1994
1995/*
1996 * Extends L.DomEvent to provide touch support for Internet Explorer and Windows-based devices.
1997 */
1998
1999
2000var POINTER_DOWN =   msPointer ? 'MSPointerDown'   : 'pointerdown';
2001var POINTER_MOVE =   msPointer ? 'MSPointerMove'   : 'pointermove';
2002var POINTER_UP =     msPointer ? 'MSPointerUp'     : 'pointerup';
2003var POINTER_CANCEL = msPointer ? 'MSPointerCancel' : 'pointercancel';
2004var TAG_WHITE_LIST = ['INPUT', 'SELECT', 'OPTION'];
2005
2006var _pointers = {};
2007var _pointerDocListener = false;
2008
2009// DomEvent.DoubleTap needs to know about this
2010var _pointersCount = 0;
2011
2012// Provides a touch events wrapper for (ms)pointer events.
2013// ref http://www.w3.org/TR/pointerevents/ https://www.w3.org/Bugs/Public/show_bug.cgi?id=22890
2014
2015function addPointerListener(obj, type, handler, id) {
2016	if (type === 'touchstart') {
2017		_addPointerStart(obj, handler, id);
2018
2019	} else if (type === 'touchmove') {
2020		_addPointerMove(obj, handler, id);
2021
2022	} else if (type === 'touchend') {
2023		_addPointerEnd(obj, handler, id);
2024	}
2025
2026	return this;
2027}
2028
2029function removePointerListener(obj, type, id) {
2030	var handler = obj['_leaflet_' + type + id];
2031
2032	if (type === 'touchstart') {
2033		obj.removeEventListener(POINTER_DOWN, handler, false);
2034
2035	} else if (type === 'touchmove') {
2036		obj.removeEventListener(POINTER_MOVE, handler, false);
2037
2038	} else if (type === 'touchend') {
2039		obj.removeEventListener(POINTER_UP, handler, false);
2040		obj.removeEventListener(POINTER_CANCEL, handler, false);
2041	}
2042
2043	return this;
2044}
2045
2046function _addPointerStart(obj, handler, id) {
2047	var onDown = bind(function (e) {
2048		if (e.pointerType !== 'mouse' && e.MSPOINTER_TYPE_MOUSE && e.pointerType !== e.MSPOINTER_TYPE_MOUSE) {
2049			// In IE11, some touch events needs to fire for form controls, or
2050			// the controls will stop working. We keep a whitelist of tag names that
2051			// need these events. For other target tags, we prevent default on the event.
2052			if (TAG_WHITE_LIST.indexOf(e.target.tagName) < 0) {
2053				preventDefault(e);
2054			} else {
2055				return;
2056			}
2057		}
2058
2059		_handlePointer(e, handler);
2060	});
2061
2062	obj['_leaflet_touchstart' + id] = onDown;
2063	obj.addEventListener(POINTER_DOWN, onDown, false);
2064
2065	// need to keep track of what pointers and how many are active to provide e.touches emulation
2066	if (!_pointerDocListener) {
2067		// we listen documentElement as any drags that end by moving the touch off the screen get fired there
2068		document.documentElement.addEventListener(POINTER_DOWN, _globalPointerDown, true);
2069		document.documentElement.addEventListener(POINTER_MOVE, _globalPointerMove, true);
2070		document.documentElement.addEventListener(POINTER_UP, _globalPointerUp, true);
2071		document.documentElement.addEventListener(POINTER_CANCEL, _globalPointerUp, true);
2072
2073		_pointerDocListener = true;
2074	}
2075}
2076
2077function _globalPointerDown(e) {
2078	_pointers[e.pointerId] = e;
2079	_pointersCount++;
2080}
2081
2082function _globalPointerMove(e) {
2083	if (_pointers[e.pointerId]) {
2084		_pointers[e.pointerId] = e;
2085	}
2086}
2087
2088function _globalPointerUp(e) {
2089	delete _pointers[e.pointerId];
2090	_pointersCount--;
2091}
2092
2093function _handlePointer(e, handler) {
2094	e.touches = [];
2095	for (var i in _pointers) {
2096		e.touches.push(_pointers[i]);
2097	}
2098	e.changedTouches = [e];
2099
2100	handler(e);
2101}
2102
2103function _addPointerMove(obj, handler, id) {
2104	var onMove = function (e) {
2105		// don't fire touch moves when mouse isn't down
2106		if ((e.pointerType === e.MSPOINTER_TYPE_MOUSE || e.pointerType === 'mouse') && e.buttons === 0) { return; }
2107
2108		_handlePointer(e, handler);
2109	};
2110
2111	obj['_leaflet_touchmove' + id] = onMove;
2112	obj.addEventListener(POINTER_MOVE, onMove, false);
2113}
2114
2115function _addPointerEnd(obj, handler, id) {
2116	var onUp = function (e) {
2117		_handlePointer(e, handler);
2118	};
2119
2120	obj['_leaflet_touchend' + id] = onUp;
2121	obj.addEventListener(POINTER_UP, onUp, false);
2122	obj.addEventListener(POINTER_CANCEL, onUp, false);
2123}
2124
2125/*
2126 * Extends the event handling code with double tap support for mobile browsers.
2127 */
2128
2129var _touchstart = msPointer ? 'MSPointerDown' : pointer ? 'pointerdown' : 'touchstart';
2130var _touchend = msPointer ? 'MSPointerUp' : pointer ? 'pointerup' : 'touchend';
2131var _pre = '_leaflet_';
2132
2133// inspired by Zepto touch code by Thomas Fuchs
2134function addDoubleTapListener(obj, handler, id) {
2135	var last, touch$$1,
2136	    doubleTap = false,
2137	    delay = 250;
2138
2139	function onTouchStart(e) {
2140		var count;
2141
2142		if (pointer) {
2143			if ((!edge) || e.pointerType === 'mouse') { return; }
2144			count = _pointersCount;
2145		} else {
2146			count = e.touches.length;
2147		}
2148
2149		if (count > 1) { return; }
2150
2151		var now = Date.now(),
2152		    delta = now - (last || now);
2153
2154		touch$$1 = e.touches ? e.touches[0] : e;
2155		doubleTap = (delta > 0 && delta <= delay);
2156		last = now;
2157	}
2158
2159	function onTouchEnd(e) {
2160		if (doubleTap && !touch$$1.cancelBubble) {
2161			if (pointer) {
2162				if ((!edge) || e.pointerType === 'mouse') { return; }
2163				// work around .type being readonly with MSPointer* events
2164				var newTouch = {},
2165				    prop, i;
2166
2167				for (i in touch$$1) {
2168					prop = touch$$1[i];
2169					newTouch[i] = prop && prop.bind ? prop.bind(touch$$1) : prop;
2170				}
2171				touch$$1 = newTouch;
2172			}
2173			touch$$1.type = 'dblclick';
2174			handler(touch$$1);
2175			last = null;
2176		}
2177	}
2178
2179	obj[_pre + _touchstart + id] = onTouchStart;
2180	obj[_pre + _touchend + id] = onTouchEnd;
2181	obj[_pre + 'dblclick' + id] = handler;
2182
2183	obj.addEventListener(_touchstart, onTouchStart, false);
2184	obj.addEventListener(_touchend, onTouchEnd, false);
2185
2186	// On some platforms (notably, chrome<55 on win10 + touchscreen + mouse),
2187	// the browser doesn't fire touchend/pointerup events but does fire
2188	// native dblclicks. See #4127.
2189	// Edge 14 also fires native dblclicks, but only for pointerType mouse, see #5180.
2190	obj.addEventListener('dblclick', handler, false);
2191
2192	return this;
2193}
2194
2195function removeDoubleTapListener(obj, id) {
2196	var touchstart = obj[_pre + _touchstart + id],
2197	    touchend = obj[_pre + _touchend + id],
2198	    dblclick = obj[_pre + 'dblclick' + id];
2199
2200	obj.removeEventListener(_touchstart, touchstart, false);
2201	obj.removeEventListener(_touchend, touchend, false);
2202	if (!edge) {
2203		obj.removeEventListener('dblclick', dblclick, false);
2204	}
2205
2206	return this;
2207}
2208
2209/*
2210 * @namespace DomUtil
2211 *
2212 * Utility functions to work with the [DOM](https://developer.mozilla.org/docs/Web/API/Document_Object_Model)
2213 * tree, used by Leaflet internally.
2214 *
2215 * Most functions expecting or returning a `HTMLElement` also work for
2216 * SVG elements. The only difference is that classes refer to CSS classes
2217 * in HTML and SVG classes in SVG.
2218 */
2219
2220
2221// @property TRANSFORM: String
2222// Vendor-prefixed transform style name (e.g. `'webkitTransform'` for WebKit).
2223var TRANSFORM = testProp(
2224	['transform', 'webkitTransform', 'OTransform', 'MozTransform', 'msTransform']);
2225
2226// webkitTransition comes first because some browser versions that drop vendor prefix don't do
2227// the same for the transitionend event, in particular the Android 4.1 stock browser
2228
2229// @property TRANSITION: String
2230// Vendor-prefixed transition style name.
2231var TRANSITION = testProp(
2232	['webkitTransition', 'transition', 'OTransition', 'MozTransition', 'msTransition']);
2233
2234// @property TRANSITION_END: String
2235// Vendor-prefixed transitionend event name.
2236var TRANSITION_END =
2237	TRANSITION === 'webkitTransition' || TRANSITION === 'OTransition' ? TRANSITION + 'End' : 'transitionend';
2238
2239
2240// @function get(id: String|HTMLElement): HTMLElement
2241// Returns an element given its DOM id, or returns the element itself
2242// if it was passed directly.
2243function get(id) {
2244	return typeof id === 'string' ? document.getElementById(id) : id;
2245}
2246
2247// @function getStyle(el: HTMLElement, styleAttrib: String): String
2248// Returns the value for a certain style attribute on an element,
2249// including computed values or values set through CSS.
2250function getStyle(el, style) {
2251	var value = el.style[style] || (el.currentStyle && el.currentStyle[style]);
2252
2253	if ((!value || value === 'auto') && document.defaultView) {
2254		var css = document.defaultView.getComputedStyle(el, null);
2255		value = css ? css[style] : null;
2256	}
2257	return value === 'auto' ? null : value;
2258}
2259
2260// @function create(tagName: String, className?: String, container?: HTMLElement): HTMLElement
2261// Creates an HTML element with `tagName`, sets its class to `className`, and optionally appends it to `container` element.
2262function create$1(tagName, className, container) {
2263	var el = document.createElement(tagName);
2264	el.className = className || '';
2265
2266	if (container) {
2267		container.appendChild(el);
2268	}
2269	return el;
2270}
2271
2272// @function remove(el: HTMLElement)
2273// Removes `el` from its parent element
2274function remove(el) {
2275	var parent = el.parentNode;
2276	if (parent) {
2277		parent.removeChild(el);
2278	}
2279}
2280
2281// @function empty(el: HTMLElement)
2282// Removes all of `el`'s children elements from `el`
2283function empty(el) {
2284	while (el.firstChild) {
2285		el.removeChild(el.firstChild);
2286	}
2287}
2288
2289// @function toFront(el: HTMLElement)
vendor: 9,278 bytes, lines 2290-2586
2290// Makes `el` the last child of its parent, so it renders in front of the other children.
2291function toFront(el) {
2292	var parent = el.parentNode;
2293	if (parent.lastChild !== el) {
2294		parent.appendChild(el);
2295	}
2296}
2297
2298// @function toBack(el: HTMLElement)
2299// Makes `el` the first child of its parent, so it renders behind the other children.
2300function toBack(el) {
2301	var parent = el.parentNode;
2302	if (parent.firstChild !== el) {
2303		parent.insertBefore(el, parent.firstChild);
2304	}
2305}
2306
2307// @function hasClass(el: HTMLElement, name: String): Boolean
2308// Returns `true` if the element's class attribute contains `name`.
2309function hasClass(el, name) {
2310	if (el.classList !== undefined) {
2311		return el.classList.contains(name);
2312	}
2313	var className = getClass(el);
2314	return className.length > 0 && new RegExp('(^|\\s)' + name + '(\\s|$)').test(className);
2315}
2316
2317// @function addClass(el: HTMLElement, name: String)
2318// Adds `name` to the element's class attribute.
2319function addClass(el, name) {
2320	if (el.classList !== undefined) {
2321		var classes = splitWords(name);
2322		for (var i = 0, len = classes.length; i < len; i++) {
2323			el.classList.add(classes[i]);
2324		}
2325	} else if (!hasClass(el, name)) {
2326		var className = getClass(el);
2327		setClass(el, (className ? className + ' ' : '') + name);
2328	}
2329}
2330
2331// @function removeClass(el: HTMLElement, name: String)
2332// Removes `name` from the element's class attribute.
2333function removeClass(el, name) {
2334	if (el.classList !== undefined) {
2335		el.classList.remove(name);
2336	} else {
2337		setClass(el, trim((' ' + getClass(el) + ' ').replace(' ' + name + ' ', ' ')));
2338	}
2339}
2340
2341// @function setClass(el: HTMLElement, name: String)
2342// Sets the element's class.
2343function setClass(el, name) {
2344	if (el.className.baseVal === undefined) {
2345		el.className = name;
2346	} else {
2347		// in case of SVG element
2348		el.className.baseVal = name;
2349	}
2350}
2351
2352// @function getClass(el: HTMLElement): String
2353// Returns the element's class.
2354function getClass(el) {
2355	return el.className.baseVal === undefined ? el.className : el.className.baseVal;
2356}
2357
2358// @function setOpacity(el: HTMLElement, opacity: Number)
2359// Set the opacity of an element (including old IE support).
2360// `opacity` must be a number from `0` to `1`.
2361function setOpacity(el, value) {
2362	if ('opacity' in el.style) {
2363		el.style.opacity = value;
2364	} else if ('filter' in el.style) {
2365		_setOpacityIE(el, value);
2366	}
2367}
2368
2369function _setOpacityIE(el, value) {
2370	var filter = false,
2371	    filterName = 'DXImageTransform.Microsoft.Alpha';
2372
2373	// filters collection throws an error if we try to retrieve a filter that doesn't exist
2374	try {
2375		filter = el.filters.item(filterName);
2376	} catch (e) {
2377		// don't set opacity to 1 if we haven't already set an opacity,
2378		// it isn't needed and breaks transparent pngs.
2379		if (value === 1) { return; }
2380	}
2381
2382	value = Math.round(value * 100);
2383
2384	if (filter) {
2385		filter.Enabled = (value !== 100);
2386		filter.Opacity = value;
2387	} else {
2388		el.style.filter += ' progid:' + filterName + '(opacity=' + value + ')';
2389	}
2390}
2391
2392// @function testProp(props: String[]): String|false
2393// Goes through the array of style names and returns the first name
2394// that is a valid style name for an element. If no such name is found,
2395// it returns false. Useful for vendor-prefixed styles like `transform`.
2396function testProp(props) {
2397	var style = document.documentElement.style;
2398
2399	for (var i = 0; i < props.length; i++) {
2400		if (props[i] in style) {
2401			return props[i];
2402		}
2403	}
2404	return false;
2405}
2406
2407// @function setTransform(el: HTMLElement, offset: Point, scale?: Number)
2408// Resets the 3D CSS transform of `el` so it is translated by `offset` pixels
2409// and optionally scaled by `scale`. Does not have an effect if the
2410// browser doesn't support 3D CSS transforms.
2411function setTransform(el, offset, scale) {
2412	var pos = offset || new Point(0, 0);
2413
2414	el.style[TRANSFORM] =
2415		(ie3d ?
2416			'translate(' + pos.x + 'px,' + pos.y + 'px)' :
2417			'translate3d(' + pos.x + 'px,' + pos.y + 'px,0)') +
2418		(scale ? ' scale(' + scale + ')' : '');
2419}
2420
2421// @function setPosition(el: HTMLElement, position: Point)
2422// Sets the position of `el` to coordinates specified by `position`,
2423// using CSS translate or top/left positioning depending on the browser
2424// (used by Leaflet internally to position its layers).
2425function setPosition(el, point) {
2426
2427	/*eslint-disable */
2428	el._leaflet_pos = point;
2429	/* eslint-enable */
2430
2431	if (any3d) {
2432		setTransform(el, point);
2433	} else {
2434		el.style.left = point.x + 'px';
2435		el.style.top = point.y + 'px';
2436	}
2437}
2438
2439// @function getPosition(el: HTMLElement): Point
2440// Returns the coordinates of an element previously positioned with setPosition.
2441function getPosition(el) {
2442	// this method is only used for elements previously positioned using setPosition,
2443	// so it's safe to cache the position for performance
2444
2445	return el._leaflet_pos || new Point(0, 0);
2446}
2447
2448// @function disableTextSelection()
2449// Prevents the user from generating `selectstart` DOM events, usually generated
2450// when the user drags the mouse through a page with text. Used internally
2451// by Leaflet to override the behaviour of any click-and-drag interaction on
2452// the map. Affects drag interactions on the whole document.
2453
2454// @function enableTextSelection()
2455// Cancels the effects of a previous [`L.DomUtil.disableTextSelection`](#domutil-disabletextselection).
2456var disableTextSelection;
2457var enableTextSelection;
2458var _userSelect;
2459if ('onselectstart' in document) {
2460	disableTextSelection = function () {
2461		on(window, 'selectstart', preventDefault);
2462	};
2463	enableTextSelection = function () {
2464		off(window, 'selectstart', preventDefault);
2465	};
2466} else {
2467	var userSelectProperty = testProp(
2468		['userSelect', 'WebkitUserSelect', 'OUserSelect', 'MozUserSelect', 'msUserSelect']);
2469
2470	disableTextSelection = function () {
2471		if (userSelectProperty) {
2472			var style = document.documentElement.style;
2473			_userSelect = style[userSelectProperty];
2474			style[userSelectProperty] = 'none';
2475		}
2476	};
2477	enableTextSelection = function () {
2478		if (userSelectProperty) {
2479			document.documentElement.style[userSelectProperty] = _userSelect;
2480			_userSelect = undefined;
2481		}
2482	};
2483}
2484
2485// @function disableImageDrag()
2486// As [`L.DomUtil.disableTextSelection`](#domutil-disabletextselection), but
2487// for `dragstart` DOM events, usually generated when the user drags an image.
2488function disableImageDrag() {
2489	on(window, 'dragstart', preventDefault);
2490}
2491
2492// @function enableImageDrag()
2493// Cancels the effects of a previous [`L.DomUtil.disableImageDrag`](#domutil-disabletextselection).
2494function enableImageDrag() {
2495	off(window, 'dragstart', preventDefault);
2496}
2497
2498var _outlineElement;
2499var _outlineStyle;
2500// @function preventOutline(el: HTMLElement)
2501// Makes the [outline](https://developer.mozilla.org/docs/Web/CSS/outline)
2502// of the element `el` invisible. Used internally by Leaflet to prevent
2503// focusable elements from displaying an outline when the user performs a
2504// drag interaction on them.
2505function preventOutline(element) {
2506	while (element.tabIndex === -1) {
2507		element = element.parentNode;
2508	}
2509	if (!element.style) { return; }
2510	restoreOutline();
2511	_outlineElement = element;
2512	_outlineStyle = element.style.outline;
2513	element.style.outline = 'none';
2514	on(window, 'keydown', restoreOutline);
2515}
2516
2517// @function restoreOutline()
2518// Cancels the effects of a previous [`L.DomUtil.preventOutline`]().
2519function restoreOutline() {
2520	if (!_outlineElement) { return; }
2521	_outlineElement.style.outline = _outlineStyle;
2522	_outlineElement = undefined;
2523	_outlineStyle = undefined;
2524	off(window, 'keydown', restoreOutline);
2525}
2526
2527// @function getSizedParentNode(el: HTMLElement): HTMLElement
2528// Finds the closest parent node which size (width and height) is not null.
2529function getSizedParentNode(element) {
2530	do {
2531		element = element.parentNode;
2532	} while ((!element.offsetWidth || !element.offsetHeight) && element !== document.body);
2533	return element;
2534}
2535
2536// @function getScale(el: HTMLElement): Object
2537// Computes the CSS scale currently applied on the element.
2538// Returns an object with `x` and `y` members as horizontal and vertical scales respectively,
2539// and `boundingClientRect` as the result of [`getBoundingClientRect()`](https://developer.mozilla.org/en-US/docs/Web/API/Element/getBoundingClientRect).
2540function getScale(element) {
2541	var rect = element.getBoundingClientRect(); // Read-only in old browsers.
2542
2543	return {
2544		x: rect.width / element.offsetWidth || 1,
2545		y: rect.height / element.offsetHeight || 1,
2546		boundingClientRect: rect
2547	};
2548}
2549
2550
2551var DomUtil = (Object.freeze || Object)({
2552	TRANSFORM: TRANSFORM,
2553	TRANSITION: TRANSITION,
2554	TRANSITION_END: TRANSITION_END,
2555	get: get,
2556	getStyle: getStyle,
2557	create: create$1,
2558	remove: remove,
2559	empty: empty,
2560	toFront: toFront,
2561	toBack: toBack,
2562	hasClass: hasClass,
2563	addClass: addClass,
2564	removeClass: removeClass,
2565	setClass: setClass,
2566	getClass: getClass,
2567	setOpacity: setOpacity,
2568	testProp: testProp,
2569	setTransform: setTransform,
2570	setPosition: setPosition,
2571	getPosition: getPosition,
2572	disableTextSelection: disableTextSelection,
2573	enableTextSelection: enableTextSelection,
2574	disableImageDrag: disableImageDrag,
2575	enableImageDrag: enableImageDrag,
2576	preventOutline: preventOutline,
2577	restoreOutline: restoreOutline,
2578	getSizedParentNode: getSizedParentNode,
2579	getScale: getScale
2580});
2581
2582/*
2583 * @namespace DomEvent
2584 * Utility functions to work with the [DOM events](https://developer.mozilla.org/docs/Web/API/Event), used by Leaflet internally.
2585 */
2586
2587// Inspired by John Resig, Dean Edwards and YUI addEvent implementations.
2588
2589// @function on(el: HTMLElement, types: String, fn: Function, context?: Object): this
2590// Adds a listener function (`fn`) to a particular DOM event type of the
2591// element `el`. You can optionally specify the context of the listener
2592// (object the `this` keyword will point to). You can also pass several
2593// space-separated types (e.g. `'click dblclick'`).
2594
2595// @alternative
2596// @function on(el: HTMLElement, eventMap: Object, context?: Object): this
2597// Adds a set of type/listener pairs, e.g. `{click: onClick, mousemove: onMouseMove}`
2598function on(obj, types, fn, context) {
2599
2600	if (typeof types === 'object') {
2601		for (var type in types) {
2602			addOne(obj, type, types[type], fn);
2603		}
2604	} else {
2605		types = splitWords(types);
2606
2607		for (var i = 0, len = types.length; i < len; i++) {
2608			addOne(obj, types[i], fn, context);
2609		}
2610	}
2611
2612	return this;
2613}
2614
2615var eventsKey = '_leaflet_events';
2616
2617// @function off(el: HTMLElement, types: String, fn: Function, context?: Object): this
2618// Removes a previously added listener function.
2619// Note that if you passed a custom context to on, you must pass the same
2620// context to `off` in order to remove the listener.
2621
2622// @alternative
2623// @function off(el: HTMLElement, eventMap: Object, context?: Object): this
2624// Removes a set of type/listener pairs, e.g. `{click: onClick, mousemove: onMouseMove}`
2625function off(obj, types, fn, context) {
2626
2627	if (typeof types === 'object') {
2628		for (var type in types) {
2629			removeOne(obj, type, types[type], fn);
2630		}
2631	} else if (types) {
2632		types = splitWords(types);
2633
2634		for (var i = 0, len = types.length; i < len; i++) {
2635			removeOne(obj, types[i], fn, context);
2636		}
2637	} else {
2638		for (var j in obj[eventsKey]) {
2639			removeOne(obj, j, obj[eventsKey][j]);
2640		}
2641		delete obj[eventsKey];
2642	}
2643
2644	return this;
2645}
2646
2647function addOne(obj, type, fn, context) {
2648	var id = type + stamp(fn) + (context ? '_' + stamp(context) : '');
2649
2650	if (obj[eventsKey] && obj[eventsKey][id]) { return this; }
2651
2652	var handler = function (e) {
2653		return fn.call(context || obj, e || window.event);
2654	};
2655
2656	var originalHandler = handler;
2657
2658	if (pointer && type.indexOf('touch') === 0) {
2659		// Needs DomEvent.Pointer.js
2660		addPointerListener(obj, type, handler, id);
2661
2662	} else if (touch && (type === 'dblclick') && addDoubleTapListener &&
2663	           !(pointer && chrome)) {
2664		// Chrome >55 does not need the synthetic dblclicks from addDoubleTapListener
2665		// See #5180
2666		addDoubleTapListener(obj, handler, id);
2667
2668	} else if ('addEventListener' in obj) {
2669
2670		if (type === 'mousewheel') {
2671			obj.addEventListener('onwheel' in obj ? 'wheel' : 'mousewheel', handler, false);
2672
2673		} else if ((type === 'mouseenter') || (type === 'mouseleave')) {
2674			handler = function (e) {
2675				e = e || window.event;
2676				if (isExternalTarget(obj, e)) {
2677					originalHandler(e);
2678				}
2679			};
2680			obj.addEventListener(type === 'mouseenter' ? 'mouseover' : 'mouseout', handler, false);
2681
2682		} else {
2683			if (type === 'click' && android) {
2684				handler = function (e) {
2685					filterClick(e, originalHandler);
2686				};
2687			}
2688			obj.addEventListener(type, handler, false);
2689		}
2690
2691	} else if ('attachEvent' in obj) {
2692		obj.attachEvent('on' + type, handler);
2693	}
2694
2695	obj[eventsKey] = obj[eventsKey] || {};
2696	obj[eventsKey][id] = handler;
2697}
2698
2699function removeOne(obj, type, fn, context) {
2700
2701	var id = type + stamp(fn) + (context ? '_' + stamp(context) : ''),
2702	    handler = obj[eventsKey] && obj[eventsKey][id];
2703
2704	if (!handler) { return this; }
2705
2706	if (pointer && type.indexOf('touch') === 0) {
2707		removePointerListener(obj, type, id);
2708
2709	} else if (touch && (type === 'dblclick') && removeDoubleTapListener &&
2710	           !(pointer && chrome)) {
2711		removeDoubleTapListener(obj, id);
2712
2713	} else if ('removeEventListener' in obj) {
2714
2715		if (type === 'mousewheel') {
2716			obj.removeEventListener('onwheel' in obj ? 'wheel' : 'mousewheel', handler, false);
2717
2718		} else {
2719			obj.removeEventListener(
2720				type === 'mouseenter' ? 'mouseover' :
2721				type === 'mouseleave' ? 'mouseout' : type, handler, false);
2722		}
2723
2724	} else if ('detachEvent' in obj) {
2725		obj.detachEvent('on' + type, handler);
2726	}
2727
2728	obj[eventsKey][id] = null;
2729}
2730
2731// @function stopPropagation(ev: DOMEvent): this
2732// Stop the given event from propagation to parent elements. Used inside the listener functions:
2733// ```js
2734// L.DomEvent.on(div, 'click', function (ev) {
2735// 	L.DomEvent.stopPropagation(ev);
2736// });
2737// ```
2738function stopPropagation(e) {
2739
2740	if (e.stopPropagation) {
2741		e.stopPropagation();
2742	} else if (e.originalEvent) {  // In case of Leaflet event.
2743		e.originalEvent._stopped = true;
2744	} else {
2745		e.cancelBubble = true;
2746	}
2747	skipped(e);
2748
2749	return this;
2750}
2751
2752// @function disableScrollPropagation(el: HTMLElement): this
2753// Adds `stopPropagation` to the element's `'mousewheel'` events (plus browser variants).
2754function disableScrollPropagation(el) {
2755	addOne(el, 'mousewheel', stopPropagation);
2756	return this;
2757}
2758
2759// @function disableClickPropagation(el: HTMLElement): this
2760// Adds `stopPropagation` to the element's `'click'`, `'doubleclick'`,
2761// `'mousedown'` and `'touchstart'` events (plus browser variants).
2762function disableClickPropagation(el) {
2763	on(el, 'mousedown touchstart dblclick', stopPropagation);
2764	addOne(el, 'click', fakeStop);
2765	return this;
2766}
2767
2768// @function preventDefault(ev: DOMEvent): this
2769// Prevents the default action of the DOM Event `ev` from happening (such as
2770// following a link in the href of the a element, or doing a POST request
2771// with page reload when a `<form>` is submitted).
2772// Use it inside listener functions.
2773function preventDefault(e) {
2774	if (e.preventDefault) {
2775		e.preventDefault();
2776	} else {
2777		e.returnValue = false;
2778	}
2779	return this;
2780}
2781
2782// @function stop(ev: DOMEvent): this
2783// Does `stopPropagation` and `preventDefault` at the same time.
2784function stop(e) {
2785	preventDefault(e);
2786	stopPropagation(e);
2787	return this;
2788}
2789
2790// @function getMousePosition(ev: DOMEvent, container?: HTMLElement): Point
2791// Gets normalized mouse position from a DOM event relative to the
2792// `container` (border excluded) or to the whole page if not specified.
2793function getMousePosition(e, container) {
2794	if (!container) {
2795		return new Point(e.clientX, e.clientY);
2796	}
2797
2798	var scale = getScale(container),
2799	    offset = scale.boundingClientRect; // left and top  values are in page scale (like the event clientX/Y)
2800
2801	return new Point(
2802		// offset.left/top values are in page scale (like clientX/Y),
2803		// whereas clientLeft/Top (border width) values are the original values (before CSS scale applies).
2804		(e.clientX - offset.left) / scale.x - container.clientLeft,
2805		(e.clientY - offset.top) / scale.y - container.clientTop
2806	);
2807}
2808
2809// Chrome on Win scrolls double the pixels as in other platforms (see #4538),
2810// and Firefox scrolls device pixels, not CSS pixels
2811var wheelPxFactor =
2812	(win && chrome) ? 2 * window.devicePixelRatio :
2813	gecko ? window.devicePixelRatio : 1;
2814
2815// @function getWheelDelta(ev: DOMEvent): Number
2816// Gets normalized wheel delta from a mousewheel DOM event, in vertical
2817// pixels scrolled (negative if scrolling down).
2818// Events from pointing devices without precise scrolling are mapped to
2819// a best guess of 60 pixels.
2820function getWheelDelta(e) {
2821	return (edge) ? e.wheelDeltaY / 2 : // Don't trust window-geometry-based delta
2822	       (e.deltaY && e.deltaMode === 0) ? -e.deltaY / wheelPxFactor : // Pixels
2823	       (e.deltaY && e.deltaMode === 1) ? -e.deltaY * 20 : // Lines
2824	       (e.deltaY && e.deltaMode === 2) ? -e.deltaY * 60 : // Pages
2825	       (e.deltaX || e.deltaZ) ? 0 :	// Skip horizontal/depth wheel events
2826	       e.wheelDelta ? (e.wheelDeltaY || e.wheelDelta) / 2 : // Legacy IE pixels
2827	       (e.detail && Math.abs(e.detail) < 32765) ? -e.detail * 20 : // Legacy Moz lines
2828	       e.detail ? e.detail / -32765 * 60 : // Legacy Moz pages
2829	       0;
2830}
2831
2832var skipEvents = {};
2833
2834function fakeStop(e) {
2835	// fakes stopPropagation by setting a special event flag, checked/reset with skipped(e)
2836	skipEvents[e.type] = true;
2837}
2838
2839function skipped(e) {
2840	var events = skipEvents[e.type];
2841	// reset when checking, as it's only used in map container and propagates outside of the map
2842	skipEvents[e.type] = false;
2843	return events;
2844}
2845
2846// check if element really left/entered the event target (for mouseenter/mouseleave)
2847function isExternalTarget(el, e) {
2848
2849	var related = e.relatedTarget;
2850
2851	if (!related) { return true; }
2852
2853	try {
2854		while (related && (related !== el)) {
2855			related = related.parentNode;
2856		}
2857	} catch (err) {
2858		return false;
2859	}
2860	return (related !== el);
2861}
2862
2863var lastClick;
2864
2865// this is a horrible workaround for a bug in Android where a single touch triggers two click events
2866function filterClick(e, handler) {
2867	var timeStamp = (e.timeStamp || (e.originalEvent && e.originalEvent.timeStamp)),
2868	    elapsed = lastClick && (timeStamp - lastClick);
2869
2870	// are they closer together than 500ms yet more than 100ms?
2871	// Android typically triggers them ~300ms apart while multiple listeners
2872	// on the same event should be triggered far faster;
2873	// or check if click is simulated on the element, and if it is, reject any non-simulated events
2874
2875	if ((elapsed && elapsed > 100 && elapsed < 500) || (e.target._simulatedClick && !e._simulated)) {
2876		stop(e);
2877		return;
2878	}
2879	lastClick = timeStamp;
2880
2881	handler(e);
2882}
2883
2884
2885
2886
2887var DomEvent = (Object.freeze || Object)({
2888	on: on,
2889	off: off,
2890	stopPropagation: stopPropagation,
2891	disableScrollPropagation: disableScrollPropagation,
2892	disableClickPropagation: disableClickPropagation,
2893	preventDefault: preventDefault,
2894	stop: stop,
2895	getMousePosition: getMousePosition,
2896	getWheelDelta: getWheelDelta,
2897	fakeStop: fakeStop,
2898	skipped: skipped,
2899	isExternalTarget: isExternalTarget,
2900	addListener: on,
2901	removeListener: off
2902});
2903
2904/*
2905 * @class PosAnimation
2906 * @aka L.PosAnimation
2907 * @inherits Evented
2908 * Used internally for panning animations, utilizing CSS3 Transitions for modern browsers and a timer fallback for IE6-9.
2909 *
2910 * @example
2911 * ```js
2912 * var fx = new L.PosAnimation();
2913 * fx.run(el, [300, 500], 0.5);
2914 * ```
2915 *
2916 * @constructor L.PosAnimation()
2917 * Creates a `PosAnimation` object.
2918 *
2919 */
2920
2921var PosAnimation = Evented.extend({
2922
2923	// @method run(el: HTMLElement, newPos: Point, duration?: Number, easeLinearity?: Number)
2924	// Run an animation of a given element to a new position, optionally setting
2925	// duration in seconds (`0.25` by default) and easing linearity factor (3rd
2926	// argument of the [cubic bezier curve](http://cubic-bezier.com/#0,0,.5,1),
2927	// `0.5` by default).
2928	run: function (el, newPos, duration, easeLinearity) {
2929		this.stop();
2930
2931		this._el = el;
2932		this._inProgress = true;
2933		this._duration = duration || 0.25;
2934		this._easeOutPower = 1 / Math.max(easeLinearity || 0.5, 0.2);
2935
2936		this._startPos = getPosition(el);
2937		this._offset = newPos.subtract(this._startPos);
2938		this._startTime = +new Date();
2939
2940		// @event start: Event
2941		// Fired when the animation starts
2942		this.fire('start');
2943
2944		this._animate();
2945	},
2946
2947	// @method stop()
2948	// Stops the animation (if currently running).
2949	stop: function () {
2950		if (!this._inProgress) { return; }
2951
2952		this._step(true);
2953		this._complete();
2954	},
2955
2956	_animate: function () {
2957		// animation loop
2958		this._animId = requestAnimFrame(this._animate, this);
2959		this._step();
2960	},
2961
2962	_step: function (round) {
2963		var elapsed = (+new Date()) - this._startTime,
2964		    duration = this._duration * 1000;
2965
2966		if (elapsed < duration) {
2967			this._runFrame(this._easeOut(elapsed / duration), round);
2968		} else {
2969			this._runFrame(1);
2970			this._complete();
2971		}
2972	},
2973
2974	_runFrame: function (progress, round) {
2975		var pos = this._startPos.add(this._offset.multiplyBy(progress));
2976		if (round) {
2977			pos._round();
2978		}
2979		setPosition(this._el, pos);
2980
2981		// @event step: Event
2982		// Fired continuously during the animation.
2983		this.fire('step');
2984	},
2985
2986	_complete: function () {
vendor: 18,113 bytes, lines 2987-3559
2987		cancelAnimFrame(this._animId);
2988
2989		this._inProgress = false;
2990		// @event end: Event
2991		// Fired when the animation ends.
2992		this.fire('end');
2993	},
2994
2995	_easeOut: function (t) {
2996		return 1 - Math.pow(1 - t, this._easeOutPower);
2997	}
2998});
2999
3000/*
3001 * @class Map
3002 * @aka L.Map
3003 * @inherits Evented
3004 *
3005 * The central class of the API — it is used to create a map on a page and manipulate it.
3006 *
3007 * @example
3008 *
3009 * ```js
3010 * // initialize the map on the "map" div with a given center and zoom
3011 * var map = L.map('map', {
3012 * 	center: [51.505, -0.09],
3013 * 	zoom: 13
3014 * });
3015 * ```
3016 *
3017 */
3018
3019var Map = Evented.extend({
3020
3021	options: {
3022		// @section Map State Options
3023		// @option crs: CRS = L.CRS.EPSG3857
3024		// The [Coordinate Reference System](#crs) to use. Don't change this if you're not
3025		// sure what it means.
3026		crs: EPSG3857,
3027
3028		// @option center: LatLng = undefined
3029		// Initial geographic center of the map
3030		center: undefined,
3031
3032		// @option zoom: Number = undefined
3033		// Initial map zoom level
3034		zoom: undefined,
3035
3036		// @option minZoom: Number = *
3037		// Minimum zoom level of the map.
3038		// If not specified and at least one `GridLayer` or `TileLayer` is in the map,
3039		// the lowest of their `minZoom` options will be used instead.
3040		minZoom: undefined,
3041
3042		// @option maxZoom: Number = *
3043		// Maximum zoom level of the map.
3044		// If not specified and at least one `GridLayer` or `TileLayer` is in the map,
3045		// the highest of their `maxZoom` options will be used instead.
3046		maxZoom: undefined,
3047
3048		// @option layers: Layer[] = []
3049		// Array of layers that will be added to the map initially
3050		layers: [],
3051
3052		// @option maxBounds: LatLngBounds = null
3053		// When this option is set, the map restricts the view to the given
3054		// geographical bounds, bouncing the user back if the user tries to pan
3055		// outside the view. To set the restriction dynamically, use
3056		// [`setMaxBounds`](#map-setmaxbounds) method.
3057		maxBounds: undefined,
3058
3059		// @option renderer: Renderer = *
3060		// The default method for drawing vector layers on the map. `L.SVG`
3061		// or `L.Canvas` by default depending on browser support.
3062		renderer: undefined,
3063
3064
3065		// @section Animation Options
3066		// @option zoomAnimation: Boolean = true
3067		// Whether the map zoom animation is enabled. By default it's enabled
3068		// in all browsers that support CSS3 Transitions except Android.
3069		zoomAnimation: true,
3070
3071		// @option zoomAnimationThreshold: Number = 4
3072		// Won't animate zoom if the zoom difference exceeds this value.
3073		zoomAnimationThreshold: 4,
3074
3075		// @option fadeAnimation: Boolean = true
3076		// Whether the tile fade animation is enabled. By default it's enabled
3077		// in all browsers that support CSS3 Transitions except Android.
3078		fadeAnimation: true,
3079
3080		// @option markerZoomAnimation: Boolean = true
3081		// Whether markers animate their zoom with the zoom animation, if disabled
3082		// they will disappear for the length of the animation. By default it's
3083		// enabled in all browsers that support CSS3 Transitions except Android.
3084		markerZoomAnimation: true,
3085
3086		// @option transform3DLimit: Number = 2^23
3087		// Defines the maximum size of a CSS translation transform. The default
3088		// value should not be changed unless a web browser positions layers in
3089		// the wrong place after doing a large `panBy`.
3090		transform3DLimit: 8388608, // Precision limit of a 32-bit float
3091
3092		// @section Interaction Options
3093		// @option zoomSnap: Number = 1
3094		// Forces the map's zoom level to always be a multiple of this, particularly
3095		// right after a [`fitBounds()`](#map-fitbounds) or a pinch-zoom.
3096		// By default, the zoom level snaps to the nearest integer; lower values
3097		// (e.g. `0.5` or `0.1`) allow for greater granularity. A value of `0`
3098		// means the zoom level will not be snapped after `fitBounds` or a pinch-zoom.
3099		zoomSnap: 1,
3100
3101		// @option zoomDelta: Number = 1
3102		// Controls how much the map's zoom level will change after a
3103		// [`zoomIn()`](#map-zoomin), [`zoomOut()`](#map-zoomout), pressing `+`
3104		// or `-` on the keyboard, or using the [zoom controls](#control-zoom).
3105		// Values smaller than `1` (e.g. `0.5`) allow for greater granularity.
3106		zoomDelta: 1,
3107
3108		// @option trackResize: Boolean = true
3109		// Whether the map automatically handles browser window resize to update itself.
3110		trackResize: true
3111	},
3112
3113	initialize: function (id, options) { // (HTMLElement or String, Object)
3114		options = setOptions(this, options);
3115
3116		this._initContainer(id);
3117		this._initLayout();
3118
3119		// hack for https://github.com/Leaflet/Leaflet/issues/1980
3120		this._onResize = bind(this._onResize, this);
3121
3122		this._initEvents();
3123
3124		if (options.maxBounds) {
3125			this.setMaxBounds(options.maxBounds);
3126		}
3127
3128		if (options.zoom !== undefined) {
3129			this._zoom = this._limitZoom(options.zoom);
3130		}
3131
3132		if (options.center && options.zoom !== undefined) {
3133			this.setView(toLatLng(options.center), options.zoom, {reset: true});
3134		}
3135
3136		this._handlers = [];
3137		this._layers = {};
3138		this._zoomBoundLayers = {};
3139		this._sizeChanged = true;
3140
3141		this.callInitHooks();
3142
3143		// don't animate on browsers without hardware-accelerated transitions or old Android/Opera
3144		this._zoomAnimated = TRANSITION && any3d && !mobileOpera &&
3145				this.options.zoomAnimation;
3146
3147		// zoom transitions run with the same duration for all layers, so if one of transitionend events
3148		// happens after starting zoom animation (propagating to the map pane), we know that it ended globally
3149		if (this._zoomAnimated) {
3150			this._createAnimProxy();
3151			on(this._proxy, TRANSITION_END, this._catchTransitionEnd, this);
3152		}
3153
3154		this._addLayers(this.options.layers);
3155	},
3156
3157
3158	// @section Methods for modifying map state
3159
3160	// @method setView(center: LatLng, zoom: Number, options?: Zoom/pan options): this
3161	// Sets the view of the map (geographical center and zoom) with the given
3162	// animation options.
3163	setView: function (center, zoom, options) {
3164
3165		zoom = zoom === undefined ? this._zoom : this._limitZoom(zoom);
3166		center = this._limitCenter(toLatLng(center), zoom, this.options.maxBounds);
3167		options = options || {};
3168
3169		this._stop();
3170
3171		if (this._loaded && !options.reset && options !== true) {
3172
3173			if (options.animate !== undefined) {
3174				options.zoom = extend({animate: options.animate}, options.zoom);
3175				options.pan = extend({animate: options.animate, duration: options.duration}, options.pan);
3176			}
3177
3178			// try animating pan or zoom
3179			var moved = (this._zoom !== zoom) ?
3180				this._tryAnimatedZoom && this._tryAnimatedZoom(center, zoom, options.zoom) :
3181				this._tryAnimatedPan(center, options.pan);
3182
3183			if (moved) {
3184				// prevent resize handler call, the view will refresh after animation anyway
3185				clearTimeout(this._sizeTimer);
3186				return this;
3187			}
3188		}
3189
3190		// animation didn't start, just reset the map view
3191		this._resetView(center, zoom);
3192
3193		return this;
3194	},
3195
3196	// @method setZoom(zoom: Number, options?: Zoom/pan options): this
3197	// Sets the zoom of the map.
3198	setZoom: function (zoom, options) {
3199		if (!this._loaded) {
3200			this._zoom = zoom;
3201			return this;
3202		}
3203		return this.setView(this.getCenter(), zoom, {zoom: options});
3204	},
3205
3206	// @method zoomIn(delta?: Number, options?: Zoom options): this
3207	// Increases the zoom of the map by `delta` ([`zoomDelta`](#map-zoomdelta) by default).
3208	zoomIn: function (delta, options) {
3209		delta = delta || (any3d ? this.options.zoomDelta : 1);
3210		return this.setZoom(this._zoom + delta, options);
3211	},
3212
3213	// @method zoomOut(delta?: Number, options?: Zoom options): this
3214	// Decreases the zoom of the map by `delta` ([`zoomDelta`](#map-zoomdelta) by default).
3215	zoomOut: function (delta, options) {
3216		delta = delta || (any3d ? this.options.zoomDelta : 1);
3217		return this.setZoom(this._zoom - delta, options);
3218	},
3219
3220	// @method setZoomAround(latlng: LatLng, zoom: Number, options: Zoom options): this
3221	// Zooms the map while keeping a specified geographical point on the map
3222	// stationary (e.g. used internally for scroll zoom and double-click zoom).
3223	// @alternative
3224	// @method setZoomAround(offset: Point, zoom: Number, options: Zoom options): this
3225	// Zooms the map while keeping a specified pixel on the map (relative to the top-left corner) stationary.
3226	setZoomAround: function (latlng, zoom, options) {
3227		var scale = this.getZoomScale(zoom),
3228		    viewHalf = this.getSize().divideBy(2),
3229		    containerPoint = latlng instanceof Point ? latlng : this.latLngToContainerPoint(latlng),
3230
3231		    centerOffset = containerPoint.subtract(viewHalf).multiplyBy(1 - 1 / scale),
3232		    newCenter = this.containerPointToLatLng(viewHalf.add(centerOffset));
3233
3234		return this.setView(newCenter, zoom, {zoom: options});
3235	},
3236
3237	_getBoundsCenterZoom: function (bounds, options) {
3238
3239		options = options || {};
3240		bounds = bounds.getBounds ? bounds.getBounds() : toLatLngBounds(bounds);
3241
3242		var paddingTL = toPoint(options.paddingTopLeft || options.padding || [0, 0]),
3243		    paddingBR = toPoint(options.paddingBottomRight || options.padding || [0, 0]),
3244
3245		    zoom = this.getBoundsZoom(bounds, false, paddingTL.add(paddingBR));
3246
3247		zoom = (typeof options.maxZoom === 'number') ? Math.min(options.maxZoom, zoom) : zoom;
3248
3249		if (zoom === Infinity) {
3250			return {
3251				center: bounds.getCenter(),
3252				zoom: zoom
3253			};
3254		}
3255
3256		var paddingOffset = paddingBR.subtract(paddingTL).divideBy(2),
3257
3258		    swPoint = this.project(bounds.getSouthWest(), zoom),
3259		    nePoint = this.project(bounds.getNorthEast(), zoom),
3260		    center = this.unproject(swPoint.add(nePoint).divideBy(2).add(paddingOffset), zoom);
3261
3262		return {
3263			center: center,
3264			zoom: zoom
3265		};
3266	},
3267
3268	// @method fitBounds(bounds: LatLngBounds, options?: fitBounds options): this
3269	// Sets a map view that contains the given geographical bounds with the
3270	// maximum zoom level possible.
3271	fitBounds: function (bounds, options) {
3272
3273		bounds = toLatLngBounds(bounds);
3274
3275		if (!bounds.isValid()) {
3276			throw new Error('Bounds are not valid.');
3277		}
3278
3279		var target = this._getBoundsCenterZoom(bounds, options);
3280		return this.setView(target.center, target.zoom, options);
3281	},
3282
3283	// @method fitWorld(options?: fitBounds options): this
3284	// Sets a map view that mostly contains the whole world with the maximum
3285	// zoom level possible.
3286	fitWorld: function (options) {
3287		return this.fitBounds([[-90, -180], [90, 180]], options);
3288	},
3289
3290	// @method panTo(latlng: LatLng, options?: Pan options): this
3291	// Pans the map to a given center.
3292	panTo: function (center, options) { // (LatLng)
3293		return this.setView(center, this._zoom, {pan: options});
3294	},
3295
3296	// @method panBy(offset: Point, options?: Pan options): this
3297	// Pans the map by a given number of pixels (animated).
3298	panBy: function (offset, options) {
3299		offset = toPoint(offset).round();
3300		options = options || {};
3301
3302		if (!offset.x && !offset.y) {
3303			return this.fire('moveend');
3304		}
3305		// If we pan too far, Chrome gets issues with tiles
3306		// and makes them disappear or appear in the wrong place (slightly offset) #2602
3307		if (options.animate !== true && !this.getSize().contains(offset)) {
3308			this._resetView(this.unproject(this.project(this.getCenter()).add(offset)), this.getZoom());
3309			return this;
3310		}
3311
3312		if (!this._panAnim) {
3313			this._panAnim = new PosAnimation();
3314
3315			this._panAnim.on({
3316				'step': this._onPanTransitionStep,
3317				'end': this._onPanTransitionEnd
3318			}, this);
3319		}
3320
3321		// don't fire movestart if animating inertia
3322		if (!options.noMoveStart) {
3323			this.fire('movestart');
3324		}
3325
3326		// animate pan unless animate: false specified
3327		if (options.animate !== false) {
3328			addClass(this._mapPane, 'leaflet-pan-anim');
3329
3330			var newPos = this._getMapPanePos().subtract(offset).round();
3331			this._panAnim.run(this._mapPane, newPos, options.duration || 0.25, options.easeLinearity);
3332		} else {
3333			this._rawPanBy(offset);
3334			this.fire('move').fire('moveend');
3335		}
3336
3337		return this;
3338	},
3339
3340	// @method flyTo(latlng: LatLng, zoom?: Number, options?: Zoom/pan options): this
3341	// Sets the view of the map (geographical center and zoom) performing a smooth
3342	// pan-zoom animation.
3343	flyTo: function (targetCenter, targetZoom, options) {
3344
3345		options = options || {};
3346		if (options.animate === false || !any3d) {
3347			return this.setView(targetCenter, targetZoom, options);
3348		}
3349
3350		this._stop();
3351
3352		var from = this.project(this.getCenter()),
3353		    to = this.project(targetCenter),
3354		    size = this.getSize(),
3355		    startZoom = this._zoom;
3356
3357		targetCenter = toLatLng(targetCenter);
3358		targetZoom = targetZoom === undefined ? startZoom : targetZoom;
3359
3360		var w0 = Math.max(size.x, size.y),
3361		    w1 = w0 * this.getZoomScale(startZoom, targetZoom),
3362		    u1 = (to.distanceTo(from)) || 1,
3363		    rho = 1.42,
3364		    rho2 = rho * rho;
3365
3366		function r(i) {
3367			var s1 = i ? -1 : 1,
3368			    s2 = i ? w1 : w0,
3369			    t1 = w1 * w1 - w0 * w0 + s1 * rho2 * rho2 * u1 * u1,
3370			    b1 = 2 * s2 * rho2 * u1,
3371			    b = t1 / b1,
3372			    sq = Math.sqrt(b * b + 1) - b;
3373
3374			    // workaround for floating point precision bug when sq = 0, log = -Infinite,
3375			    // thus triggering an infinite loop in flyTo
3376			    var log = sq < 0.000000001 ? -18 : Math.log(sq);
3377
3378			return log;
3379		}
3380
3381		function sinh(n) { return (Math.exp(n) - Math.exp(-n)) / 2; }
3382		function cosh(n) { return (Math.exp(n) + Math.exp(-n)) / 2; }
3383		function tanh(n) { return sinh(n) / cosh(n); }
3384
3385		var r0 = r(0);
3386
3387		function w(s) { return w0 * (cosh(r0) / cosh(r0 + rho * s)); }
3388		function u(s) { return w0 * (cosh(r0) * tanh(r0 + rho * s) - sinh(r0)) / rho2; }
3389
3390		function easeOut(t) { return 1 - Math.pow(1 - t, 1.5); }
3391
3392		var start = Date.now(),
3393		    S = (r(1) - r0) / rho,
3394		    duration = options.duration ? 1000 * options.duration : 1000 * S * 0.8;
3395
3396		function frame() {
3397			var t = (Date.now() - start) / duration,
3398			    s = easeOut(t) * S;
3399
3400			if (t <= 1) {
3401				this._flyToFrame = requestAnimFrame(frame, this);
3402
3403				this._move(
3404					this.unproject(from.add(to.subtract(from).multiplyBy(u(s) / u1)), startZoom),
3405					this.getScaleZoom(w0 / w(s), startZoom),
3406					{flyTo: true});
3407
3408			} else {
3409				this
3410					._move(targetCenter, targetZoom)
3411					._moveEnd(true);
3412			}
3413		}
3414
3415		this._moveStart(true, options.noMoveStart);
3416
3417		frame.call(this);
3418		return this;
3419	},
3420
3421	// @method flyToBounds(bounds: LatLngBounds, options?: fitBounds options): this
3422	// Sets the view of the map with a smooth animation like [`flyTo`](#map-flyto),
3423	// but takes a bounds parameter like [`fitBounds`](#map-fitbounds).
3424	flyToBounds: function (bounds, options) {
3425		var target = this._getBoundsCenterZoom(bounds, options);
3426		return this.flyTo(target.center, target.zoom, options);
3427	},
3428
3429	// @method setMaxBounds(bounds: Bounds): this
3430	// Restricts the map view to the given bounds (see the [maxBounds](#map-maxbounds) option).
3431	setMaxBounds: function (bounds) {
3432		bounds = toLatLngBounds(bounds);
3433
3434		if (!bounds.isValid()) {
3435			this.options.maxBounds = null;
3436			return this.off('moveend', this._panInsideMaxBounds);
3437		} else if (this.options.maxBounds) {
3438			this.off('moveend', this._panInsideMaxBounds);
3439		}
3440
3441		this.options.maxBounds = bounds;
3442
3443		if (this._loaded) {
3444			this._panInsideMaxBounds();
3445		}
3446
3447		return this.on('moveend', this._panInsideMaxBounds);
3448	},
3449
3450	// @method setMinZoom(zoom: Number): this
3451	// Sets the lower limit for the available zoom levels (see the [minZoom](#map-minzoom) option).
3452	setMinZoom: function (zoom) {
3453		var oldZoom = this.options.minZoom;
3454		this.options.minZoom = zoom;
3455
3456		if (this._loaded && oldZoom !== zoom) {
3457			this.fire('zoomlevelschange');
3458
3459			if (this.getZoom() < this.options.minZoom) {
3460				return this.setZoom(zoom);
3461			}
3462		}
3463
3464		return this;
3465	},
3466
3467	// @method setMaxZoom(zoom: Number): this
3468	// Sets the upper limit for the available zoom levels (see the [maxZoom](#map-maxzoom) option).
3469	setMaxZoom: function (zoom) {
3470		var oldZoom = this.options.maxZoom;
3471		this.options.maxZoom = zoom;
3472
3473		if (this._loaded && oldZoom !== zoom) {
3474			this.fire('zoomlevelschange');
3475
3476			if (this.getZoom() > this.options.maxZoom) {
3477				return this.setZoom(zoom);
3478			}
3479		}
3480
3481		return this;
3482	},
3483
3484	// @method panInsideBounds(bounds: LatLngBounds, options?: Pan options): this
3485	// 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.
3486	panInsideBounds: function (bounds, options) {
3487		this._enforcingBounds = true;
3488		var center = this.getCenter(),
3489		    newCenter = this._limitCenter(center, this._zoom, toLatLngBounds(bounds));
3490
3491		if (!center.equals(newCenter)) {
3492			this.panTo(newCenter, options);
3493		}
3494
3495		this._enforcingBounds = false;
3496		return this;
3497	},
3498
3499	// @method invalidateSize(options: Zoom/pan options): this
3500	// Checks if the map container size changed and updates the map if so —
3501	// call it after you've changed the map size dynamically, also animating
3502	// pan by default. If `options.pan` is `false`, panning will not occur.
3503	// If `options.debounceMoveend` is `true`, it will delay `moveend` event so
3504	// that it doesn't happen often even if the method is called many
3505	// times in a row.
3506
3507	// @alternative
3508	// @method invalidateSize(animate: Boolean): this
3509	// Checks if the map container size changed and updates the map if so —
3510	// call it after you've changed the map size dynamically, also animating
3511	// pan by default.
3512	invalidateSize: function (options) {
3513		if (!this._loaded) { return this; }
3514
3515		options = extend({
3516			animate: false,
3517			pan: true
3518		}, options === true ? {animate: true} : options);
3519
3520		var oldSize = this.getSize();
3521		this._sizeChanged = true;
3522		this._lastCenter = null;
3523
3524		var newSize = this.getSize(),
3525		    oldCenter = oldSize.divideBy(2).round(),
3526		    newCenter = newSize.divideBy(2).round(),
3527		    offset = oldCenter.subtract(newCenter);
3528
3529		if (!offset.x && !offset.y) { return this; }
3530
3531		if (options.animate && options.pan) {
3532			this.panBy(offset);
3533
3534		} else {
3535			if (options.pan) {
3536				this._rawPanBy(offset);
3537			}
3538
3539			this.fire('move');
3540
3541			if (options.debounceMoveend) {
3542				clearTimeout(this._sizeTimer);
3543				this._sizeTimer = setTimeout(bind(this.fire, this, 'moveend'), 200);
3544			} else {
3545				this.fire('moveend');
3546			}
3547		}
3548
3549		// @section Map state change events
3550		// @event resize: ResizeEvent
3551		// Fired when the map is resized.
3552		return this.fire('resize', {
3553			oldSize: oldSize,
3554			newSize: newSize
3555		});
3556	},
3557
3558	// @section Methods for modifying map state
3559	// @method stop(): this
3560	// Stops the currently running `panTo` or `flyTo` animation, if any.
3561	stop: function () {
3562		this.setZoom(this._limitZoom(this._zoom));
3563		if (!this.options.zoomSnap) {
3564			this.fire('viewreset');
3565		}
3566		return this._stop();
3567	},
3568
3569	// @section Geolocation methods
3570	// @method locate(options?: Locate options): this
3571	// Tries to locate the user using the Geolocation API, firing a [`locationfound`](#map-locationfound)
3572	// event with location data on success or a [`locationerror`](#map-locationerror) event on failure,
3573	// and optionally sets the map view to the user's location with respect to
3574	// detection accuracy (or to the world view if geolocation failed).
3575	// Note that, if your page doesn't use HTTPS, this method will fail in
3576	// modern browsers ([Chrome 50 and newer](https://sites.google.com/a/chromium.org/dev/Home/chromium-security/deprecating-powerful-features-on-insecure-origins))
3577	// See `Locate options` for more details.
3578	locate: function (options) {
3579
3580		options = this._locateOptions = extend({
3581			timeout: 10000,
3582			watch: false
3583			// setView: false
3584			// maxZoom: <Number>
3585			// maximumAge: 0
vendor: 21,622 bytes, lines 3586-4255
3586			// enableHighAccuracy: false
3587		}, options);
3588
3589		if (!('geolocation' in navigator)) {
3590			this._handleGeolocationError({
3591				code: 0,
3592				message: 'Geolocation not supported.'
3593			});
3594			return this;
3595		}
3596
3597		var onResponse = bind(this._handleGeolocationResponse, this),
3598		    onError = bind(this._handleGeolocationError, this);
3599
3600		if (options.watch) {
3601			this._locationWatchId =
3602			        navigator.geolocation.watchPosition(onResponse, onError, options);
3603		} else {
3604			navigator.geolocation.getCurrentPosition(onResponse, onError, options);
3605		}
3606		return this;
3607	},
3608
3609	// @method stopLocate(): this
3610	// Stops watching location previously initiated by `map.locate({watch: true})`
3611	// and aborts resetting the map view if map.locate was called with
3612	// `{setView: true}`.
3613	stopLocate: function () {
3614		if (navigator.geolocation && navigator.geolocation.clearWatch) {
3615			navigator.geolocation.clearWatch(this._locationWatchId);
3616		}
3617		if (this._locateOptions) {
3618			this._locateOptions.setView = false;
3619		}
3620		return this;
3621	},
3622
3623	_handleGeolocationError: function (error) {
3624		var c = error.code,
3625		    message = error.message ||
3626		            (c === 1 ? 'permission denied' :
3627		            (c === 2 ? 'position unavailable' : 'timeout'));
3628
3629		if (this._locateOptions.setView && !this._loaded) {
3630			this.fitWorld();
3631		}
3632
3633		// @section Location events
3634		// @event locationerror: ErrorEvent
3635		// Fired when geolocation (using the [`locate`](#map-locate) method) failed.
3636		this.fire('locationerror', {
3637			code: c,
3638			message: 'Geolocation error: ' + message + '.'
3639		});
3640	},
3641
3642	_handleGeolocationResponse: function (pos) {
3643		var lat = pos.coords.latitude,
3644		    lng = pos.coords.longitude,
3645		    latlng = new LatLng(lat, lng),
3646		    bounds = latlng.toBounds(pos.coords.accuracy * 2),
3647		    options = this._locateOptions;
3648
3649		if (options.setView) {
3650			var zoom = this.getBoundsZoom(bounds);
3651			this.setView(latlng, options.maxZoom ? Math.min(zoom, options.maxZoom) : zoom);
3652		}
3653
3654		var data = {
3655			latlng: latlng,
3656			bounds: bounds,
3657			timestamp: pos.timestamp
3658		};
3659
3660		for (var i in pos.coords) {
3661			if (typeof pos.coords[i] === 'number') {
3662				data[i] = pos.coords[i];
3663			}
3664		}
3665
3666		// @event locationfound: LocationEvent
3667		// Fired when geolocation (using the [`locate`](#map-locate) method)
3668		// went successfully.
3669		this.fire('locationfound', data);
3670	},
3671
3672	// TODO Appropriate docs section?
3673	// @section Other Methods
3674	// @method addHandler(name: String, HandlerClass: Function): this
3675	// Adds a new `Handler` to the map, given its name and constructor function.
3676	addHandler: function (name, HandlerClass) {
3677		if (!HandlerClass) { return this; }
3678
3679		var handler = this[name] = new HandlerClass(this);
3680
3681		this._handlers.push(handler);
3682
3683		if (this.options[name]) {
3684			handler.enable();
3685		}
3686
3687		return this;
3688	},
3689
3690	// @method remove(): this
3691	// Destroys the map and clears all related event listeners.
3692	remove: function () {
3693
3694		this._initEvents(true);
3695
3696		if (this._containerId !== this._container._leaflet_id) {
3697			throw new Error('Map container is being reused by another instance');
3698		}
3699
3700		try {
3701			// throws error in IE6-8
3702			delete this._container._leaflet_id;
3703			delete this._containerId;
3704		} catch (e) {
3705			/*eslint-disable */
3706			this._container._leaflet_id = undefined;
3707			/* eslint-enable */
3708			this._containerId = undefined;
3709		}
3710
3711		if (this._locationWatchId !== undefined) {
3712			this.stopLocate();
3713		}
3714
3715		this._stop();
3716
3717		remove(this._mapPane);
3718
3719		if (this._clearControlPos) {
3720			this._clearControlPos();
3721		}
3722		if (this._resizeRequest) {
3723			cancelAnimFrame(this._resizeRequest);
3724			this._resizeRequest = null;
3725		}
3726
3727		this._clearHandlers();
3728
3729		if (this._loaded) {
3730			// @section Map state change events
3731			// @event unload: Event
3732			// Fired when the map is destroyed with [remove](#map-remove) method.
3733			this.fire('unload');
3734		}
3735
3736		var i;
3737		for (i in this._layers) {
3738			this._layers[i].remove();
3739		}
3740		for (i in this._panes) {
3741			remove(this._panes[i]);
3742		}
3743
3744		this._layers = [];
3745		this._panes = [];
3746		delete this._mapPane;
3747		delete this._renderer;
3748
3749		return this;
3750	},
3751
3752	// @section Other Methods
3753	// @method createPane(name: String, container?: HTMLElement): HTMLElement
3754	// Creates a new [map pane](#map-pane) with the given name if it doesn't exist already,
3755	// then returns it. The pane is created as a child of `container`, or
3756	// as a child of the main map pane if not set.
3757	createPane: function (name, container) {
3758		var className = 'leaflet-pane' + (name ? ' leaflet-' + name.replace('Pane', '') + '-pane' : ''),
3759		    pane = create$1('div', className, container || this._mapPane);
3760
3761		if (name) {
3762			this._panes[name] = pane;
3763		}
3764		return pane;
3765	},
3766
3767	// @section Methods for Getting Map State
3768
3769	// @method getCenter(): LatLng
3770	// Returns the geographical center of the map view
3771	getCenter: function () {
3772		this._checkIfLoaded();
3773
3774		if (this._lastCenter && !this._moved()) {
3775			return this._lastCenter;
3776		}
3777		return this.layerPointToLatLng(this._getCenterLayerPoint());
3778	},
3779
3780	// @method getZoom(): Number
3781	// Returns the current zoom level of the map view
3782	getZoom: function () {
3783		return this._zoom;
3784	},
3785
3786	// @method getBounds(): LatLngBounds
3787	// Returns the geographical bounds visible in the current map view
3788	getBounds: function () {
3789		var bounds = this.getPixelBounds(),
3790		    sw = this.unproject(bounds.getBottomLeft()),
3791		    ne = this.unproject(bounds.getTopRight());
3792
3793		return new LatLngBounds(sw, ne);
3794	},
3795
3796	// @method getMinZoom(): Number
3797	// 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.
3798	getMinZoom: function () {
3799		return this.options.minZoom === undefined ? this._layersMinZoom || 0 : this.options.minZoom;
3800	},
3801
3802	// @method getMaxZoom(): Number
3803	// Returns the maximum zoom level of the map (if set in the `maxZoom` option of the map or of any layers).
3804	getMaxZoom: function () {
3805		return this.options.maxZoom === undefined ?
3806			(this._layersMaxZoom === undefined ? Infinity : this._layersMaxZoom) :
3807			this.options.maxZoom;
3808	},
3809
3810	// @method getBoundsZoom(bounds: LatLngBounds, inside?: Boolean, padding?: Point): Number
3811	// Returns the maximum zoom level on which the given bounds fit to the map
3812	// view in its entirety. If `inside` (optional) is set to `true`, the method
3813	// instead returns the minimum zoom level on which the map view fits into
3814	// the given bounds in its entirety.
3815	getBoundsZoom: function (bounds, inside, padding) { // (LatLngBounds[, Boolean, Point]) -> Number
3816		bounds = toLatLngBounds(bounds);
3817		padding = toPoint(padding || [0, 0]);
3818
3819		var zoom = this.getZoom() || 0,
3820		    min = this.getMinZoom(),
3821		    max = this.getMaxZoom(),
3822		    nw = bounds.getNorthWest(),
3823		    se = bounds.getSouthEast(),
3824		    size = this.getSize().subtract(padding),
3825		    boundsSize = toBounds(this.project(se, zoom), this.project(nw, zoom)).getSize(),
3826		    snap = any3d ? this.options.zoomSnap : 1,
3827		    scalex = size.x / boundsSize.x,
3828		    scaley = size.y / boundsSize.y,
3829		    scale = inside ? Math.max(scalex, scaley) : Math.min(scalex, scaley);
3830
3831		zoom = this.getScaleZoom(scale, zoom);
3832
3833		if (snap) {
3834			zoom = Math.round(zoom / (snap / 100)) * (snap / 100); // don't jump if within 1% of a snap level
3835			zoom = inside ? Math.ceil(zoom / snap) * snap : Math.floor(zoom / snap) * snap;
3836		}
3837
3838		return Math.max(min, Math.min(max, zoom));
3839	},
3840
3841	// @method getSize(): Point
3842	// Returns the current size of the map container (in pixels).
3843	getSize: function () {
3844		if (!this._size || this._sizeChanged) {
3845			this._size = new Point(
3846				this._container.clientWidth || 0,
3847				this._container.clientHeight || 0);
3848
3849			this._sizeChanged = false;
3850		}
3851		return this._size.clone();
3852	},
3853
3854	// @method getPixelBounds(): Bounds
3855	// Returns the bounds of the current map view in projected pixel
3856	// coordinates (sometimes useful in layer and overlay implementations).
3857	getPixelBounds: function (center, zoom) {
3858		var topLeftPoint = this._getTopLeftPoint(center, zoom);
3859		return new Bounds(topLeftPoint, topLeftPoint.add(this.getSize()));
3860	},
3861
3862	// TODO: Check semantics - isn't the pixel origin the 0,0 coord relative to
3863	// the map pane? "left point of the map layer" can be confusing, specially
3864	// since there can be negative offsets.
3865	// @method getPixelOrigin(): Point
3866	// Returns the projected pixel coordinates of the top left point of
3867	// the map layer (useful in custom layer and overlay implementations).
3868	getPixelOrigin: function () {
3869		this._checkIfLoaded();
3870		return this._pixelOrigin;
3871	},
3872
3873	// @method getPixelWorldBounds(zoom?: Number): Bounds
3874	// Returns the world's bounds in pixel coordinates for zoom level `zoom`.
3875	// If `zoom` is omitted, the map's current zoom level is used.
3876	getPixelWorldBounds: function (zoom) {
3877		return this.options.crs.getProjectedBounds(zoom === undefined ? this.getZoom() : zoom);
3878	},
3879
3880	// @section Other Methods
3881
3882	// @method getPane(pane: String|HTMLElement): HTMLElement
3883	// Returns a [map pane](#map-pane), given its name or its HTML element (its identity).
3884	getPane: function (pane) {
3885		return typeof pane === 'string' ? this._panes[pane] : pane;
3886	},
3887
3888	// @method getPanes(): Object
3889	// Returns a plain object containing the names of all [panes](#map-pane) as keys and
3890	// the panes as values.
3891	getPanes: function () {
3892		return this._panes;
3893	},
3894
3895	// @method getContainer: HTMLElement
3896	// Returns the HTML element that contains the map.
3897	getContainer: function () {
3898		return this._container;
3899	},
3900
3901
3902	// @section Conversion Methods
3903
3904	// @method getZoomScale(toZoom: Number, fromZoom: Number): Number
3905	// Returns the scale factor to be applied to a map transition from zoom level
3906	// `fromZoom` to `toZoom`. Used internally to help with zoom animations.
3907	getZoomScale: function (toZoom, fromZoom) {
3908		// TODO replace with universal implementation after refactoring projections
3909		var crs = this.options.crs;
3910		fromZoom = fromZoom === undefined ? this._zoom : fromZoom;
3911		return crs.scale(toZoom) / crs.scale(fromZoom);
3912	},
3913
3914	// @method getScaleZoom(scale: Number, fromZoom: Number): Number
3915	// Returns the zoom level that the map would end up at, if it is at `fromZoom`
3916	// level and everything is scaled by a factor of `scale`. Inverse of
3917	// [`getZoomScale`](#map-getZoomScale).
3918	getScaleZoom: function (scale, fromZoom) {
3919		var crs = this.options.crs;
3920		fromZoom = fromZoom === undefined ? this._zoom : fromZoom;
3921		var zoom = crs.zoom(scale * crs.scale(fromZoom));
3922		return isNaN(zoom) ? Infinity : zoom;
3923	},
3924
3925	// @method project(latlng: LatLng, zoom: Number): Point
3926	// Projects a geographical coordinate `LatLng` according to the projection
3927	// of the map's CRS, then scales it according to `zoom` and the CRS's
3928	// `Transformation`. The result is pixel coordinate relative to
3929	// the CRS origin.
3930	project: function (latlng, zoom) {
3931		zoom = zoom === undefined ? this._zoom : zoom;
3932		return this.options.crs.latLngToPoint(toLatLng(latlng), zoom);
3933	},
3934
3935	// @method unproject(point: Point, zoom: Number): LatLng
3936	// Inverse of [`project`](#map-project).
3937	unproject: function (point, zoom) {
3938		zoom = zoom === undefined ? this._zoom : zoom;
3939		return this.options.crs.pointToLatLng(toPoint(point), zoom);
3940	},
3941
3942	// @method layerPointToLatLng(point: Point): LatLng
3943	// Given a pixel coordinate relative to the [origin pixel](#map-getpixelorigin),
3944	// returns the corresponding geographical coordinate (for the current zoom level).
3945	layerPointToLatLng: function (point) {
3946		var projectedPoint = toPoint(point).add(this.getPixelOrigin());
3947		return this.unproject(projectedPoint);
3948	},
3949
3950	// @method latLngToLayerPoint(latlng: LatLng): Point
3951	// Given a geographical coordinate, returns the corresponding pixel coordinate
3952	// relative to the [origin pixel](#map-getpixelorigin).
3953	latLngToLayerPoint: function (latlng) {
3954		var projectedPoint = this.project(toLatLng(latlng))._round();
3955		return projectedPoint._subtract(this.getPixelOrigin());
3956	},
3957
3958	// @method wrapLatLng(latlng: LatLng): LatLng
3959	// Returns a `LatLng` where `lat` and `lng` has been wrapped according to the
3960	// map's CRS's `wrapLat` and `wrapLng` properties, if they are outside the
3961	// CRS's bounds.
3962	// By default this means longitude is wrapped around the dateline so its
3963	// value is between -180 and +180 degrees.
3964	wrapLatLng: function (latlng) {
3965		return this.options.crs.wrapLatLng(toLatLng(latlng));
3966	},
3967
3968	// @method wrapLatLngBounds(bounds: LatLngBounds): LatLngBounds
3969	// Returns a `LatLngBounds` with the same size as the given one, ensuring that
3970	// its center is within the CRS's bounds.
3971	// By default this means the center longitude is wrapped around the dateline so its
3972	// value is between -180 and +180 degrees, and the majority of the bounds
3973	// overlaps the CRS's bounds.
3974	wrapLatLngBounds: function (latlng) {
3975		return this.options.crs.wrapLatLngBounds(toLatLngBounds(latlng));
3976	},
3977
3978	// @method distance(latlng1: LatLng, latlng2: LatLng): Number
3979	// Returns the distance between two geographical coordinates according to
3980	// the map's CRS. By default this measures distance in meters.
3981	distance: function (latlng1, latlng2) {
3982		return this.options.crs.distance(toLatLng(latlng1), toLatLng(latlng2));
3983	},
3984
3985	// @method containerPointToLayerPoint(point: Point): Point
3986	// Given a pixel coordinate relative to the map container, returns the corresponding
3987	// pixel coordinate relative to the [origin pixel](#map-getpixelorigin).
3988	containerPointToLayerPoint: function (point) { // (Point)
3989		return toPoint(point).subtract(this._getMapPanePos());
3990	},
3991
3992	// @method layerPointToContainerPoint(point: Point): Point
3993	// Given a pixel coordinate relative to the [origin pixel](#map-getpixelorigin),
3994	// returns the corresponding pixel coordinate relative to the map container.
3995	layerPointToContainerPoint: function (point) { // (Point)
3996		return toPoint(point).add(this._getMapPanePos());
3997	},
3998
3999	// @method containerPointToLatLng(point: Point): LatLng
4000	// Given a pixel coordinate relative to the map container, returns
4001	// the corresponding geographical coordinate (for the current zoom level).
4002	containerPointToLatLng: function (point) {
4003		var layerPoint = this.containerPointToLayerPoint(toPoint(point));
4004		return this.layerPointToLatLng(layerPoint);
4005	},
4006
4007	// @method latLngToContainerPoint(latlng: LatLng): Point
4008	// Given a geographical coordinate, returns the corresponding pixel coordinate
4009	// relative to the map container.
4010	latLngToContainerPoint: function (latlng) {
4011		return this.layerPointToContainerPoint(this.latLngToLayerPoint(toLatLng(latlng)));
4012	},
4013
4014	// @method mouseEventToContainerPoint(ev: MouseEvent): Point
4015	// Given a MouseEvent object, returns the pixel coordinate relative to the
4016	// map container where the event took place.
4017	mouseEventToContainerPoint: function (e) {
4018		return getMousePosition(e, this._container);
4019	},
4020
4021	// @method mouseEventToLayerPoint(ev: MouseEvent): Point
4022	// Given a MouseEvent object, returns the pixel coordinate relative to
4023	// the [origin pixel](#map-getpixelorigin) where the event took place.
4024	mouseEventToLayerPoint: function (e) {
4025		return this.containerPointToLayerPoint(this.mouseEventToContainerPoint(e));
4026	},
4027
4028	// @method mouseEventToLatLng(ev: MouseEvent): LatLng
4029	// Given a MouseEvent object, returns geographical coordinate where the
4030	// event took place.
4031	mouseEventToLatLng: function (e) { // (MouseEvent)
4032		return this.layerPointToLatLng(this.mouseEventToLayerPoint(e));
4033	},
4034
4035
4036	// map initialization methods
4037
4038	_initContainer: function (id) {
4039		var container = this._container = get(id);
4040
4041		if (!container) {
4042			throw new Error('Map container not found.');
4043		} else if (container._leaflet_id) {
4044			throw new Error('Map container is already initialized.');
4045		}
4046
4047		on(container, 'scroll', this._onScroll, this);
4048		this._containerId = stamp(container);
4049	},
4050
4051	_initLayout: function () {
4052		var container = this._container;
4053
4054		this._fadeAnimated = this.options.fadeAnimation && any3d;
4055
4056		addClass(container, 'leaflet-container' +
4057			(touch ? ' leaflet-touch' : '') +
4058			(retina ? ' leaflet-retina' : '') +
4059			(ielt9 ? ' leaflet-oldie' : '') +
4060			(safari ? ' leaflet-safari' : '') +
4061			(this._fadeAnimated ? ' leaflet-fade-anim' : ''));
4062
4063		var position = getStyle(container, 'position');
4064
4065		if (position !== 'absolute' && position !== 'relative' && position !== 'fixed') {
4066			container.style.position = 'relative';
4067		}
4068
4069		this._initPanes();
4070
4071		if (this._initControlPos) {
4072			this._initControlPos();
4073		}
4074	},
4075
4076	_initPanes: function () {
4077		var panes = this._panes = {};
4078		this._paneRenderers = {};
4079
4080		// @section
4081		//
4082		// Panes are DOM elements used to control the ordering of layers on the map. You
4083		// can access panes with [`map.getPane`](#map-getpane) or
4084		// [`map.getPanes`](#map-getpanes) methods. New panes can be created with the
4085		// [`map.createPane`](#map-createpane) method.
4086		//
4087		// Every map has the following default panes that differ only in zIndex.
4088		//
4089		// @pane mapPane: HTMLElement = 'auto'
4090		// Pane that contains all other map panes
4091
4092		this._mapPane = this.createPane('mapPane', this._container);
4093		setPosition(this._mapPane, new Point(0, 0));
4094
4095		// @pane tilePane: HTMLElement = 200
4096		// Pane for `GridLayer`s and `TileLayer`s
4097		this.createPane('tilePane');
4098		// @pane overlayPane: HTMLElement = 400
4099		// Pane for vectors (`Path`s, like `Polyline`s and `Polygon`s), `ImageOverlay`s and `VideoOverlay`s
4100		this.createPane('shadowPane');
4101		// @pane shadowPane: HTMLElement = 500
4102		// Pane for overlay shadows (e.g. `Marker` shadows)
4103		this.createPane('overlayPane');
4104		// @pane markerPane: HTMLElement = 600
4105		// Pane for `Icon`s of `Marker`s
4106		this.createPane('markerPane');
4107		// @pane tooltipPane: HTMLElement = 650
4108		// Pane for `Tooltip`s.
4109		this.createPane('tooltipPane');
4110		// @pane popupPane: HTMLElement = 700
4111		// Pane for `Popup`s.
4112		this.createPane('popupPane');
4113
4114		if (!this.options.markerZoomAnimation) {
4115			addClass(panes.markerPane, 'leaflet-zoom-hide');
4116			addClass(panes.shadowPane, 'leaflet-zoom-hide');
4117		}
4118	},
4119
4120
4121	// private methods that modify map state
4122
4123	// @section Map state change events
4124	_resetView: function (center, zoom) {
4125		setPosition(this._mapPane, new Point(0, 0));
4126
4127		var loading = !this._loaded;
4128		this._loaded = true;
4129		zoom = this._limitZoom(zoom);
4130
4131		this.fire('viewprereset');
4132
4133		var zoomChanged = this._zoom !== zoom;
4134		this
4135			._moveStart(zoomChanged, false)
4136			._move(center, zoom)
4137			._moveEnd(zoomChanged);
4138
4139		// @event viewreset: Event
4140		// Fired when the map needs to redraw its content (this usually happens
4141		// on map zoom or load). Very useful for creating custom overlays.
4142		this.fire('viewreset');
4143
4144		// @event load: Event
4145		// Fired when the map is initialized (when its center and zoom are set
4146		// for the first time).
4147		if (loading) {
4148			this.fire('load');
4149		}
4150	},
4151
4152	_moveStart: function (zoomChanged, noMoveStart) {
4153		// @event zoomstart: Event
4154		// Fired when the map zoom is about to change (e.g. before zoom animation).
4155		// @event movestart: Event
4156		// Fired when the view of the map starts changing (e.g. user starts dragging the map).
4157		if (zoomChanged) {
4158			this.fire('zoomstart');
4159		}
4160		if (!noMoveStart) {
4161			this.fire('movestart');
4162		}
4163		return this;
4164	},
4165
4166	_move: function (center, zoom, data) {
4167		if (zoom === undefined) {
4168			zoom = this._zoom;
4169		}
4170		var zoomChanged = this._zoom !== zoom;
4171
4172		this._zoom = zoom;
4173		this._lastCenter = center;
4174		this._pixelOrigin = this._getNewPixelOrigin(center);
4175
4176		// @event zoom: Event
4177		// Fired repeatedly during any change in zoom level, including zoom
4178		// and fly animations.
4179		if (zoomChanged || (data && data.pinch)) {	// Always fire 'zoom' if pinching because #3530
4180			this.fire('zoom', data);
4181		}
4182
4183		// @event move: Event
4184		// Fired repeatedly during any movement of the map, including pan and
4185		// fly animations.
4186		return this.fire('move', data);
4187	},
4188
4189	_moveEnd: function (zoomChanged) {
4190		// @event zoomend: Event
4191		// Fired when the map has changed, after any animations.
4192		if (zoomChanged) {
4193			this.fire('zoomend');
4194		}
4195
4196		// @event moveend: Event
4197		// Fired when the center of the map stops changing (e.g. user stopped
4198		// dragging the map).
4199		return this.fire('moveend');
4200	},
4201
4202	_stop: function () {
4203		cancelAnimFrame(this._flyToFrame);
4204		if (this._panAnim) {
4205			this._panAnim.stop();
4206		}
4207		return this;
4208	},
4209
4210	_rawPanBy: function (offset) {
4211		setPosition(this._mapPane, this._getMapPanePos().subtract(offset));
4212	},
4213
4214	_getZoomSpan: function () {
4215		return this.getMaxZoom() - this.getMinZoom();
4216	},
4217
4218	_panInsideMaxBounds: function () {
4219		if (!this._enforcingBounds) {
4220			this.panInsideBounds(this.options.maxBounds);
4221		}
4222	},
4223
4224	_checkIfLoaded: function () {
4225		if (!this._loaded) {
4226			throw new Error('Set map center and zoom first.');
4227		}
4228	},
4229
4230	// DOM event handling
4231
4232	// @section Interaction events
4233	_initEvents: function (remove$$1) {
4234		this._targets = {};
4235		this._targets[stamp(this._container)] = this;
4236
4237		var onOff = remove$$1 ? off : on;
4238
4239		// @event click: MouseEvent
4240		// Fired when the user clicks (or taps) the map.
4241		// @event dblclick: MouseEvent
4242		// Fired when the user double-clicks (or double-taps) the map.
4243		// @event mousedown: MouseEvent
4244		// Fired when the user pushes the mouse button on the map.
4245		// @event mouseup: MouseEvent
4246		// Fired when the user releases the mouse button on the map.
4247		// @event mouseover: MouseEvent
4248		// Fired when the mouse enters the map.
4249		// @event mouseout: MouseEvent
4250		// Fired when the mouse leaves the map.
4251		// @event mousemove: MouseEvent
4252		// Fired while the mouse moves over the map.
4253		// @event contextmenu: MouseEvent
4254		// Fired when the user pushes the right mouse button on the map, prevents
4255		// default browser context menu from showing if there are listeners on
4256		// this event. Also fired on mobile when the user holds a single touch
4257		// for a second (also called long press).
4258		// @event keypress: KeyboardEvent
4259		// Fired when the user presses a key from the keyboard while the map is focused.
4260		onOff(this._container, 'click dblclick mousedown mouseup ' +
4261			'mouseover mouseout mousemove contextmenu keypress', this._handleDOMEvent, this);
4262
4263		if (this.options.trackResize) {
4264			onOff(window, 'resize', this._onResize, this);
4265		}
4266
4267		if (any3d && this.options.transform3DLimit) {
4268			(remove$$1 ? this.off : this.on).call(this, 'moveend', this._onMoveEnd);
4269		}
4270	},
4271
4272	_onResize: function () {
4273		cancelAnimFrame(this._resizeRequest);
4274		this._resizeRequest = requestAnimFrame(
4275		        function () { this.invalidateSize({debounceMoveend: true}); }, this);
4276	},
4277
4278	_onScroll: function () {
4279		this._container.scrollTop  = 0;
4280		this._container.scrollLeft = 0;
4281	},
4282
4283	_onMoveEnd: function () {
4284		var pos = this._getMapPanePos();
4285		if (Math.max(Math.abs(pos.x), Math.abs(pos.y)) >= this.options.transform3DLimit) {
4286			// https://bugzilla.mozilla.org/show_bug.cgi?id=1203873 but Webkit also have
4287			// a pixel offset on very high values, see: http://jsfiddle.net/dg6r5hhb/
4288			this._resetView(this.getCenter(), this.getZoom());
4289		}
4290	},
4291
4292	_findEventTargets: function (e, type) {
4293		var targets = [],
4294		    target,
4295		    isHover = type === 'mouseout' || type === 'mouseover',
4296		    src = e.target || e.srcElement,
4297		    dragging = false;
4298
4299		while (src) {
4300			target = this._targets[stamp(src)];
4301			if (target && (type === 'click' || type === 'preclick') && !e._simulated && this._draggableMoved(target)) {
4302				// Prevent firing click after you just dragged an object.
4303				dragging = true;
4304				break;
4305			}
4306			if (target && target.listens(type, true)) {
4307				if (isHover && !isExternalTarget(src, e)) { break; }
4308				targets.push(target);
4309				if (isHover) { break; }
4310			}
4311			if (src === this._container) { break; }
4312			src = src.parentNode;
4313		}
4314		if (!targets.length && !dragging && !isHover && isExternalTarget(src, e)) {
4315			targets = [this];
4316		}
4317		return targets;
4318	},
4319
4320	_handleDOMEvent: function (e) {
4321		if (!this._loaded || skipped(e)) { return; }
4322
4323		var type = e.type;
4324
4325		if (type === 'mousedown' || type === 'keypress') {
4326			// prevents outline when clicking on keyboard-focusable element
4327			preventOutline(e.target || e.srcElement);
4328		}
4329
4330		this._fireDOMEvent(e, type);
4331	},
4332
4333	_mouseEvents: ['click', 'dblclick', 'mouseover', 'mouseout', 'contextmenu'],
4334
4335	_fireDOMEvent: function (e, type, targets) {
4336
4337		if (e.type === 'click') {
4338			// Fire a synthetic 'preclick' event which propagates up (mainly for closing popups).
4339			// @event preclick: MouseEvent
4340			// Fired before mouse click on the map (sometimes useful when you
4341			// want something to happen on click before any existing click
4342			// handlers start running).
4343			var synth = extend({}, e);
4344			synth.type = 'preclick';
4345			this._fireDOMEvent(synth, synth.type, targets);
4346		}
4347
4348		if (e._stopped) { return; }
4349
4350		// Find the layer the event is propagating from and its parents.
4351		targets = (targets || []).concat(this._findEventTargets(e, type));
4352
4353		if (!targets.length) { return; }
4354
4355		var target = targets[0];
4356		if (type === 'contextmenu' && target.listens(type, true)) {
4357			preventDefault(e);
4358		}
4359
4360		var data = {
4361			originalEvent: e
4362		};
4363
4364		if (e.type !== 'keypress') {
4365			var isMarker = target.getLatLng && (!target._radius || target._radius <= 10);
4366			data.containerPoint = isMarker ?
4367				this.latLngToContainerPoint(target.getLatLng()) : this.mouseEventToContainerPoint(e);
4368			data.layerPoint = this.containerPointToLayerPoint(data.containerPoint);
4369			data.latlng = isMarker ? target.getLatLng() : this.layerPointToLatLng(data.layerPoint);
4370		}
4371
4372		for (var i = 0; i < targets.length; i++) {
4373			targets[i].fire(type, data, true);
4374			if (data.originalEvent._stopped ||
4375				(targets[i].options.bubblingMouseEvents === false && indexOf(this._mouseEvents, type) !== -1)) { return; }
4376		}
4377	},
4378
4379	_draggableMoved: function (obj) {
4380		obj = obj.dragging && obj.dragging.enabled() ? obj : this;
4381		return (obj.dragging && obj.dragging.moved()) || (this.boxZoom && this.boxZoom.moved());
4382	},
4383
4384	_clearHandlers: function () {
4385		for (var i = 0, len = this._handlers.length; i < len; i++) {
4386			this._handlers[i].disable();
4387		}
4388	},
4389
4390	// @section Other Methods
4391
4392	// @method whenReady(fn: Function, context?: Object): this
4393	// Runs the given function `fn` when the map gets initialized with
4394	// a view (center and zoom) and at least one layer, or immediately
4395	// if it's already initialized, optionally passing a function context.
4396	whenReady: function (callback, context) {
4397		if (this._loaded) {
4398			callback.call(context || this, {target: this});
4399		} else {
4400			this.on('load', callback, context);
4401		}
4402		return this;
4403	},
4404
4405
4406	// private methods for getting map state
4407
4408	_getMapPanePos: function () {
4409		return getPosition(this._mapPane) || new Point(0, 0);
4410	},
4411
4412	_moved: function () {
4413		var pos = this._getMapPanePos();
4414		return pos && !pos.equals([0, 0]);
4415	},
4416
4417	_getTopLeftPoint: function (center, zoom) {
4418		var pixelOrigin = center && zoom !== undefined ?
4419			this._getNewPixelOrigin(center, zoom) :
4420			this.getPixelOrigin();
4421		return pixelOrigin.subtract(this._getMapPanePos());
4422	},
4423
4424	_getNewPixelOrigin: function (center, zoom) {
vendor: 34,248 bytes, lines 4425-5679
4425		var viewHalf = this.getSize()._divideBy(2);
4426		return this.project(center, zoom)._subtract(viewHalf)._add(this._getMapPanePos())._round();
4427	},
4428
4429	_latLngToNewLayerPoint: function (latlng, zoom, center) {
4430		var topLeft = this._getNewPixelOrigin(center, zoom);
4431		return this.project(latlng, zoom)._subtract(topLeft);
4432	},
4433
4434	_latLngBoundsToNewLayerBounds: function (latLngBounds, zoom, center) {
4435		var topLeft = this._getNewPixelOrigin(center, zoom);
4436		return toBounds([
4437			this.project(latLngBounds.getSouthWest(), zoom)._subtract(topLeft),
4438			this.project(latLngBounds.getNorthWest(), zoom)._subtract(topLeft),
4439			this.project(latLngBounds.getSouthEast(), zoom)._subtract(topLeft),
4440			this.project(latLngBounds.getNorthEast(), zoom)._subtract(topLeft)
4441		]);
4442	},
4443
4444	// layer point of the current center
4445	_getCenterLayerPoint: function () {
4446		return this.containerPointToLayerPoint(this.getSize()._divideBy(2));
4447	},
4448
4449	// offset of the specified place to the current center in pixels
4450	_getCenterOffset: function (latlng) {
4451		return this.latLngToLayerPoint(latlng).subtract(this._getCenterLayerPoint());
4452	},
4453
4454	// adjust center for view to get inside bounds
4455	_limitCenter: function (center, zoom, bounds) {
4456
4457		if (!bounds) { return center; }
4458
4459		var centerPoint = this.project(center, zoom),
4460		    viewHalf = this.getSize().divideBy(2),
4461		    viewBounds = new Bounds(centerPoint.subtract(viewHalf), centerPoint.add(viewHalf)),
4462		    offset = this._getBoundsOffset(viewBounds, bounds, zoom);
4463
4464		// If offset is less than a pixel, ignore.
4465		// This prevents unstable projections from getting into
4466		// an infinite loop of tiny offsets.
4467		if (offset.round().equals([0, 0])) {
4468			return center;
4469		}
4470
4471		return this.unproject(centerPoint.add(offset), zoom);
4472	},
4473
4474	// adjust offset for view to get inside bounds
4475	_limitOffset: function (offset, bounds) {
4476		if (!bounds) { return offset; }
4477
4478		var viewBounds = this.getPixelBounds(),
4479		    newBounds = new Bounds(viewBounds.min.add(offset), viewBounds.max.add(offset));
4480
4481		return offset.add(this._getBoundsOffset(newBounds, bounds));
4482	},
4483
4484	// returns offset needed for pxBounds to get inside maxBounds at a specified zoom
4485	_getBoundsOffset: function (pxBounds, maxBounds, zoom) {
4486		var projectedMaxBounds = toBounds(
4487		        this.project(maxBounds.getNorthEast(), zoom),
4488		        this.project(maxBounds.getSouthWest(), zoom)
4489		    ),
4490		    minOffset = projectedMaxBounds.min.subtract(pxBounds.min),
4491		    maxOffset = projectedMaxBounds.max.subtract(pxBounds.max),
4492
4493		    dx = this._rebound(minOffset.x, -maxOffset.x),
4494		    dy = this._rebound(minOffset.y, -maxOffset.y);
4495
4496		return new Point(dx, dy);
4497	},
4498
4499	_rebound: function (left, right) {
4500		return left + right > 0 ?
4501			Math.round(left - right) / 2 :
4502			Math.max(0, Math.ceil(left)) - Math.max(0, Math.floor(right));
4503	},
4504
4505	_limitZoom: function (zoom) {
4506		var min = this.getMinZoom(),
4507		    max = this.getMaxZoom(),
4508		    snap = any3d ? this.options.zoomSnap : 1;
4509		if (snap) {
4510			zoom = Math.round(zoom / snap) * snap;
4511		}
4512		return Math.max(min, Math.min(max, zoom));
4513	},
4514
4515	_onPanTransitionStep: function () {
4516		this.fire('move');
4517	},
4518
4519	_onPanTransitionEnd: function () {
4520		removeClass(this._mapPane, 'leaflet-pan-anim');
4521		this.fire('moveend');
4522	},
4523
4524	_tryAnimatedPan: function (center, options) {
4525		// difference between the new and current centers in pixels
4526		var offset = this._getCenterOffset(center)._trunc();
4527
4528		// don't animate too far unless animate: true specified in options
4529		if ((options && options.animate) !== true && !this.getSize().contains(offset)) { return false; }
4530
4531		this.panBy(offset, options);
4532
4533		return true;
4534	},
4535
4536	_createAnimProxy: function () {
4537
4538		var proxy = this._proxy = create$1('div', 'leaflet-proxy leaflet-zoom-animated');
4539		this._panes.mapPane.appendChild(proxy);
4540
4541		this.on('zoomanim', function (e) {
4542			var prop = TRANSFORM,
4543			    transform = this._proxy.style[prop];
4544
4545			setTransform(this._proxy, this.project(e.center, e.zoom), this.getZoomScale(e.zoom, 1));
4546
4547			// workaround for case when transform is the same and so transitionend event is not fired
4548			if (transform === this._proxy.style[prop] && this._animatingZoom) {
4549				this._onZoomTransitionEnd();
4550			}
4551		}, this);
4552
4553		this.on('load moveend', function () {
4554			var c = this.getCenter(),
4555			    z = this.getZoom();
4556			setTransform(this._proxy, this.project(c, z), this.getZoomScale(z, 1));
4557		}, this);
4558
4559		this._on('unload', this._destroyAnimProxy, this);
4560	},
4561
4562	_destroyAnimProxy: function () {
4563		remove(this._proxy);
4564		delete this._proxy;
4565	},
4566
4567	_catchTransitionEnd: function (e) {
4568		if (this._animatingZoom && e.propertyName.indexOf('transform') >= 0) {
4569			this._onZoomTransitionEnd();
4570		}
4571	},
4572
4573	_nothingToAnimate: function () {
4574		return !this._container.getElementsByClassName('leaflet-zoom-animated').length;
4575	},
4576
4577	_tryAnimatedZoom: function (center, zoom, options) {
4578
4579		if (this._animatingZoom) { return true; }
4580
4581		options = options || {};
4582
4583		// don't animate if disabled, not supported or zoom difference is too large
4584		if (!this._zoomAnimated || options.animate === false || this._nothingToAnimate() ||
4585		        Math.abs(zoom - this._zoom) > this.options.zoomAnimationThreshold) { return false; }
4586
4587		// offset is the pixel coords of the zoom origin relative to the current center
4588		var scale = this.getZoomScale(zoom),
4589		    offset = this._getCenterOffset(center)._divideBy(1 - 1 / scale);
4590
4591		// don't animate if the zoom origin isn't within one screen from the current center, unless forced
4592		if (options.animate !== true && !this.getSize().contains(offset)) { return false; }
4593
4594		requestAnimFrame(function () {
4595			this
4596			    ._moveStart(true, false)
4597			    ._animateZoom(center, zoom, true);
4598		}, this);
4599
4600		return true;
4601	},
4602
4603	_animateZoom: function (center, zoom, startAnim, noUpdate) {
4604		if (!this._mapPane) { return; }
4605
4606		if (startAnim) {
4607			this._animatingZoom = true;
4608
4609			// remember what center/zoom to set after animation
4610			this._animateToCenter = center;
4611			this._animateToZoom = zoom;
4612
4613			addClass(this._mapPane, 'leaflet-zoom-anim');
4614		}
4615
4616		// @event zoomanim: ZoomAnimEvent
4617		// Fired on every frame of a zoom animation
4618		this.fire('zoomanim', {
4619			center: center,
4620			zoom: zoom,
4621			noUpdate: noUpdate
4622		});
4623
4624		// Work around webkit not firing 'transitionend', see https://github.com/Leaflet/Leaflet/issues/3689, 2693
4625		setTimeout(bind(this._onZoomTransitionEnd, this), 250);
4626	},
4627
4628	_onZoomTransitionEnd: function () {
4629		if (!this._animatingZoom) { return; }
4630
4631		if (this._mapPane) {
4632			removeClass(this._mapPane, 'leaflet-zoom-anim');
4633		}
4634
4635		this._animatingZoom = false;
4636
4637		this._move(this._animateToCenter, this._animateToZoom);
4638
4639		// This anim frame should prevent an obscure iOS webkit tile loading race condition.
4640		requestAnimFrame(function () {
4641			this._moveEnd(true);
4642		}, this);
4643	}
4644});
4645
4646// @section
4647
4648// @factory L.map(id: String, options?: Map options)
4649// Instantiates a map object given the DOM ID of a `<div>` element
4650// and optionally an object literal with `Map options`.
4651//
4652// @alternative
4653// @factory L.map(el: HTMLElement, options?: Map options)
4654// Instantiates a map object given an instance of a `<div>` HTML element
4655// and optionally an object literal with `Map options`.
4656function createMap(id, options) {
4657	return new Map(id, options);
4658}
4659
4660/*
4661 * @class Control
4662 * @aka L.Control
4663 * @inherits Class
4664 *
4665 * L.Control is a base class for implementing map controls. Handles positioning.
4666 * All other controls extend from this class.
4667 */
4668
4669var Control = Class.extend({
4670	// @section
4671	// @aka Control options
4672	options: {
4673		// @option position: String = 'topright'
4674		// The position of the control (one of the map corners). Possible values are `'topleft'`,
4675		// `'topright'`, `'bottomleft'` or `'bottomright'`
4676		position: 'topright'
4677	},
4678
4679	initialize: function (options) {
4680		setOptions(this, options);
4681	},
4682
4683	/* @section
4684	 * Classes extending L.Control will inherit the following methods:
4685	 *
4686	 * @method getPosition: string
4687	 * Returns the position of the control.
4688	 */
4689	getPosition: function () {
4690		return this.options.position;
4691	},
4692
4693	// @method setPosition(position: string): this
4694	// Sets the position of the control.
4695	setPosition: function (position) {
4696		var map = this._map;
4697
4698		if (map) {
4699			map.removeControl(this);
4700		}
4701
4702		this.options.position = position;
4703
4704		if (map) {
4705			map.addControl(this);
4706		}
4707
4708		return this;
4709	},
4710
4711	// @method getContainer: HTMLElement
4712	// Returns the HTMLElement that contains the control.
4713	getContainer: function () {
4714		return this._container;
4715	},
4716
4717	// @method addTo(map: Map): this
4718	// Adds the control to the given map.
4719	addTo: function (map) {
4720		this.remove();
4721		this._map = map;
4722
4723		var container = this._container = this.onAdd(map),
4724		    pos = this.getPosition(),
4725		    corner = map._controlCorners[pos];
4726
4727		addClass(container, 'leaflet-control');
4728
4729		if (pos.indexOf('bottom') !== -1) {
4730			corner.insertBefore(container, corner.firstChild);
4731		} else {
4732			corner.appendChild(container);
4733		}
4734
4735		return this;
4736	},
4737
4738	// @method remove: this
4739	// Removes the control from the map it is currently active on.
4740	remove: function () {
4741		if (!this._map) {
4742			return this;
4743		}
4744
4745		remove(this._container);
4746
4747		if (this.onRemove) {
4748			this.onRemove(this._map);
4749		}
4750
4751		this._map = null;
4752
4753		return this;
4754	},
4755
4756	_refocusOnMap: function (e) {
4757		// if map exists and event is not a keyboard event
4758		if (this._map && e && e.screenX > 0 && e.screenY > 0) {
4759			this._map.getContainer().focus();
4760		}
4761	}
4762});
4763
4764var control = function (options) {
4765	return new Control(options);
4766};
4767
4768/* @section Extension methods
4769 * @uninheritable
4770 *
4771 * Every control should extend from `L.Control` and (re-)implement the following methods.
4772 *
4773 * @method onAdd(map: Map): HTMLElement
4774 * Should return the container DOM element for the control and add listeners on relevant map events. Called on [`control.addTo(map)`](#control-addTo).
4775 *
4776 * @method onRemove(map: Map)
4777 * Optional method. Should contain all clean up code that removes the listeners previously added in [`onAdd`](#control-onadd). Called on [`control.remove()`](#control-remove).
4778 */
4779
4780/* @namespace Map
4781 * @section Methods for Layers and Controls
4782 */
4783Map.include({
4784	// @method addControl(control: Control): this
4785	// Adds the given control to the map
4786	addControl: function (control) {
4787		control.addTo(this);
4788		return this;
4789	},
4790
4791	// @method removeControl(control: Control): this
4792	// Removes the given control from the map
4793	removeControl: function (control) {
4794		control.remove();
4795		return this;
4796	},
4797
4798	_initControlPos: function () {
4799		var corners = this._controlCorners = {},
4800		    l = 'leaflet-',
4801		    container = this._controlContainer =
4802		            create$1('div', l + 'control-container', this._container);
4803
4804		function createCorner(vSide, hSide) {
4805			var className = l + vSide + ' ' + l + hSide;
4806
4807			corners[vSide + hSide] = create$1('div', className, container);
4808		}
4809
4810		createCorner('top', 'left');
4811		createCorner('top', 'right');
4812		createCorner('bottom', 'left');
4813		createCorner('bottom', 'right');
4814	},
4815
4816	_clearControlPos: function () {
4817		for (var i in this._controlCorners) {
4818			remove(this._controlCorners[i]);
4819		}
4820		remove(this._controlContainer);
4821		delete this._controlCorners;
4822		delete this._controlContainer;
4823	}
4824});
4825
4826/*
4827 * @class Control.Layers
4828 * @aka L.Control.Layers
4829 * @inherits Control
4830 *
4831 * 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`.
4832 *
4833 * @example
4834 *
4835 * ```js
4836 * var baseLayers = {
4837 * 	"Mapbox": mapbox,
4838 * 	"OpenStreetMap": osm
4839 * };
4840 *
4841 * var overlays = {
4842 * 	"Marker": marker,
4843 * 	"Roads": roadsLayer
4844 * };
4845 *
4846 * L.control.layers(baseLayers, overlays).addTo(map);
4847 * ```
4848 *
4849 * The `baseLayers` and `overlays` parameters are object literals with layer names as keys and `Layer` objects as values:
4850 *
4851 * ```js
4852 * {
4853 *     "<someName1>": layer1,
4854 *     "<someName2>": layer2
4855 * }
4856 * ```
4857 *
4858 * The layer names can contain HTML, which allows you to add additional styling to the items:
4859 *
4860 * ```js
4861 * {"<img src='my-layer-icon' /> <span class='my-layer-item'>My Layer</span>": myLayer}
4862 * ```
4863 */
4864
4865var Layers = Control.extend({
4866	// @section
4867	// @aka Control.Layers options
4868	options: {
4869		// @option collapsed: Boolean = true
4870		// If `true`, the control will be collapsed into an icon and expanded on mouse hover or touch.
4871		collapsed: true,
4872		position: 'topright',
4873
4874		// @option autoZIndex: Boolean = true
4875		// 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.
4876		autoZIndex: true,
4877
4878		// @option hideSingleBase: Boolean = false
4879		// If `true`, the base layers in the control will be hidden when there is only one.
4880		hideSingleBase: false,
4881
4882		// @option sortLayers: Boolean = false
4883		// Whether to sort the layers. When `false`, layers will keep the order
4884		// in which they were added to the control.
4885		sortLayers: false,
4886
4887		// @option sortFunction: Function = *
4888		// A [compare function](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Array/sort)
4889		// that will be used for sorting the layers, when `sortLayers` is `true`.
4890		// The function receives both the `L.Layer` instances and their names, as in
4891		// `sortFunction(layerA, layerB, nameA, nameB)`.
4892		// By default, it sorts layers alphabetically by their name.
4893		sortFunction: function (layerA, layerB, nameA, nameB) {
4894			return nameA < nameB ? -1 : (nameB < nameA ? 1 : 0);
4895		}
4896	},
4897
4898	initialize: function (baseLayers, overlays, options) {
4899		setOptions(this, options);
4900
4901		this._layerControlInputs = [];
4902		this._layers = [];
4903		this._lastZIndex = 0;
4904		this._handlingClick = false;
4905
4906		for (var i in baseLayers) {
4907			this._addLayer(baseLayers[i], i);
4908		}
4909
4910		for (i in overlays) {
4911			this._addLayer(overlays[i], i, true);
4912		}
4913	},
4914
4915	onAdd: function (map) {
4916		this._initLayout();
4917		this._update();
4918
4919		this._map = map;
4920		map.on('zoomend', this._checkDisabledLayers, this);
4921
4922		for (var i = 0; i < this._layers.length; i++) {
4923			this._layers[i].layer.on('add remove', this._onLayerChange, this);
4924		}
4925
4926		return this._container;
4927	},
4928
4929	addTo: function (map) {
4930		Control.prototype.addTo.call(this, map);
4931		// Trigger expand after Layers Control has been inserted into DOM so that is now has an actual height.
4932		return this._expandIfNotCollapsed();
4933	},
4934
4935	onRemove: function () {
4936		this._map.off('zoomend', this._checkDisabledLayers, this);
4937
4938		for (var i = 0; i < this._layers.length; i++) {
4939			this._layers[i].layer.off('add remove', this._onLayerChange, this);
4940		}
4941	},
4942
4943	// @method addBaseLayer(layer: Layer, name: String): this
4944	// Adds a base layer (radio button entry) with the given name to the control.
4945	addBaseLayer: function (layer, name) {
4946		this._addLayer(layer, name);
4947		return (this._map) ? this._update() : this;
4948	},
4949
4950	// @method addOverlay(layer: Layer, name: String): this
4951	// Adds an overlay (checkbox entry) with the given name to the control.
4952	addOverlay: function (layer, name) {
4953		this._addLayer(layer, name, true);
4954		return (this._map) ? this._update() : this;
4955	},
4956
4957	// @method removeLayer(layer: Layer): this
4958	// Remove the given layer from the control.
4959	removeLayer: function (layer) {
4960		layer.off('add remove', this._onLayerChange, this);
4961
4962		var obj = this._getLayer(stamp(layer));
4963		if (obj) {
4964			this._layers.splice(this._layers.indexOf(obj), 1);
4965		}
4966		return (this._map) ? this._update() : this;
4967	},
4968
4969	// @method expand(): this
4970	// Expand the control container if collapsed.
4971	expand: function () {
4972		addClass(this._container, 'leaflet-control-layers-expanded');
4973		this._form.style.height = null;
4974		var acceptableHeight = this._map.getSize().y - (this._container.offsetTop + 50);
4975		if (acceptableHeight < this._form.clientHeight) {
4976			addClass(this._form, 'leaflet-control-layers-scrollbar');
4977			this._form.style.height = acceptableHeight + 'px';
4978		} else {
4979			removeClass(this._form, 'leaflet-control-layers-scrollbar');
4980		}
4981		this._checkDisabledLayers();
4982		return this;
4983	},
4984
4985	// @method collapse(): this
4986	// Collapse the control container if expanded.
4987	collapse: function () {
4988		removeClass(this._container, 'leaflet-control-layers-expanded');
4989		return this;
4990	},
4991
4992	_initLayout: function () {
4993		var className = 'leaflet-control-layers',
4994		    container = this._container = create$1('div', className),
4995		    collapsed = this.options.collapsed;
4996
4997		// makes this work on IE touch devices by stopping it from firing a mouseout event when the touch is released
4998		container.setAttribute('aria-haspopup', true);
4999
5000		disableClickPropagation(container);
5001		disableScrollPropagation(container);
5002
5003		var form = this._form = create$1('form', className + '-list');
5004
5005		if (collapsed) {
5006			this._map.on('click', this.collapse, this);
5007
5008			if (!android) {
5009				on(container, {
5010					mouseenter: this.expand,
5011					mouseleave: this.collapse
5012				}, this);
5013			}
5014		}
5015
5016		var link = this._layersLink = create$1('a', className + '-toggle', container);
5017		link.href = '#';
5018		link.title = 'Layers';
5019
5020		if (touch) {
5021			on(link, 'click', stop);
5022			on(link, 'click', this.expand, this);
5023		} else {
5024			on(link, 'focus', this.expand, this);
5025		}
5026
5027		if (!collapsed) {
5028			this.expand();
5029		}
5030
5031		this._baseLayersList = create$1('div', className + '-base', form);
5032		this._separator = create$1('div', className + '-separator', form);
5033		this._overlaysList = create$1('div', className + '-overlays', form);
5034
5035		container.appendChild(form);
5036	},
5037
5038	_getLayer: function (id) {
5039		for (var i = 0; i < this._layers.length; i++) {
5040
5041			if (this._layers[i] && stamp(this._layers[i].layer) === id) {
5042				return this._layers[i];
5043			}
5044		}
5045	},
5046
5047	_addLayer: function (layer, name, overlay) {
5048		if (this._map) {
5049			layer.on('add remove', this._onLayerChange, this);
5050		}
5051
5052		this._layers.push({
5053			layer: layer,
5054			name: name,
5055			overlay: overlay
5056		});
5057
5058		if (this.options.sortLayers) {
5059			this._layers.sort(bind(function (a, b) {
5060				return this.options.sortFunction(a.layer, b.layer, a.name, b.name);
5061			}, this));
5062		}
5063
5064		if (this.options.autoZIndex && layer.setZIndex) {
5065			this._lastZIndex++;
5066			layer.setZIndex(this._lastZIndex);
5067		}
5068
5069		this._expandIfNotCollapsed();
5070	},
5071
5072	_update: function () {
5073		if (!this._container) { return this; }
5074
5075		empty(this._baseLayersList);
5076		empty(this._overlaysList);
5077
5078		this._layerControlInputs = [];
5079		var baseLayersPresent, overlaysPresent, i, obj, baseLayersCount = 0;
5080
5081		for (i = 0; i < this._layers.length; i++) {
5082			obj = this._layers[i];
5083			this._addItem(obj);
5084			overlaysPresent = overlaysPresent || obj.overlay;
5085			baseLayersPresent = baseLayersPresent || !obj.overlay;
5086			baseLayersCount += !obj.overlay ? 1 : 0;
5087		}
5088
5089		// Hide base layers section if there's only one layer.
5090		if (this.options.hideSingleBase) {
5091			baseLayersPresent = baseLayersPresent && baseLayersCount > 1;
5092			this._baseLayersList.style.display = baseLayersPresent ? '' : 'none';
5093		}
5094
5095		this._separator.style.display = overlaysPresent && baseLayersPresent ? '' : 'none';
5096
5097		return this;
5098	},
5099
5100	_onLayerChange: function (e) {
5101		if (!this._handlingClick) {
5102			this._update();
5103		}
5104
5105		var obj = this._getLayer(stamp(e.target));
5106
5107		// @namespace Map
5108		// @section Layer events
5109		// @event baselayerchange: LayersControlEvent
5110		// Fired when the base layer is changed through the [layer control](#control-layers).
5111		// @event overlayadd: LayersControlEvent
5112		// Fired when an overlay is selected through the [layer control](#control-layers).
5113		// @event overlayremove: LayersControlEvent
5114		// Fired when an overlay is deselected through the [layer control](#control-layers).
5115		// @namespace Control.Layers
5116		var type = obj.overlay ?
5117			(e.type === 'add' ? 'overlayadd' : 'overlayremove') :
5118			(e.type === 'add' ? 'baselayerchange' : null);
5119
5120		if (type) {
5121			this._map.fire(type, obj);
5122		}
5123	},
5124
5125	// IE7 bugs out if you create a radio dynamically, so you have to do it this hacky way (see http://bit.ly/PqYLBe)
5126	_createRadioElement: function (name, checked) {
5127
5128		var radioHtml = '<input type="radio" class="leaflet-control-layers-selector" name="' +
5129				name + '"' + (checked ? ' checked="checked"' : '') + '/>';
5130
5131		var radioFragment = document.createElement('div');
5132		radioFragment.innerHTML = radioHtml;
5133
5134		return radioFragment.firstChild;
5135	},
5136
5137	_addItem: function (obj) {
5138		var label = document.createElement('label'),
5139		    checked = this._map.hasLayer(obj.layer),
5140		    input;
5141
5142		if (obj.overlay) {
5143			input = document.createElement('input');
5144			input.type = 'checkbox';
5145			input.className = 'leaflet-control-layers-selector';
5146			input.defaultChecked = checked;
5147		} else {
5148			input = this._createRadioElement('leaflet-base-layers', checked);
5149		}
5150
5151		this._layerControlInputs.push(input);
5152		input.layerId = stamp(obj.layer);
5153
5154		on(input, 'click', this._onInputClick, this);
5155
5156		var name = document.createElement('span');
5157		name.innerHTML = ' ' + obj.name;
5158
5159		// Helps from preventing layer control flicker when checkboxes are disabled
5160		// https://github.com/Leaflet/Leaflet/issues/2771
5161		var holder = document.createElement('div');
5162
5163		label.appendChild(holder);
5164		holder.appendChild(input);
5165		holder.appendChild(name);
5166
5167		var container = obj.overlay ? this._overlaysList : this._baseLayersList;
5168		container.appendChild(label);
5169
5170		this._checkDisabledLayers();
5171		return label;
5172	},
5173
5174	_onInputClick: function () {
5175		var inputs = this._layerControlInputs,
5176		    input, layer;
5177		var addedLayers = [],
5178		    removedLayers = [];
5179
5180		this._handlingClick = true;
5181
5182		for (var i = inputs.length - 1; i >= 0; i--) {
5183			input = inputs[i];
5184			layer = this._getLayer(input.layerId).layer;
5185
5186			if (input.checked) {
5187				addedLayers.push(layer);
5188			} else if (!input.checked) {
5189				removedLayers.push(layer);
5190			}
5191		}
5192
5193		// Bugfix issue 2318: Should remove all old layers before readding new ones
5194		for (i = 0; i < removedLayers.length; i++) {
5195			if (this._map.hasLayer(removedLayers[i])) {
5196				this._map.removeLayer(removedLayers[i]);
5197			}
5198		}
5199		for (i = 0; i < addedLayers.length; i++) {
5200			if (!this._map.hasLayer(addedLayers[i])) {
5201				this._map.addLayer(addedLayers[i]);
5202			}
5203		}
5204
5205		this._handlingClick = false;
5206
5207		this._refocusOnMap();
5208	},
5209
5210	_checkDisabledLayers: function () {
5211		var inputs = this._layerControlInputs,
5212		    input,
5213		    layer,
5214		    zoom = this._map.getZoom();
5215
5216		for (var i = inputs.length - 1; i >= 0; i--) {
5217			input = inputs[i];
5218			layer = this._getLayer(input.layerId).layer;
5219			input.disabled = (layer.options.minZoom !== undefined && zoom < layer.options.minZoom) ||
5220			                 (layer.options.maxZoom !== undefined && zoom > layer.options.maxZoom);
5221
5222		}
5223	},
5224
5225	_expandIfNotCollapsed: function () {
5226		if (this._map && !this.options.collapsed) {
5227			this.expand();
5228		}
5229		return this;
5230	},
5231
5232	_expand: function () {
5233		// Backward compatibility, remove me in 1.1.
5234		return this.expand();
5235	},
5236
5237	_collapse: function () {
5238		// Backward compatibility, remove me in 1.1.
5239		return this.collapse();
5240	}
5241
5242});
5243
5244
5245// @factory L.control.layers(baselayers?: Object, overlays?: Object, options?: Control.Layers options)
5246// Creates an attribution control with the given layers. Base layers will be switched with radio buttons, while overlays will be switched with checkboxes. Note that all base layers should be passed in the base layers object, but only one should be added to the map during map instantiation.
5247var layers = function (baseLayers, overlays, options) {
5248	return new Layers(baseLayers, overlays, options);
5249};
5250
5251/*
5252 * @class Control.Zoom
5253 * @aka L.Control.Zoom
5254 * @inherits Control
5255 *
5256 * 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`.
5257 */
5258
5259var Zoom = Control.extend({
5260	// @section
5261	// @aka Control.Zoom options
5262	options: {
5263		position: 'topleft',
5264
5265		// @option zoomInText: String = '+'
5266		// The text set on the 'zoom in' button.
5267		zoomInText: '+',
5268
5269		// @option zoomInTitle: String = 'Zoom in'
5270		// The title set on the 'zoom in' button.
5271		zoomInTitle: 'Zoom in',
5272
5273		// @option zoomOutText: String = '&#x2212;'
5274		// The text set on the 'zoom out' button.
5275		zoomOutText: '&#x2212;',
5276
5277		// @option zoomOutTitle: String = 'Zoom out'
5278		// The title set on the 'zoom out' button.
5279		zoomOutTitle: 'Zoom out'
5280	},
5281
5282	onAdd: function (map) {
5283		var zoomName = 'leaflet-control-zoom',
5284		    container = create$1('div', zoomName + ' leaflet-bar'),
5285		    options = this.options;
5286
5287		this._zoomInButton  = this._createButton(options.zoomInText, options.zoomInTitle,
5288		        zoomName + '-in',  container, this._zoomIn);
5289		this._zoomOutButton = this._createButton(options.zoomOutText, options.zoomOutTitle,
5290		        zoomName + '-out', container, this._zoomOut);
5291
5292		this._updateDisabled();
5293		map.on('zoomend zoomlevelschange', this._updateDisabled, this);
5294
5295		return container;
5296	},
5297
5298	onRemove: function (map) {
5299		map.off('zoomend zoomlevelschange', this._updateDisabled, this);
5300	},
5301
5302	disable: function () {
5303		this._disabled = true;
5304		this._updateDisabled();
5305		return this;
5306	},
5307
5308	enable: function () {
5309		this._disabled = false;
5310		this._updateDisabled();
5311		return this;
5312	},
5313
5314	_zoomIn: function (e) {
5315		if (!this._disabled && this._map._zoom < this._map.getMaxZoom()) {
5316			this._map.zoomIn(this._map.options.zoomDelta * (e.shiftKey ? 3 : 1));
5317		}
5318	},
5319
5320	_zoomOut: function (e) {
5321		if (!this._disabled && this._map._zoom > this._map.getMinZoom()) {
5322			this._map.zoomOut(this._map.options.zoomDelta * (e.shiftKey ? 3 : 1));
5323		}
5324	},
5325
5326	_createButton: function (html, title, className, container, fn) {
5327		var link = create$1('a', className, container);
5328		link.innerHTML = html;
5329		link.href = '#';
5330		link.title = title;
5331
5332		/*
5333		 * Will force screen readers like VoiceOver to read this as "Zoom in - button"
5334		 */
5335		link.setAttribute('role', 'button');
5336		link.setAttribute('aria-label', title);
5337
5338		disableClickPropagation(link);
5339		on(link, 'click', stop);
5340		on(link, 'click', fn, this);
5341		on(link, 'click', this._refocusOnMap, this);
5342
5343		return link;
5344	},
5345
5346	_updateDisabled: function () {
5347		var map = this._map,
5348		    className = 'leaflet-disabled';
5349
5350		removeClass(this._zoomInButton, className);
5351		removeClass(this._zoomOutButton, className);
5352
5353		if (this._disabled || map._zoom === map.getMinZoom()) {
5354			addClass(this._zoomOutButton, className);
5355		}
5356		if (this._disabled || map._zoom === map.getMaxZoom()) {
5357			addClass(this._zoomInButton, className);
5358		}
5359	}
5360});
5361
5362// @namespace Map
5363// @section Control options
5364// @option zoomControl: Boolean = true
5365// Whether a [zoom control](#control-zoom) is added to the map by default.
5366Map.mergeOptions({
5367	zoomControl: true
5368});
5369
5370Map.addInitHook(function () {
5371	if (this.options.zoomControl) {
5372		// @section Controls
5373		// @property zoomControl: Control.Zoom
5374		// The default zoom control (only available if the
5375		// [`zoomControl` option](#map-zoomcontrol) was `true` when creating the map).
5376		this.zoomControl = new Zoom();
5377		this.addControl(this.zoomControl);
5378	}
5379});
5380
5381// @namespace Control.Zoom
5382// @factory L.control.zoom(options: Control.Zoom options)
5383// Creates a zoom control
5384var zoom = function (options) {
5385	return new Zoom(options);
5386};
5387
5388/*
5389 * @class Control.Scale
5390 * @aka L.Control.Scale
5391 * @inherits Control
5392 *
5393 * 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`.
5394 *
5395 * @example
5396 *
5397 * ```js
5398 * L.control.scale().addTo(map);
5399 * ```
5400 */
5401
5402var Scale = Control.extend({
5403	// @section
5404	// @aka Control.Scale options
5405	options: {
5406		position: 'bottomleft',
5407
5408		// @option maxWidth: Number = 100
5409		// Maximum width of the control in pixels. The width is set dynamically to show round values (e.g. 100, 200, 500).
5410		maxWidth: 100,
5411
5412		// @option metric: Boolean = True
5413		// Whether to show the metric scale line (m/km).
5414		metric: true,
5415
5416		// @option imperial: Boolean = True
5417		// Whether to show the imperial scale line (mi/ft).
5418		imperial: true
5419
5420		// @option updateWhenIdle: Boolean = false
5421		// If `true`, the control is updated on [`moveend`](#map-moveend), otherwise it's always up-to-date (updated on [`move`](#map-move)).
5422	},
5423
5424	onAdd: function (map) {
5425		var className = 'leaflet-control-scale',
5426		    container = create$1('div', className),
5427		    options = this.options;
5428
5429		this._addScales(options, className + '-line', container);
5430
5431		map.on(options.updateWhenIdle ? 'moveend' : 'move', this._update, this);
5432		map.whenReady(this._update, this);
5433
5434		return container;
5435	},
5436
5437	onRemove: function (map) {
5438		map.off(this.options.updateWhenIdle ? 'moveend' : 'move', this._update, this);
5439	},
5440
5441	_addScales: function (options, className, container) {
5442		if (options.metric) {
5443			this._mScale = create$1('div', className, container);
5444		}
5445		if (options.imperial) {
5446			this._iScale = create$1('div', className, container);
5447		}
5448	},
5449
5450	_update: function () {
5451		var map = this._map,
5452		    y = map.getSize().y / 2;
5453
5454		var maxMeters = map.distance(
5455			map.containerPointToLatLng([0, y]),
5456			map.containerPointToLatLng([this.options.maxWidth, y]));
5457
5458		this._updateScales(maxMeters);
5459	},
5460
5461	_updateScales: function (maxMeters) {
5462		if (this.options.metric && maxMeters) {
5463			this._updateMetric(maxMeters);
5464		}
5465		if (this.options.imperial && maxMeters) {
5466			this._updateImperial(maxMeters);
5467		}
5468	},
5469
5470	_updateMetric: function (maxMeters) {
5471		var meters = this._getRoundNum(maxMeters),
5472		    label = meters < 1000 ? meters + ' m' : (meters / 1000) + ' km';
5473
5474		this._updateScale(this._mScale, label, meters / maxMeters);
5475	},
5476
5477	_updateImperial: function (maxMeters) {
5478		var maxFeet = maxMeters * 3.2808399,
5479		    maxMiles, miles, feet;
5480
5481		if (maxFeet > 5280) {
5482			maxMiles = maxFeet / 5280;
5483			miles = this._getRoundNum(maxMiles);
5484			this._updateScale(this._iScale, miles + ' mi', miles / maxMiles);
5485
5486		} else {
5487			feet = this._getRoundNum(maxFeet);
5488			this._updateScale(this._iScale, feet + ' ft', feet / maxFeet);
5489		}
5490	},
5491
5492	_updateScale: function (scale, text, ratio) {
5493		scale.style.width = Math.round(this.options.maxWidth * ratio) + 'px';
5494		scale.innerHTML = text;
5495	},
5496
5497	_getRoundNum: function (num) {
5498		var pow10 = Math.pow(10, (Math.floor(num) + '').length - 1),
5499		    d = num / pow10;
5500
5501		d = d >= 10 ? 10 :
5502		    d >= 5 ? 5 :
5503		    d >= 3 ? 3 :
5504		    d >= 2 ? 2 : 1;
5505
5506		return pow10 * d;
5507	}
5508});
5509
5510
5511// @factory L.control.scale(options?: Control.Scale options)
5512// Creates an scale control with the given options.
5513var scale = function (options) {
5514	return new Scale(options);
5515};
5516
5517/*
5518 * @class Control.Attribution
5519 * @aka L.Control.Attribution
5520 * @inherits Control
5521 *
5522 * 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.
5523 */
5524
5525var Attribution = Control.extend({
5526	// @section
5527	// @aka Control.Attribution options
5528	options: {
5529		position: 'bottomright',
5530
5531		// @option prefix: String = 'Leaflet'
5532		// The HTML text shown before the attributions. Pass `false` to disable.
5533		prefix: '<a href="http://leafletjs.com" title="A JS library for interactive maps">Leaflet</a>'
5534	},
5535
5536	initialize: function (options) {
5537		setOptions(this, options);
5538
5539		this._attributions = {};
5540	},
5541
5542	onAdd: function (map) {
5543		map.attributionControl = this;
5544		this._container = create$1('div', 'leaflet-control-attribution');
5545		disableClickPropagation(this._container);
5546
5547		// TODO ugly, refactor
5548		for (var i in map._layers) {
5549			if (map._layers[i].getAttribution) {
5550				this.addAttribution(map._layers[i].getAttribution());
5551			}
5552		}
5553
5554		this._update();
5555
5556		return this._container;
5557	},
5558
5559	// @method setPrefix(prefix: String): this
5560	// Sets the text before the attributions.
5561	setPrefix: function (prefix) {
5562		this.options.prefix = prefix;
5563		this._update();
5564		return this;
5565	},
5566
5567	// @method addAttribution(text: String): this
5568	// Adds an attribution text (e.g. `'Vector data &copy; Mapbox'`).
5569	addAttribution: function (text) {
5570		if (!text) { return this; }
5571
5572		if (!this._attributions[text]) {
5573			this._attributions[text] = 0;
5574		}
5575		this._attributions[text]++;
5576
5577		this._update();
5578
5579		return this;
5580	},
5581
5582	// @method removeAttribution(text: String): this
5583	// Removes an attribution text.
5584	removeAttribution: function (text) {
5585		if (!text) { return this; }
5586
5587		if (this._attributions[text]) {
5588			this._attributions[text]--;
5589			this._update();
5590		}
5591
5592		return this;
5593	},
5594
5595	_update: function () {
5596		if (!this._map) { return; }
5597
5598		var attribs = [];
5599
5600		for (var i in this._attributions) {
5601			if (this._attributions[i]) {
5602				attribs.push(i);
5603			}
5604		}
5605
5606		var prefixAndAttribs = [];
5607
5608		if (this.options.prefix) {
5609			prefixAndAttribs.push(this.options.prefix);
5610		}
5611		if (attribs.length) {
5612			prefixAndAttribs.push(attribs.join(', '));
5613		}
5614
5615		this._container.innerHTML = prefixAndAttribs.join(' | ');
5616	}
5617});
5618
5619// @namespace Map
5620// @section Control options
5621// @option attributionControl: Boolean = true
5622// Whether a [attribution control](#control-attribution) is added to the map by default.
5623Map.mergeOptions({
5624	attributionControl: true
5625});
5626
5627Map.addInitHook(function () {
5628	if (this.options.attributionControl) {
5629		new Attribution().addTo(this);
5630	}
5631});
5632
5633// @namespace Control.Attribution
5634// @factory L.control.attribution(options: Control.Attribution options)
5635// Creates an attribution control.
5636var attribution = function (options) {
5637	return new Attribution(options);
5638};
5639
5640Control.Layers = Layers;
5641Control.Zoom = Zoom;
5642Control.Scale = Scale;
5643Control.Attribution = Attribution;
5644
5645control.layers = layers;
5646control.zoom = zoom;
5647control.scale = scale;
5648control.attribution = attribution;
5649
5650/*
5651	L.Handler is a base class for handler classes that are used internally to inject
5652	interaction features like dragging to classes like Map and Marker.
5653*/
5654
5655// @class Handler
5656// @aka L.Handler
5657// Abstract class for map interaction handlers
5658
5659var Handler = Class.extend({
5660	initialize: function (map) {
5661		this._map = map;
5662	},
5663
5664	// @method enable(): this
5665	// Enables the handler
5666	enable: function () {
5667		if (this._enabled) { return this; }
5668
5669		this._enabled = true;
5670		this.addHooks();
5671		return this;
5672	},
5673
5674	// @method disable(): this
5675	// Disables the handler
5676	disable: function () {
5677		if (!this._enabled) { return this; }
5678
5679		this._enabled = false;
5680		this.removeHooks();
5681		return this;
5682	},
5683
5684	// @method enabled(): Boolean
5685	// Returns `true` if the handler is enabled
5686	enabled: function () {
5687		return !!this._enabled;
5688	}
5689
5690	// @section Extension methods
5691	// Classes inheriting from `Handler` must implement the two following methods:
5692	// @method addHooks()
5693	// Called when the handler is enabled, should add event hooks.
5694	// @method removeHooks()
5695	// Called when the handler is disabled, should remove the event hooks added previously.
5696});
5697
5698// @section There is static function which can be called without instantiating L.Handler:
5699// @function addTo(map: Map, name: String): this
5700// Adds a new Handler to the given map with the given name.
5701Handler.addTo = function (map, name) {
5702	map.addHandler(name, this);
5703	return this;
5704};
5705
5706var Mixin = {Events: Events};
5707
5708/*
5709 * @class Draggable
5710 * @aka L.Draggable
5711 * @inherits Evented
5712 *
5713 * A class for making DOM elements draggable (including touch support).
5714 * Used internally for map and marker dragging. Only works for elements
5715 * that were positioned with [`L.DomUtil.setPosition`](#domutil-setposition).
5716 *
5717 * @example
5718 * ```js
5719 * var draggable = new L.Draggable(elementToDrag);
5720 * draggable.enable();
5721 * ```
5722 */
5723
5724var START = touch ? 'touchstart mousedown' : 'mousedown';
5725var END = {
5726	mousedown: 'mouseup',
5727	touchstart: 'touchend',
5728	pointerdown: 'touchend',
5729	MSPointerDown: 'touchend'
5730};
5731var MOVE = {
5732	mousedown: 'mousemove',
5733	touchstart: 'touchmove',
5734	pointerdown: 'touchmove',
5735	MSPointerDown: 'touchmove'
5736};
5737
5738
5739var Draggable = Evented.extend({
5740
5741	options: {
5742		// @section
5743		// @aka Draggable options
5744		// @option clickTolerance: Number = 3
5745		// The max number of pixels a user can shift the mouse pointer during a click
5746		// for it to be considered a valid click (as opposed to a mouse drag).
5747		clickTolerance: 3
5748	},
5749
5750	// @constructor L.Draggable(el: HTMLElement, dragHandle?: HTMLElement, preventOutline?: Boolean, options?: Draggable options)
5751	// Creates a `Draggable` object for moving `el` when you start dragging the `dragHandle` element (equals `el` itself by default).
5752	initialize: function (element, dragStartTarget, preventOutline$$1, options) {
5753		setOptions(this, options);
5754
5755		this._element = element;
5756		this._dragStartTarget = dragStartTarget || element;
5757		this._preventOutline = preventOutline$$1;
5758	},
5759
5760	// @method enable()
5761	// Enables the dragging ability
5762	enable: function () {
5763		if (this._enabled) { return; }
5764
5765		on(this._dragStartTarget, START, this._onDown, this);
5766
5767		this._enabled = true;
5768	},
5769
5770	// @method disable()
5771	// Disables the dragging ability
5772	disable: function () {
5773		if (!this._enabled) { return; }
5774
5775		// If we're currently dragging this draggable,
5776		// disabling it counts as first ending the drag.
5777		if (Draggable._dragging === this) {
5778			this.finishDrag();
5779		}
5780
5781		off(this._dragStartTarget, START, this._onDown, this);
5782
5783		this._enabled = false;
5784		this._moved = false;
5785	},
5786
5787	_onDown: function (e) {
5788		// Ignore simulated events, since we handle both touch and
5789		// mouse explicitly; otherwise we risk getting duplicates of
5790		// touch events, see #4315.
5791		// Also ignore the event if disabled; this happens in IE11
5792		// under some circumstances, see #3666.
5793		if (e._simulated || !this._enabled) { return; }
5794
5795		this._moved = false;
5796
5797		if (hasClass(this._element, 'leaflet-zoom-anim')) { return; }
5798
5799		if (Draggable._dragging || e.shiftKey || ((e.which !== 1) && (e.button !== 1) && !e.touches)) { return; }
5800		Draggable._dragging = this;  // Prevent dragging multiple objects at once.
5801
5802		if (this._preventOutline) {
5803			preventOutline(this._element);
5804		}
5805
5806		disableImageDrag();
5807		disableTextSelection();
5808
5809		if (this._moving) { return; }
5810
5811		// @event down: Event
5812		// Fired when a drag is about to start.
5813		this.fire('down');
5814
5815		var first = e.touches ? e.touches[0] : e,
5816		    sizedParent = getSizedParentNode(this._element);
5817
5818		this._startPoint = new Point(first.clientX, first.clientY);
5819
5820		// Cache the scale, so that we can continuously compensate for it during drag (_onMove).
5821		this._parentScale = getScale(sizedParent);
5822
5823		on(document, MOVE[e.type], this._onMove, this);
5824		on(document, END[e.type], this._onUp, this);
5825	},
5826
5827	_onMove: function (e) {
5828		// Ignore simulated events, since we handle both touch and
5829		// mouse explicitly; otherwise we risk getting duplicates of
5830		// touch events, see #4315.
5831		// Also ignore the event if disabled; this happens in IE11
5832		// under some circumstances, see #3666.
5833		if (e._simulated || !this._enabled) { return; }
5834
5835		if (e.touches && e.touches.length > 1) {
5836			this._moved = true;
5837			return;
5838		}
5839
5840		var first = (e.touches && e.touches.length === 1 ? e.touches[0] : e),
5841		    offset = new Point(first.clientX, first.clientY)._subtract(this._startPoint);
5842
5843		if (!offset.x && !offset.y) { return; }
5844		if (Math.abs(offset.x) + Math.abs(offset.y) < this.options.clickTolerance) { return; }
5845
5846		// We assume that the parent container's position, border and scale do not change for the duration of the drag.
5847		// Therefore there is no need to account for the position and border (they are eliminated by the subtraction)
5848		// and we can use the cached value for the scale.
5849		offset.x /= this._parentScale.x;
5850		offset.y /= this._parentScale.y;
5851
5852		preventDefault(e);
5853
5854		if (!this._moved) {
5855			// @event dragstart: Event
5856			// Fired when a drag starts
5857			this.fire('dragstart');
5858
5859			this._moved = true;
5860			this._startPos = getPosition(this._element).subtract(offset);
5861
5862			addClass(document.body, 'leaflet-dragging');
5863
5864			this._lastTarget = e.target || e.srcElement;
5865			// IE and Edge do not give the <use> element, so fetch it
5866			// if necessary
5867			if ((window.SVGElementInstance) && (this._lastTarget instanceof SVGElementInstance)) {
5868				this._lastTarget = this._lastTarget.correspondingUseElement;
5869			}
5870			addClass(this._lastTarget, 'leaflet-drag-target');
5871		}
5872
5873		this._newPos = this._startPos.add(offset);
5874		this._moving = true;
5875
5876		cancelAnimFrame(this._animRequest);
5877		this._lastEvent = e;
5878		this._animRequest = requestAnimFrame(this._updatePosition, this, true);
5879	},
5880
5881	_updatePosition: function () {
5882		var e = {originalEvent: this._lastEvent};
5883
5884		// @event predrag: Event
5885		// Fired continuously during dragging *before* each corresponding
5886		// update of the element's position.
5887		this.fire('predrag', e);
5888		setPosition(this._element, this._newPos);
5889
5890		// @event drag: Event
5891		// Fired continuously during dragging.
5892		this.fire('drag', e);
5893	},
5894
5895	_onUp: function (e) {
5896		// Ignore simulated events, since we handle both touch and
5897		// mouse explicitly; otherwise we risk getting duplicates of
5898		// touch events, see #4315.
5899		// Also ignore the event if disabled; this happens in IE11
5900		// under some circumstances, see #3666.
5901		if (e._simulated || !this._enabled) { return; }
5902		this.finishDrag();
5903	},
5904
5905	finishDrag: function () {
5906		removeClass(document.body, 'leaflet-dragging');
5907
5908		if (this._lastTarget) {
5909			removeClass(this._lastTarget, 'leaflet-drag-target');
5910			this._lastTarget = null;
5911		}
5912
5913		for (var i in MOVE) {
5914			off(document, MOVE[i], this._onMove, this);
5915			off(document, END[i], this._onUp, this);
5916		}
5917
5918		enableImageDrag();
5919		enableTextSelection();
5920
5921		if (this._moved && this._moving) {
5922			// ensure drag is not fired after dragend
5923			cancelAnimFrame(this._animRequest);
5924
5925			// @event dragend: DragEndEvent
5926			// Fired when the drag ends.
5927			this.fire('dragend', {
5928				distance: this._newPos.distanceTo(this._startPos)
5929			});
5930		}
5931
5932		this._moving = false;
5933		Draggable._dragging = false;
5934	}
5935
5936});
5937
5938/*
5939 * @namespace LineUtil
5940 *
5941 * Various utility functions for polyline points processing, used by Leaflet internally to make polylines lightning-fast.
5942 */
5943
5944// Simplify polyline with vertex reduction and Douglas-Peucker simplification.
5945// Improves rendering performance dramatically by lessening the number of points to draw.
5946
5947// @function simplify(points: Point[], tolerance: Number): Point[]
5948// Dramatically reduces the number of points in a polyline while retaining
5949// its shape and returns a new array of simplified points, using the
5950// [Douglas-Peucker algorithm](http://en.wikipedia.org/wiki/Douglas-Peucker_algorithm).
5951// Used for a huge performance boost when processing/displaying Leaflet polylines for
5952// each zoom level and also reducing visual noise. tolerance affects the amount of
5953// simplification (lesser value means higher quality but slower and with more points).
5954// Also released as a separated micro-library [Simplify.js](http://mourner.github.com/simplify-js/).
5955function simplify(points, tolerance) {
5956	if (!tolerance || !points.length) {
5957		return points.slice();
5958	}
5959
5960	var sqTolerance = tolerance * tolerance;
5961
5962	    // stage 1: vertex reduction
5963	    points = _reducePoints(points, sqTolerance);
5964
5965	    // stage 2: Douglas-Peucker simplification
5966	    points = _simplifyDP(points, sqTolerance);
5967
5968	return points;
5969}
5970
5971// @function pointToSegmentDistance(p: Point, p1: Point, p2: Point): Number
5972// Returns the distance between point `p` and segment `p1` to `p2`.
5973function pointToSegmentDistance(p, p1, p2) {
5974	return Math.sqrt(_sqClosestPointOnSegment(p, p1, p2, true));
5975}
5976
5977// @function closestPointOnSegment(p: Point, p1: Point, p2: Point): Number
5978// Returns the closest point from a point `p` on a segment `p1` to `p2`.
5979function closestPointOnSegment(p, p1, p2) {
5980	return _sqClosestPointOnSegment(p, p1, p2);
5981}
5982
5983// Douglas-Peucker simplification, see http://en.wikipedia.org/wiki/Douglas-Peucker_algorithm
5984function _simplifyDP(points, sqTolerance) {
5985
5986	var len = points.length,
5987	    ArrayConstructor = typeof Uint8Array !== undefined + '' ? Uint8Array : Array,
5988	    markers = new ArrayConstructor(len);
5989
5990	    markers[0] = markers[len - 1] = 1;
5991
5992	_simplifyDPStep(points, markers, sqTolerance, 0, len - 1);
5993
5994	var i,
5995	    newPoints = [];
5996
5997	for (i = 0; i < len; i++) {
5998		if (markers[i]) {
5999			newPoints.push(points[i]);
6000		}
6001	}
6002
6003	return newPoints;
6004}
6005
6006function _simplifyDPStep(points, markers, sqTolerance, first, last) {
6007
6008	var maxSqDist = 0,
6009	index, i, sqDist;
6010
6011	for (i = first + 1; i <= last - 1; i++) {
6012		sqDist = _sqClosestPointOnSegment(points[i], points[first], points[last], true);
6013
6014		if (sqDist > maxSqDist) {
6015			index = i;
6016			maxSqDist = sqDist;
6017		}
6018	}
6019
6020	if (maxSqDist > sqTolerance) {
6021		markers[index] = 1;
6022
6023		_simplifyDPStep(points, markers, sqTolerance, first, index);
6024		_simplifyDPStep(points, markers, sqTolerance, index, last);
6025	}
6026}
6027
6028// reduce points that are too close to each other to a single point
6029function _reducePoints(points, sqTolerance) {
6030	var reducedPoints = [points[0]];
6031
6032	for (var i = 1, prev = 0, len = points.length; i < len; i++) {
6033		if (_sqDist(points[i], points[prev]) > sqTolerance) {
6034			reducedPoints.push(points[i]);
6035			prev = i;
6036		}
6037	}
6038	if (prev < len - 1) {
6039		reducedPoints.push(points[len - 1]);
6040	}
6041	return reducedPoints;
6042}
6043
6044var _lastCode;
6045
6046// @function clipSegment(a: Point, b: Point, bounds: Bounds, useLastCode?: Boolean, roun
vendor: 66,777 bytes, lines 6046-8557
6046d?: Boolean): Point[]|Boolean
6047// Clips the segment a to b by rectangular bounds with the
6048// [Cohen-Sutherland algorithm](https://en.wikipedia.org/wiki/Cohen%E2%80%93Sutherland_algorithm)
6049// (modifying the segment points directly!). Used by Leaflet to only show polyline
6050// points that are on the screen or near, increasing performance.
6051function clipSegment(a, b, bounds, useLastCode, round) {
6052	var codeA = useLastCode ? _lastCode : _getBitCode(a, bounds),
6053	    codeB = _getBitCode(b, bounds),
6054
6055	    codeOut, p, newCode;
6056
6057	    // save 2nd code to avoid calculating it on the next segment
6058	    _lastCode = codeB;
6059
6060	while (true) {
6061		// if a,b is inside the clip window (trivial accept)
6062		if (!(codeA | codeB)) {
6063			return [a, b];
6064		}
6065
6066		// if a,b is outside the clip window (trivial reject)
6067		if (codeA & codeB) {
6068			return false;
6069		}
6070
6071		// other cases
6072		codeOut = codeA || codeB;
6073		p = _getEdgeIntersection(a, b, codeOut, bounds, round);
6074		newCode = _getBitCode(p, bounds);
6075
6076		if (codeOut === codeA) {
6077			a = p;
6078			codeA = newCode;
6079		} else {
6080			b = p;
6081			codeB = newCode;
6082		}
6083	}
6084}
6085
6086function _getEdgeIntersection(a, b, code, bounds, round) {
6087	var dx = b.x - a.x,
6088	    dy = b.y - a.y,
6089	    min = bounds.min,
6090	    max = bounds.max,
6091	    x, y;
6092
6093	if (code & 8) { // top
6094		x = a.x + dx * (max.y - a.y) / dy;
6095		y = max.y;
6096
6097	} else if (code & 4) { // bottom
6098		x = a.x + dx * (min.y - a.y) / dy;
6099		y = min.y;
6100
6101	} else if (code & 2) { // right
6102		x = max.x;
6103		y = a.y + dy * (max.x - a.x) / dx;
6104
6105	} else if (code & 1) { // left
6106		x = min.x;
6107		y = a.y + dy * (min.x - a.x) / dx;
6108	}
6109
6110	return new Point(x, y, round);
6111}
6112
6113function _getBitCode(p, bounds) {
6114	var code = 0;
6115
6116	if (p.x < bounds.min.x) { // left
6117		code |= 1;
6118	} else if (p.x > bounds.max.x) { // right
6119		code |= 2;
6120	}
6121
6122	if (p.y < bounds.min.y) { // bottom
6123		code |= 4;
6124	} else if (p.y > bounds.max.y) { // top
6125		code |= 8;
6126	}
6127
6128	return code;
6129}
6130
6131// square distance (to avoid unnecessary Math.sqrt calls)
6132function _sqDist(p1, p2) {
6133	var dx = p2.x - p1.x,
6134	    dy = p2.y - p1.y;
6135	return dx * dx + dy * dy;
6136}
6137
6138// return closest point on segment or distance to that point
6139function _sqClosestPointOnSegment(p, p1, p2, sqDist) {
6140	var x = p1.x,
6141	    y = p1.y,
6142	    dx = p2.x - x,
6143	    dy = p2.y - y,
6144	    dot = dx * dx + dy * dy,
6145	    t;
6146
6147	if (dot > 0) {
6148		t = ((p.x - x) * dx + (p.y - y) * dy) / dot;
6149
6150		if (t > 1) {
6151			x = p2.x;
6152			y = p2.y;
6153		} else if (t > 0) {
6154			x += dx * t;
6155			y += dy * t;
6156		}
6157	}
6158
6159	dx = p.x - x;
6160	dy = p.y - y;
6161
6162	return sqDist ? dx * dx + dy * dy : new Point(x, y);
6163}
6164
6165
6166// @function isFlat(latlngs: LatLng[]): Boolean
6167// Returns true if `latlngs` is a flat array, false is nested.
6168function isFlat(latlngs) {
6169	return !isArray(latlngs[0]) || (typeof latlngs[0][0] !== 'object' && typeof latlngs[0][0] !== 'undefined');
6170}
6171
6172function _flat(latlngs) {
6173	console.warn('Deprecated use of _flat, please use L.LineUtil.isFlat instead.');
6174	return isFlat(latlngs);
6175}
6176
6177
6178var LineUtil = (Object.freeze || Object)({
6179	simplify: simplify,
6180	pointToSegmentDistance: pointToSegmentDistance,
6181	closestPointOnSegment: closestPointOnSegment,
6182	clipSegment: clipSegment,
6183	_getEdgeIntersection: _getEdgeIntersection,
6184	_getBitCode: _getBitCode,
6185	_sqClosestPointOnSegment: _sqClosestPointOnSegment,
6186	isFlat: isFlat,
6187	_flat: _flat
6188});
6189
6190/*
6191 * @namespace PolyUtil
6192 * Various utility functions for polygon geometries.
6193 */
6194
6195/* @function clipPolygon(points: Point[], bounds: Bounds, round?: Boolean): Point[]
6196 * 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)).
6197 * Used by Leaflet to only show polygon points that are on the screen or near, increasing
6198 * performance. Note that polygon points needs different algorithm for clipping
6199 * than polyline, so there's a separate method for it.
6200 */
6201function clipPolygon(points, bounds, round) {
6202	var clippedPoints,
6203	    edges = [1, 4, 2, 8],
6204	    i, j, k,
6205	    a, b,
6206	    len, edge, p;
6207
6208	for (i = 0, len = points.length; i < len; i++) {
6209		points[i]._code = _getBitCode(points[i], bounds);
6210	}
6211
6212	// for each edge (left, bottom, right, top)
6213	for (k = 0; k < 4; k++) {
6214		edge = edges[k];
6215		clippedPoints = [];
6216
6217		for (i = 0, len = points.length, j = len - 1; i < len; j = i++) {
6218			a = points[i];
6219			b = points[j];
6220
6221			// if a is inside the clip window
6222			if (!(a._code & edge)) {
6223				// if b is outside the clip window (a->b goes out of screen)
6224				if (b._code & edge) {
6225					p = _getEdgeIntersection(b, a, edge, bounds, round);
6226					p._code = _getBitCode(p, bounds);
6227					clippedPoints.push(p);
6228				}
6229				clippedPoints.push(a);
6230
6231			// else if b is inside the clip window (a->b enters the screen)
6232			} else if (!(b._code & edge)) {
6233				p = _getEdgeIntersection(b, a, edge, bounds, round);
6234				p._code = _getBitCode(p, bounds);
6235				clippedPoints.push(p);
6236			}
6237		}
6238		points = clippedPoints;
6239	}
6240
6241	return points;
6242}
6243
6244
6245var PolyUtil = (Object.freeze || Object)({
6246	clipPolygon: clipPolygon
6247});
6248
6249/*
6250 * @namespace Projection
6251 * @section
6252 * Leaflet comes with a set of already defined Projections out of the box:
6253 *
6254 * @projection L.Projection.LonLat
6255 *
6256 * Equirectangular, or Plate Carree projection — the most simple projection,
6257 * mostly used by GIS enthusiasts. Directly maps `x` as longitude, and `y` as
6258 * latitude. Also suitable for flat worlds, e.g. game maps. Used by the
6259 * `EPSG:4326` and `Simple` CRS.
6260 */
6261
6262var LonLat = {
6263	project: function (latlng) {
6264		return new Point(latlng.lng, latlng.lat);
6265	},
6266
6267	unproject: function (point) {
6268		return new LatLng(point.y, point.x);
6269	},
6270
6271	bounds: new Bounds([-180, -90], [180, 90])
6272};
6273
6274/*
6275 * @namespace Projection
6276 * @projection L.Projection.Mercator
6277 *
6278 * Elliptical Mercator projection — more complex than Spherical Mercator. Takes into account that Earth is a geoid, not a perfect sphere. Used by the EPSG:3395 CRS.
6279 */
6280
6281var Mercator = {
6282	R: 6378137,
6283	R_MINOR: 6356752.314245179,
6284
6285	bounds: new Bounds([-20037508.34279, -15496570.73972], [20037508.34279, 18764656.23138]),
6286
6287	project: function (latlng) {
6288		var d = Math.PI / 180,
6289		    r = this.R,
6290		    y = latlng.lat * d,
6291		    tmp = this.R_MINOR / r,
6292		    e = Math.sqrt(1 - tmp * tmp),
6293		    con = e * Math.sin(y);
6294
6295		var ts = Math.tan(Math.PI / 4 - y / 2) / Math.pow((1 - con) / (1 + con), e / 2);
6296		y = -r * Math.log(Math.max(ts, 1E-10));
6297
6298		return new Point(latlng.lng * d * r, y);
6299	},
6300
6301	unproject: function (point) {
6302		var d = 180 / Math.PI,
6303		    r = this.R,
6304		    tmp = this.R_MINOR / r,
6305		    e = Math.sqrt(1 - tmp * tmp),
6306		    ts = Math.exp(-point.y / r),
6307		    phi = Math.PI / 2 - 2 * Math.atan(ts);
6308
6309		for (var i = 0, dphi = 0.1, con; i < 15 && Math.abs(dphi) > 1e-7; i++) {
6310			con = e * Math.sin(phi);
6311			con = Math.pow((1 - con) / (1 + con), e / 2);
6312			dphi = Math.PI / 2 - 2 * Math.atan(ts * con) - phi;
6313			phi += dphi;
6314		}
6315
6316		return new LatLng(phi * d, point.x * d / r);
6317	}
6318};
6319
6320/*
6321 * @class Projection
6322
6323 * An object with methods for projecting geographical coordinates of the world onto
6324 * a flat surface (and back). See [Map projection](http://en.wikipedia.org/wiki/Map_projection).
6325
6326 * @property bounds: Bounds
6327 * The bounds (specified in CRS units) where the projection is valid
6328
6329 * @method project(latlng: LatLng): Point
6330 * Projects geographical coordinates into a 2D point.
6331 * Only accepts actual `L.LatLng` instances, not arrays.
6332
6333 * @method unproject(point: Point): LatLng
6334 * The inverse of `project`. Projects a 2D point into a geographical location.
6335 * Only accepts actual `L.Point` instances, not arrays.
6336
6337 * Note that the projection instances do not inherit from Leafet's `Class` object,
6338 * and can't be instantiated. Also, new classes can't inherit from them,
6339 * and methods can't be added to them with the `include` function.
6340
6341 */
6342
6343
6344
6345
6346var index = (Object.freeze || Object)({
6347	LonLat: LonLat,
6348	Mercator: Mercator,
6349	SphericalMercator: SphericalMercator
6350});
6351
6352/*
6353 * @namespace CRS
6354 * @crs L.CRS.EPSG3395
6355 *
6356 * Rarely used by some commercial tile providers. Uses Elliptical Mercator projection.
6357 */
6358var EPSG3395 = extend({}, Earth, {
6359	code: 'EPSG:3395',
6360	projection: Mercator,
6361
6362	transformation: (function () {
6363		var scale = 0.5 / (Math.PI * Mercator.R);
6364		return toTransformation(scale, 0.5, -scale, 0.5);
6365	}())
6366});
6367
6368/*
6369 * @namespace CRS
6370 * @crs L.CRS.EPSG4326
6371 *
6372 * A common CRS among GIS enthusiasts. Uses simple Equirectangular projection.
6373 *
6374 * Leaflet 1.0.x complies with the [TMS coordinate scheme for EPSG:4326](https://wiki.osgeo.org/wiki/Tile_Map_Service_Specification#global-geodetic),
6375 * which is a breaking change from 0.7.x behaviour.  If you are using a `TileLayer`
6376 * with this CRS, ensure that there are two 256x256 pixel tiles covering the
6377 * whole earth at zoom level zero, and that the tile coordinate origin is (-180,+90),
6378 * or (-180,-90) for `TileLayer`s with [the `tms` option](#tilelayer-tms) set.
6379 */
6380
6381var EPSG4326 = extend({}, Earth, {
6382	code: 'EPSG:4326',
6383	projection: LonLat,
6384	transformation: toTransformation(1 / 180, 1, -1 / 180, 0.5)
6385});
6386
6387/*
6388 * @namespace CRS
6389 * @crs L.CRS.Simple
6390 *
6391 * A simple CRS that maps longitude and latitude into `x` and `y` directly.
6392 * May be used for maps of flat surfaces (e.g. game maps). Note that the `y`
6393 * axis should still be inverted (going from bottom to top). `distance()` returns
6394 * simple euclidean distance.
6395 */
6396
6397var Simple = extend({}, CRS, {
6398	projection: LonLat,
6399	transformation: toTransformation(1, 0, -1, 0),
6400
6401	scale: function (zoom) {
6402		return Math.pow(2, zoom);
6403	},
6404
6405	zoom: function (scale) {
6406		return Math.log(scale) / Math.LN2;
6407	},
6408
6409	distance: function (latlng1, latlng2) {
6410		var dx = latlng2.lng - latlng1.lng,
6411		    dy = latlng2.lat - latlng1.lat;
6412
6413		return Math.sqrt(dx * dx + dy * dy);
6414	},
6415
6416	infinite: true
6417});
6418
6419CRS.Earth = Earth;
6420CRS.EPSG3395 = EPSG3395;
6421CRS.EPSG3857 = EPSG3857;
6422CRS.EPSG900913 = EPSG900913;
6423CRS.EPSG4326 = EPSG4326;
6424CRS.Simple = Simple;
6425
6426/*
6427 * @class Layer
6428 * @inherits Evented
6429 * @aka L.Layer
6430 * @aka ILayer
6431 *
6432 * A set of methods from the Layer base class that all Leaflet layers use.
6433 * Inherits all methods, options and events from `L.Evented`.
6434 *
6435 * @example
6436 *
6437 * ```js
6438 * var layer = L.Marker(latlng).addTo(map);
6439 * layer.addTo(map);
6440 * layer.remove();
6441 * ```
6442 *
6443 * @event add: Event
6444 * Fired after the layer is added to a map
6445 *
6446 * @event remove: Event
6447 * Fired after the layer is removed from a map
6448 */
6449
6450
6451var Layer = Evented.extend({
6452
6453	// Classes extending `L.Layer` will inherit the following options:
6454	options: {
6455		// @option pane: String = 'overlayPane'
6456		// 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.
6457		pane: 'overlayPane',
6458
6459		// @option attribution: String = null
6460		// String to be shown in the attribution control, describes the layer data, e.g. "© Mapbox".
6461		attribution: null,
6462
6463		bubblingMouseEvents: true
6464	},
6465
6466	/* @section
6467	 * Classes extending `L.Layer` will inherit the following methods:
6468	 *
6469	 * @method addTo(map: Map|LayerGroup): this
6470	 * Adds the layer to the given map or layer group.
6471	 */
6472	addTo: function (map) {
6473		map.addLayer(this);
6474		return this;
6475	},
6476
6477	// @method remove: this
6478	// Removes the layer from the map it is currently active on.
6479	remove: function () {
6480		return this.removeFrom(this._map || this._mapToAdd);
6481	},
6482
6483	// @method removeFrom(map: Map): this
6484	// Removes the layer from the given map
6485	removeFrom: function (obj) {
6486		if (obj) {
6487			obj.removeLayer(this);
6488		}
6489		return this;
6490	},
6491
6492	// @method getPane(name? : String): HTMLElement
6493	// Returns the `HTMLElement` representing the named pane on the map. If `name` is omitted, returns the pane for this layer.
6494	getPane: function (name) {
6495		return this._map.getPane(name ? (this.options[name] || name) : this.options.pane);
6496	},
6497
6498	addInteractiveTarget: function (targetEl) {
6499		this._map._targets[stamp(targetEl)] = this;
6500		return this;
6501	},
6502
6503	removeInteractiveTarget: function (targetEl) {
6504		delete this._map._targets[stamp(targetEl)];
6505		return this;
6506	},
6507
6508	// @method getAttribution: String
6509	// Used by the `attribution control`, returns the [attribution option](#gridlayer-attribution).
6510	getAttribution: function () {
6511		return this.options.attribution;
6512	},
6513
6514	_layerAdd: function (e) {
6515		var map = e.target;
6516
6517		// check in case layer gets added and then removed before the map is ready
6518		if (!map.hasLayer(this)) { return; }
6519
6520		this._map = map;
6521		this._zoomAnimated = map._zoomAnimated;
6522
6523		if (this.getEvents) {
6524			var events = this.getEvents();
6525			map.on(events, this);
6526			this.once('remove', function () {
6527				map.off(events, this);
6528			}, this);
6529		}
6530
6531		this.onAdd(map);
6532
6533		if (this.getAttribution && map.attributionControl) {
6534			map.attributionControl.addAttribution(this.getAttribution());
6535		}
6536
6537		this.fire('add');
6538		map.fire('layeradd', {layer: this});
6539	}
6540});
6541
6542/* @section Extension methods
6543 * @uninheritable
6544 *
6545 * Every layer should extend from `L.Layer` and (re-)implement the following methods.
6546 *
6547 * @method onAdd(map: Map): this
6548 * 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).
6549 *
6550 * @method onRemove(map: Map): this
6551 * 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).
6552 *
6553 * @method getEvents(): Object
6554 * 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.
6555 *
6556 * @method getAttribution(): String
6557 * This optional method should return a string containing HTML to be shown on the `Attribution control` whenever the layer is visible.
6558 *
6559 * @method beforeAdd(map: Map): this
6560 * 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.
6561 */
6562
6563
6564/* @namespace Map
6565 * @section Layer events
6566 *
6567 * @event layeradd: LayerEvent
6568 * Fired when a new layer is added to the map.
6569 *
6570 * @event layerremove: LayerEvent
6571 * Fired when some layer is removed from the map
6572 *
6573 * @section Methods for Layers and Controls
6574 */
6575Map.include({
6576	// @method addLayer(layer: Layer): this
6577	// Adds the given layer to the map
6578	addLayer: function (layer) {
6579		if (!layer._layerAdd) {
6580			throw new Error('The provided object is not a Layer.');
6581		}
6582
6583		var id = stamp(layer);
6584		if (this._layers[id]) { return this; }
6585		this._layers[id] = layer;
6586
6587		layer._mapToAdd = this;
6588
6589		if (layer.beforeAdd) {
6590			layer.beforeAdd(this);
6591		}
6592
6593		this.whenReady(layer._layerAdd, layer);
6594
6595		return this;
6596	},
6597
6598	// @method removeLayer(layer: Layer): this
6599	// Removes the given layer from the map.
6600	removeLayer: function (layer) {
6601		var id = stamp(layer);
6602
6603		if (!this._layers[id]) { return this; }
6604
6605		if (this._loaded) {
6606			layer.onRemove(this);
6607		}
6608
6609		if (layer.getAttribution && this.attributionControl) {
6610			this.attributionControl.removeAttribution(layer.getAttribution());
6611		}
6612
6613		delete this._layers[id];
6614
6615		if (this._loaded) {
6616			this.fire('layerremove', {layer: layer});
6617			layer.fire('remove');
6618		}
6619
6620		layer._map = layer._mapToAdd = null;
6621
6622		return this;
6623	},
6624
6625	// @method hasLayer(layer: Layer): Boolean
6626	// Returns `true` if the given layer is currently added to the map
6627	hasLayer: function (layer) {
6628		return !!layer && (stamp(layer) in this._layers);
6629	},
6630
6631	/* @method eachLayer(fn: Function, context?: Object): this
6632	 * Iterates over the layers of the map, optionally specifying context of the iterator function.
6633	 * ```
6634	 * map.eachLayer(function(layer){
6635	 *     layer.bindPopup('Hello');
6636	 * });
6637	 * ```
6638	 */
6639	eachLayer: function (method, context) {
6640		for (var i in this._layers) {
6641			method.call(context, this._layers[i]);
6642		}
6643		return this;
6644	},
6645
6646	_addLayers: function (layers) {
6647		layers = layers ? (isArray(layers) ? layers : [layers]) : [];
6648
6649		for (var i = 0, len = layers.length; i < len; i++) {
6650			this.addLayer(layers[i]);
6651		}
6652	},
6653
6654	_addZoomLimit: function (layer) {
6655		if (isNaN(layer.options.maxZoom) || !isNaN(layer.options.minZoom)) {
6656			this._zoomBoundLayers[stamp(layer)] = layer;
6657			this._updateZoomLevels();
6658		}
6659	},
6660
6661	_removeZoomLimit: function (layer) {
6662		var id = stamp(layer);
6663
6664		if (this._zoomBoundLayers[id]) {
6665			delete this._zoomBoundLayers[id];
6666			this._updateZoomLevels();
6667		}
6668	},
6669
6670	_updateZoomLevels: function () {
6671		var minZoom = Infinity,
6672		    maxZoom = -Infinity,
6673		    oldZoomSpan = this._getZoomSpan();
6674
6675		for (var i in this._zoomBoundLayers) {
6676			var options = this._zoomBoundLayers[i].options;
6677
6678			minZoom = options.minZoom === undefined ? minZoom : Math.min(minZoom, options.minZoom);
6679			maxZoom = options.maxZoom === undefined ? maxZoom : Math.max(maxZoom, options.maxZoom);
6680		}
6681
6682		this._layersMaxZoom = maxZoom === -Infinity ? undefined : maxZoom;
6683		this._layersMinZoom = minZoom === Infinity ? undefined : minZoom;
6684
6685		// @section Map state change events
6686		// @event zoomlevelschange: Event
6687		// Fired when the number of zoomlevels on the map is changed due
6688		// to adding or removing a layer.
6689		if (oldZoomSpan !== this._getZoomSpan()) {
6690			this.fire('zoomlevelschange');
6691		}
6692
6693		if (this.options.maxZoom === undefined && this._layersMaxZoom && this.getZoom() > this._layersMaxZoom) {
6694			this.setZoom(this._layersMaxZoom);
6695		}
6696		if (this.options.minZoom === undefined && this._layersMinZoom && this.getZoom() < this._layersMinZoom) {
6697			this.setZoom(this._layersMinZoom);
6698		}
6699	}
6700});
6701
6702/*
6703 * @class LayerGroup
6704 * @aka L.LayerGroup
6705 * @inherits Layer
6706 *
6707 * Used to group several layers and handle them as one. If you add it to the map,
6708 * any layers added or removed from the group will be added/removed on the map as
6709 * well. Extends `Layer`.
6710 *
6711 * @example
6712 *
6713 * ```js
6714 * L.layerGroup([marker1, marker2])
6715 * 	.addLayer(polyline)
6716 * 	.addTo(map);
6717 * ```
6718 */
6719
6720var LayerGroup = Layer.extend({
6721
6722	initialize: function (layers, options) {
6723		setOptions(this, options);
6724
6725		this._layers = {};
6726
6727		var i, len;
6728
6729		if (layers) {
6730			for (i = 0, len = layers.length; i < len; i++) {
6731				this.addLayer(layers[i]);
6732			}
6733		}
6734	},
6735
6736	// @method addLayer(layer: Layer): this
6737	// Adds the given layer to the group.
6738	addLayer: function (layer) {
6739		var id = this.getLayerId(layer);
6740
6741		this._layers[id] = layer;
6742
6743		if (this._map) {
6744			this._map.addLayer(layer);
6745		}
6746
6747		return this;
6748	},
6749
6750	// @method removeLayer(layer: Layer): this
6751	// Removes the given layer from the group.
6752	// @alternative
6753	// @method removeLayer(id: Number): this
6754	// Removes the layer with the given internal ID from the group.
6755	removeLayer: function (layer) {
6756		var id = layer in this._layers ? layer : this.getLayerId(layer);
6757
6758		if (this._map && this._layers[id]) {
6759			this._map.removeLayer(this._layers[id]);
6760		}
6761
6762		delete this._layers[id];
6763
6764		return this;
6765	},
6766
6767	// @method hasLayer(layer: Layer): Boolean
6768	// Returns `true` if the given layer is currently added to the group.
6769	// @alternative
6770	// @method hasLayer(id: Number): Boolean
6771	// Returns `true` if the given internal ID is currently added to the group.
6772	hasLayer: function (layer) {
6773		return !!layer && (layer in this._layers || this.getLayerId(layer) in this._layers);
6774	},
6775
6776	// @method clearLayers(): this
6777	// Removes all the layers from the group.
6778	clearLayers: function () {
6779		return this.eachLayer(this.removeLayer, this);
6780	},
6781
6782	// @method invoke(methodName: String, …): this
6783	// Calls `methodName` on every layer contained in this group, passing any
6784	// additional parameters. Has no effect if the layers contained do not
6785	// implement `methodName`.
6786	invoke: function (methodName) {
6787		var args = Array.prototype.slice.call(arguments, 1),
6788		    i, layer;
6789
6790		for (i in this._layers) {
6791			layer = this._layers[i];
6792
6793			if (layer[methodName]) {
6794				layer[methodName].apply(layer, args);
6795			}
6796		}
6797
6798		return this;
6799	},
6800
6801	onAdd: function (map) {
6802		this.eachLayer(map.addLayer, map);
6803	},
6804
6805	onRemove: function (map) {
6806		this.eachLayer(map.removeLayer, map);
6807	},
6808
6809	// @method eachLayer(fn: Function, context?: Object): this
6810	// Iterates over the layers of the group, optionally specifying context of the iterator function.
6811	// ```js
6812	// group.eachLayer(function (layer) {
6813	// 	layer.bindPopup('Hello');
6814	// });
6815	// ```
6816	eachLayer: function (method, context) {
6817		for (var i in this._layers) {
6818			method.call(context, this._layers[i]);
6819		}
6820		return this;
6821	},
6822
6823	// @method getLayer(id: Number): Layer
6824	// Returns the layer with the given internal ID.
6825	getLayer: function (id) {
6826		return this._layers[id];
6827	},
6828
6829	// @method getLayers(): Layer[]
6830	// Returns an array of all the layers added to the group.
6831	getLayers: function () {
6832		var layers = [];
6833		this.eachLayer(layers.push, layers);
6834		return layers;
6835	},
6836
6837	// @method setZIndex(zIndex: Number): this
6838	// Calls `setZIndex` on every layer contained in this group, passing the z-index.
6839	setZIndex: function (zIndex) {
6840		return this.invoke('setZIndex', zIndex);
6841	},
6842
6843	// @method getLayerId(layer: Layer): Number
6844	// Returns the internal ID for a layer
6845	getLayerId: function (layer) {
6846		return stamp(layer);
6847	}
6848});
6849
6850
6851// @factory L.layerGroup(layers?: Layer[], options?: Object)
6852// Create a layer group, optionally given an initial set of layers and an `options` object.
6853var layerGroup = function (layers, options) {
6854	return new LayerGroup(layers, options);
6855};
6856
6857/*
6858 * @class FeatureGroup
6859 * @aka L.FeatureGroup
6860 * @inherits LayerGroup
6861 *
6862 * Extended `LayerGroup` that makes it easier to do the same thing to all its member layers:
6863 *  * [`bindPopup`](#layer-bindpopup) binds a popup to all of the layers at once (likewise with [`bindTooltip`](#layer-bindtooltip))
6864 *  * Events are propagated to the `FeatureGroup`, so if the group has an event
6865 * handler, it will handle events from any of the layers. This includes mouse events
6866 * and custom events.
6867 *  * Has `layeradd` and `layerremove` events
6868 *
6869 * @example
6870 *
6871 * ```js
6872 * L.featureGroup([marker1, marker2, polyline])
6873 * 	.bindPopup('Hello world!')
6874 * 	.on('click', function() { alert('Clicked on a member of the group!'); })
6875 * 	.addTo(map);
6876 * ```
6877 */
6878
6879var FeatureGroup = LayerGroup.extend({
6880
6881	addLayer: function (layer) {
6882		if (this.hasLayer(layer)) {
6883			return this;
6884		}
6885
6886		layer.addEventParent(this);
6887
6888		LayerGroup.prototype.addLayer.call(this, layer);
6889
6890		// @event layeradd: LayerEvent
6891		// Fired when a layer is added to this `FeatureGroup`
6892		return this.fire('layeradd', {layer: layer});
6893	},
6894
6895	removeLayer: function (layer) {
6896		if (!this.hasLayer(layer)) {
6897			return this;
6898		}
6899		if (layer in this._layers) {
6900			layer = this._layers[layer];
6901		}
6902
6903		layer.removeEventParent(this);
6904
6905		LayerGroup.prototype.removeLayer.call(this, layer);
6906
6907		// @event layerremove: LayerEvent
6908		// Fired when a layer is removed from this `FeatureGroup`
6909		return this.fire('layerremove', {layer: layer});
6910	},
6911
6912	// @method setStyle(style: Path options): this
6913	// Sets the given path options to each layer of the group that has a `setStyle` method.
6914	setStyle: function (style) {
6915		return this.invoke('setStyle', style);
6916	},
6917
6918	// @method bringToFront(): this
6919	// Brings the layer group to the top of all other layers
6920	bringToFront: function () {
6921		return this.invoke('bringToFront');
6922	},
6923
6924	// @method bringToBack(): this
6925	// Brings the layer group to the back of all other layers
6926	bringToBack: function () {
6927		return this.invoke('bringToBack');
6928	},
6929
6930	// @method getBounds(): LatLngBounds
6931	// Returns the LatLngBounds of the Feature Group (created from bounds and coordinates of its children).
6932	getBounds: function () {
6933		var bounds = new LatLngBounds();
6934
6935		for (var id in this._layers) {
6936			var layer = this._layers[id];
6937			bounds.extend(layer.getBounds ? layer.getBounds() : layer.getLatLng());
6938		}
6939		return bounds;
6940	}
6941});
6942
6943// @factory L.featureGroup(layers: Layer[])
6944// Create a feature group, optionally given an initial set of layers.
6945var featureGroup = function (layers) {
6946	return new FeatureGroup(layers);
6947};
6948
6949/*
6950 * @class Icon
6951 * @aka L.Icon
6952 *
6953 * Represents an icon to provide when creating a marker.
6954 *
6955 * @example
6956 *
6957 * ```js
6958 * var myIcon = L.icon({
6959 *     iconUrl: 'my-icon.png',
6960 *     iconRetinaUrl: '[email protected]',
6961 *     iconSize: [38, 95],
6962 *     iconAnchor: [22, 94],
6963 *     popupAnchor: [-3, -76],
6964 *     shadowUrl: 'my-icon-shadow.png',
6965 *     shadowRetinaUrl: '[email protected]',
6966 *     shadowSize: [68, 95],
6967 *     shadowAnchor: [22, 94]
6968 * });
6969 *
6970 * L.marker([50.505, 30.57], {icon: myIcon}).addTo(map);
6971 * ```
6972 *
6973 * `L.Icon.Default` extends `L.Icon` and is the blue icon Leaflet uses for markers by default.
6974 *
6975 */
6976
6977var Icon = Class.extend({
6978
6979	/* @section
6980	 * @aka Icon options
6981	 *
6982	 * @option iconUrl: String = null
6983	 * **(required)** The URL to the icon image (absolute or relative to your script path).
6984	 *
6985	 * @option iconRetinaUrl: String = null
6986	 * The URL to a retina sized version of the icon image (absolute or relative to your
6987	 * script path). Used for Retina screen devices.
6988	 *
6989	 * @option iconSize: Point = null
6990	 * Size of the icon image in pixels.
6991	 *
6992	 * @option iconAnchor: Point = null
6993	 * The coordinates of the "tip" of the icon (relative to its top left corner). The icon
6994	 * will be aligned so that this point is at the marker's geographical location. Centered
6995	 * by default if size is specified, also can be set in CSS with negative margins.
6996	 *
6997	 * @option popupAnchor: Point = [0, 0]
6998	 * The coordinates of the point from which popups will "open", relative to the icon anchor.
6999	 *
7000	 * @option tooltipAnchor: Point = [0, 0]
7001	 * The coordinates of the point from which tooltips will "open", relative to the icon anchor.
7002	 *
7003	 * @option shadowUrl: String = null
7004	 * The URL to the icon shadow image. If not specified, no shadow image will be created.
7005	 *
7006	 * @option shadowRetinaUrl: String = null
7007	 *
7008	 * @option shadowSize: Point = null
7009	 * Size of the shadow image in pixels.
7010	 *
7011	 * @option shadowAnchor: Point = null
7012	 * The coordinates of the "tip" of the shadow (relative to its top left corner) (the same
7013	 * as iconAnchor if not specified).
7014	 *
7015	 * @option className: String = ''
7016	 * A custom class name to assign to both icon and shadow images. Empty by default.
7017	 */
7018
7019	options: {
7020		popupAnchor: [0, 0],
7021		tooltipAnchor: [0, 0]
7022	},
7023
7024	initialize: function (options) {
7025		setOptions(this, options);
7026	},
7027
7028	// @method createIcon(oldIcon?: HTMLElement): HTMLElement
7029	// Called internally when the icon has to be shown, returns a `<img>` HTML element
7030	// styled according to the options.
7031	createIcon: function (oldIcon) {
7032		return this._createIcon('icon', oldIcon);
7033	},
7034
7035	// @method createShadow(oldIcon?: HTMLElement): HTMLElement
7036	// As `createIcon`, but for the shadow beneath it.
7037	createShadow: function (oldIcon) {
7038		return this._createIcon('shadow', oldIcon);
7039	},
7040
7041	_createIcon: function (name, oldIcon) {
7042		var src = this._getIconUrl(name);
7043
7044		if (!src) {
7045			if (name === 'icon') {
7046				throw new Error('iconUrl not set in Icon options (see the docs).');
7047			}
7048			return null;
7049		}
7050
7051		var img = this._createImg(src, oldIcon && oldIcon.tagName === 'IMG' ? oldIcon : null);
7052		this._setIconStyles(img, name);
7053
7054		return img;
7055	},
7056
7057	_setIconStyles: function (img, name) {
7058		var options = this.options;
7059		var sizeOption = options[name + 'Size'];
7060
7061		if (typeof sizeOption === 'number') {
7062			sizeOption = [sizeOption, sizeOption];
7063		}
7064
7065		var size = toPoint(sizeOption),
7066		    anchor = toPoint(name === 'shadow' && options.shadowAnchor || options.iconAnchor ||
7067		            size && size.divideBy(2, true));
7068
7069		img.className = 'leaflet-marker-' + name + ' ' + (options.className || '');
7070
7071		if (anchor) {
7072			img.style.marginLeft = (-anchor.x) + 'px';
7073			img.style.marginTop  = (-anchor.y) + 'px';
7074		}
7075
7076		if (size) {
7077			img.style.width  = size.x + 'px';
7078			img.style.height = size.y + 'px';
7079		}
7080	},
7081
7082	_createImg: function (src, el) {
7083		el = el || document.createElement('img');
7084		el.src = src;
7085		return el;
7086	},
7087
7088	_getIconUrl: function (name) {
7089		return retina && this.options[name + 'RetinaUrl'] || this.options[name + 'Url'];
7090	}
7091});
7092
7093
7094// @factory L.icon(options: Icon options)
7095// Creates an icon instance with the given options.
7096function icon(options) {
7097	return new Icon(options);
7098}
7099
7100/*
7101 * @miniclass Icon.Default (Icon)
7102 * @aka L.Icon.Default
7103 * @section
7104 *
7105 * A trivial subclass of `Icon`, represents the icon to use in `Marker`s when
7106 * no icon is specified. Points to the blue marker image distributed with Leaflet
7107 * releases.
7108 *
7109 * In order to customize the default icon, just change the properties of `L.Icon.Default.prototype.options`
7110 * (which is a set of `Icon options`).
7111 *
7112 * If you want to _completely_ replace the default icon, override the
7113 * `L.Marker.prototype.options.icon` with your own icon instead.
7114 */
7115
7116var IconDefault = Icon.extend({
7117
7118	options: {
7119		iconUrl:       'marker-icon-old.png',
7120		iconRetinaUrl: 'marker-icon-2x.png',
7121		shadowUrl:     'marker-shadow.png',
7122		iconSize:    [25, 41],
7123		iconAnchor:  [12, 41],
7124		popupAnchor: [1, -34],
7125		tooltipAnchor: [16, -28],
7126		shadowSize:  [41, 41]
7127	},
7128
7129	_getIconUrl: function (name) {
7130		if (!IconDefault.imagePath) {	// Deprecated, backwards-compatibility only
7131			IconDefault.imagePath = this._detectIconPath();
7132		}
7133
7134		// @option imagePath: String
7135		// `Icon.Default` will try to auto-detect the location of the
7136		// blue icon images. If you are placing these images in a non-standard
7137		// way, set this option to point to the right path.
7138		return (this.options.imagePath || IconDefault.imagePath) + Icon.prototype._getIconUrl.call(this, name);
7139	},
7140
7141	_detectIconPath: function () {
7142		var el = create$1('div',  'leaflet-default-icon-path', document.body);
7143		var path = getStyle(el, 'background-image') ||
7144		           getStyle(el, 'backgroundImage');	// IE8
7145
7146		document.body.removeChild(el);
7147
7148		if (path === null || path.indexOf('url') !== 0) {
7149			path = '';
7150		} else {
7151			path = path.replace(/^url\(["']?/, '').replace(/marker-icon\.png["']?\)$/, '');
7152		}
7153
7154		return path;
7155	}
7156});
7157
7158/*
7159 * L.Handler.MarkerDrag is used internally by L.Marker to make the markers draggable.
7160 */
7161
7162
7163/* @namespace Marker
7164 * @section Interaction handlers
7165 *
7166 * 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:
7167 *
7168 * ```js
7169 * marker.dragging.disable();
7170 * ```
7171 *
7172 * @property dragging: Handler
7173 * Marker dragging handler (by both mouse and touch). Only valid when the marker is on the map (Otherwise set [`marker.options.draggable`](#marker-draggable)).
7174 */
7175
7176var MarkerDrag = Handler.extend({
7177	initialize: function (marker) {
7178		this._marker = marker;
7179	},
7180
7181	addHooks: function () {
7182		var icon = this._marker._icon;
7183
7184		if (!this._draggable) {
7185			this._draggable = new Draggable(icon, icon, true);
7186		}
7187
7188		this._draggable.on({
7189			dragstart: this._onDragStart,
7190			predrag: this._onPreDrag,
7191			drag: this._onDrag,
7192			dragend: this._onDragEnd
7193		}, this).enable();
7194
7195		addClass(icon, 'leaflet-marker-draggable');
7196	},
7197
7198	removeHooks: function () {
7199		this._draggable.off({
7200			dragstart: this._onDragStart,
7201			predrag: this._onPreDrag,
7202			drag: this._onDrag,
7203			dragend: this._onDragEnd
7204		}, this).disable();
7205
7206		if (this._marker._icon) {
7207			removeClass(this._marker._icon, 'leaflet-marker-draggable');
7208		}
7209	},
7210
7211	moved: function () {
7212		return this._draggable && this._draggable._moved;
7213	},
7214
7215	_adjustPan: function (e) {
7216		var marker = this._marker,
7217		    map = marker._map,
7218		    speed = this._marker.options.autoPanSpeed,
7219		    padding = this._marker.options.autoPanPadding,
7220		    iconPos = getPosition(marker._icon),
7221		    bounds = map.getPixelBounds(),
7222		    origin = map.getPixelOrigin();
7223
7224		var panBounds = toBounds(
7225			bounds.min._subtract(origin).add(padding),
7226			bounds.max._subtract(origin).subtract(padding)
7227		);
7228
7229		if (!panBounds.contains(iconPos)) {
7230			// Compute incremental movement
7231			var movement = toPoint(
7232				(Math.max(panBounds.max.x, iconPos.x) - panBounds.max.x) / (bounds.max.x - panBounds.max.x) -
7233				(Math.min(panBounds.min.x, iconPos.x) - panBounds.min.x) / (bounds.min.x - panBounds.min.x),
7234
7235				(Math.max(panBounds.max.y, iconPos.y) - panBounds.max.y) / (bounds.max.y - panBounds.max.y) -
7236				(Math.min(panBounds.min.y, iconPos.y) - panBounds.min.y) / (bounds.min.y - panBounds.min.y)
7237			).multiplyBy(speed);
7238
7239			map.panBy(movement, {animate: false});
7240
7241			this._draggable._newPos._add(movement);
7242			this._draggable._startPos._add(movement);
7243
7244			setPosition(marker._icon, this._draggable._newPos);
7245			this._onDrag(e);
7246
7247			this._panRequest = requestAnimFrame(this._adjustPan.bind(this, e));
7248		}
7249	},
7250
7251	_onDragStart: function () {
7252		// @section Dragging events
7253		// @event dragstart: Event
7254		// Fired when the user starts dragging the marker.
7255
7256		// @event movestart: Event
7257		// Fired when the marker starts moving (because of dragging).
7258
7259		this._oldLatLng = this._marker.getLatLng();
7260		this._marker
7261		    .closePopup()
7262		    .fire('movestart')
7263		    .fire('dragstart');
7264	},
7265
7266	_onPreDrag: function (e) {
7267		if (this._marker.options.autoPan) {
7268			cancelAnimFrame(this._panRequest);
7269			this._panRequest = requestAnimFrame(this._adjustPan.bind(this, e));
7270		}
7271	},
7272
7273	_onDrag: function (e) {
7274		var marker = this._marker,
7275		    shadow = marker._shadow,
7276		    iconPos = getPosition(marker._icon),
7277		    latlng = marker._map.layerPointToLatLng(iconPos);
7278
7279		// update shadow position
7280		if (shadow) {
7281			setPosition(shadow, iconPos);
7282		}
7283
7284		marker._latlng = latlng;
7285		e.latlng = latlng;
7286		e.oldLatLng = this._oldLatLng;
7287
7288		// @event drag: Event
7289		// Fired repeatedly while the user drags the marker.
7290		marker
7291		    .fire('move', e)
7292		    .fire('drag', e);
7293	},
7294
7295	_onDragEnd: function (e) {
7296		// @event dragend: DragEndEvent
7297		// Fired when the user stops dragging the marker.
7298
7299		 cancelAnimFrame(this._panRequest);
7300
7301		// @event moveend: Event
7302		// Fired when the marker stops moving (because of dragging).
7303		delete this._oldLatLng;
7304		this._marker
7305		    .fire('moveend')
7306		    .fire('dragend', e);
7307	}
7308});
7309
7310/*
7311 * @class Marker
7312 * @inherits Interactive layer
7313 * @aka L.Marker
7314 * L.Marker is used to display clickable/draggable icons on the map. Extends `Layer`.
7315 *
7316 * @example
7317 *
7318 * ```js
7319 * L.marker([50.5, 30.5]).addTo(map);
7320 * ```
7321 */
7322
7323var Marker = Layer.extend({
7324
7325	// @section
7326	// @aka Marker options
7327	options: {
7328		// @option icon: Icon = *
7329		// Icon instance to use for rendering the marker.
7330		// See [Icon documentation](#L.Icon) for details on how to customize the marker icon.
7331		// If not specified, a common instance of `L.Icon.Default` is used.
7332		icon: new IconDefault(),
7333
7334		// Option inherited from "Interactive layer" abstract class
7335		interactive: true,
7336
7337		// @option keyboard: Boolean = true
7338		// Whether the marker can be tabbed to with a keyboard and clicked by pressing enter.
7339		keyboard: true,
7340
7341		// @option title: String = ''
7342		// Text for the browser tooltip that appear on marker hover (no tooltip by default).
7343		title: '',
7344
7345		// @option alt: String = ''
7346		// Text for the `alt` attribute of the icon image (useful for accessibility).
7347		alt: '',
7348
7349		// @option zIndexOffset: Number = 0
7350		// 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).
7351		zIndexOffset: 0,
7352
7353		// @option opacity: Number = 1.0
7354		// The opacity of the marker.
7355		opacity: 1,
7356
7357		// @option riseOnHover: Boolean = false
7358		// If `true`, the marker will get on top of others when you hover the mouse over it.
7359		riseOnHover: false,
7360
7361		// @option riseOffset: Number = 250
7362		// The z-index offset used for the `riseOnHover` feature.
7363		riseOffset: 250,
7364
7365		// @option pane: String = 'markerPane'
7366		// `Map pane` where the markers icon will be added.
7367		pane: 'markerPane',
7368
7369		// @option bubblingMouseEvents: Boolean = false
7370		// When `true`, a mouse event on this marker will trigger the same event on the map
7371		// (unless [`L.DomEvent.stopPropagation`](#domevent-stoppropagation) is used).
7372		bubblingMouseEvents: false,
7373
7374		// @section Draggable marker options
7375		// @option draggable: Boolean = false
7376		// Whether the marker is draggable with mouse/touch or not.
7377		draggable: false,
7378
7379		// @option autoPan: Boolean = false
7380		// Whether to pan the map when dragging this marker near its edge or not.
7381		autoPan: false,
7382
7383		// @option autoPanPadding: Point = Point(50, 50)
7384		// Distance (in pixels to the left/right and to the top/bottom) of the
7385		// map edge to start panning the map.
7386		autoPanPadding: [50, 50],
7387
7388		// @option autoPanSpeed: Number = 10
7389		// Number of pixels the map should pan by.
7390		autoPanSpeed: 10
7391	},
7392
7393	/* @section
7394	 *
7395	 * In addition to [shared layer methods](#Layer) like `addTo()` and `remove()` and [popup methods](#Popup) like bindPopup() you can also use the following methods:
7396	 */
7397
7398	initialize: function (latlng, options) {
7399		setOptions(this, options);
7400		this._latlng = toLatLng(latlng);
7401	},
7402
7403	onAdd: function (map) {
7404		this._zoomAnimated = this._zoomAnimated && map.options.markerZoomAnimation;
7405
7406		if (this._zoomAnimated) {
7407			map.on('zoomanim', this._animateZoom, this);
7408		}
7409
7410		this._initIcon();
7411		this.update();
7412	},
7413
7414	onRemove: function (map) {
7415		if (this.dragging && this.dragging.enabled()) {
7416			this.options.draggable = true;
7417			this.dragging.removeHooks();
7418		}
7419		delete this.dragging;
7420
7421		if (this._zoomAnimated) {
7422			map.off('zoomanim', this._animateZoom, this);
7423		}
7424
7425		this._removeIcon();
7426		this._removeShadow();
7427	},
7428
7429	getEvents: function () {
7430		return {
7431			zoom: this.update,
7432			viewreset: this.update
7433		};
7434	},
7435
7436	// @method getLatLng: LatLng
7437	// Returns the current geographical position of the marker.
7438	getLatLng: function () {
7439		return this._latlng;
7440	},
7441
7442	// @method setLatLng(latlng: LatLng): this
7443	// Changes the marker position to the given point.
7444	setLatLng: function (latlng) {
7445		var oldLatLng = this._latlng;
7446		this._latlng = toLatLng(latlng);
7447		this.update();
7448
7449		// @event move: Event
7450		// 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`.
7451		return this.fire('move', {oldLatLng: oldLatLng, latlng: this._latlng});
7452	},
7453
7454	// @method setZIndexOffset(offset: Number): this
7455	// Changes the [zIndex offset](#marker-zindexoffset) of the marker.
7456	setZIndexOffset: function (offset) {
7457		this.options.zIndexOffset = offset;
7458		return this.update();
7459	},
7460
7461	// @method setIcon(icon: Icon): this
7462	// Changes the marker icon.
7463	setIcon: function (icon) {
7464
7465		this.options.icon = icon;
7466
7467		if (this._map) {
7468			this._initIcon();
7469			this.update();
7470		}
7471
7472		if (this._popup) {
7473			this.bindPopup(this._popup, this._popup.options);
7474		}
7475
7476		return this;
7477	},
7478
7479	getElement: function () {
7480		return this._icon;
7481	},
7482
7483	update: function () {
7484
7485		if (this._icon && this._map) {
7486			var pos = this._map.latLngToLayerPoint(this._latlng).round();
7487			this._setPos(pos);
7488		}
7489
7490		return this;
7491	},
7492
7493	_initIcon: function () {
7494		var options = this.options,
7495		    classToAdd = 'leaflet-zoom-' + (this._zoomAnimated ? 'animated' : 'hide');
7496
7497		var icon = options.icon.createIcon(this._icon),
7498		    addIcon = false;
7499
7500		// if we're not reusing the icon, remove the old one and init new one
7501		if (icon !== this._icon) {
7502			if (this._icon) {
7503				this._removeIcon();
7504			}
7505			addIcon = true;
7506
7507			if (options.title) {
7508				icon.title = options.title;
7509			}
7510
7511			if (icon.tagName === 'IMG') {
7512				icon.alt = options.alt || '';
7513			}
7514		}
7515
7516		addClass(icon, classToAdd);
7517
7518		if (options.keyboard) {
7519			icon.tabIndex = '0';
7520		}
7521
7522		this._icon = icon;
7523
7524		if (options.riseOnHover) {
7525			this.on({
7526				mouseover: this._bringToFront,
7527				mouseout: this._resetZIndex
7528			});
7529		}
7530
7531		var newShadow = options.icon.createShadow(this._shadow),
7532		    addShadow = false;
7533
7534		if (newShadow !== this._shadow) {
7535			this._removeShadow();
7536			addShadow = true;
7537		}
7538
7539		if (newShadow) {
7540			addClass(newShadow, classToAdd);
7541			newShadow.alt = '';
7542		}
7543		this._shadow = newShadow;
7544
7545
7546		if (options.opacity < 1) {
7547			this._updateOpacity();
7548		}
7549
7550
7551		if (addIcon) {
7552			this.getPane().appendChild(this._icon);
7553		}
7554		this._initInteraction();
7555		if (newShadow && addShadow) {
7556			this.getPane('shadowPane').appendChild(this._shadow);
7557		}
7558	},
7559
7560	_removeIcon: function () {
7561		if (this.options.riseOnHover) {
7562			this.off({
7563				mouseover: this._bringToFront,
7564				mouseout: this._resetZIndex
7565			});
7566		}
7567
7568		remove(this._icon);
7569		this.removeInteractiveTarget(this._icon);
7570
7571		this._icon = null;
7572	},
7573
7574	_removeShadow: function () {
7575		if (this._shadow) {
7576			remove(this._shadow);
7577		}
7578		this._shadow = null;
7579	},
7580
7581	_setPos: function (pos) {
7582		setPosition(this._icon, pos);
7583
7584		if (this._shadow) {
7585			setPosition(this._shadow, pos);
7586		}
7587
7588		this._zIndex = pos.y + this.options.zIndexOffset;
7589
7590		this._resetZIndex();
7591	},
7592
7593	_updateZIndex: function (offset) {
7594		this._icon.style.zIndex = this._zIndex + offset;
7595	},
7596
7597	_animateZoom: function (opt) {
7598		var pos = this._map._latLngToNewLayerPoint(this._latlng, opt.zoom, opt.center).round();
7599
7600		this._setPos(pos);
7601	},
7602
7603	_initInteraction: function () {
7604
7605		if (!this.options.interactive) { return; }
7606
7607		addClass(this._icon, 'leaflet-interactive');
7608
7609		this.addInteractiveTarget(this._icon);
7610
7611		if (MarkerDrag) {
7612			var draggable = this.options.draggable;
7613			if (this.dragging) {
7614				draggable = this.dragging.enabled();
7615				this.dragging.disable();
7616			}
7617
7618			this.dragging = new MarkerDrag(this);
7619
7620			if (draggable) {
7621				this.dragging.enable();
7622			}
7623		}
7624	},
7625
7626	// @method setOpacity(opacity: Number): this
7627	// Changes the opacity of the marker.
7628	setOpacity: function (opacity) {
7629		this.options.opacity = opacity;
7630		if (this._map) {
7631			this._updateOpacity();
7632		}
7633
7634		return this;
7635	},
7636
7637	_updateOpacity: function () {
7638		var opacity = this.options.opacity;
7639
7640		setOpacity(this._icon, opacity);
7641
7642		if (this._shadow) {
7643			setOpacity(this._shadow, opacity);
7644		}
7645	},
7646
7647	_bringToFront: function () {
7648		this._updateZIndex(this.options.riseOffset);
7649	},
7650
7651	_resetZIndex: function () {
7652		this._updateZIndex(0);
7653	},
7654
7655	_getPopupAnchor: function () {
7656		return this.options.icon.options.popupAnchor;
7657	},
7658
7659	_getTooltipAnchor: function () {
7660		return this.options.icon.options.tooltipAnchor;
7661	}
7662});
7663
7664
7665// factory L.marker(latlng: LatLng, options? : Marker options)
7666
7667// @factory L.marker(latlng: LatLng, options? : Marker options)
7668// Instantiates a Marker object given a geographical point and optionally an options object.
7669function marker(latlng, options) {
7670	return new Marker(latlng, options);
7671}
7672
7673/*
7674 * @class Path
7675 * @aka L.Path
7676 * @inherits Interactive layer
7677 *
7678 * An abstract class that contains options and constants shared between vector
7679 * overlays (Polygon, Polyline, Circle). Do not use it directly. Extends `Layer`.
7680 */
7681
7682var Path = Layer.extend({
7683
7684	// @section
7685	// @aka Path options
7686	options: {
7687		// @option stroke: Boolean = true
7688		// Whether to draw stroke along the path. Set it to `false` to disable borders on polygons or circles.
7689		stroke: true,
7690
7691		// @option color: String = '#3388ff'
7692		// Stroke color
7693		color: '#3388ff',
7694
7695		// @option weight: Number = 3
7696		// Stroke width in pixels
7697		weight: 3,
7698
7699		// @option opacity: Number = 1.0
7700		// Stroke opacity
7701		opacity: 1,
7702
7703		// @option lineCap: String= 'round'
7704		// A string that defines [shape to be used at the end](https://developer.mozilla.org/docs/Web/SVG/Attribute/stroke-linecap) of the stroke.
7705		lineCap: 'round',
7706
7707		// @option lineJoin: String = 'round'
7708		// A string that defines [shape to be used at the corners](https://developer.mozilla.org/docs/Web/SVG/Attribute/stroke-linejoin) of the stroke.
7709		lineJoin: 'round',
7710
7711		// @option dashArray: String = null
7712		// 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).
7713		dashArray: null,
7714
7715		// @option dashOffset: String = null
7716		// 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).
7717		dashOffset: null,
7718
7719		// @option fill: Boolean = depends
7720		// Whether to fill the path with color. Set it to `false` to disable filling on polygons or circles.
7721		fill: false,
7722
7723		// @option fillColor: String = *
7724		// Fill color. Defaults to the value of the [`color`](#path-color) option
7725		fillColor: null,
7726
7727		// @option fillOpacity: Number = 0.2
7728		// Fill opacity.
7729		fillOpacity: 0.2,
7730
7731		// @option fillRule: String = 'evenodd'
7732		// A string that defines [how the inside of a shape](https://developer.mozilla.org/docs/Web/SVG/Attribute/fill-rule) is determined.
7733		fillRule: 'evenodd',
7734
7735		// className: '',
7736
7737		// Option inherited from "Interactive layer" abstract class
7738		interactive: true,
7739
7740		// @option bubblingMouseEvents: Boolean = true
7741		// When `true`, a mouse event on this path will trigger the same event on the map
7742		// (unless [`L.DomEvent.stopPropagation`](#domevent-stoppropagation) is used).
7743		bubblingMouseEvents: true
7744	},
7745
7746	beforeAdd: function (map) {
7747		// Renderer is set here because we need to call renderer.getEvents
7748		// before this.getEvents.
7749		this._renderer = map.getRenderer(this);
7750	},
7751
7752	onAdd: function () {
7753		this._renderer._initPath(this);
7754		this._reset();
7755		this._renderer._addPath(this);
7756	},
7757
7758	onRemove: function () {
7759		this._renderer._removePath(this);
7760	},
7761
7762	// @method redraw(): this
7763	// Redraws the layer. Sometimes useful after you changed the coordinates that the path uses.
7764	redraw: function () {
7765		if (this._map) {
7766			this._renderer._updatePath(this);
7767		}
7768		return this;
7769	},
7770
7771	// @method setStyle(style: Path options): this
7772	// Changes the appearance of a Path based on the options in the `Path options` object.
7773	setStyle: function (style) {
7774		setOptions(this, style);
7775		if (this._renderer) {
7776			this._renderer._updateStyle(this);
7777		}
7778		return this;
7779	},
7780
7781	// @method bringToFront(): this
7782	// Brings the layer to the top of all path layers.
7783	bringToFront: function () {
7784		if (this._renderer) {
7785			this._renderer._bringToFront(this);
7786		}
7787		return this;
7788	},
7789
7790	// @method bringToBack(): this
7791	// Brings the layer to the bottom of all path layers.
7792	bringToBack: function () {
7793		if (this._renderer) {
7794			this._renderer._bringToBack(this);
7795		}
7796		return this;
7797	},
7798
7799	getElement: function () {
7800		return this._path;
7801	},
7802
7803	_reset: function () {
7804		// defined in child classes
7805		this._project();
7806		this._update();
7807	},
7808
7809	_clickTolerance: function () {
7810		// used when doing hit detection for Canvas layers
7811		return (this.options.stroke ? this.options.weight / 2 : 0) + this._renderer.options.tolerance;
7812	}
7813});
7814
7815/*
7816 * @class CircleMarker
7817 * @aka L.CircleMarker
7818 * @inherits Path
7819 *
7820 * A circle of a fixed size with radius specified in pixels. Extends `Path`.
7821 */
7822
7823var CircleMarker = Path.extend({
7824
7825	// @section
7826	// @aka CircleMarker options
7827	options: {
7828		fill: true,
7829
7830		// @option radius: Number = 10
7831		// Radius of the circle marker, in pixels
7832		radius: 10
7833	},
7834
7835	initialize: function (latlng, options) {
7836		setOptions(this, options);
7837		this._latlng = toLatLng(latlng);
7838		this._radius = this.options.radius;
7839	},
7840
7841	// @method setLatLng(latLng: LatLng): this
7842	// Sets the position of a circle marker to a new location.
7843	setLatLng: function (latlng) {
7844		this._latlng = toLatLng(latlng);
7845		this.redraw();
7846		return this.fire('move', {latlng: this._latlng});
7847	},
7848
7849	// @method getLatLng(): LatLng
7850	// Returns the current geographical position of the circle marker
7851	getLatLng: function () {
7852		return this._latlng;
7853	},
7854
7855	// @method setRadius(radius: Number): this
7856	// Sets the radius of a circle marker. Units are in pixels.
7857	setRadius: function (radius) {
7858		this.options.radius = this._radius = radius;
7859		return this.redraw();
7860	},
7861
7862	// @method getRadius(): Number
7863	// Returns the current radius of the circle
7864	getRadius: function () {
7865		return this._radius;
7866	},
7867
7868	setStyle : function (options) {
7869		var radius = options && options.radius || this._radius;
7870		Path.prototype.setStyle.call(this, options);
7871		this.setRadius(radius);
7872		return this;
7873	},
7874
7875	_project: function () {
7876		this._point = this._map.latLngToLayerPoint(this._latlng);
7877		this._updateBounds();
7878	},
7879
7880	_updateBounds: function () {
7881		var r = this._radius,
7882		    r2 = this._radiusY || r,
7883		    w = this._clickTolerance(),
7884		    p = [r + w, r2 + w];
7885		this._pxBounds = new Bounds(this._point.subtract(p), this._point.add(p));
7886	},
7887
7888	_update: function () {
7889		if (this._map) {
7890			this._updatePath();
7891		}
7892	},
7893
7894	_updatePath: function () {
7895		this._renderer._updateCircle(this);
7896	},
7897
7898	_empty: function () {
7899		return this._radius && !this._renderer._bounds.intersects(this._pxBounds);
7900	},
7901
7902	// Needed by the `Canvas` renderer for interactivity
7903	_containsPoint: function (p) {
7904		return p.distanceTo(this._point) <= this._radius + this._clickTolerance();
7905	}
7906});
7907
7908
7909// @factory L.circleMarker(latlng: LatLng, options?: CircleMarker options)
7910// Instantiates a circle marker object given a geographical point, and an optional options object.
7911function circleMarker(latlng, options) {
7912	return new CircleMarker(latlng, options);
7913}
7914
7915/*
7916 * @class Circle
7917 * @aka L.Circle
7918 * @inherits CircleMarker
7919 *
7920 * A class for drawing circle overlays on a map. Extends `CircleMarker`.
7921 *
7922 * It's an approximation and starts to diverge from a real circle closer to poles (due to projection distortion).
7923 *
7924 * @example
7925 *
7926 * ```js
7927 * L.circle([50.5, 30.5], {radius: 200}).addTo(map);
7928 * ```
7929 */
7930
7931var Circle = CircleMarker.extend({
7932
7933	initialize: function (latlng, options, legacyOptions) {
7934		if (typeof options === 'number') {
7935			// Backwards compatibility with 0.7.x factory (latlng, radius, options?)
7936			options = extend({}, legacyOptions, {radius: options});
7937		}
7938		setOptions(this, options);
7939		this._latlng = toLatLng(latlng);
7940
7941		if (isNaN(this.options.radius)) { throw new Error('Circle radius cannot be NaN'); }
7942
7943		// @section
7944		// @aka Circle options
7945		// @option radius: Number; Radius of the circle, in meters.
7946		this._mRadius = this.options.radius;
7947	},
7948
7949	// @method setRadius(radius: Number): this
7950	// Sets the radius of a circle. Units are in meters.
7951	setRadius: function (radius) {
7952		this._mRadius = radius;
7953		return this.redraw();
7954	},
7955
7956	// @method getRadius(): Number
7957	// Returns the current radius of a circle. Units are in meters.
7958	getRadius: function () {
7959		return this._mRadius;
7960	},
7961
7962	// @method getBounds(): LatLngBounds
7963	// Returns the `LatLngBounds` of the path.
7964	getBounds: function () {
7965		var half = [this._radius, this._radiusY || this._radius];
7966
7967		return new LatLngBounds(
7968			this._map.layerPointToLatLng(this._point.subtract(half)),
7969			this._map.layerPointToLatLng(this._point.add(half)));
7970	},
7971
7972	setStyle: Path.prototype.setStyle,
7973
7974	_project: function () {
7975
7976		var lng = this._latlng.lng,
7977		    lat = this._latlng.lat,
7978		    map = this._map,
7979		    crs = map.options.crs;
7980
7981		if (crs.distance === Earth.distance) {
7982			var d = Math.PI / 180,
7983			    latR = (this._mRadius / Earth.R) / d,
7984			    top = map.project([lat + latR, lng]),
7985			    bottom = map.project([lat - latR, lng]),
7986			    p = top.add(bottom).divideBy(2),
7987			    lat2 = map.unproject(p).lat,
7988			    lngR = Math.acos((Math.cos(latR * d) - Math.sin(lat * d) * Math.sin(lat2 * d)) /
7989			            (Math.cos(lat * d) * Math.cos(lat2 * d))) / d;
7990
7991			if (isNaN(lngR) || lngR === 0) {
7992				lngR = latR / Math.cos(Math.PI / 180 * lat); // Fallback for edge case, #2425
7993			}
7994
7995			this._point = p.subtract(map.getPixelOrigin());
7996			this._radius = isNaN(lngR) ? 0 : p.x - map.project([lat2, lng - lngR]).x;
7997			this._radiusY = p.y - top.y;
7998
7999		} else {
8000			var latlng2 = crs.unproject(crs.project(this._latlng).subtract([this._mRadius, 0]));
8001
8002			this._point = map.latLngToLayerPoint(this._latlng);
8003			this._radius = this._point.x - map.latLngToLayerPoint(latlng2).x;
8004		}
8005
8006		this._updateBounds();
8007	}
8008});
8009
8010// @factory L.circle(latlng: LatLng, options?: Circle options)
8011// Instantiates a circle object given a geographical point, and an options object
8012// which contains the circle radius.
8013// @alternative
8014// @factory L.circle(latlng: LatLng, radius: Number, options?: Circle options)
8015// Obsolete way of instantiating a circle, for compatibility with 0.7.x code.
8016// Do not use in new applications or plugins.
8017function circle(latlng, options, legacyOptions) {
8018	return new Circle(latlng, options, legacyOptions);
8019}
8020
8021/*
8022 * @class Polyline
8023 * @aka L.Polyline
8024 * @inherits Path
8025 *
8026 * A class for drawing polyline overlays on a map. Extends `Path`.
8027 *
8028 * @example
8029 *
8030 * ```js
8031 * // create a red polyline from an array of LatLng points
8032 * var latlngs = [
8033 * 	[45.51, -122.68],
8034 * 	[37.77, -122.43],
8035 * 	[34.04, -118.2]
8036 * ];
8037 *
8038 * var polyline = L.polyline(latlngs, {color: 'red'}).addTo(map);
8039 *
8040 * // zoom the map to the polyline
8041 * map.fitBounds(polyline.getBounds());
8042 * ```
8043 *
8044 * You can also pass a multi-dimensional array to represent a `MultiPolyline` shape:
8045 *
8046 * ```js
8047 * // create a red polyline from an array of arrays of LatLng points
8048 * var latlngs = [
8049 * 	[[45.51, -122.68],
8050 * 	 [37.77, -122.43],
8051 * 	 [34.04, -118.2]],
8052 * 	[[40.78, -73.91],
8053 * 	 [41.83, -87.62],
8054 * 	 [32.76, -96.72]]
8055 * ];
8056 * ```
8057 */
8058
8059
8060var Polyline = Path.extend({
8061
8062	// @section
8063	// @aka Polyline options
8064	options: {
8065		// @option smoothFactor: Number = 1.0
8066		// How much to simplify the polyline on each zoom level. More means
8067		// better performance and smoother look, and less means more accurate representation.
8068		smoothFactor: 1.0,
8069
8070		// @option noClip: Boolean = false
8071		// Disable polyline clipping.
8072		noClip: false
8073	},
8074
8075	initialize: function (latlngs, options) {
8076		setOptions(this, options);
8077		this._setLatLngs(latlngs);
8078	},
8079
8080	// @method getLatLngs(): LatLng[]
8081	// Returns an array of the points in the path, or nested arrays of points in case of multi-polyline.
8082	getLatLngs: function () {
8083		return this._latlngs;
8084	},
8085
8086	// @method setLatLngs(latlngs: LatLng[]): this
8087	// Replaces all the points in the polyline with the given array of geographical points.
8088	setLatLngs: function (latlngs) {
8089		this._setLatLngs(latlngs);
8090		return this.redraw();
8091	},
8092
8093	// @method isEmpty(): Boolean
8094	// Returns `true` if the Polyline has no LatLngs.
8095	isEmpty: function () {
8096		return !this._latlngs.length;
8097	},
8098
8099	// @method closestLayerPoint(p: Point): Point
8100	// Returns the point closest to `p` on the Polyline.
8101	closestLayerPoint: function (p) {
8102		var minDistance = Infinity,
8103		    minPoint = null,
8104		    closest = _sqClosestPointOnSegment,
8105		    p1, p2;
8106
8107		for (var j = 0, jLen = this._parts.length; j < jLen; j++) {
8108			var points = this._parts[j];
8109
8110			for (var i = 1, len = points.length; i < len; i++) {
8111				p1 = points[i - 1];
8112				p2 = points[i];
8113
8114				var sqDist = closest(p, p1, p2, true);
8115
8116				if (sqDist < minDistance) {
8117					minDistance = sqDist;
8118					minPoint = closest(p, p1, p2);
8119				}
8120			}
8121		}
8122		if (minPoint) {
8123			minPoint.distance = Math.sqrt(minDistance);
8124		}
8125		return minPoint;
8126	},
8127
8128	// @method getCenter(): LatLng
8129	// Returns the center ([centroid](http://en.wikipedia.org/wiki/Centroid)) of the polyline.
8130	getCenter: function () {
8131		// throws error when not yet added to map as this center calculation requires projected coordinates
8132		if (!this._map) {
8133			throw new Error('Must add layer to map before using getCenter()');
8134		}
8135
8136		var i, halfDist, segDist, dist, p1, p2, ratio,
8137		    points = this._rings[0],
8138		    len = points.length;
8139
8140		if (!len) { return null; }
8141
8142		// polyline centroid algorithm; only uses the first ring if there are multiple
8143
8144		for (i = 0, halfDist = 0; i < len - 1; i++) {
8145			halfDist += points[i].distanceTo(points[i + 1]) / 2;
8146		}
8147
8148		// The line is so small in the current view that all points are on the same pixel.
8149		if (halfDist === 0) {
8150			return this._map.layerPointToLatLng(points[0]);
8151		}
8152
8153		for (i = 0, dist = 0; i < len - 1; i++) {
8154			p1 = points[i];
8155			p2 = points[i + 1];
8156			segDist = p1.distanceTo(p2);
8157			dist += segDist;
8158
8159			if (dist > halfDist) {
8160				ratio = (dist - halfDist) / segDist;
8161				return this._map.layerPointToLatLng([
8162					p2.x - ratio * (p2.x - p1.x),
8163					p2.y - ratio * (p2.y - p1.y)
8164				]);
8165			}
8166		}
8167	},
8168
8169	// @method getBounds(): LatLngBounds
8170	// Returns the `LatLngBounds` of the path.
8171	getBounds: function () {
8172		return this._bounds;
8173	},
8174
8175	// @method addLatLng(latlng: LatLng, latlngs? LatLng[]): this
8176	// Adds a given point to the polyline. By default, adds to the first ring of
8177	// the polyline in case of a multi-polyline, but can be overridden by passing
8178	// a specific ring as a LatLng array (that you can earlier access with [`getLatLngs`](#polyline-getlatlngs)).
8179	addLatLng: function (latlng, latlngs) {
8180		latlngs = latlngs || this._defaultShape();
8181		latlng = toLatLng(latlng);
8182		latlngs.push(latlng);
8183		this._bounds.extend(latlng);
8184		return this.redraw();
8185	},
8186
8187	_setLatLngs: function (latlngs) {
8188		this._bounds = new LatLngBounds();
8189		this._latlngs = this._convertLatLngs(latlngs);
8190	},
8191
8192	_defaultShape: function () {
8193		return isFlat(this._latlngs) ? this._latlngs : this._latlngs[0];
8194	},
8195
8196	// recursively convert latlngs input into actual LatLng instances; calculate bounds along the way
8197	_convertLatLngs: function (latlngs) {
8198		var result = [],
8199		    flat = isFlat(latlngs);
8200
8201		for (var i = 0, len = latlngs.length; i < len; i++) {
8202			if (flat) {
8203				result[i] = toLatLng(latlngs[i]);
8204				this._bounds.extend(result[i]);
8205			} else {
8206				result[i] = this._convertLatLngs(latlngs[i]);
8207			}
8208		}
8209
8210		return result;
8211	},
8212
8213	_project: function () {
8214		var pxBounds = new Bounds();
8215		this._rings = [];
8216		this._projectLatlngs(this._latlngs, this._rings, pxBounds);
8217
8218		var w = this._clickTolerance(),
8219		    p = new Point(w, w);
8220
8221		if (this._bounds.isValid() && pxBounds.isValid()) {
8222			pxBounds.min._subtract(p);
8223			pxBounds.max._add(p);
8224			this._pxBounds = pxBounds;
8225		}
8226	},
8227
8228	// recursively turns latlngs into a set of rings with projected coordinates
8229	_projectLatlngs: function (latlngs, result, projectedBounds) {
8230		var flat = latlngs[0] instanceof LatLng,
8231		    len = latlngs.length,
8232		    i, ring;
8233
8234		if (flat) {
8235			ring = [];
8236			for (i = 0; i < len; i++) {
8237				ring[i] = this._map.latLngToLayerPoint(latlngs[i]);
8238				projectedBounds.extend(ring[i]);
8239			}
8240			result.push(ring);
8241		} else {
8242			for (i = 0; i < len; i++) {
8243				this._projectLatlngs(latlngs[i], result, projectedBounds);
8244			}
8245		}
8246	},
8247
8248	// clip polyline by renderer bounds so that we have less to render for performance
8249	_clipPoints: function () {
8250		var bounds = this._renderer._bounds;
8251
8252		this._parts = [];
8253		if (!this._pxBounds || !this._pxBounds.intersects(bounds)) {
8254			return;
8255		}
8256
8257		if (this.options.noClip) {
8258			this._parts = this._rings;
8259			return;
8260		}
8261
8262		var parts = this._parts,
8263		    i, j, k, len, len2, segment, points;
8264
8265		for (i = 0, k = 0, len = this._rings.length; i < len; i++) {
8266			points = this._rings[i];
8267
8268			for (j = 0, len2 = points.length; j < len2 - 1; j++) {
8269				segment = clipSegment(points[j], points[j + 1], bounds, j, true);
8270
8271				if (!segment) { continue; }
8272
8273				parts[k] = parts[k] || [];
8274				parts[k].push(segment[0]);
8275
8276				// if segment goes out of screen, or it's the last one, it's the end of the line part
8277				if ((segment[1] !== points[j + 1]) || (j === len2 - 2)) {
8278					parts[k].push(segment[1]);
8279					k++;
8280				}
8281			}
8282		}
8283	},
8284
8285	// simplify each clipped part of the polyline for performance
8286	_simplifyPoints: function () {
8287		var parts = this._parts,
8288		    tolerance = this.options.smoothFactor;
8289
8290		for (var i = 0, len = parts.length; i < len; i++) {
8291			parts[i] = simplify(parts[i], tolerance);
8292		}
8293	},
8294
8295	_update: function () {
8296		if (!this._map) { return; }
8297
8298		this._clipPoints();
8299		this._simplifyPoints();
8300		this._updatePath();
8301	},
8302
8303	_updatePath: function () {
8304		this._renderer._updatePoly(this);
8305	},
8306
8307	// Needed by the `Canvas` renderer for interactivity
8308	_containsPoint: function (p, closed) {
8309		var i, j, k, len, len2, part,
8310		    w = this._clickTolerance();
8311
8312		if (!this._pxBounds || !this._pxBounds.contains(p)) { return false; }
8313
8314		// hit detection for polylines
8315		for (i = 0, len = this._parts.length; i < len; i++) {
8316			part = this._parts[i];
8317
8318			for (j = 0, len2 = part.length, k = len2 - 1; j < len2; k = j++) {
8319				if (!closed && (j === 0)) { continue; }
8320
8321				if (pointToSegmentDistance(p, part[k], part[j]) <= w) {
8322					return true;
8323				}
8324			}
8325		}
8326		return false;
8327	}
8328});
8329
8330// @factory L.polyline(latlngs: LatLng[], options?: Polyline options)
8331// Instantiates a polyline object given an array of geographical points and
8332// optionally an options object. You can create a `Polyline` object with
8333// multiple separate lines (`MultiPolyline`) by passing an array of arrays
8334// of geographic points.
8335function polyline(latlngs, options) {
8336	return new Polyline(latlngs, options);
8337}
8338
8339// Retrocompat. Allow plugins to support Leaflet versions before and after 1.1.
8340Polyline._flat = _flat;
8341
8342/*
8343 * @class Polygon
8344 * @aka L.Polygon
8345 * @inherits Polyline
8346 *
8347 * A class for drawing polygon overlays on a map. Extends `Polyline`.
8348 *
8349 * 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.
8350 *
8351 *
8352 * @example
8353 *
8354 * ```js
8355 * // create a red polygon from an array of LatLng points
8356 * var latlngs = [[37, -109.05],[41, -109.03],[41, -102.05],[37, -102.04]];
8357 *
8358 * var polygon = L.polygon(latlngs, {color: 'red'}).addTo(map);
8359 *
8360 * // zoom the map to the polygon
8361 * map.fitBounds(polygon.getBounds());
8362 * ```
8363 *
8364 * 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:
8365 *
8366 * ```js
8367 * var latlngs = [
8368 *   [[37, -109.05],[41, -109.03],[41, -102.05],[37, -102.04]], // outer ring
8369 *   [[37.29, -108.58],[40.71, -108.58],[40.71, -102.50],[37.29, -102.50]] // hole
8370 * ];
8371 * ```
8372 *
8373 * Additionally, you can pass a multi-dimensional array to represent a MultiPolygon shape.
8374 *
8375 * ```js
8376 * var latlngs = [
8377 *   [ // first polygon
8378 *     [[37, -109.05],[41, -109.03],[41, -102.05],[37, -102.04]], // outer ring
8379 *     [[37.29, -108.58],[40.71, -108.58],[40.71, -102.50],[37.29, -102.50]] // hole
8380 *   ],
8381 *   [ // second polygon
8382 *     [[41, -111.03],[45, -111.04],[45, -104.05],[41, -104.05]]
8383 *   ]
8384 * ];
8385 * ```
8386 */
8387
8388var Polygon = Polyline.extend({
8389
8390	options: {
8391		fill: true
8392	},
8393
8394	isEmpty: function () {
8395		return !this._latlngs.length || !this._latlngs[0].length;
8396	},
8397
8398	getCenter: function () {
8399		// throws error when not yet added to map as this center calculation requires projected coordinates
8400		if (!this._map) {
8401			throw new Error('Must add layer to map before using getCenter()');
8402		}
8403
8404		var i, j, p1, p2, f, area, x, y, center,
8405		    points = this._rings[0],
8406		    len = points.length;
8407
8408		if (!len) { return null; }
8409
8410		// polygon centroid algorithm; only uses the first ring if there are multiple
8411
8412		area = x = y = 0;
8413
8414		for (i = 0, j = len - 1; i < len; j = i++) {
8415			p1 = points[i];
8416			p2 = points[j];
8417
8418			f = p1.y * p2.x - p2.y * p1.x;
8419			x += (p1.x + p2.x) * f;
8420			y += (p1.y + p2.y) * f;
8421			area += f * 3;
8422		}
8423
8424		if (area === 0) {
8425			// Polygon is so small that all points are on same pixel.
8426			center = points[0];
8427		} else {
8428			center = [x / area, y / area];
8429		}
8430		return this._map.layerPointToLatLng(center);
8431	},
8432
8433	_convertLatLngs: function (latlngs) {
8434		var result = Polyline.prototype._convertLatLngs.call(this, latlngs),
8435		    len = result.length;
8436
8437		// remove last point if it equals first one
8438		if (len >= 2 && result[0] instanceof LatLng && result[0].equals(result[len - 1])) {
8439			result.pop();
8440		}
8441		return result;
8442	},
8443
8444	_setLatLngs: function (latlngs) {
8445		Polyline.prototype._setLatLngs.call(this, latlngs);
8446		if (isFlat(this._latlngs)) {
8447			this._latlngs = [this._latlngs];
8448		}
8449	},
8450
8451	_defaultShape: function () {
8452		return isFlat(this._latlngs[0]) ? this._latlngs[0] : this._latlngs[0][0];
8453	},
8454
8455	_clipPoints: function () {
8456		// polygons need a different clipping algorithm so we redefine that
8457
8458		var bounds = this._renderer._bounds,
8459		    w = this.options.weight,
8460		    p = new Point(w, w);
8461
8462		// increase clip padding by stroke width to avoid stroke on clip edges
8463		bounds = new Bounds(bounds.min.subtract(p), bounds.max.add(p));
8464
8465		this._parts = [];
8466		if (!this._pxBounds || !this._pxBounds.intersects(bounds)) {
8467			return;
8468		}
8469
8470		if (this.options.noClip) {
8471			this._parts = this._rings;
8472			return;
8473		}
8474
8475		for (var i = 0, len = this._rings.length, clipped; i < len; i++) {
8476			clipped = clipPolygon(this._rings[i], bounds, true);
8477			if (clipped.length) {
8478				this._parts.push(clipped);
8479			}
8480		}
8481	},
8482
8483	_updatePath: function () {
8484		this._renderer._updatePoly(this, true);
8485	},
8486
8487	// Needed by the `Canvas` renderer for interactivity
8488	_containsPoint: function (p) {
8489		var inside = false,
8490		    part, p1, p2, i, j, k, len, len2;
8491
8492		if (!this._pxBounds || !this._pxBounds.contains(p)) { return false; }
8493
8494		// ray casting algorithm for detecting if point is in polygon
8495		for (i = 0, len = this._parts.length; i < len; i++) {
8496			part = this._parts[i];
8497
8498			for (j = 0, len2 = part.length, k = len2 - 1; j < len2; k = j++) {
8499				p1 = part[j];
8500				p2 = part[k];
8501
8502				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)) {
8503					inside = !inside;
8504				}
8505			}
8506		}
8507
8508		// also check if it's on polygon stroke
8509		return inside || Polyline.prototype._containsPoint.call(this, p, true);
8510	}
8511
8512});
8513
8514
8515// @factory L.polygon(latlngs: LatLng[], options?: Polyline options)
8516function polygon(latlngs, options) {
8517	return new Polygon(latlngs, options);
8518}
8519
8520/*
8521 * @class GeoJSON
8522 * @aka L.GeoJSON
8523 * @inherits FeatureGroup
8524 *
8525 * Represents a GeoJSON object or an array of GeoJSON objects. Allows you to parse
8526 * GeoJSON data and display it on the map. Extends `FeatureGroup`.
8527 *
8528 * @example
8529 *
8530 * ```js
8531 * L.geoJSON(data, {
8532 * 	style: function (feature) {
8533 * 		return {color: feature.properties.color};
8534 * 	}
8535 * }).bindPopup(function (layer) {
8536 * 	return layer.feature.properties.description;
8537 * }).addTo(map);
8538 * ```
8539 */
8540
8541var GeoJSON = FeatureGroup.extend({
8542
8543	/* @section
8544	 * @aka GeoJSON options
8545	 *
8546	 * @option pointToLayer: Function = *
8547	 * A `Function` defining how GeoJSON points spawn Leaflet layers. It is internally
8548	 * called when data is added, passing the GeoJSON point feature and its `LatLng`.
8549	 * The default is to spawn a default `Marker`:
8550	 * ```js
8551	 * function(geoJsonPoint, latlng) {
8552	 * 	return L.marker(latlng);
8553	 * }
8554	 * ```
8555	 *
8556	 * @option style: Function = *
8557	 * A `Function` defining the `Path options` for styling GeoJSON lines and polygons,
8558	 * called internally when data is added.
8559	 * The default value is to not override any defaults:
8560	 * ```js
8561	 * function (geoJsonFeature) {
8562	 * 	return {}
8563	 * }
8564	 * ```
8565	 *
8566	 * @option onEachFeature: Function = *
8567	 * A `Function` that will be called once for each created `Feature`, after it has
8568	 * been created and styled. Useful for attaching events and popups to features.
8569	 * The default is to do nothing with the newly created layers:
8570	 * ```js
8571	 * function (feature, layer) {}
8572	 * ```
8573	 *
8574	 * @option filter: Function = *
8575	 * A `Function` that will be used to decide whether to include a feature or not.
8576	 * The default is to include all features:
8577	 * ```js
8578	 * function (geoJsonFeature) {
8579	 * 	return true;
8580	 * }
8581	 * ```
8582	 * Note: dynamically changing the `filter` option will have effect only on newly
8583	 * added data. It will _not_ re-evaluate already included features.
8584	 *
8585	 * @option coordsToLatLng: Function = *
8586	 * A `Function` that will be used for converting GeoJSON coordinates to `LatLng`s.
8587	 * The default is the `coordsToLatLng` static method.
8588	 */
8589
8590	initialize: function (geojson, options) {
8591		setOptions(this, options);
8592
8593		this._layers = {};
8594
8595		if (geojson) {
8596			this.addData(geojson);
8597		}
8598	},
8599
8600	// @method addData( <GeoJSON> data ): this
8601	// Adds a GeoJSON object to the layer.
8602	addData: function (geojson) {
8603		var features = isArray(geojson) ? geojson : geojson.features,
8604		    i, len, feature;
8605
8606		if (features) {
8607			for (i = 0, len = features.length; i < len; i++) {
8608				// only add this if geometry or geometries are set and not null
8609				feature = features[i];
8610				if (feature.geometries || feature.geometry || feature.features || feature.coordinates) {
8611					this.addData(feature);
8612				}
8613			}
8614			return this;
8615		}
8616
8617		var options = this.options;
8618
8619		if (options.filter && !options.filter(geojson)) { return this; }
8620
8621		var layer = geometryToLayer(geojson, options);
8622		if (!layer) {
8623			return this;
8624		}
8625		layer.feature = asFeature(geojson);
8626
8627		layer.defaultOptions = layer.options;
8628		this.resetStyle(layer);
8629
8630		if (options.onEachFeature) {
8631			options.onEachFeature(geojson, layer);
8632		}
8633
8634		return this.addLayer(layer);
8635	},
8636
8637	// @method resetStyle( <Path> layer ): this
8638	// Resets the given vector layer's style to the original GeoJSON style, useful for resetting style after hover events.
8639	resetStyle: function (layer) {
8640		// reset any custom styles
8641		layer.options = extend({}, layer.defaultOptions);
8642		this._setLayerStyle(layer, this.options.style);
8643		return this;
8644	},
8645
8646	// @method setStyle( <Function> style ): this
8647	// Changes styles of GeoJSON vector layers with the given style function.
8648	setStyle: function (style) {
8649		return this.eachLayer(function (layer) {
8650			this._setLayerStyle(layer, style);
8651		}, this);
8652	},
8653
8654	_setLayerStyle: function (layer, style) {
8655		if (typeof style === 'function') {
8656			style = style(layer.feature);
8657		}
8658		if (layer.setStyle) {
8659			layer.setStyle(style);
8660		}
8661	}
8662});
8663
8664// @section
8665// There are several static functions which can be called without instantiating L.GeoJSON:
8666
8667// @function geometryToLayer(featureData: Object, options?: GeoJSON options): Layer
8668// Creates a `Layer` from a given GeoJSON feature. Can use a custom
8669// [`pointToLayer`](#geojson-pointtolayer) and/or [`coordsToLatLng`](#geojson-coordstolatlng)
8670// functions if provided as options.
8671function geometryToLayer(geojson, options) {
8672
8673	var geometry = geojson.type === 'Feature' ? geojson.geometry : geojson,
8674	    coords = geometry ? geometry.coordinates : null,
8675	    layers = [],
8676	    pointToLayer = options && options.pointToLayer,
8677	    _coordsToLatLng = options && options.coordsToLatLng || coordsToLatLng,
8678	    latlng, latlngs, i, len;
8679
8680	if (!coords && !geometry) {
8681		return null;
8682	}
8683
8684	switch (geometry.type) {
8685	case 'Point':
8686		latlng = _coordsToLatLng(coords);
8687		return pointToLayer ? pointToLayer(geojson, latlng) : new Marker(latlng);
8688
8689	case 'MultiPoint':
8690		for (i = 0, len = coords.length; i < len; i++) {
8691			latlng = _coordsToLatLng(coords[i]);
8692			layers.push(pointToLayer ? pointToLayer(geojson, latlng) : new Marker(latlng));
8693		}
8694		return new FeatureGroup(layers);
8695
8696	case 'LineString':
8697	case 'MultiLineString':
8698		latlngs = coordsToLatLngs(coords, geometry.type === 'LineString' ? 0 : 1, _coordsToLatLng);
8699		return new Polyline(latlngs, options);
8700
8701	case 'Polygon':
8702	case 'MultiPolygon':
8703		latlngs = coordsToLatLngs(coords, geometry.type === 'Polygon' ? 1 : 2, _coordsToLatLng);
8704		return new Polygon(latlngs, options);
8705
8706	case 'GeometryCollection':
8707		for (i = 0, len = geometry.geometries.length; i < len; i++) {
8708			var layer = geometryToLayer({
8709				geometry: geometry.geometries[i],
8710				type: 'Feature',
8711				properties: geojson.properties
8712			}, options);
8713
8714			if (layer) {
8715				layers.push(layer);
8716			}
8717		}
8718		return new FeatureGroup(layers);
8719
8720	default:
8721		throw new Error('Invalid GeoJSON object.');
8722	}
8723}
8724
8725// @function coordsToLatLng(coords: Array): LatLng
8726// Creates a `LatLng` object from an array of 2 numbers (longitude, latitude)
8727// or 3 numbers (longitude, latitude, altitude) used in GeoJSON for points.
8728function coordsToLatLng(coords) {
vendor: 12,744 bytes, lines 8729-9191
8729	return new LatLng(coords[1], coords[0], coords[2]);
8730}
8731
8732// @function coordsToLatLngs(coords: Array, levelsDeep?: Number, coordsToLatLng?: Function): Array
8733// Creates a multidimensional array of `LatLng`s from a GeoJSON coordinates array.
8734// `levelsDeep` specifies the nesting level (0 is for an array of points, 1 for an array of arrays of points, etc., 0 by default).
8735// Can use a custom [`coordsToLatLng`](#geojson-coordstolatlng) function.
8736function coordsToLatLngs(coords, levelsDeep, _coordsToLatLng) {
8737	var latlngs = [];
8738
8739	for (var i = 0, len = coords.length, latlng; i < len; i++) {
8740		latlng = levelsDeep ?
8741			coordsToLatLngs(coords[i], levelsDeep - 1, _coordsToLatLng) :
8742			(_coordsToLatLng || coordsToLatLng)(coords[i]);
8743
8744		latlngs.push(latlng);
8745	}
8746
8747	return latlngs;
8748}
8749
8750// @function latLngToCoords(latlng: LatLng, precision?: Number): Array
8751// Reverse of [`coordsToLatLng`](#geojson-coordstolatlng)
8752function latLngToCoords(latlng, precision) {
8753	precision = typeof precision === 'number' ? precision : 6;
8754	return latlng.alt !== undefined ?
8755		[formatNum(latlng.lng, precision), formatNum(latlng.lat, precision), formatNum(latlng.alt, precision)] :
8756		[formatNum(latlng.lng, precision), formatNum(latlng.lat, precision)];
8757}
8758
8759// @function latLngsToCoords(latlngs: Array, levelsDeep?: Number, closed?: Boolean): Array
8760// Reverse of [`coordsToLatLngs`](#geojson-coordstolatlngs)
8761// `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.
8762function latLngsToCoords(latlngs, levelsDeep, closed, precision) {
8763	var coords = [];
8764
8765	for (var i = 0, len = latlngs.length; i < len; i++) {
8766		coords.push(levelsDeep ?
8767			latLngsToCoords(latlngs[i], levelsDeep - 1, closed, precision) :
8768			latLngToCoords(latlngs[i], precision));
8769	}
8770
8771	if (!levelsDeep && closed) {
8772		coords.push(coords[0]);
8773	}
8774
8775	return coords;
8776}
8777
8778function getFeature(layer, newGeometry) {
8779	return layer.feature ?
8780		extend({}, layer.feature, {geometry: newGeometry}) :
8781		asFeature(newGeometry);
8782}
8783
8784// @function asFeature(geojson: Object): Object
8785// Normalize GeoJSON geometries/features into GeoJSON features.
8786function asFeature(geojson) {
8787	if (geojson.type === 'Feature' || geojson.type === 'FeatureCollection') {
8788		return geojson;
8789	}
8790
8791	return {
8792		type: 'Feature',
8793		properties: {},
8794		geometry: geojson
8795	};
8796}
8797
8798var PointToGeoJSON = {
8799	toGeoJSON: function (precision) {
8800		return getFeature(this, {
8801			type: 'Point',
8802			coordinates: latLngToCoords(this.getLatLng(), precision)
8803		});
8804	}
8805};
8806
8807// @namespace Marker
8808// @method toGeoJSON(): Object
8809// Returns a [`GeoJSON`](http://en.wikipedia.org/wiki/GeoJSON) representation of the marker (as a GeoJSON `Point` Feature).
8810Marker.include(PointToGeoJSON);
8811
8812// @namespace CircleMarker
8813// @method toGeoJSON(): Object
8814// Returns a [`GeoJSON`](http://en.wikipedia.org/wiki/GeoJSON) representation of the circle marker (as a GeoJSON `Point` Feature).
8815Circle.include(PointToGeoJSON);
8816CircleMarker.include(PointToGeoJSON);
8817
8818
8819// @namespace Polyline
8820// @method toGeoJSON(): Object
8821// Returns a [`GeoJSON`](http://en.wikipedia.org/wiki/GeoJSON) representation of the polyline (as a GeoJSON `LineString` or `MultiLineString` Feature).
8822Polyline.include({
8823	toGeoJSON: function (precision) {
8824		var multi = !isFlat(this._latlngs);
8825
8826		var coords = latLngsToCoords(this._latlngs, multi ? 1 : 0, false, precision);
8827
8828		return getFeature(this, {
8829			type: (multi ? 'Multi' : '') + 'LineString',
8830			coordinates: coords
8831		});
8832	}
8833});
8834
8835// @namespace Polygon
8836// @method toGeoJSON(): Object
8837// Returns a [`GeoJSON`](http://en.wikipedia.org/wiki/GeoJSON) representation of the polygon (as a GeoJSON `Polygon` or `MultiPolygon` Feature).
8838Polygon.include({
8839	toGeoJSON: function (precision) {
8840		var holes = !isFlat(this._latlngs),
8841		    multi = holes && !isFlat(this._latlngs[0]);
8842
8843		var coords = latLngsToCoords(this._latlngs, multi ? 2 : holes ? 1 : 0, true, precision);
8844
8845		if (!holes) {
8846			coords = [coords];
8847		}
8848
8849		return getFeature(this, {
8850			type: (multi ? 'Multi' : '') + 'Polygon',
8851			coordinates: coords
8852		});
8853	}
8854});
8855
8856
8857// @namespace LayerGroup
8858LayerGroup.include({
8859	toMultiPoint: function (precision) {
8860		var coords = [];
8861
8862		this.eachLayer(function (layer) {
8863			coords.push(layer.toGeoJSON(precision).geometry.coordinates);
8864		});
8865
8866		return getFeature(this, {
8867			type: 'MultiPoint',
8868			coordinates: coords
8869		});
8870	},
8871
8872	// @method toGeoJSON(): Object
8873	// Returns a [`GeoJSON`](http://en.wikipedia.org/wiki/GeoJSON) representation of the layer group (as a GeoJSON `FeatureCollection`, `GeometryCollection`, or `MultiPoint`).
8874	toGeoJSON: function (precision) {
8875
8876		var type = this.feature && this.feature.geometry && this.feature.geometry.type;
8877
8878		if (type === 'MultiPoint') {
8879			return this.toMultiPoint(precision);
8880		}
8881
8882		var isGeometryCollection = type === 'GeometryCollection',
8883		    jsons = [];
8884
8885		this.eachLayer(function (layer) {
8886			if (layer.toGeoJSON) {
8887				var json = layer.toGeoJSON(precision);
8888				if (isGeometryCollection) {
8889					jsons.push(json.geometry);
8890				} else {
8891					var feature = asFeature(json);
8892					// Squash nested feature collections
8893					if (feature.type === 'FeatureCollection') {
8894						jsons.push.apply(jsons, feature.features);
8895					} else {
8896						jsons.push(feature);
8897					}
8898				}
8899			}
8900		});
8901
8902		if (isGeometryCollection) {
8903			return getFeature(this, {
8904				geometries: jsons,
8905				type: 'GeometryCollection'
8906			});
8907		}
8908
8909		return {
8910			type: 'FeatureCollection',
8911			features: jsons
8912		};
8913	}
8914});
8915
8916// @namespace GeoJSON
8917// @factory L.geoJSON(geojson?: Object, options?: GeoJSON options)
8918// Creates a GeoJSON layer. Optionally accepts an object in
8919// [GeoJSON format](https://tools.ietf.org/html/rfc7946) to display on the map
8920// (you can alternatively add it later with `addData` method) and an `options` object.
8921function geoJSON(geojson, options) {
8922	return new GeoJSON(geojson, options);
8923}
8924
8925// Backward compatibility.
8926var geoJson = geoJSON;
8927
8928/*
8929 * @class ImageOverlay
8930 * @aka L.ImageOverlay
8931 * @inherits Interactive layer
8932 *
8933 * Used to load and display a single image over specific bounds of the map. Extends `Layer`.
8934 *
8935 * @example
8936 *
8937 * ```js
8938 * var imageUrl = 'http://www.lib.utexas.edu/maps/historical/newark_nj_1922.jpg',
8939 * 	imageBounds = [[40.712216, -74.22655], [40.773941, -74.12544]];
8940 * L.imageOverlay(imageUrl, imageBounds).addTo(map);
8941 * ```
8942 */
8943
8944var ImageOverlay = Layer.extend({
8945
8946	// @section
8947	// @aka ImageOverlay options
8948	options: {
8949		// @option opacity: Number = 1.0
8950		// The opacity of the image overlay.
8951		opacity: 1,
8952
8953		// @option alt: String = ''
8954		// Text for the `alt` attribute of the image (useful for accessibility).
8955		alt: '',
8956
8957		// @option interactive: Boolean = false
8958		// If `true`, the image overlay will emit [mouse events](#interactive-layer) when clicked or hovered.
8959		interactive: false,
8960
8961		// @option crossOrigin: Boolean|String = false
8962		// Whether the crossOrigin attribute will be added to the image.
8963		// 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.
8964		// Refer to [CORS Settings](https://developer.mozilla.org/en-US/docs/Web/HTML/CORS_settings_attributes) for valid String values.
8965		crossOrigin: false,
8966
8967		// @option errorOverlayUrl: String = ''
8968		// URL to the overlay image to show in place of the overlay that failed to load.
8969		errorOverlayUrl: '',
8970
8971		// @option zIndex: Number = 1
8972		// The explicit [zIndex](https://developer.mozilla.org/docs/Web/CSS/CSS_Positioning/Understanding_z_index) of the overlay layer.
8973		zIndex: 1,
8974
8975		// @option className: String = ''
8976		// A custom class name to assign to the image. Empty by default.
8977		className: ''
8978	},
8979
8980	initialize: function (url, bounds, options) { // (String, LatLngBounds, Object)
8981		this._url = url;
8982		this._bounds = toLatLngBounds(bounds);
8983
8984		setOptions(this, options);
8985	},
8986
8987	onAdd: function () {
8988		if (!this._image) {
8989			this._initImage();
8990
8991			if (this.options.opacity < 1) {
8992				this._updateOpacity();
8993			}
8994		}
8995
8996		if (this.options.interactive) {
8997			addClass(this._image, 'leaflet-interactive');
8998			this.addInteractiveTarget(this._image);
8999		}
9000
9001		this.getPane().appendChild(this._image);
9002		this._reset();
9003	},
9004
9005	onRemove: function () {
9006		remove(this._image);
9007		if (this.options.interactive) {
9008			this.removeInteractiveTarget(this._image);
9009		}
9010	},
9011
9012	// @method setOpacity(opacity: Number): this
9013	// Sets the opacity of the overlay.
9014	setOpacity: function (opacity) {
9015		this.options.opacity = opacity;
9016
9017		if (this._image) {
9018			this._updateOpacity();
9019		}
9020		return this;
9021	},
9022
9023	setStyle: function (styleOpts) {
9024		if (styleOpts.opacity) {
9025			this.setOpacity(styleOpts.opacity);
9026		}
9027		return this;
9028	},
9029
9030	// @method bringToFront(): this
9031	// Brings the layer to the top of all overlays.
9032	bringToFront: function () {
9033		if (this._map) {
9034			toFront(this._image);
9035		}
9036		return this;
9037	},
9038
9039	// @method bringToBack(): this
9040	// Brings the layer to the bottom of all overlays.
9041	bringToBack: function () {
9042		if (this._map) {
9043			toBack(this._image);
9044		}
9045		return this;
9046	},
9047
9048	// @method setUrl(url: String): this
9049	// Changes the URL of the image.
9050	setUrl: function (url) {
9051		this._url = url;
9052
9053		if (this._image) {
9054			this._image.src = url;
9055		}
9056		return this;
9057	},
9058
9059	// @method setBounds(bounds: LatLngBounds): this
9060	// Update the bounds that this ImageOverlay covers
9061	setBounds: function (bounds) {
9062		this._bounds = toLatLngBounds(bounds);
9063
9064		if (this._map) {
9065			this._reset();
9066		}
9067		return this;
9068	},
9069
9070	getEvents: function () {
9071		var events = {
9072			zoom: this._reset,
9073			viewreset: this._reset
9074		};
9075
9076		if (this._zoomAnimated) {
9077			events.zoomanim = this._animateZoom;
9078		}
9079
9080		return events;
9081	},
9082
9083	// @method setZIndex(value: Number): this
9084	// Changes the [zIndex](#imageoverlay-zindex) of the image overlay.
9085	setZIndex: function (value) {
9086		this.options.zIndex = value;
9087		this._updateZIndex();
9088		return this;
9089	},
9090
9091	// @method getBounds(): LatLngBounds
9092	// Get the bounds that this ImageOverlay covers
9093	getBounds: function () {
9094		return this._bounds;
9095	},
9096
9097	// @method getElement(): HTMLElement
9098	// Returns the instance of [`HTMLImageElement`](https://developer.mozilla.org/docs/Web/API/HTMLImageElement)
9099	// used by this overlay.
9100	getElement: function () {
9101		return this._image;
9102	},
9103
9104	_initImage: function () {
9105		var wasElementSupplied = this._url.tagName === 'IMG';
9106		var img = this._image = wasElementSupplied ? this._url : create$1('img');
9107
9108		addClass(img, 'leaflet-image-layer');
9109		if (this._zoomAnimated) { addClass(img, 'leaflet-zoom-animated'); }
9110		if (this.options.className) { addClass(img, this.options.className); }
9111
9112		img.onselectstart = falseFn;
9113		img.onmousemove = falseFn;
9114
9115		// @event load: Event
9116		// Fired when the ImageOverlay layer has loaded its image
9117		img.onload = bind(this.fire, this, 'load');
9118		img.onerror = bind(this._overlayOnError, this, 'error');
9119
9120		if (this.options.crossOrigin || this.options.crossOrigin === '') {
9121			img.crossOrigin = this.options.crossOrigin === true ? '' : this.options.crossOrigin;
9122		}
9123
9124		if (this.options.zIndex) {
9125			this._updateZIndex();
9126		}
9127
9128		if (wasElementSupplied) {
9129			this._url = img.src;
9130			return;
9131		}
9132
9133		img.src = this._url;
9134		img.alt = this.options.alt;
9135	},
9136
9137	_animateZoom: function (e) {
9138		var scale = this._map.getZoomScale(e.zoom),
9139		    offset = this._map._latLngBoundsToNewLayerBounds(this._bounds, e.zoom, e.center).min;
9140
9141		setTransform(this._image, offset, scale);
9142	},
9143
9144	_reset: function () {
9145		var image = this._image,
9146		    bounds = new Bounds(
9147		        this._map.latLngToLayerPoint(this._bounds.getNorthWest()),
9148		        this._map.latLngToLayerPoint(this._bounds.getSouthEast())),
9149		    size = bounds.getSize();
9150
9151		setPosition(image, bounds.min);
9152
9153		image.style.width  = size.x + 'px';
9154		image.style.height = size.y + 'px';
9155	},
9156
9157	_updateOpacity: function () {
9158		setOpacity(this._image, this.options.opacity);
9159	},
9160
9161	_updateZIndex: function () {
9162		if (this._image && this.options.zIndex !== undefined && this.options.zIndex !== null) {
9163			this._image.style.zIndex = this.options.zIndex;
9164		}
9165	},
9166
9167	_overlayOnError: function () {
9168		// @event error: Event
9169		// Fired when the ImageOverlay layer fails to load its image
9170		this.fire('error');
9171
9172		var errorUrl = this.options.errorOverlayUrl;
9173		if (errorUrl && this._url !== errorUrl) {
9174			this._url = errorUrl;
9175			this._image.src = errorUrl;
9176		}
9177	}
9178});
9179
9180// @factory L.imageOverlay(imageUrl: String, bounds: LatLngBounds, options?: ImageOverlay options)
9181// Instantiates an image overlay object given the URL of the image and the
9182// geographical bounds it is tied to.
9183var imageOverlay = function (url, bounds, options) {
9184	return new ImageOverlay(url, bounds, options);
9185};
9186
9187/*
9188 * @class VideoOverlay
9189 * @aka L.VideoOverlay
9190 * @inherits ImageOverlay
9191 *
9192 * Used to load and display a video player over specific bounds of the map. Extends `ImageOverlay`.
9193 *
9194 * A video overlay uses the [`<video>`](https://developer.mozilla.org/docs/Web/HTML/Element/video)
9195 * HTML5 element.
9196 *
9197 * @example
9198 *
9199 * ```js
9200 * var videoUrl = 'https://www.mapbox.com/bites/00188/patricia_nasa.webm',
9201 * 	videoBounds = [[ 32, -130], [ 13, -100]];
9202 * L.videoOverlay(videoUrl, videoBounds ).addTo(map);
9203 * ```
9204 */
9205
9206var VideoOverlay = ImageOverlay.extend({
9207
9208	// @section
9209	// @aka VideoOverlay options
9210	options: {
9211		// @option autoplay: Boolean = true
9212		// Whether the video starts playing automatically when loaded.
9213		autoplay: true,
9214
9215		// @option loop: Boolean = true
9216		// Whether the video will loop back to the beginning when played.
9217		loop: true
9218	},
9219
9220	_initImage: function () {
9221		var wasElementSupplied = this._url.tagName === 'VIDEO';
9222		var vid = this._image = wasElementSupplied ? this._url : create$1('video');
9223
9224		addClass(vid, 'leaflet-image-layer');
9225		if (this._zoomAnimated) { addClass(vid, 'leaflet-zoom-animated'); }
9226
9227		vid.onselectstart = falseFn;
9228		vid.onmousemove = falseFn;
9229
9230		// @event load: Event
9231		// Fired when the video has finished loading the first frame
9232		vid.onloadeddata = bind(this.fire, this, 'load');
9233
9234		if (wasElementSupplied) {
9235			var sourceElements = vid.getElementsByTagName('source');
9236			var sources = [];
9237			for (var j = 0; j < sourceElements.length; j++) {
9238				sources.push(sourceElements[j].src);
9239			}
9240
9241			this._url = (sourceElements.length > 0) ? sources : [vid.src];
9242			return;
9243		}
9244
9245		if (!isArray(this._url)) { this._url = [this._url]; }
9246
9247		vid.autoplay = !!this.options.autoplay;
9248		vid.loop = !!this.options.loop;
9249		for (var i = 0; i < this._url.length; i++) {
9250			var source = create$1('source');
9251			source.src = this._url[i];
9252			vid.appendChild(source);
9253		}
9254	}
9255
9256	// @method getElement(): HTMLVideoElement
9257	// Returns the instance of [`HTMLVideoElement`](https://developer.mozilla.org/docs/Web/API/HTMLVideoElement)
9258	// used by this overlay.
9259});
9260
9261
9262// @factory L.videoOverlay(video: String|Array|HTMLVideoElement, bounds: LatLngBounds, options?: VideoOverlay options)
9263// Instantiates an image overlay object given the URL of the video (or array of URLs, or even a video element) and the
9264// geographical bounds it is tied to.
9265
9266function videoOverlay(video, bounds, options) {
9267	return new VideoOverlay(video, bounds, options);
9268}
9269
9270/*
9271 * @class DivOverlay
9272 * @inherits Layer
9273 * @aka L.DivOverlay
9274 * Base model for L.Popup and L.Tooltip. Inherit from it for custom popup like plugins.
9275 */
9276
9277// @namespace DivOverlay
9278var DivOverlay = Layer.extend({
9279
9280	// @section
9281	// @aka DivOverlay options
9282	options: {
9283		// @option offset: Point = Point(0, 7)
9284		// The offset of the popup position. Useful to control the anchor
9285		// of the popup when opening it on some overlays.
9286		offset: [0, 7],
9287
9288		// @option className: String = ''
9289		// A custom CSS class name to assign to the popup.
9290		className: '',
9291
9292		// @option pane: String = 'popupPane'
9293		// `Map pane` where the popup will be added.
9294		pane: 'popupPane'
9295	},
9296
9297	initialize: function (options, source) {
9298		setOptions(this, options);
9299
9300		this._source = source;
9301	},
9302
9303	onAdd: function (map) {
9304		this._zoomAnimated = map._zoomAnimated;
9305
9306		if (!this._container) {
9307			this._initLayout();
9308		}
9309
9310		if (map._fadeAnimated) {
9311			setOpacity(this._container, 0);
9312		}
9313
9314		clearTimeout(this._removeTimeout);
9315		this.getPane().appendChild(this._container);
9316		this.update();
9317
9318		if (map._fadeAnimated) {
9319			setOpacity(this._container, 1);
9320		}
9321
9322		this.bringToFront();
9323	},
9324
9325	onRemove: function (map) {
9326		if (map._fadeAnimated) {
9327			setOpacity(this._container, 0);
9328			this._removeTimeout = setTimeout(bind(remove, undefined, this._container), 200);
9329		} else {
9330			remove(this._container);
9331		}
9332	},
9333
9334	// @namespace Popup
9335	// @method getLatLng: LatLng
9336	// Returns the geographical point of popup.
9337	getLatLng: function () {
9338		return this._latlng;
9339	},
9340
9341	// @method setLatLng(latlng: LatLng): this
9342	// Sets the geographical point where the popup will open.
9343	setLatLng: function (latlng) {
9344		this._latlng = toLatLng(latlng);
9345		if (this._map) {
9346			this._updatePosition();
9347			this._adjustPan();
9348		}
9349		return this;
9350	},
9351
9352	// @method getContent: String|HTMLElement
9353	// Returns the content of the popup.
9354	getContent: function () {
9355		return this._content;
9356	},
9357
9358	// @method setContent(htmlContent: String|HTMLElement|Function): this
vendor: 3,443 bytes, lines 9359-9488
9359	// 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.
9360	setContent: function (content) {
9361		this._content = content;
9362		this.update();
9363		return this;
9364	},
9365
9366	// @method getElement: String|HTMLElement
9367	// Alias for [getContent()](#popup-getcontent)
9368	getElement: function () {
9369		return this._container;
9370	},
9371
9372	// @method update: null
9373	// Updates the popup content, layout and position. Useful for updating the popup after something inside changed, e.g. image loaded.
9374	update: function () {
9375		if (!this._map) { return; }
9376
9377		this._container.style.visibility = 'hidden';
9378
9379		this._updateContent();
9380		this._updateLayout();
9381		this._updatePosition();
9382
9383		this._container.style.visibility = '';
9384
9385		this._adjustPan();
9386	},
9387
9388	getEvents: function () {
9389		var events = {
9390			zoom: this._updatePosition,
9391			viewreset: this._updatePosition
9392		};
9393
9394		if (this._zoomAnimated) {
9395			events.zoomanim = this._animateZoom;
9396		}
9397		return events;
9398	},
9399
9400	// @method isOpen: Boolean
9401	// Returns `true` when the popup is visible on the map.
9402	isOpen: function () {
9403		return !!this._map && this._map.hasLayer(this);
9404	},
9405
9406	// @method bringToFront: this
9407	// Brings this popup in front of other popups (in the same map pane).
9408	bringToFront: function () {
9409		if (this._map) {
9410			toFront(this._container);
9411		}
9412		return this;
9413	},
9414
9415	// @method bringToBack: this
9416	// Brings this popup to the back of other popups (in the same map pane).
9417	bringToBack: function () {
9418		if (this._map) {
9419			toBack(this._container);
9420		}
9421		return this;
9422	},
9423
9424	_updateContent: function () {
9425		if (!this._content) { return; }
9426
9427		var node = this._contentNode;
9428		var content = (typeof this._content === 'function') ? this._content(this._source || this) : this._content;
9429
9430		if (typeof content === 'string') {
9431			node.innerHTML = content;
9432		} else {
9433			while (node.hasChildNodes()) {
9434				node.removeChild(node.firstChild);
9435			}
9436			node.appendChild(content);
9437		}
9438		this.fire('contentupdate');
9439	},
9440
9441	_updatePosition: function () {
9442		if (!this._map) { return; }
9443
9444		var pos = this._map.latLngToLayerPoint(this._latlng),
9445		    offset = toPoint(this.options.offset),
9446		    anchor = this._getAnchor();
9447
9448		if (this._zoomAnimated) {
9449			setPosition(this._container, pos.add(anchor));
9450		} else {
9451			offset = offset.add(pos).add(anchor);
9452		}
9453
9454		var bottom = this._containerBottom = -offset.y,
9455		    left = this._containerLeft = -Math.round(this._containerWidth / 2) + offset.x;
9456
9457		// bottom position the popup in case the height of the popup changes (images loading etc)
9458		this._container.style.bottom = bottom + 'px';
9459		this._container.style.left = left + 'px';
9460	},
9461
9462	_getAnchor: function () {
9463		return [0, 0];
9464	}
9465
9466});
9467
9468/*
9469 * @class Popup
9470 * @inherits DivOverlay
9471 * @aka L.Popup
9472 * Used to open popups in certain places of the map. Use [Map.openPopup](#map-openpopup) to
9473 * open popups while making sure that only one popup is open at one time
9474 * (recommended for usability), or use [Map.addLayer](#map-addlayer) to open as many as you want.
9475 *
9476 * @example
9477 *
9478 * If you want to just bind a popup to marker click and then open it, it's really easy:
9479 *
9480 * ```js
9481 * marker.bindPopup(popupContent).openPopup();
9482 * ```
9483 * Path overlays like polylines also have a `bindPopup` method.
9484 * Here's a more complicated way to open a popup on a map:
9485 *
9486 * ```js
9487 * var popup = L.popup()
9488 * 	.setLatLng(latlng)
9489 * 	.setContent('<p>Hello world!<br />This is a nice popup.</p>')
9490 * 	.openOn(map);
9491 * ```
9492 */
9493
9494
9495// @namespace Popup
9496var Popup = DivOverlay.extend({
9497
9498	// @section
9499	// @aka Popup options
9500	options: {
9501		// @option maxWidth: Number = 300
9502		// Max width of the popup, in pixels.
9503		maxWidth: 300,
9504
9505		// @option minWidth: Number = 50
9506		// Min width of the popup, in pixels.
9507		minWidth: 50,
9508
9509		// @option maxHeight: Number = null
9510		// If set, creates a scrollable container of the given height
9511		// inside a popup if its content exceeds it.
9512		maxHeight: null,
9513
9514		// @option autoPan: Boolean = true
9515		// Set it to `false` if you don't want the map to do panning animation
9516		// to fit the opened popup.
9517		autoPan: true,
9518
9519		// @option autoPanPaddingTopLeft: Point = null
9520		// The margin between the popup and the top left corner of the map
9521		// view after autopanning was performed.
9522		autoPanPaddingTopLeft: null,
9523
9524		// @option autoPanPaddingBottomRight: Point = null
9525		// The margin between the popup and the bottom right corner of the map
9526		// view after autopanning was performed.
9527		autoPanPaddingBottomRight: null,
9528
9529		// @option autoPanPadding: Point = Point(5, 5)
9530		// Equivalent of setting both top left and bottom right autopan padding to the same value.
9531		autoPanPadding: [5, 5],
9532
9533		// @option keepInView: Boolean = false
9534		// Set it to `true` if you want to prevent users from panning the popup
9535		// off of the screen while it is open.
9536		keepInView: false,
9537
9538		// @option closeButton: Boolean = true
9539		// Controls the presence of a close button in the popup.
9540		closeButton: true,
9541
9542		// @option autoClose: Boolean = true
9543		// Set it to `false` if you want to override the default behavior of
9544		// the popup closing when another popup is opened.
9545		autoClose: true,
9546
9547		// @option closeOnEscapeKey: Boolean = true
9548		// Set it to `false` if you want to override the default behavior of
9549		// the ESC key for closing of the popup.
9550		closeOnEscapeKey: true,
9551
9552		// @option closeOnClick: Boolean = *
9553		// Set it if you want to override the default behavior of the popup closing when user clicks
9554		// on the map. Defaults to the map's [`closePopupOnClick`](#map-closepopuponclick) option.
9555
9556		// @option className: String = ''
9557		// A custom CSS class name to assign to the popup.
9558		className: ''
9559	},
9560
9561	// @namespace Popup
9562	// @method openOn(map: Map): this
9563	// Adds the popup to the map and closes the previous one. The same as `map.openPopup(popup)`.
9564	openOn: function (map) {
9565		map.openPopup(this);
9566		return this;
9567	},
9568
9569	onAdd: function (map) {
9570		DivOverlay.prototype.onAdd.call(this, map);
9571
9572		// @namespace Map
9573		// @section Popup events
9574		// @event popupopen: PopupEvent
9575		// Fired when a popup is opened in the map
9576		map.fire('popupopen', {popup: this});
9577
9578		if (this._source) {
9579			// @namespace Layer
9580			// @section Popup events
9581			// @event popupopen: PopupEvent
9582			// Fired when a popup bound to this layer is opened
9583			this._source.fire('popupopen', {popup: this}, true);
9584			// For non-path layers, we toggle the popup when clicking
9585			// again the layer, so prevent the map to reopen it.
9586			if (!(this._source instanceof Path)) {
9587				this._source.on('preclick', stopPropagation);
9588			}
9589		}
9590	},
9591
9592	onRemove: function (map) {
9593		DivOverlay.prototype.onRemove.call(this, map);
9594
9595		// @namespace Map
9596		// @section Popup events
9597		// @event popupclose: PopupEvent
9598		// Fired when a popup in the map is closed
9599		map.fire('popupclose', {popup: this});
9600
9601		if (this._source) {
9602			// @namespace Layer
9603			// @section Popup events
9604			// @event popupclose: PopupEvent
9605			// Fired when a popup bound to this layer is closed
9606			this._source.fire('popupclose', {popup: this}, true);
9607			if (!(this._source instanceof Path)) {
9608				this._source.off('preclick', stopPropagation);
9609			}
9610		}
9611	},
9612
9613	getEvents: function () {
9614		var events = DivOverlay.prototype.getEvents.call(this);
9615
9616		if (this.options.closeOnClick !== undefined ? this.options.closeOnClick : this._map.options.closePopupOnClick) {
9617			events.preclick = this._close;
9618		}
9619
9620		if (this.options.keepInView) {
9621			events.moveend = this._adjustPan;
9622		}
9623
9624		return events;
9625	},
9626
9627	_close: function () {
9628		if (this._map) {
9629			this._map.closePopup(this);
9630		}
9631	},
9632
9633	_initLayout: function () {
9634		var prefix = 'leaflet-popup',
9635		    container = this._container = create$1('div',
9636			prefix + ' ' + (this.options.className || '') +
9637			' leaflet-zoom-animated');
9638
9639		var wrapper = this._wrapper = create$1('div', prefix + '-content-wrapper', container);
9640		this._contentNode = create$1('div', prefix + '-content', wrapper);
9641
9642		disableClickPropagation(wrapper);
9643		disableScrollPropagation(this._contentNode);
9644		on(wrapper, 'contextmenu', stopPropagation);
9645
9646		this._tipContainer = create$1('div', prefix + '-tip-container', container);
9647		this._tip = create$1('div', prefix + '-tip', this._tipContainer);
9648
9649		if (this.options.closeButton) {
9650			var closeButton = this._closeButton = create$1('a', prefix + '-close-button', container);
9651			closeButton.href = '#close';
9652			closeButton.innerHTML = '&#215;';
9653
9654			on(closeButton, 'click', this._onCloseButtonClick, this);
9655		}
9656	},
9657
9658	_updateLayout: function () {
9659		var container = this._contentNode,
9660		    style = container.style;
9661
9662		style.width = '';
9663		style.whiteSpace = 'nowrap';
9664
9665		var width = container.offsetWidth;
9666		width = Math.min(width, this.options.maxWidth);
9667		width = Math.max(width, this.options.minWidth);
9668
9669		style.width = (width + 1) + 'px';
9670		style.whiteSpace = '';
9671
9672		style.height = '';
9673
9674		var height = container.offsetHeight,
9675		    maxHeight = this.options.maxHeight,
9676		    scrolledClass = 'leaflet-popup-scrolled';
9677
9678		if (maxHeight && height > maxHeight) {
9679			style.height = maxHeight + 'px';
9680			addClass(container, scrolledClass);
9681		} else {
9682			removeClass(container, scrolledClass);
9683		}
9684
9685		this._containerWidth = this._container.offsetWidth;
9686	},
9687
9688	_animateZoom: function (e) {
9689		var pos = this._map._latLngToNewLayerPoint(this._latlng, e.zoom, e.center),
9690		    anchor = this._getAnchor();
9691		setPosition(this._container, pos.add(anchor));
9692	},
9693
9694	_adjustPan: function () {
9695		if (!this.options.autoPan || (this._map._panAnim && this._map._panAnim._inProgress)) { return; }
9696
9697		var map = this._map,
9698		    marginBottom = parseInt(getStyle(this._container, 'marginBottom'), 10) || 0,
9699		    containerHeight = this._container.offsetHeight + marginBottom,
9700		    containerWidth = this._containerWidth,
9701		    layerPos = new Point(this._containerLeft, -containerHeight - this._containerBottom);
9702
9703		layerPos._add(getPosition(this._container));
9704
9705		var containerPos = map.layerPointToContainerPoint(layerPos),
9706		    padding = toPoint(this.options.autoPanPadding),
9707		    paddingTL = toPoint(this.options.autoPanPaddingTopLeft || padding),
9708		    paddingBR = toPoint(this.options.autoPanPaddingBottomRight || padding),
9709		    size = map.getSize(),
9710		    dx = 0,
9711		    dy = 0;
9712
9713		if (containerPos.x + containerWidth + paddingBR.x > size.x) { // right
9714			dx = containerPos.x + containerWidth - size.x + paddingBR.x;
9715		}
9716		if (containerPos.x - dx - paddingTL.x < 0) { // left
9717			dx = containerPos.x - paddingTL.x;
9718		}
9719		if (containerPos.y + containerHeight + paddingBR.y > size.y) { // bottom
9720			dy = containerPos.y + containerHeight - size.y + paddingBR.y;
9721		}
9722		if (containerPos.y - dy - paddingTL.y < 0) { // top
9723			dy = containerPos.y - paddingTL.y;
9724		}
9725
9726		// @namespace Map
9727		// @section Popup events
9728		// @event autopanstart: Event
9729		// Fired when the map starts autopanning when opening a popup.
9730		if (dx || dy) {
9731			map
9732			    .fire('autopanstart')
9733			    .panBy([dx, dy]);
9734		}
9735	},
9736
9737	_onCloseButtonClick: function (e) {
9738		this._close();
9739		stop(e);
9740	},
9741
9742	_getAnchor: function () {
9743		// Where should we anchor the popup on the source layer?
9744		return toPoint(this._source && this._source._getPopupAnchor ? this._source._getPopupAnchor() : [0, 0]);
9745	}
9746
9747});
9748
9749// @namespace Popup
9750// @factory L.popup(options?: Popup options, source?: Layer)
9751// 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.
9752var popup = function (options, source) {
9753	return new Popup(options, source);
9754};
9755
9756
9757/* @namespace Map
9758 * @section Interaction Options
9759 * @option closePopupOnClick: Boolean = true
9760 * Set it to `false` if you don't want popups to close when user clicks the map.
9761 */
9762Map.mergeOptions({
9763	closePopupOnClick: true
9764});
9765
9766
9767// @namespace Map
9768// @section Methods for Layers and Controls
9769Map.include({
9770	// @method openPopup(popup: Popup): this
9771	// Opens the specified popup while closing the previously opened (to make sure only one is opened at one time for usability).
9772	// @alternative
9773	// @method openPopup(content: String|HTMLElement, latlng: LatLng, options?: Popup options): this
9774	// Creates a popup with the specified content and options and opens it in the given point on a map.
9775	openPopup: function (popup, latlng, options) {
9776		if (!(popup instanceof Popup)) {
9777			popup = new Popup(options).setContent(popup);
9778		}
9779
9780		if (latlng) {
9781			popup.setLatLng(latlng);
9782		}
9783
9784		if (this.hasLayer(popup)) {
9785			return this;
9786		}
9787
9788		if (this._popup && this._popup.options.autoClose) {
9789			this.closePopup();
9790		}
9791
9792		this._popup = popup;
9793		return this.addLayer(popup);
9794	},
9795
9796	// @method closePopup(popup?: Popup): this
9797	// Closes the popup previously opened with [openPopup](#map-openpopup) (or the given one).
9798	closePopup: function (popup) {
9799		if (!popup || popup === this._popup) {
9800			popup = this._popup;
9801			this._popup = null;
9802		}
9803		if (popup) {
9804			this.removeLayer(popup);
9805		}
9806		return this;
9807	}
9808});
9809
9810/*
9811 * @namespace Layer
9812 * @section Popup methods example
9813 *
9814 * All layers share a set of methods convenient for binding popups to it.
9815 *
9816 * ```js
9817 * var layer = L.Polygon(latlngs).bindPopup('Hi There!').addTo(map);
9818 * layer.openPopup();
9819 * layer.closePopup();
9820 * ```
9821 *
9822 * 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.
9823 */
9824
9825// @section Popup methods
9826Layer.include({
9827
9828	// @method bindPopup(content: String|HTMLElement|Function|Popup, options?: Popup options): this
9829	// Binds a popup to the layer with the passed `content` and sets up the
9830	// necessary event listeners. If a `Function` is passed it will receive
9831	// the layer as the first argument and should return a `String` or `HTMLElement`.
9832	bindPopup: function (content, options) {
9833
9834		if (content instanceof Popup) {
9835			setOptions(content, options);
9836			this._popup = content;
9837			content._source = this;
9838		} else {
9839			if (!this._popup || options) {
9840				this._popup = new Popup(options, this);
9841			}
9842			this._popup.setContent(content);
9843		}
9844
9845		if (!this._popupHandlersAdded) {
9846			this.on({
9847				click: this._openPopup,
9848				keypress: this._onKeyPress,
9849				remove: this.closePopup,
9850				move: this._movePopup
9851			});
9852			this._popupHandlersAdded = true;
9853		}
9854
9855		return this;
9856	},
9857
9858	// @method unbindPopup(): this
9859	// Removes the popup previously bound with `bindPopup`.
9860	unbindPopup: function () {
9861		if (this._popup) {
9862			this.off({
9863				click: this._openPopup,
9864				keypress: this._onKeyPress,
9865				remove: this.closePopup,
9866				move: this._movePopup
9867			});
9868			this._popupHandlersAdded = false;
9869			this._popup = null;
9870		}
9871		return this;
9872	},
9873
9874	// @method openPopup(latlng?: LatLng): this
9875	// Opens the bound popup at the specified `latlng` or at the default popup anchor if no `latlng` is passed.
9876	openPopup: function (layer, latlng) {
9877		if (!(layer instanceof Layer)) {
9878			latlng = layer;
9879			layer = this;
9880		}
9881
9882		if (layer instanceof FeatureGroup) {
9883			for (var id in this._layers) {
9884				layer = this._layers[id];
9885				break;
9886			}
9887		}
9888
9889		if (!latlng) {
9890			latlng = layer.getCenter ? layer.getCenter() : layer.getLatLng();
9891		}
9892
9893		if (this._popup && this._map) {
9894			// set popup source to this layer
9895			this._popup._source = layer;
9896
9897			// update the popup (content, layout, ect...)
9898			this._popup.update();
9899
9900			// open the popup on the map
9901			this._map.openPopup(this._popup, latlng);
9902		}
9903
9904		return this;
9905	},
9906
9907	// @method closePopup(): this
9908	// Closes the popup bound to this layer if it is open.
9909	closePopup: function () {
9910		if (this._popup) {
9911			this._popup._close();
9912		}
9913		return this;
9914	},
9915
9916	// @method togglePopup(): this
9917	// Opens or closes the popup bound to this layer depending on its current state.
9918	togglePopup: function (target) {
9919		if (this._popup) {
9920			if (this._popup._map) {
9921				this.closePopup();
9922			} else {
9923				this.openPopup(target);
9924			}
9925		}
9926		return this;
9927	},
9928
9929	// @method isPopupOpen(): boolean
9930	// Returns `true` if the popup bound to this layer is currently open.
9931	isPopupOpen: function () {
9932		return (this._popup ? this._popup.isOpen() : false);
9933	},
9934
9935	// @method setPopupContent(content: String|HTMLElement|Popup): this
9936	// Sets the content of the popup bound to this layer.
9937	setPopupContent: function (content) {
9938		if (this._popup) {
9939			this._popup.setContent(content);
9940		}
9941		return this;
9942	},
9943
9944	// @method getPopup(): Popup
9945	// Returns the popup bound to this layer.
9946	getPopup: function () {
9947		return this._popup;
9948	},
9949
9950	_openPopup: function (e) {
9951		var layer = e.layer || e.target;
9952
9953		if (!this._popup) {
9954			return;
9955		}
9956
9957		if (!this._map) {
9958			return;
9959		}
9960
9961		// prevent map click
9962		stop(e);
9963
9964		// if this inherits from Path its a vector and we can just
9965		// open the popup at the new location
9966		if (layer instanceof Path) {
9967			this.openPopup(e.layer || e.target, e.latlng);
9968			return;
9969		}
9970
9971		// otherwise treat it like a marker and figure out
9972		// if we should toggle it open/closed
9973		if (this._map.hasLayer(this._popup) && this._popup._source === layer) {
9974			this.closePopup();
9975		} else {
9976			this.openPopup(layer, e.latlng);
9977		}
9978	},
9979
9980	_movePopup: function (e) {
9981		this._popup.setLatLng(e.latlng);
9982	},
9983
9984	_onKeyPress: function (e) {
9985		if (e.originalEvent.keyCode === 13) {
9986			this._openPopup(e);
9987		}
9988	}
9989});
9990
9991/*
9992 * @class Tooltip
9993 * @inherits DivOverlay
9994 * @aka L.Tooltip
9995 * Used to display small texts on top of map layers.
9996 *
9997 * @example
9998 *
9999 * ```js
10000 * marker.bindTooltip("my tooltip text").openTooltip();
10001 * ```
10002 * Note about tooltip offset. Leaflet takes two options in consideration
10003 * for computing tooltip offsetting:
10004 * - the `offset` Tooltip option: it defaults to [0, 0], and it's specific to one tooltip.
10005 *   Add a positive x offset to move the tooltip to the right, and a positive y offset to
10006 *   move it to the bottom. Negatives will move to the left and top.
10007 * - the `tooltipAnchor` Icon option: this will only be considered for Marker. You
10008 *   should adapt this value if you use a custom icon.
10009 */
10010
10011
10012// @namespace Tooltip
10013var Tooltip = DivOverlay.extend({
10014
10015	// @section
10016	// @aka Tooltip options
10017	options: {
10018		// @option pane: String = 'tooltipPane'
10019		// `Map pane` where the tooltip will be added.
10020		pane: 'tooltipPane',
10021
10022		// @option offset: Point = Point(0, 0)
10023		// Optional offset of the tooltip position.
10024		offset: [0, 0],
10025
10026		// @option direction: String = 'auto'
10027		// Direction where to open the tooltip. Possible values are: `right`, `left`,
10028		// `top`, `bottom`, `center`, `auto`.
10029		// `auto` will dynamically switch between `right` and `left` according to the tooltip
10030		// position on the map.
10031		direction: 'auto',
10032
10033		// @option permanent: Boolean = false
10034		// Whether to open the tooltip permanently or only on mouseover.
10035		permanent: false,
10036
10037		// @option sticky: Boolean = false
10038		// If true, the tooltip will follow the mouse instead of being fixed at the feature center.
10039		sticky: false,
10040
10041		// @option interactive: Boolean = false
10042		// If true, the tooltip will listen to the feature events.
10043		interactive: false,
10044
10045		// @option opacity: Number = 0.9
10046		// Tooltip container opacity.
10047		opacity: 0.9
10048	},
10049
10050	onAdd: function (map) {
10051		DivOverlay.prototype.onAdd.call(this, map);
10052		this.setOpacity(this.options.opacity);
10053
10054		// @namespace Map
10055		// @section Tooltip events
10056		// @event tooltipopen: TooltipEvent
10057		// Fired when a tooltip is opened in the map.
10058		map.fire('tooltipopen', {tooltip: this});
10059
10060		if (this._source) {
10061			// @namespace Layer
10062			// @section Tooltip events
10063			// @event tooltipopen: TooltipEvent
10064			// Fired when a tooltip bound to this layer is opened.
10065			this._source.fire('tooltipopen', {tooltip: this}, true);
10066		}
10067	},
10068
10069	onRemove: function (map) {
10070		DivOverlay.prototype.onRemove.call(this, map);
10071
10072		// @namespace Map
10073		// @section Tooltip events
10074		// @event tooltipclose: TooltipEvent
10075		// Fired when a tooltip in the map is closed.
10076		map.fire('tooltipclose', {tooltip: this});
10077
10078		if (this._source) {
10079			// @namespace Layer
10080			// @section Tooltip events
10081			// @event tooltipclose: TooltipEvent
10082			// Fired when a tooltip bound to this layer is closed.
10083			this._source.fire('tooltipclose', {tooltip: this}, true);
10084		}
10085	},
10086
10087	getEvents: function () {
10088		var events = DivOverlay.prototype.getEvents.call(this);
10089
10090		if (touch && !this.options.permanent) {
10091			events.preclick = this._close;
10092		}
10093
10094		return events;
10095	},
10096
10097	_close: function () {
10098		if (this._map) {
10099			this._map.closeTooltip(this);
10100		}
10101	},
10102
10103	_initLayout: function () {
10104		var prefix = 'leaflet-tooltip',
10105		    className = prefix + ' ' + (this.options.className || '') + ' leaflet-zoom-' + (this._zoomAnimated ? 'animated' : 'hide');
10106
10107		this._contentNode = this._container = create$1('div', className);
10108	},
10109
10110	_updateLayout: function () {},
10111
10112	_adjustPan: function () {},
10113
10114	_setPosition: function (pos) {
10115		var map = this._map,
10116		    container = this._container,
10117		    centerPoint = map.latLngToContainerPoint(map.getCenter()),
10118		    tooltipPoint = map.layerPointToContainerPoint(pos),
10119		    direction = this.options.direction,
10120		    tooltipWidth = container.offsetWidth,
10121		    tooltipHeight = container.offsetHeight,
10122		    offset = toPoint(this.options.offset),
10123		    anchor = this._getAnchor();
10124
10125		if (direction === 'top') {
10126			pos = pos.add(toPoint(-tooltipWidth / 2 + offset.x, -tooltipHeight + offset.y + anchor.y, true));
10127		} else if (direction === 'bottom') {
10128			pos = pos.subtract(toPoint(tooltipWidth / 2 - offset.x, -offset.y, true));
10129		} else if (direction === 'center') {
10130			pos = pos.subtract(toPoint(tooltipWidth / 2 + offset.x, tooltipHeight / 2 - anchor.y + offset.y, true));
10131		} else if (direction === 'right' || direction === 'auto' && tooltipPoint.x < centerPoint.x) {
10132			direction = 'right';
10133			pos = pos.add(toPoint(offset.x + anchor.x, anchor.y - tooltipHeight / 2 + offset.y, true));
10134		} else {
10135			direction = 'left';
10136			pos = pos.subtract(toPoint(tooltipWidth + anchor.x - offset.x, tooltipHeight / 2 - anchor.y - offset.y, true));
10137		}
10138
10139		removeClass(container, 'leaflet-tooltip-right');
10140		removeClass(container, 'leaflet-tooltip-left');
10141		removeClass(container, 'leaflet-tooltip-top');
10142		removeClass(container, 'leaflet-tooltip-bottom');
10143		addClass(container, 'leaflet-tooltip-' + direction);
10144		setPosition(container, pos);
10145	},
10146
10147	_updatePosition: function () {
10148		var pos = this._map.latLngToLayerPoint(this._latlng);
10149		this._setPosition(pos);
10150	},
10151
10152	setOpacity: function (opacity) {
10153		this.options.opacity = opacity;
10154
10155		if (this._container) {
10156			setOpacity(this._container, opacity);
10157		}
10158	},
10159
10160	_animateZoom: function (e) {
10161		var pos = this._map._latLngToNewLayerPoint(this._latlng, e.zoom, e.center);
10162		this._setPosition(pos);
10163	},
10164
10165	_getAnchor: function () {
10166		// Where should we anchor the tooltip on the source layer?
10167		return toPoint(this._source && this._source._getTooltipAnchor && !this.options.sticky ? this._source._getTooltipAnchor() : [0, 0]);
10168	}
10169
10170});
10171
10172// @namespace Tooltip
10173// @factory L.tooltip(options?: Tooltip options, source?: Layer)
10174// 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.
10175var tooltip = function (options, source) {
10176	return new Tooltip(options, source);
10177};
10178
10179// @namespace Map
10180// @section Methods for Layers and Controls
10181Map.include({
10182
10183	// @method openTooltip(tooltip: Tooltip): this
10184	// Opens the specified tooltip.
10185	// @alternative
10186	// @method openTooltip(content: String|HTMLElement, latlng: LatLng, options?: Tooltip options): this
10187	// Creates a tooltip with the specified content and options and open it.
10188	openTooltip: function (tooltip, latlng, options) {
10189		if (!(tooltip instanceof Tooltip)) {
10190			tooltip = new Tooltip(options).setContent(tooltip);
10191		}
10192
10193		if (latlng) {
10194			tooltip.setLatLng(latlng);
10195		}
10196
10197		if (this.hasLayer(tooltip)) {
10198			return this;
10199		}
10200
10201		return this.addLayer(tooltip);
10202	},
10203
10204	// @method closeTooltip(tooltip?: Tooltip): this
10205	// Closes the tooltip given as parameter.
10206	closeTooltip: function (tooltip) {
10207		if (tooltip) {
10208			this.removeLayer(tooltip);
10209		}
10210		return this;
10211	}
10212
10213});
10214
10215/*
10216 * @namespace Layer
10217 * @section Tooltip methods example
10218 *
10219 * All layers share a set of methods convenient for binding tooltips to it.
10220 *
10221 * ```js
10222 * var layer = L.Polygon(latlngs).bindTooltip('Hi There!').addTo(map);
10223 * layer.openTooltip();
10224 * layer.closeTooltip();
10225 * ```
10226 */
10227
10228// @section Tooltip methods
10229Layer.include({
10230
10231	// @method bindTooltip(content: String|HTMLElement|Function|Tooltip, options?: Tooltip options): this
10232	// Binds a tooltip to the layer with the passed `content` and sets up the
10233	// necessary event listeners. If a `Function` is passed it will receive
10234	// the layer as the first argument and should return a `String` or `HTMLElement`.
10235	bindTooltip: function (content, options) {
10236
10237		if (content instanceof Tooltip) {
10238			setOptions(content, options);
10239			this._tooltip = content;
10240			content._source = this;
10241		} else {
10242			if (!this._tooltip || options) {
10243				this._tooltip = new Tooltip(options, this);
10244			}
10245			this._tooltip.setContent(content);
10246
10247		}
10248
10249		this._initTooltipInteractions();
10250
10251		if (this._tooltip.options.permanent && this._map && this._map.hasLayer(this)) {
10252			this.openTooltip();
10253		}
10254
10255		return this;
10256	},
10257
10258	// @method unbindTooltip(): this
10259	// Removes the tooltip previously bound with `bindTooltip`.
10260	unbindTooltip: function () {
10261		if (this._tooltip) {
10262			this._initTooltipInteractions(true);
10263			this.closeTooltip();
10264			this._tooltip = null;
10265		}
10266		return this;
10267	},
10268
10269	_initTooltipInteractions: function (remove$$1) {
10270		if (!remove$$1 && this._tooltipHandlersAdded) { return; }
10271		var onOff = remove$$1 ? 'off' : 'on',
10272		    events = {
10273			remove: this.closeTooltip,
10274			move: this._moveTooltip
10275		    };
10276		if (!this._tooltip.options.permanent) {
10277			events.mouseover = this._openTooltip;
10278			events.mouseout = this.closeTooltip;
10279			if (this._tooltip.options.sticky) {
10280				events.mousemove = this._moveTooltip;
10281			}
10282			if (touch) {
10283				events.click = this._openTooltip;
10284			}
10285		} else {
10286			events.add = this._openTooltip;
10287		}
10288		this[onOff](events);
10289		this._tooltipHandlersAdded = !remove$$1;
10290	},
10291
10292	// @method openTooltip(latlng?: LatLng): this
10293	// Opens the bound tooltip at the specified `latlng` or at the default tooltip anchor if no `latlng` is passed.
10294	openTooltip: function (layer, latlng) {
10295		if (!(layer instanceof Layer)) {
10296			latlng = layer;
10297			layer = this;
10298		}
10299
10300		if (layer instanceof FeatureGroup) {
10301			for (var id in this._layers) {
10302				layer = this._layers[id];
10303				break;
10304			}
10305		}
10306
10307		if (!latlng) {
10308			latlng = layer.getCenter ? layer.getCenter() : layer.getLatLng();
10309		}
10310
10311		if (this._tooltip && this._map) {
10312
10313			// set tooltip source to this layer
10314			this._tooltip._source = layer;
10315
10316			// update the tooltip (content, layout, ect...)
10317			this._tooltip.update();
10318
10319			// open the tooltip on the map
10320			this._map.openTooltip(this._tooltip, latlng);
10321
10322			// Tooltip container may not be defined if not permanent and never
10323			// opened.
10324			if (this._tooltip.options.interactive && this._tooltip._container) {
10325				addClass(this._tooltip._container, 'leaflet-clickable');
10326				this.addInteractiveTarget(this._tooltip._container);
10327			}
10328		}
10329
10330		return this;
10331	},
10332
10333	// @method closeTooltip(): this
10334	// Closes the tooltip bound to this layer if it is open.
10335	closeTooltip: function () {
10336		if (this._tooltip) {
10337			this._tooltip._close();
10338			if (this._tooltip.options.interactive && this._tooltip._container) {
10339				removeClass(this._tooltip._container, 'leaflet-clickable');
10340				this.removeInteractiveTarget(this._tooltip._container);
10341			}
10342		}
10343		return this;
10344	},
10345
10346	// @method toggleTooltip(): this
10347	// Opens or closes the tooltip bound to this layer depending on its current state.
10348	toggleTooltip: function (target) {
10349		if (this._tooltip) {
10350			if (this._tooltip._map) {
10351				this.closeTooltip();
10352			} else {
10353				this.openTooltip(target);
10354			}
10355		}
10356		return this;
10357	},
10358
10359	// @method isTooltipOpen(): boolean
10360	// Returns `true` if the tooltip bound to this layer is currently open.
10361	isTooltipOpen: function () {
10362		return this._tooltip.isOpen();
10363	},
10364
10365	// @method setTooltipContent(content: String|HTMLElement|Tooltip): this
10366	// Sets the content of the tooltip bound to this layer.
10367	setTooltipContent: function (content) {
10368		if (this._tooltip) {
10369			this._tooltip.setContent(content);
10370		}
10371		return this;
10372	},
10373
10374	// @method getTooltip(): Tooltip
10375	// Returns the tooltip bound to this layer.
10376	getTooltip: function () {
10377		return this._tooltip;
10378	},
10379
10380	_openTooltip: function (e) {
10381		var layer = e.layer || e.target;
10382
10383		if (!this._tooltip || !this._map) {
10384			return;
10385		}
10386		this.openTooltip(layer, this._tooltip.options.sticky ? e.latlng : undefined);
10387	},
10388
10389	_moveTooltip: function (e) {
10390		var latlng = e.latlng, containerPoint, layerPoint;
10391		if (this._tooltip.options.sticky && e.originalEvent) {
10392			containerPoint = this._map.mouseEventToContainerPoint(e.originalEvent);
10393			layerPoint = this._map.containerPointToLayerPoint(containerPoint);
10394			latlng = this._map.layerPointToLatLng(layerPoint);
10395		}
10396		this._tooltip.setLatLng(latlng);
10397	}
10398});
10399
10400/*
10401 * @class DivIcon
10402 * @aka L.DivIcon
10403 * @inherits Icon
10404 *
10405 * Represents a lightweight icon for markers that uses a simple `<div>`
10406 * element instead of an image. Inherits from `Icon` but ignores the `iconUrl` and shadow options.
10407 *
10408 * @example
10409 * ```js
10410 * var myIcon = L.divIcon({className: 'my-div-icon'});
10411 * // you can set .my-div-icon styles in CSS
10412 *
10413 * L.marker([50.505, 30.57], {icon: myIcon}).addTo(map);
10414 * ```
10415 *
10416 * By default, it has a 'leaflet-div-icon' CSS class and is styled as a little white square with a shadow.
10417 */
10418
10419var DivIcon = Icon.extend({
10420	options: {
10421		// @section
10422		// @aka DivIcon options
10423		iconSize: [12, 12], // also can be set through CSS
10424
10425		// iconAnchor: (Point),
10426		// popupAnchor: (Point),
10427
10428		// @option html: String = ''
10429		// Custom HTML code to put inside the div element, empty by default.
10430		html: false,
10431
10432		// @option bgPos: Point = [0, 0]
10433		// Optional relative position of the background, in pixels
10434		bgPos: null,
10435
10436		className: 'leaflet-div-icon'
10437	},
10438
10439	createIcon: function (oldIcon) {
10440		var div = (oldIcon && oldIcon.tagName === 'DIV') ? oldIcon : document.createElement('div'),
10441		    options = this.options;
10442
10443		div.innerHTML = options.html !== false ? options.html : '';
10444
10445		if (options.bgPos) {
10446			var bgPos = toPoint(options.bgPos);
10447			div.style.backgroundPosition = (-bgPos.x) + 'px ' + (-bgPos.y) + 'px';
10448		}
10449		this._setIconStyles(div, 'icon');
10450
10451		return div;
10452	},
10453
10454	createShadow: function () {
10455		return null;
10456	}
10457});
10458
10459// @factory L.divIcon(options: DivIcon options)
10460// Creates a `DivIcon` instance with the given options.
10461function divIcon(options) {
10462	return new DivIcon(options);
10463}
10464
10465Icon.Default = IconDefault;
10466
10467/*
10468 * @class GridLayer
10469 * @inherits Layer
10470 * @aka L.GridLayer
10471 *
10472 * Generic class for handling a tiled grid of HTML elements. This is the base class for all tile layers and replaces `TileLayer.Canvas`.
10473 * 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.
10474 *
10475 *
10476 * @section Synchronous usage
10477 * @example
10478 *
10479 * 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.
10480 *
10481 * ```js
10482 * var CanvasLayer = L.GridLayer.extend({
10483 *     createTile: function(coords){
10484 *         // create a <canvas> element for drawing
10485 *         var tile = L.DomUtil.create('canvas', 'leaflet-tile');
10486 *
10487 *         // setup tile width and height according to the options
10488 *         var size = this.getTileSize();
10489 *         tile.width = size.x;
10490 *         tile.height = size.y;
10491 *
10492 *         // get a canvas context and draw something on it using coords.x, coords.y and coords.z
10493 *         var ctx = tile.getContext('2d');
10494 *
10495 *         // return the tile so it can be rendered on screen
10496 *         return tile;
10497 *     }
10498 * });
10499 * ```
10500 *
10501 * @section Asynchronous usage
10502 * @example
10503 *
10504 * Tile creation can also be asynchronous, this is useful when using a third-party drawing library. Once the tile is finished drawing it can be passed to the `done()` callback.
10505 *
10506 * ```js
10507 * var CanvasLayer = L.GridLayer.extend({
10508 *     createTile: function(coords, done){
10509 *         var error;
10510 *
10511 *         // create a <canvas> element for drawing
10512 *         var tile = L.DomUtil.create('canvas', 'leaflet-tile');
10513 *
10514 *         // setup tile width and height according to the options
10515 *         var size = this.getTileSize();
10516 *         tile.width = size.x;
10517 *         tile.height = size.y;
10518 *
10519 *         // draw something asynchronously and pass the tile to the done() callback
10520 *         setTimeout(function() {
10521 *             done(error, tile);
10522 *         }, 1000);
10523 *
10524 *         return tile;
10525 *     }
10526 * });
10527 * ```
10528 *
10529 * @section
10530 */
10531
10532
10533var GridLayer = Layer.extend({
10534
10535	// @section
10536	// @aka GridLayer options
10537	options: {
vendor: 44,535 bytes, lines 10538-12167
10538		// @option tileSize: Number|Point = 256
10539		// Width and height of tiles in the grid. Use a number if width and height are equal, or `L.point(width, height)` otherwise.
10540		tileSize: 256,
10541
10542		// @option opacity: Number = 1.0
10543		// Opacity of the tiles. Can be used in the `createTile()` function.
10544		opacity: 1,
10545
10546		// @option updateWhenIdle: Boolean = (depends)
10547		// Load new tiles only when panning ends.
10548		// `true` by default on mobile browsers, in order to avoid too many requests and keep smooth navigation.
10549		// `false` otherwise in order to display new tiles _during_ panning, since it is easy to pan outside the
10550		// [`keepBuffer`](#gridlayer-keepbuffer) option in desktop browsers.
10551		updateWhenIdle: mobile,
10552
10553		// @option updateWhenZooming: Boolean = true
10554		// 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.
10555		updateWhenZooming: true,
10556
10557		// @option updateInterval: Number = 200
10558		// Tiles will not update more than once every `updateInterval` milliseconds when panning.
10559		updateInterval: 200,
10560
10561		// @option zIndex: Number = 1
10562		// The explicit zIndex of the tile layer.
10563		zIndex: 1,
10564
10565		// @option bounds: LatLngBounds = undefined
10566		// If set, tiles will only be loaded inside the set `LatLngBounds`.
10567		bounds: null,
10568
10569		// @option minZoom: Number = 0
10570		// The minimum zoom level down to which this layer will be displayed (inclusive).
10571		minZoom: 0,
10572
10573		// @option maxZoom: Number = undefined
10574		// The maximum zoom level up to which this layer will be displayed (inclusive).
10575		maxZoom: undefined,
10576
10577		// @option maxNativeZoom: Number = undefined
10578		// Maximum zoom number the tile source has available. If it is specified,
10579		// the tiles on all zoom levels higher than `maxNativeZoom` will be loaded
10580		// from `maxNativeZoom` level and auto-scaled.
10581		maxNativeZoom: undefined,
10582
10583		// @option minNativeZoom: Number = undefined
10584		// Minimum zoom number the tile source has available. If it is specified,
10585		// the tiles on all zoom levels lower than `minNativeZoom` will be loaded
10586		// from `minNativeZoom` level and auto-scaled.
10587		minNativeZoom: undefined,
10588
10589		// @option noWrap: Boolean = false
10590		// Whether the layer is wrapped around the antimeridian. If `true`, the
10591		// GridLayer will only be displayed once at low zoom levels. Has no
10592		// effect when the [map CRS](#map-crs) doesn't wrap around. Can be used
10593		// in combination with [`bounds`](#gridlayer-bounds) to prevent requesting
10594		// tiles outside the CRS limits.
10595		noWrap: false,
10596
10597		// @option pane: String = 'tilePane'
10598		// `Map pane` where the grid layer will be added.
10599		pane: 'tilePane',
10600
10601		// @option className: String = ''
10602		// A custom class name to assign to the tile layer. Empty by default.
10603		className: '',
10604
10605		// @option keepBuffer: Number = 2
10606		// When panning the map, keep this many rows and columns of tiles before unloading them.
10607		keepBuffer: 2
10608	},
10609
10610	initialize: function (options) {
10611		setOptions(this, options);
10612	},
10613
10614	onAdd: function () {
10615		this._initContainer();
10616
10617		this._levels = {};
10618		this._tiles = {};
10619
10620		this._resetView();
10621		this._update();
10622	},
10623
10624	beforeAdd: function (map) {
10625		map._addZoomLimit(this);
10626	},
10627
10628	onRemove: function (map) {
10629		this._removeAllTiles();
10630		remove(this._container);
10631		map._removeZoomLimit(this);
10632		this._container = null;
10633		this._tileZoom = undefined;
10634	},
10635
10636	// @method bringToFront: this
10637	// Brings the tile layer to the top of all tile layers.
10638	bringToFront: function () {
10639		if (this._map) {
10640			toFront(this._container);
10641			this._setAutoZIndex(Math.max);
10642		}
10643		return this;
10644	},
10645
10646	// @method bringToBack: this
10647	// Brings the tile layer to the bottom of all tile layers.
10648	bringToBack: function () {
10649		if (this._map) {
10650			toBack(this._container);
10651			this._setAutoZIndex(Math.min);
10652		}
10653		return this;
10654	},
10655
10656	// @method getContainer: HTMLElement
10657	// Returns the HTML element that contains the tiles for this layer.
10658	getContainer: function () {
10659		return this._container;
10660	},
10661
10662	// @method setOpacity(opacity: Number): this
10663	// Changes the [opacity](#gridlayer-opacity) of the grid layer.
10664	setOpacity: function (opacity) {
10665		this.options.opacity = opacity;
10666		this._updateOpacity();
10667		return this;
10668	},
10669
10670	// @method setZIndex(zIndex: Number): this
10671	// Changes the [zIndex](#gridlayer-zindex) of the grid layer.
10672	setZIndex: function (zIndex) {
10673		this.options.zIndex = zIndex;
10674		this._updateZIndex();
10675
10676		return this;
10677	},
10678
10679	// @method isLoading: Boolean
10680	// Returns `true` if any tile in the grid layer has not finished loading.
10681	isLoading: function () {
10682		return this._loading;
10683	},
10684
10685	// @method redraw: this
10686	// Causes the layer to clear all the tiles and request them again.
10687	redraw: function () {
10688		if (this._map) {
10689			this._removeAllTiles();
10690			this._update();
10691		}
10692		return this;
10693	},
10694
10695	getEvents: function () {
10696		var events = {
10697			viewprereset: this._invalidateAll,
10698			viewreset: this._resetView,
10699			zoom: this._resetView,
10700			moveend: this._onMoveEnd
10701		};
10702
10703		if (!this.options.updateWhenIdle) {
10704			// update tiles on move, but not more often than once per given interval
10705			if (!this._onMove) {
10706				this._onMove = throttle(this._onMoveEnd, this.options.updateInterval, this);
10707			}
10708
10709			events.move = this._onMove;
10710		}
10711
10712		if (this._zoomAnimated) {
10713			events.zoomanim = this._animateZoom;
10714		}
10715
10716		return events;
10717	},
10718
10719	// @section Extension methods
10720	// Layers extending `GridLayer` shall reimplement the following method.
10721	// @method createTile(coords: Object, done?: Function): HTMLElement
10722	// Called only internally, must be overridden by classes extending `GridLayer`.
10723	// Returns the `HTMLElement` corresponding to the given `coords`. If the `done` callback
10724	// is specified, it must be called when the tile has finished loading and drawing.
10725	createTile: function () {
10726		return document.createElement('div');
10727	},
10728
10729	// @section
10730	// @method getTileSize: Point
10731	// Normalizes the [tileSize option](#gridlayer-tilesize) into a point. Used by the `createTile()` method.
10732	getTileSize: function () {
10733		var s = this.options.tileSize;
10734		return s instanceof Point ? s : new Point(s, s);
10735	},
10736
10737	_updateZIndex: function () {
10738		if (this._container && this.options.zIndex !== undefined && this.options.zIndex !== null) {
10739			this._container.style.zIndex = this.options.zIndex;
10740		}
10741	},
10742
10743	_setAutoZIndex: function (compare) {
10744		// go through all other layers of the same pane, set zIndex to max + 1 (front) or min - 1 (back)
10745
10746		var layers = this.getPane().children,
10747		    edgeZIndex = -compare(-Infinity, Infinity); // -Infinity for max, Infinity for min
10748
10749		for (var i = 0, len = layers.length, zIndex; i < len; i++) {
10750
10751			zIndex = layers[i].style.zIndex;
10752
10753			if (layers[i] !== this._container && zIndex) {
10754				edgeZIndex = compare(edgeZIndex, +zIndex);
10755			}
10756		}
10757
10758		if (isFinite(edgeZIndex)) {
10759			this.options.zIndex = edgeZIndex + compare(-1, 1);
10760			this._updateZIndex();
10761		}
10762	},
10763
10764	_updateOpacity: function () {
10765		if (!this._map) { return; }
10766
10767		// IE doesn't inherit filter opacity properly, so we're forced to set it on tiles
10768		if (ielt9) { return; }
10769
10770		setOpacity(this._container, this.options.opacity);
10771
10772		var now = +new Date(),
10773		    nextFrame = false,
10774		    willPrune = false;
10775
10776		for (var key in this._tiles) {
10777			var tile = this._tiles[key];
10778			if (!tile.current || !tile.loaded) { continue; }
10779
10780			var fade = Math.min(1, (now - tile.loaded) / 200);
10781
10782			setOpacity(tile.el, fade);
10783			if (fade < 1) {
10784				nextFrame = true;
10785			} else {
10786				if (tile.active) {
10787					willPrune = true;
10788				} else {
10789					this._onOpaqueTile(tile);
10790				}
10791				tile.active = true;
10792			}
10793		}
10794
10795		if (willPrune && !this._noPrune) { this._pruneTiles(); }
10796
10797		if (nextFrame) {
10798			cancelAnimFrame(this._fadeFrame);
10799			this._fadeFrame = requestAnimFrame(this._updateOpacity, this);
10800		}
10801	},
10802
10803	_onOpaqueTile: falseFn,
10804
10805	_initContainer: function () {
10806		if (this._container) { return; }
10807
10808		this._container = create$1('div', 'leaflet-layer ' + (this.options.className || ''));
10809		this._updateZIndex();
10810
10811		if (this.options.opacity < 1) {
10812			this._updateOpacity();
10813		}
10814
10815		this.getPane().appendChild(this._container);
10816	},
10817
10818	_updateLevels: function () {
10819
10820		var zoom = this._tileZoom,
10821		    maxZoom = this.options.maxZoom;
10822
10823		if (zoom === undefined) { return undefined; }
10824
10825		for (var z in this._levels) {
10826			if (this._levels[z].el.children.length || z === zoom) {
10827				this._levels[z].el.style.zIndex = maxZoom - Math.abs(zoom - z);
10828				this._onUpdateLevel(z);
10829			} else {
10830				remove(this._levels[z].el);
10831				this._removeTilesAtZoom(z);
10832				this._onRemoveLevel(z);
10833				delete this._levels[z];
10834			}
10835		}
10836
10837		var level = this._levels[zoom],
10838		    map = this._map;
10839
10840		if (!level) {
10841			level = this._levels[zoom] = {};
10842
10843			level.el = create$1('div', 'leaflet-tile-container leaflet-zoom-animated', this._container);
10844			level.el.style.zIndex = maxZoom;
10845
10846			level.origin = map.project(map.unproject(map.getPixelOrigin()), zoom).round();
10847			level.zoom = zoom;
10848
10849			this._setZoomTransform(level, map.getCenter(), map.getZoom());
10850
10851			// force the browser to consider the newly added element for transition
10852			falseFn(level.el.offsetWidth);
10853
10854			this._onCreateLevel(level);
10855		}
10856
10857		this._level = level;
10858
10859		return level;
10860	},
10861
10862	_onUpdateLevel: falseFn,
10863
10864	_onRemoveLevel: falseFn,
10865
10866	_onCreateLevel: falseFn,
10867
10868	_pruneTiles: function () {
10869		if (!this._map) {
10870			return;
10871		}
10872
10873		var key, tile;
10874
10875		var zoom = this._map.getZoom();
10876		if (zoom > this.options.maxZoom ||
10877			zoom < this.options.minZoom) {
10878			this._removeAllTiles();
10879			return;
10880		}
10881
10882		for (key in this._tiles) {
10883			tile = this._tiles[key];
10884			tile.retain = tile.current;
10885		}
10886
10887		for (key in this._tiles) {
10888			tile = this._tiles[key];
10889			if (tile.current && !tile.active) {
10890				var coords = tile.coords;
10891				if (!this._retainParent(coords.x, coords.y, coords.z, coords.z - 5)) {
10892					this._retainChildren(coords.x, coords.y, coords.z, coords.z + 2);
10893				}
10894			}
10895		}
10896
10897		for (key in this._tiles) {
10898			if (!this._tiles[key].retain) {
10899				this._removeTile(key);
10900			}
10901		}
10902	},
10903
10904	_removeTilesAtZoom: function (zoom) {
10905		for (var key in this._tiles) {
10906			if (this._tiles[key].coords.z !== zoom) {
10907				continue;
10908			}
10909			this._removeTile(key);
10910		}
10911	},
10912
10913	_removeAllTiles: function () {
10914		for (var key in this._tiles) {
10915			this._removeTile(key);
10916		}
10917	},
10918
10919	_invalidateAll: function () {
10920		for (var z in this._levels) {
10921			remove(this._levels[z].el);
10922			this._onRemoveLevel(z);
10923			delete this._levels[z];
10924		}
10925		this._removeAllTiles();
10926
10927		this._tileZoom = undefined;
10928	},
10929
10930	_retainParent: function (x, y, z, minZoom) {
10931		var x2 = Math.floor(x / 2),
10932		    y2 = Math.floor(y / 2),
10933		    z2 = z - 1,
10934		    coords2 = new Point(+x2, +y2);
10935		coords2.z = +z2;
10936
10937		var key = this._tileCoordsToKey(coords2),
10938		    tile = this._tiles[key];
10939
10940		if (tile && tile.active) {
10941			tile.retain = true;
10942			return true;
10943
10944		} else if (tile && tile.loaded) {
10945			tile.retain = true;
10946		}
10947
10948		if (z2 > minZoom) {
10949			return this._retainParent(x2, y2, z2, minZoom);
10950		}
10951
10952		return false;
10953	},
10954
10955	_retainChildren: function (x, y, z, maxZoom) {
10956
10957		for (var i = 2 * x; i < 2 * x + 2; i++) {
10958			for (var j = 2 * y; j < 2 * y + 2; j++) {
10959
10960				var coords = new Point(i, j);
10961				coords.z = z + 1;
10962
10963				var key = this._tileCoordsToKey(coords),
10964				    tile = this._tiles[key];
10965
10966				if (tile && tile.active) {
10967					tile.retain = true;
10968					continue;
10969
10970				} else if (tile && tile.loaded) {
10971					tile.retain = true;
10972				}
10973
10974				if (z + 1 < maxZoom) {
10975					this._retainChildren(i, j, z + 1, maxZoom);
10976				}
10977			}
10978		}
10979	},
10980
10981	_resetView: function (e) {
10982		var animating = e && (e.pinch || e.flyTo);
10983		this._setView(this._map.getCenter(), this._map.getZoom(), animating, animating);
10984	},
10985
10986	_animateZoom: function (e) {
10987		this._setView(e.center, e.zoom, true, e.noUpdate);
10988	},
10989
10990	_clampZoom: function (zoom) {
10991		var options = this.options;
10992
10993		if (undefined !== options.minNativeZoom && zoom < options.minNativeZoom) {
10994			return options.minNativeZoom;
10995		}
10996
10997		if (undefined !== options.maxNativeZoom && options.maxNativeZoom < zoom) {
10998			return options.maxNativeZoom;
10999		}
11000
11001		return zoom;
11002	},
11003
11004	_setView: function (center, zoom, noPrune, noUpdate) {
11005		var tileZoom = this._clampZoom(Math.round(zoom));
11006		if ((this.options.maxZoom !== undefined && tileZoom > this.options.maxZoom) ||
11007		    (this.options.minZoom !== undefined && tileZoom < this.options.minZoom)) {
11008			tileZoom = undefined;
11009		}
11010
11011		var tileZoomChanged = this.options.updateWhenZooming && (tileZoom !== this._tileZoom);
11012
11013		if (!noUpdate || tileZoomChanged) {
11014
11015			this._tileZoom = tileZoom;
11016
11017			if (this._abortLoading) {
11018				this._abortLoading();
11019			}
11020
11021			this._updateLevels();
11022			this._resetGrid();
11023
11024			if (tileZoom !== undefined) {
11025				this._update(center);
11026			}
11027
11028			if (!noPrune) {
11029				this._pruneTiles();
11030			}
11031
11032			// Flag to prevent _updateOpacity from pruning tiles during
11033			// a zoom anim or a pinch gesture
11034			this._noPrune = !!noPrune;
11035		}
11036
11037		this._setZoomTransforms(center, zoom);
11038	},
11039
11040	_setZoomTransforms: function (center, zoom) {
11041		for (var i in this._levels) {
11042			this._setZoomTransform(this._levels[i], center, zoom);
11043		}
11044	},
11045
11046	_setZoomTransform: function (level, center, zoom) {
11047		var scale = this._map.getZoomScale(zoom, level.zoom),
11048		    translate = level.origin.multiplyBy(scale)
11049		        .subtract(this._map._getNewPixelOrigin(center, zoom)).round();
11050
11051		if (any3d) {
11052			setTransform(level.el, translate, scale);
11053		} else {
11054			setPosition(level.el, translate);
11055		}
11056	},
11057
11058	_resetGrid: function () {
11059		var map = this._map,
11060		    crs = map.options.crs,
11061		    tileSize = this._tileSize = this.getTileSize(),
11062		    tileZoom = this._tileZoom;
11063
11064		var bounds = this._map.getPixelWorldBounds(this._tileZoom);
11065		if (bounds) {
11066			this._globalTileRange = this._pxBoundsToTileRange(bounds);
11067		}
11068
11069		this._wrapX = crs.wrapLng && !this.options.noWrap && [
11070			Math.floor(map.project([0, crs.wrapLng[0]], tileZoom).x / tileSize.x),
11071			Math.ceil(map.project([0, crs.wrapLng[1]], tileZoom).x / tileSize.y)
11072		];
11073		this._wrapY = crs.wrapLat && !this.options.noWrap && [
11074			Math.floor(map.project([crs.wrapLat[0], 0], tileZoom).y / tileSize.x),
11075			Math.ceil(map.project([crs.wrapLat[1], 0], tileZoom).y / tileSize.y)
11076		];
11077	},
11078
11079	_onMoveEnd: function () {
11080		if (!this._map || this._map._animatingZoom) { return; }
11081
11082		this._update();
11083	},
11084
11085	_getTiledPixelBounds: function (center) {
11086		var map = this._map,
11087		    mapZoom = map._animatingZoom ? Math.max(map._animateToZoom, map.getZoom()) : map.getZoom(),
11088		    scale = map.getZoomScale(mapZoom, this._tileZoom),
11089		    pixelCenter = map.project(center, this._tileZoom).floor(),
11090		    halfSize = map.getSize().divideBy(scale * 2);
11091
11092		return new Bounds(pixelCenter.subtract(halfSize), pixelCenter.add(halfSize));
11093	},
11094
11095	// Private method to load tiles in the grid's active zoom level according to map bounds
11096	_update: function (center) {
11097		var map = this._map;
11098		if (!map) { return; }
11099		var zoom = this._clampZoom(map.getZoom());
11100
11101		if (center === undefined) { center = map.getCenter(); }
11102		if (this._tileZoom === undefined) { return; }	// if out of minzoom/maxzoom
11103
11104		var pixelBounds = this._getTiledPixelBounds(center),
11105		    tileRange = this._pxBoundsToTileRange(pixelBounds),
11106		    tileCenter = tileRange.getCenter(),
11107		    queue = [],
11108		    margin = this.options.keepBuffer,
11109		    noPruneRange = new Bounds(tileRange.getBottomLeft().subtract([margin, -margin]),
11110		                              tileRange.getTopRight().add([margin, -margin]));
11111
11112		// Sanity check: panic if the tile range contains Infinity somewhere.
11113		if (!(isFinite(tileRange.min.x) &&
11114		      isFinite(tileRange.min.y) &&
11115		      isFinite(tileRange.max.x) &&
11116		      isFinite(tileRange.max.y))) { throw new Error('Attempted to load an infinite number of tiles'); }
11117
11118		for (var key in this._tiles) {
11119			var c = this._tiles[key].coords;
11120			if (c.z !== this._tileZoom || !noPruneRange.contains(new Point(c.x, c.y))) {
11121				this._tiles[key].current = false;
11122			}
11123		}
11124
11125		// _update just loads more tiles. If the tile zoom level differs too much
11126		// from the map's, let _setView reset levels and prune old tiles.
11127		if (Math.abs(zoom - this._tileZoom) > 1) { this._setView(center, zoom); return; }
11128
11129		// create a queue of coordinates to load tiles from
11130		for (var j = tileRange.min.y; j <= tileRange.max.y; j++) {
11131			for (var i = tileRange.min.x; i <= tileRange.max.x; i++) {
11132				var coords = new Point(i, j);
11133				coords.z = this._tileZoom;
11134
11135				if (!this._isValidTile(coords)) { continue; }
11136
11137				var tile = this._tiles[this._tileCoordsToKey(coords)];
11138				if (tile) {
11139					tile.current = true;
11140				} else {
11141					queue.push(coords);
11142				}
11143			}
11144		}
11145
11146		// sort tile queue to load tiles in order of their distance to center
11147		queue.sort(function (a, b) {
11148			return a.distanceTo(tileCenter) - b.distanceTo(tileCenter);
11149		});
11150
11151		if (queue.length !== 0) {
11152			// if it's the first batch of tiles to load
11153			if (!this._loading) {
11154				this._loading = true;
11155				// @event loading: Event
11156				// Fired when the grid layer starts loading tiles.
11157				this.fire('loading');
11158			}
11159
11160			// create DOM fragment to append tiles in one batch
11161			var fragment = document.createDocumentFragment();
11162
11163			for (i = 0; i < queue.length; i++) {
11164				this._addTile(queue[i], fragment);
11165			}
11166
11167			this._level.el.appendChild(fragment);
11168		}
11169	},
11170
11171	_isValidTile: function (coords) {
11172		var crs = this._map.options.crs;
11173
11174		if (!crs.infinite) {
11175			// don't load tile if it's out of bounds and not wrapped
11176			var bounds = this._globalTileRange;
11177			if ((!crs.wrapLng && (coords.x < bounds.min.x || coords.x > bounds.max.x)) ||
11178			    (!crs.wrapLat && (coords.y < bounds.min.y || coords.y > bounds.max.y))) { return false; }
11179		}
11180
11181		if (!this.options.bounds) { return true; }
11182
11183		// don't load tile if it doesn't intersect the bounds in options
11184		var tileBounds = this._tileCoordsToBounds(coords);
11185		return toLatLngBounds(this.options.bounds).overlaps(tileBounds);
11186	},
11187
11188	_keyToBounds: function (key) {
11189		return this._tileCoordsToBounds(this._keyToTileCoords(key));
11190	},
11191
11192	_tileCoordsToNwSe: function (coords) {
11193		var map = this._map,
11194		    tileSize = this.getTileSize(),
11195		    nwPoint = coords.scaleBy(tileSize),
11196		    sePoint = nwPoint.add(tileSize),
11197		    nw = map.unproject(nwPoint, coords.z),
11198		    se = map.unproject(sePoint, coords.z);
11199		return [nw, se];
11200	},
11201
11202	// converts tile coordinates to its geographical bounds
11203	_tileCoordsToBounds: function (coords) {
11204		var bp = this._tileCoordsToNwSe(coords),
11205		    bounds = new LatLngBounds(bp[0], bp[1]);
11206
11207		if (!this.options.noWrap) {
11208			bounds = this._map.wrapLatLngBounds(bounds);
11209		}
11210		return bounds;
11211	},
11212	// converts tile coordinates to key for the tile cache
11213	_tileCoordsToKey: function (coords) {
11214		return coords.x + ':' + coords.y + ':' + coords.z;
11215	},
11216
11217	// converts tile cache key to coordinates
11218	_keyToTileCoords: function (key) {
11219		var k = key.split(':'),
11220		    coords = new Point(+k[0], +k[1]);
11221		coords.z = +k[2];
11222		return coords;
11223	},
11224
11225	_removeTile: function (key) {
11226		var tile = this._tiles[key];
11227		if (!tile) { return; }
11228
11229		remove(tile.el);
11230
11231		delete this._tiles[key];
11232
11233		// @event tileunload: TileEvent
11234		// Fired when a tile is removed (e.g. when a tile goes off the screen).
11235		this.fire('tileunload', {
11236			tile: tile.el,
11237			coords: this._keyToTileCoords(key)
11238		});
11239	},
11240
11241	_initTile: function (tile) {
11242		addClass(tile, 'leaflet-tile');
11243
11244		var tileSize = this.getTileSize();
11245		tile.style.width = tileSize.x + 'px';
11246		tile.style.height = tileSize.y + 'px';
11247
11248		tile.onselectstart = falseFn;
11249		tile.onmousemove = falseFn;
11250
11251		// update opacity on tiles in IE7-8 because of filter inheritance problems
11252		if (ielt9 && this.options.opacity < 1) {
11253			setOpacity(tile, this.options.opacity);
11254		}
11255
11256		// without this hack, tiles disappear after zoom on Chrome for Android
11257		// https://github.com/Leaflet/Leaflet/issues/2078
11258		if (android && !android23) {
11259			tile.style.WebkitBackfaceVisibility = 'hidden';
11260		}
11261	},
11262
11263	_addTile: function (coords, container) {
11264		var tilePos = this._getTilePos(coords),
11265		    key = this._tileCoordsToKey(coords);
11266
11267		var tile = this.createTile(this._wrapCoords(coords), bind(this._tileReady, this, coords));
11268
11269		this._initTile(tile);
11270
11271		// if createTile is defined with a second argument ("done" callback),
11272		// we know that tile is async and will be ready later; otherwise
11273		if (this.createTile.length < 2) {
11274			// mark tile as ready, but delay one frame for opacity animation to happen
11275			requestAnimFrame(bind(this._tileReady, this, coords, null, tile));
11276		}
11277
11278		setPosition(tile, tilePos);
11279
11280		// save tile in cache
11281		this._tiles[key] = {
11282			el: tile,
11283			coords: coords,
11284			current: true
11285		};
11286
11287		container.appendChild(tile);
11288		// @event tileloadstart: TileEvent
11289		// Fired when a tile is requested and starts loading.
11290		this.fire('tileloadstart', {
11291			tile: tile,
11292			coords: coords
11293		});
11294	},
11295
11296	_tileReady: function (coords, err, tile) {
11297		if (err) {
11298			// @event tileerror: TileErrorEvent
11299			// Fired when there is an error loading a tile.
11300			this.fire('tileerror', {
11301				error: err,
11302				tile: tile,
11303				coords: coords
11304			});
11305		}
11306
11307		var key = this._tileCoordsToKey(coords);
11308
11309		tile = this._tiles[key];
11310		if (!tile) { return; }
11311
11312		tile.loaded = +new Date();
11313		if (this._map._fadeAnimated) {
11314			setOpacity(tile.el, 0);
11315			cancelAnimFrame(this._fadeFrame);
11316			this._fadeFrame = requestAnimFrame(this._updateOpacity, this);
11317		} else {
11318			tile.active = true;
11319			this._pruneTiles();
11320		}
11321
11322		if (!err) {
11323			addClass(tile.el, 'leaflet-tile-loaded');
11324
11325			// @event tileload: TileEvent
11326			// Fired when a tile loads.
11327			this.fire('tileload', {
11328				tile: tile.el,
11329				coords: coords
11330			});
11331		}
11332
11333		if (this._noTilesToLoad()) {
11334			this._loading = false;
11335			// @event load: Event
11336			// Fired when the grid layer loaded all visible tiles.
11337			this.fire('load');
11338
11339			if (ielt9 || !this._map._fadeAnimated) {
11340				requestAnimFrame(this._pruneTiles, this);
11341			} else {
11342				// Wait a bit more than 0.2 secs (the duration of the tile fade-in)
11343				// to trigger a pruning.
11344				setTimeout(bind(this._pruneTiles, this), 250);
11345			}
11346		}
11347	},
11348
11349	_getTilePos: function (coords) {
11350		return coords.scaleBy(this.getTileSize()).subtract(this._level.origin);
11351	},
11352
11353	_wrapCoords: function (coords) {
11354		var newCoords = new Point(
11355			this._wrapX ? wrapNum(coords.x, this._wrapX) : coords.x,
11356			this._wrapY ? wrapNum(coords.y, this._wrapY) : coords.y);
11357		newCoords.z = coords.z;
11358		return newCoords;
11359	},
11360
11361	_pxBoundsToTileRange: function (bounds) {
11362		var tileSize = this.getTileSize();
11363		return new Bounds(
11364			bounds.min.unscaleBy(tileSize).floor(),
11365			bounds.max.unscaleBy(tileSize).ceil().subtract([1, 1]));
11366	},
11367
11368	_noTilesToLoad: function () {
11369		for (var key in this._tiles) {
11370			if (!this._tiles[key].loaded) { return false; }
11371		}
11372		return true;
11373	}
11374});
11375
11376// @factory L.gridLayer(options?: GridLayer options)
11377// Creates a new instance of GridLayer with the supplied options.
11378function gridLayer(options) {
11379	return new GridLayer(options);
11380}
11381
11382/*
11383 * @class TileLayer
11384 * @inherits GridLayer
11385 * @aka L.TileLayer
11386 * Used to load and display tile layers on the map. Extends `GridLayer`.
11387 *
11388 * @example
11389 *
11390 * ```js
11391 * L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png?{foo}', {foo: 'bar'}).addTo(map);
11392 * ```
11393 *
11394 * @section URL template
11395 * @example
11396 *
11397 * A string of the following form:
11398 *
11399 * ```
11400 * 'http://{s}.somedomain.com/blabla/{z}/{x}/{y}{r}.png'
11401 * ```
11402 *
11403 * `{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.
11404 *
11405 * You can use custom keys in the template, which will be [evaluated](#util-template) from TileLayer options, like this:
11406 *
11407 * ```
11408 * L.tileLayer('http://{s}.somedomain.com/{foo}/{z}/{x}/{y}.png', {foo: 'bar'});
11409 * ```
11410 */
11411
11412
11413var TileLayer = GridLayer.extend({
11414
11415	// @section
11416	// @aka TileLayer options
11417	options: {
11418		// @option minZoom: Number = 0
11419		// The minimum zoom level down to which this layer will be displayed (inclusive).
11420		minZoom: 0,
11421
11422		// @option maxZoom: Number = 18
11423		// The maximum zoom level up to which this layer will be displayed (inclusive).
11424		maxZoom: 18,
11425
11426		// @option subdomains: String|String[] = 'abc'
11427		// Subdomains of the tile service. Can be passed in the form of one string (where each letter is a subdomain name) or an array of strings.
11428		subdomains: 'abc',
11429
11430		// @option errorTileUrl: String = ''
11431		// URL to the tile image to show in place of the tile that failed to load.
11432		errorTileUrl: '',
11433
11434		// @option zoomOffset: Number = 0
11435		// The zoom number used in tile URLs will be offset with this value.
11436		zoomOffset: 0,
11437
11438		// @option tms: Boolean = false
11439		// If `true`, inverses Y axis numbering for tiles (turn this on for [TMS](https://en.wikipedia.org/wiki/Tile_Map_Service) services).
11440		tms: false,
11441
11442		// @option zoomReverse: Boolean = false
11443		// If set to true, the zoom number used in tile URLs will be reversed (`maxZoom - zoom` instead of `zoom`)
11444		zoomReverse: false,
11445
11446		// @option detectRetina: Boolean = false
11447		// 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.
11448		detectRetina: false,
11449
11450		// @option crossOrigin: Boolean|String = false
11451		// Whether the crossOrigin attribute will be added to the tiles.
11452		// 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.
11453		// Refer to [CORS Settings](https://developer.mozilla.org/en-US/docs/Web/HTML/CORS_settings_attributes) for valid String values.
11454		crossOrigin: false
11455	},
11456
11457	initialize: function (url, options) {
11458
11459		this._url = url;
11460
11461		options = setOptions(this, options);
11462
11463		// detecting retina displays, adjusting tileSize and zoom levels
11464		if (options.detectRetina && retina && options.maxZoom > 0) {
11465
11466			options.tileSize = Math.floor(options.tileSize / 2);
11467
11468			if (!options.zoomReverse) {
11469				options.zoomOffset++;
11470				options.maxZoom--;
11471			} else {
11472				options.zoomOffset--;
11473				options.minZoom++;
11474			}
11475
11476			options.minZoom = Math.max(0, options.minZoom);
11477		}
11478
11479		if (typeof options.subdomains === 'string') {
11480			options.subdomains = options.subdomains.split('');
11481		}
11482
11483		// for https://github.com/Leaflet/Leaflet/issues/137
11484		if (!android) {
11485			this.on('tileunload', this._onTileRemove);
11486		}
11487	},
11488
11489	// @method setUrl(url: String, noRedraw?: Boolean): this
11490	// Updates the layer's URL template and redraws it (unless `noRedraw` is set to `true`).
11491	setUrl: function (url, noRedraw) {
11492		this._url = url;
11493
11494		if (!noRedraw) {
11495			this.redraw();
11496		}
11497		return this;
11498	},
11499
11500	// @method createTile(coords: Object, done?: Function): HTMLElement
11501	// Called only internally, overrides GridLayer's [`createTile()`](#gridlayer-createtile)
11502	// to return an `<img>` HTML element with the appropriate image URL given `coords`. The `done`
11503	// callback is called when the tile has been loaded.
11504	createTile: function (coords, done) {
11505		var tile = document.createElement('img');
11506
11507		on(tile, 'load', bind(this._tileOnLoad, this, done, tile));
11508		on(tile, 'error', bind(this._tileOnError, this, done, tile));
11509
11510		if (this.options.crossOrigin || this.options.crossOrigin === '') {
11511			tile.crossOrigin = this.options.crossOrigin === true ? '' : this.options.crossOrigin;
11512		}
11513
11514		/*
11515		 Alt tag is set to empty string to keep screen readers from reading URL and for compliance reasons
11516		 http://www.w3.org/TR/WCAG20-TECHS/H67
11517		*/
11518		tile.alt = '';
11519
11520		/*
11521		 Set role="presentation" to force screen readers to ignore this
11522		 https://www.w3.org/TR/wai-aria/roles#textalternativecomputation
11523		*/
11524		tile.setAttribute('role', 'presentation');
11525
11526		tile.src = this.getTileUrl(coords);
11527
11528		return tile;
11529	},
11530
11531	// @section Extension methods
11532	// @uninheritable
11533	// Layers extending `TileLayer` might reimplement the following method.
11534	// @method getTileUrl(coords: Object): String
11535	// Called only internally, returns the URL for a tile given its coordinates.
11536	// Classes extending `TileLayer` can override this function to provide custom tile URL naming schemes.
11537	getTileUrl: function (coords) {
11538		var data = {
11539			r: retina ? '@2x' : '',
11540			s: this._getSubdomain(coords),
11541			x: coords.x,
11542			y: coords.y,
11543			z: this._getZoomForUrl()
11544		};
11545		if (this._map && !this._map.options.crs.infinite) {
11546			var invertedY = this._globalTileRange.max.y - coords.y;
11547			if (this.options.tms) {
11548				data['y'] = invertedY;
11549			}
11550			data['-y'] = invertedY;
11551		}
11552
11553		return template(this._url, extend(data, this.options));
11554	},
11555
11556	_tileOnLoad: function (done, tile) {
11557		// For https://github.com/Leaflet/Leaflet/issues/3332
11558		if (ielt9) {
11559			setTimeout(bind(done, this, null, tile), 0);
11560		} else {
11561			done(null, tile);
11562		}
11563	},
11564
11565	_tileOnError: function (done, tile, e) {
11566		var errorUrl = this.options.errorTileUrl;
11567		if (errorUrl && tile.getAttribute('src') !== errorUrl) {
11568			tile.src = errorUrl;
11569		}
11570		done(e, tile);
11571	},
11572
11573	_onTileRemove: function (e) {
11574		e.tile.onload = null;
11575	},
11576
11577	_getZoomForUrl: function () {
11578		var zoom = this._tileZoom,
11579		maxZoom = this.options.maxZoom,
11580		zoomReverse = this.options.zoomReverse,
11581		zoomOffset = this.options.zoomOffset;
11582
11583		if (zoomReverse) {
11584			zoom = maxZoom - zoom;
11585		}
11586
11587		return zoom + zoomOffset;
11588	},
11589
11590	_getSubdomain: function (tilePoint) {
11591		var index = Math.abs(tilePoint.x + tilePoint.y) % this.options.subdomains.length;
11592		return this.options.subdomains[index];
11593	},
11594
11595	// stops loading all tiles in the background layer
11596	_abortLoading: function () {
11597		var i, tile;
11598		for (i in this._tiles) {
11599			if (this._tiles[i].coords.z !== this._tileZoom) {
11600				tile = this._tiles[i].el;
11601
11602				tile.onload = falseFn;
11603				tile.onerror = falseFn;
11604
11605				if (!tile.complete) {
11606					tile.src = emptyImageUrl;
11607					remove(tile);
11608					delete this._tiles[i];
11609				}
11610			}
11611		}
11612	},
11613
11614	_removeTile: function (key) {
11615		var tile = this._tiles[key];
11616		if (!tile) { return; }
11617
11618		// Cancels any pending http requests associated with the tile
11619		// unless we're on Android's stock browser,
11620		// see https://github.com/Leaflet/Leaflet/issues/137
11621		if (!androidStock) {
11622			tile.el.setAttribute('src', emptyImageUrl);
11623		}
11624
11625		return GridLayer.prototype._removeTile.call(this, key);
11626	},
11627
11628	_tileReady: function (coords, err, tile) {
11629		if (!this._map || (tile && tile.getAttribute('src') === emptyImageUrl)) {
11630			return;
11631		}
11632
11633		return GridLayer.prototype._tileReady.call(this, coords, err, tile);
11634	}
11635});
11636
11637
11638// @factory L.tilelayer(urlTemplate: String, options?: TileLayer options)
11639// Instantiates a tile layer object given a `URL template` and optionally an options object.
11640
11641function tileLayer(url, options) {
11642	return new TileLayer(url, options);
11643}
11644
11645/*
11646 * @class TileLayer.WMS
11647 * @inherits TileLayer
11648 * @aka L.TileLayer.WMS
11649 * Used to display [WMS](https://en.wikipedia.org/wiki/Web_Map_Service) services as tile layers on the map. Extends `TileLayer`.
11650 *
11651 * @example
11652 *
11653 * ```js
11654 * var nexrad = L.tileLayer.wms("http://mesonet.agron.iastate.edu/cgi-bin/wms/nexrad/n0r.cgi", {
11655 * 	layers: 'nexrad-n0r-900913',
11656 * 	format: 'image/png',
11657 * 	transparent: true,
11658 * 	attribution: "Weather data © 2012 IEM Nexrad"
11659 * });
11660 * ```
11661 */
11662
11663var TileLayerWMS = TileLayer.extend({
11664
11665	// @section
11666	// @aka TileLayer.WMS options
11667	// If any custom options not documented here are used, they will be sent to the
11668	// WMS server as extra parameters in each request URL. This can be useful for
11669	// [non-standard vendor WMS parameters](http://docs.geoserver.org/stable/en/user/services/wms/vendor.html).
11670	defaultWmsParams: {
11671		service: 'WMS',
11672		request: 'GetMap',
11673
11674		// @option layers: String = ''
11675		// **(required)** Comma-separated list of WMS layers to show.
11676		layers: '',
11677
11678		// @option styles: String = ''
11679		// Comma-separated list of WMS styles.
11680		styles: '',
11681
11682		// @option format: String = 'image/jpeg'
11683		// WMS image format (use `'image/png'` for layers with transparency).
11684		format: 'image/jpeg',
11685
11686		// @option transparent: Boolean = false
11687		// If `true`, the WMS service will return images with transparency.
11688		transparent: false,
11689
11690		// @option version: String = '1.1.1'
11691		// Version of the WMS service to use
11692		version: '1.1.1'
11693	},
11694
11695	options: {
11696		// @option crs: CRS = null
11697		// Coordinate Reference System to use for the WMS requests, defaults to
11698		// map CRS. Don't change this if you're not sure what it means.
11699		crs: null,
11700
11701		// @option uppercase: Boolean = false
11702		// If `true`, WMS request parameter keys will be uppercase.
11703		uppercase: false
11704	},
11705
11706	initialize: function (url, options) {
11707
11708		this._url = url;
11709
11710		var wmsParams = extend({}, this.defaultWmsParams);
11711
11712		// all keys that are not TileLayer options go to WMS params
11713		for (var i in options) {
11714			if (!(i in this.options)) {
11715				wmsParams[i] = options[i];
11716			}
11717		}
11718
11719		options = setOptions(this, options);
11720
11721		var realRetina = options.detectRetina && retina ? 2 : 1;
11722		var tileSize = this.getTileSize();
11723		wmsParams.width = tileSize.x * realRetina;
11724		wmsParams.height = tileSize.y * realRetina;
11725
11726		this.wmsParams = wmsParams;
11727	},
11728
11729	onAdd: function (map) {
11730
11731		this._crs = this.options.crs || map.options.crs;
11732		this._wmsVersion = parseFloat(this.wmsParams.version);
11733
11734		var projectionKey = this._wmsVersion >= 1.3 ? 'crs' : 'srs';
11735		this.wmsParams[projectionKey] = this._crs.code;
11736
11737		TileLayer.prototype.onAdd.call(this, map);
11738	},
11739
11740	getTileUrl: function (coords) {
11741
11742		var tileBounds = this._tileCoordsToNwSe(coords),
11743		    crs = this._crs,
11744		    bounds = toBounds(crs.project(tileBounds[0]), crs.project(tileBounds[1])),
11745		    min = bounds.min,
11746		    max = bounds.max,
11747		    bbox = (this._wmsVersion >= 1.3 && this._crs === EPSG4326 ?
11748		    [min.y, min.x, max.y, max.x] :
11749		    [min.x, min.y, max.x, max.y]).join(','),
11750		    url = TileLayer.prototype.getTileUrl.call(this, coords);
11751		return url +
11752			getParamString(this.wmsParams, url, this.options.uppercase) +
11753			(this.options.uppercase ? '&BBOX=' : '&bbox=') + bbox;
11754	},
11755
11756	// @method setParams(params: Object, noRedraw?: Boolean): this
11757	// Merges an object with the new parameters and re-requests tiles on the current screen (unless `noRedraw` was set to true).
11758	setParams: function (params, noRedraw) {
11759
11760		extend(this.wmsParams, params);
11761
11762		if (!noRedraw) {
11763			this.redraw();
11764		}
11765
11766		return this;
11767	}
11768});
11769
11770
11771// @factory L.tileLayer.wms(baseUrl: String, options: TileLayer.WMS options)
11772// Instantiates a WMS tile layer object given a base URL of the WMS service and a WMS parameters/options object.
11773function tileLayerWMS(url, options) {
11774	return new TileLayerWMS(url, options);
11775}
11776
11777TileLayer.WMS = TileLayerWMS;
11778tileLayer.wms = tileLayerWMS;
11779
11780/*
11781 * @class Renderer
11782 * @inherits Layer
11783 * @aka L.Renderer
11784 *
11785 * Base class for vector renderer implementations (`SVG`, `Canvas`). Handles the
11786 * DOM container of the renderer, its bounds, and its zoom animation.
11787 *
11788 * A `Renderer` works as an implicit layer group for all `Path`s - the renderer
11789 * itself can be added or removed to the map. All paths use a renderer, which can
11790 * be implicit (the map will decide the type of renderer and use it automatically)
11791 * or explicit (using the [`renderer`](#path-renderer) option of the path).
11792 *
11793 * Do not use this class directly, use `SVG` and `Canvas` instead.
11794 *
11795 * @event update: Event
11796 * Fired when the renderer updates its bounds, center and zoom, for example when
11797 * its map has moved
11798 */
11799
11800var Renderer = Layer.extend({
11801
11802	// @section
11803	// @aka Renderer options
11804	options: {
11805		// @option padding: Number = 0.1
11806		// How much to extend the clip area around the map view (relative to its size)
11807		// e.g. 0.1 would be 10% of map view in each direction
11808		padding: 0.1,
11809
11810		// @option tolerance: Number = 0
11811		// How much to extend click tolerance round a path/object on the map
11812		tolerance : 0
11813	},
11814
11815	initialize: function (options) {
11816		setOptions(this, options);
11817		stamp(this);
11818		this._layers = this._layers || {};
11819	},
11820
11821	onAdd: function () {
11822		if (!this._container) {
11823			this._initContainer(); // defined by renderer implementations
11824
11825			if (this._zoomAnimated) {
11826				addClass(this._container, 'leaflet-zoom-animated');
11827			}
11828		}
11829
11830		this.getPane().appendChild(this._container);
11831		this._update();
11832		this.on('update', this._updatePaths, this);
11833	},
11834
11835	onRemove: function () {
11836		this.off('update', this._updatePaths, this);
11837		this._destroyContainer();
11838	},
11839
11840	getEvents: function () {
11841		var events = {
11842			viewreset: this._reset,
11843			zoom: this._onZoom,
11844			moveend: this._update,
11845			zoomend: this._onZoomEnd
11846		};
11847		if (this._zoomAnimated) {
11848			events.zoomanim = this._onAnimZoom;
11849		}
11850		return events;
11851	},
11852
11853	_onAnimZoom: function (ev) {
11854		this._updateTransform(ev.center, ev.zoom);
11855	},
11856
11857	_onZoom: function () {
11858		this._updateTransform(this._map.getCenter(), this._map.getZoom());
11859	},
11860
11861	_updateTransform: function (center, zoom) {
11862		var scale = this._map.getZoomScale(zoom, this._zoom),
11863		    position = getPosition(this._container),
11864		    viewHalf = this._map.getSize().multiplyBy(0.5 + this.options.padding),
11865		    currentCenterPoint = this._map.project(this._center, zoom),
11866		    destCenterPoint = this._map.project(center, zoom),
11867		    centerOffset = destCenterPoint.subtract(currentCenterPoint),
11868
11869		    topLeftOffset = viewHalf.multiplyBy(-scale).add(position).add(viewHalf).subtract(centerOffset);
11870
11871		if (any3d) {
11872			setTransform(this._container, topLeftOffset, scale);
11873		} else {
11874			setPosition(this._container, topLeftOffset);
11875		}
11876	},
11877
11878	_reset: function () {
11879		this._update();
11880		this._updateTransform(this._center, this._zoom);
11881
11882		for (var id in this._layers) {
11883			this._layers[id]._reset();
11884		}
11885	},
11886
11887	_onZoomEnd: function () {
11888		for (var id in this._layers) {
11889			this._layers[id]._project();
11890		}
11891	},
11892
11893	_updatePaths: function () {
11894		for (var id in this._layers) {
11895			this._layers[id]._update();
11896		}
11897	},
11898
11899	_update: function () {
11900		// Update pixel bounds of renderer container (for positioning/sizing/clipping later)
11901		// Subclasses are responsible of firing the 'update' event.
11902		var p = this.options.padding,
11903		    size = this._map.getSize(),
11904		    min = this._map.containerPointToLayerPoint(size.multiplyBy(-p)).round();
11905
11906		this._bounds = new Bounds(min, min.add(size.multiplyBy(1 + p * 2)).round());
11907
11908		this._center = this._map.getCenter();
11909		this._zoom = this._map.getZoom();
11910	}
11911});
11912
11913/*
11914 * @class Canvas
11915 * @inherits Renderer
11916 * @aka L.Canvas
11917 *
11918 * Allows vector layers to be displayed with [`<canvas>`](https://developer.mozilla.org/docs/Web/API/Canvas_API).
11919 * Inherits `Renderer`.
11920 *
11921 * Due to [technical limitations](http://caniuse.com/#search=canvas), Canvas is not
11922 * available in all web browsers, notably IE8, and overlapping geometries might
11923 * not display properly in some edge cases.
11924 *
11925 * @example
11926 *
11927 * Use Canvas by default for all paths in the map:
11928 *
11929 * ```js
11930 * var map = L.map('map', {
11931 * 	renderer: L.canvas()
11932 * });
11933 * ```
11934 *
11935 * Use a Canvas renderer with extra padding for specific vector geometries:
11936 *
11937 * ```js
11938 * var map = L.map('map');
11939 * var myRenderer = L.canvas({ padding: 0.5 });
11940 * var line = L.polyline( coordinates, { renderer: myRenderer } );
11941 * var circle = L.circle( center, { renderer: myRenderer } );
11942 * ```
11943 */
11944
11945var Canvas = Renderer.extend({
11946	getEvents: function () {
11947		var events = Renderer.prototype.getEvents.call(this);
11948		events.viewprereset = this._onViewPreReset;
11949		return events;
11950	},
11951
11952	_onViewPreReset: function () {
11953		// Set a flag so that a viewprereset+moveend+viewreset only updates&redraws once
11954		this._postponeUpdatePaths = true;
11955	},
11956
11957	onAdd: function () {
11958		Renderer.prototype.onAdd.call(this);
11959
11960		// Redraw vectors since canvas is cleared upon removal,
11961		// in case of removing the renderer itself from the map.
11962		this._draw();
11963	},
11964
11965	_initContainer: function () {
11966		var container = this._container = document.createElement('canvas');
11967
11968		on(container, 'mousemove', throttle(this._onMouseMove, 32, this), this);
11969		on(container, 'click dblclick mousedown mouseup contextmenu', this._onClick, this);
11970		on(container, 'mouseout', this._handleMouseOut, this);
11971
11972		this._ctx = container.getContext('2d');
11973	},
11974
11975	_destroyContainer: function () {
11976		cancelAnimFrame(this._redrawRequest);
11977		delete this._ctx;
11978		remove(this._container);
11979		off(this._container);
11980		delete this._container;
11981	},
11982
11983	_updatePaths: function () {
11984		if (this._postponeUpdatePaths) { return; }
11985
11986		var layer;
11987		this._redrawBounds = null;
11988		for (var id in this._layers) {
11989			layer = this._layers[id];
11990			layer._update();
11991		}
11992		this._redraw();
11993	},
11994
11995	_update: function () {
11996		if (this._map._animatingZoom && this._bounds) { return; }
11997
11998		this._drawnLayers = {};
11999
12000		Renderer.prototype._update.call(this);
12001
12002		var b = this._bounds,
12003		    container = this._container,
12004		    size = b.getSize(),
12005		    m = retina ? 2 : 1;
12006
12007		setPosition(container, b.min);
12008
12009		// set canvas size (also clearing it); use double size on retina
12010		container.width = m * size.x;
12011		container.height = m * size.y;
12012		container.style.width = size.x + 'px';
12013		container.style.height = size.y + 'px';
12014
12015		if (retina) {
12016			this._ctx.scale(2, 2);
12017		}
12018
12019		// translate so we use the same path coordinates after canvas element moves
12020		this._ctx.translate(-b.min.x, -b.min.y);
12021
12022		// Tell paths to redraw themselves
12023		this.fire('update');
12024	},
12025
12026	_reset: function () {
12027		Renderer.prototype._reset.call(this);
12028
12029		if (this._postponeUpdatePaths) {
12030			this._postponeUpdatePaths = false;
12031			this._updatePaths();
12032		}
12033	},
12034
12035	_initPath: function (layer) {
12036		this._updateDashArray(layer);
12037		this._layers[stamp(layer)] = layer;
12038
12039		var order = layer._order = {
12040			layer: layer,
12041			prev: this._drawLast,
12042			next: null
12043		};
12044		if (this._drawLast) { this._drawLast.next = order; }
12045		this._drawLast = order;
12046		this._drawFirst = this._drawFirst || this._drawLast;
12047	},
12048
12049	_addPath: function (layer) {
12050		this._requestRedraw(layer);
12051	},
12052
12053	_removePath: function (layer) {
12054		var order = layer._order;
12055		var next = order.next;
12056		var prev = order.prev;
12057
12058		if (next) {
12059			next.prev = prev;
12060		} else {
12061			this._drawLast = prev;
12062		}
12063		if (prev) {
12064			prev.next = next;
12065		} else {
12066			this._drawFirst = next;
12067		}
12068
12069		delete this._drawnLayers[layer._leaflet_id];
12070
12071		delete layer._order;
12072
12073		delete this._layers[stamp(layer)];
12074
12075		this._requestRedraw(layer);
12076	},
12077
12078	_updatePath: function (layer) {
12079		// Redraw the union of the layer's old pixel
12080		// bounds and the new pixel bounds.
12081		this._extendRedrawBounds(layer);
12082		layer._project();
12083		layer._update();
12084		// The redraw will extend the redraw bounds
12085		// with the new pixel bounds.
12086		this._requestRedraw(layer);
12087	},
12088
12089	_updateStyle: function (layer) {
12090		this._updateDashArray(layer);
12091		this._requestRedraw(layer);
12092	},
12093
12094	_updateDashArray: function (layer) {
12095		if (typeof layer.options.dashArray === 'string') {
12096			var parts = layer.options.dashArray.split(/[, ]+/),
12097			    dashArray = [],
12098			    i;
12099			for (i = 0; i < parts.length; i++) {
12100				dashArray.push(Number(parts[i]));
12101			}
12102			layer.options._dashArray = dashArray;
12103		} else {
12104			layer.options._dashArray = layer.options.dashArray;
12105		}
12106	},
12107
12108	_requestRedraw: function (layer) {
12109		if (!this._map) { return; }
12110
12111		this._extendRedrawBounds(layer);
12112		this._redrawRequest = this._redrawRequest || requestAnimFrame(this._redraw, this);
12113	},
12114
12115	_extendRedrawBounds: function (layer) {
12116		if (layer._pxBounds) {
12117			var padding = (layer.options.weight || 0) + 1;
12118			this._redrawBounds = this._redrawBounds || new Bounds();
12119			this._redrawBounds.extend(layer._pxBounds.min.subtract([padding, padding]));
12120			this._redrawBounds.extend(layer._pxBounds.max.add([padding, padding]));
12121		}
12122	},
12123
12124	_redraw: function () {
12125		this._redrawRequest = null;
12126
12127		if (this._redrawBounds) {
12128			this._redrawBounds.min._floor();
12129			this._redrawBounds.max._ceil();
12130		}
12131
12132		this._clear(); // clear layers in redraw bounds
12133		this._draw(); // draw layers
12134
12135		this._redrawBounds = null;
12136	},
12137
12138	_clear: function () {
12139		var bounds = this._redrawBounds;
12140		if (bounds) {
12141			var size = bounds.getSize();
12142			this._ctx.clearRect(bounds.min.x, bounds.min.y, size.x, size.y);
12143		} else {
12144			this._ctx.clearRect(0, 0, this._container.width, this._container.height);
12145		}
12146	},
12147
12148	_draw: function () {
12149		var layer, bounds = this._redrawBounds;
12150		this._ctx.save();
12151		if (bounds) {
12152			var size = bounds.getSize();
12153			this._ctx.beginPath();
12154			this._ctx.rect(bounds.min.x, bounds.min.y, size.x, size.y);
12155			this._ctx.clip();
12156		}
12157
12158		this._drawing = true;
12159
12160		for (var order = this._drawFirst; order; order = order.next) {
12161			layer = order.layer;
12162			if (!bounds || (layer._pxBounds && layer._pxBounds.intersects(bounds))) {
12163				layer._updatePath();
12164			}
12165		}
12166
12167		this._drawing = false;
12168
12169		this._ctx.restore();  // Restore state before clipping.
12170	},
12171
12172	_updatePoly: function (layer, closed) {
12173		if (!this._drawing) { return; }
12174
12175		var i, j, len2, p,
12176		    parts = layer._parts,
12177		    len = parts.length,
12178		    ctx = this._ctx;
12179
12180		if (!len) { return; }
12181
12182		this._drawnLayers[layer._leaflet_id] = layer;
12183
12184		ctx.beginPath();
12185
12186		for (i = 0; i < len; i++) {
12187			for (j = 0, len2 = parts[i].length; j < len2; j++) {
12188				p = parts[i][j];
12189				ctx[j ? 'lineTo' : 'moveTo'](p.x, p.y);
12190			}
12191			if (closed) {
12192				ctx.closePath();
12193			}
12194		}
12195
12196		this._fillStroke(ctx, layer);
12197
12198		// TODO optimization: 1 fill/stroke for all features with equal style instead of 1 for each feature
12199	},
12200
12201	_updateCircle: function (layer) {
12202
12203		if (!this._drawing || layer._empty()) { return; }
12204
12205		var p = layer._point,
12206		    ctx = this._ctx,
12207		    r = Math.max(Math.round(layer._radius), 1),
12208		    s = (Math.max(Math.round(layer._radiusY), 1) || r) / r;
12209
12210		this._drawnLayers[layer._leaflet_id] = layer;
12211
12212		if (s !== 1) {
12213			ctx.save();
12214			ctx.scale(1, s);
12215		}
12216
12217		ctx.beginPath();
12218		ctx.arc(p.x, p.y / s, r, 0, Math.PI * 2, false);
12219
12220		if (s !== 1) {
12221			ctx.restore();
12222		}
12223
12224		this._fillStroke(ctx, layer);
12225	},
12226
12227	_fillStroke: function (ctx, layer) {
12228		var options = layer.options;
12229
12230		if (options.fill) {
12231			ctx.globalAlpha = options.fillOpacity;
12232			ctx.fillStyle = options.fillColor || options.color;
12233			ctx.fill(options.fillRule || 'evenodd');
12234		}
12235
12236		if (options.stroke && options.weight !== 0) {
12237			if (ctx.setLineDash) {
12238				ctx.setLineDash(layer.options && layer.options._dashArray || []);
12239			}
12240			ctx.globalAlpha = options.opacity;
12241			ctx.lineWidth = options.weight;
12242			ctx.strokeStyle = options.color;
12243			ctx.lineCap = options.lineCap;
12244			ctx.lineJoin = options.lineJoin;
12245			ctx.stroke();
12246		}
12247	},
12248
12249	// Canvas obviously doesn't have mouse events for individual drawn objects,
12250	// so we emulate that by calculating what's under the mouse on mousemove/click manually
12251
12252	_onClick: function (e) {
12253		var point = this._map.mouseEventToLayerPoint(e), layer, clickedLayer;
12254
12255		for (var order = this._drawFirst; order; order = order.next) {
12256			layer = order.layer;
12257			if (layer.options.interactive && layer._containsPoint(point) && !this._map._draggableMoved(layer)) {
12258				clickedLayer = layer;
12259			}
12260		}
12261		if (clickedLayer)  {
12262			fakeStop(e);
12263			this._fireEvent([clickedLayer], e);
12264		}
12265	},
12266
12267	_onMouseMove: function (e) {
12268		if (!this._map || this._map.dragging.moving() || this._map._animatingZoom) { return; }
12269
12270		var point = this._map.mouseEventToLayerPoint(e);
12271		this._handleMouseHover(e, point);
12272	},
12273
12274
12275	_handleMouseOut: function (e) {
12276		var layer = this._hoveredLayer;
12277		if (layer) {
12278			// if we're leaving the layer, fire mouseout
12279			removeClass(this._container, 'leaflet-interactive');
12280			this._fireEvent([layer], e, 'mouseout');
12281			this._hoveredLayer = null;
12282		}
12283	},
12284
12285	_handleMouseHover: function (e, point) {
12286		var layer, candidateHoveredLayer;
12287
12288		for (var order = this._drawFirst; order; order = order.next) {
12289			layer = order.layer;
12290			if (layer.options.interactive && layer._containsPoint(point)) {
12291				candidateHoveredLayer = layer;
12292			}
12293		}
12294
12295		if (candidateHoveredLayer !== this._hoveredLayer) {
12296			this._handleMouseOut(e);
12297
12298			if (candidateHoveredLayer) {
12299				addClass(this._container, 'leaflet-interactive'); // change cursor
12300				this._fireEvent([candidateHoveredLayer], e, 'mouseover');
12301				this._hoveredLayer = candidateHoveredLayer;
12302			}
12303		}
12304
12305		if (this._hoveredLayer) {
12306			this._fireEvent([this._hoveredLayer], e);
12307		}
12308	},
12309
12310	_fireEvent: function (layers, e, type) {
12311		this._map._fireDOMEvent(e, type || e.type, layers);
12312	},
12313
12314	_bringToFront: function (layer) {
12315		var order = layer._order;
12316		var next = order.next;
12317		var prev = order.prev;
12318
12319		if (next) {
12320			next.prev = prev;
12321		} else {
12322			// Already last
12323			return;
12324		}
12325		if (prev) {
12326			prev.next = next;
12327		} else if (next) {
12328			// Update first entry unless this is the
12329			// single entry
12330			this._drawFirst = next;
12331		}
12332
12333		order.prev = this._drawLast;
12334		this._drawLast.next = order;
12335
12336		order.next = null;
12337		this._drawLast = order;
12338
12339		this._requestRedraw(layer);
12340	},
12341
12342	_bringToBack: function (layer) {
12343		var order = layer._order;
12344		var next = order.next;
12345		var prev = order.prev;
12346
12347		if (prev) {
12348			prev.next = next;
12349		} else {
12350			// Already first
12351			return;
12352		}
12353		if (next) {
12354			next.prev = prev;
12355		} else if (prev) {
12356			// Update last entry unless this is the
12357			// single entry
12358			this._drawLast = prev;
12359		}
12360
12361		order.prev = null;
12362
12363		order.next = this._drawFirst;
12364		this._drawFirst.prev = order;
12365		this._drawFirst = order;
12366
12367		this._requestRedraw(layer);
12368	}
12369});
12370
12371// @factory L.canvas(options?: Renderer options)
12372// Creates a Canvas renderer with the given options.
12373function canvas$1(options) {
12374	return canvas ? new Canvas(options) : null;
12375}
12376
12377/*
12378 * Thanks to Dmitry Baranovsky and his Raphael library for inspiration!
12379 */
12380
12381
12382var vmlCreate = (function () {
12383	try {
12384		document.namespaces.add('lvml', 'urn:schemas-microsoft-com:vml');
12385		return function (name) {
12386			return document.createElement('<lvml:' + name + ' class="lvml">');
12387		};
12388	} catch (e) {
12389		return function (name) {
12390			return document.createElement('<' + name + ' xmlns="urn:schemas-microsoft.com:vml" class="lvml">');
12391		};
12392	}
12393})();
12394
12395
12396/*
12397 * @class SVG
12398 *
12399 * Although SVG is not available on IE7 and IE8, these browsers support [VML](https://en.wikipedia.org/wiki/Vector_Markup_Language), and the SVG renderer will fall back to VML in this case.
12400 *
vendor: 27,130 bytes, lines 12401-13431
12401 * VML was deprecated in 2012, which means VML functionality exists only for backwards compatibility
12402 * with old versions of Internet Explorer.
12403 */
12404
12405// mixin to redefine some SVG methods to handle VML syntax which is similar but with some differences
12406var vmlMixin = {
12407
12408	_initContainer: function () {
12409		this._container = create$1('div', 'leaflet-vml-container');
12410	},
12411
12412	_update: function () {
12413		if (this._map._animatingZoom) { return; }
12414		Renderer.prototype._update.call(this);
12415		this.fire('update');
12416	},
12417
12418	_initPath: function (layer) {
12419		var container = layer._container = vmlCreate('shape');
12420
12421		addClass(container, 'leaflet-vml-shape ' + (this.options.className || ''));
12422
12423		container.coordsize = '1 1';
12424
12425		layer._path = vmlCreate('path');
12426		container.appendChild(layer._path);
12427
12428		this._updateStyle(layer);
12429		this._layers[stamp(layer)] = layer;
12430	},
12431
12432	_addPath: function (layer) {
12433		var container = layer._container;
12434		this._container.appendChild(container);
12435
12436		if (layer.options.interactive) {
12437			layer.addInteractiveTarget(container);
12438		}
12439	},
12440
12441	_removePath: function (layer) {
12442		var container = layer._container;
12443		remove(container);
12444		layer.removeInteractiveTarget(container);
12445		delete this._layers[stamp(layer)];
12446	},
12447
12448	_updateStyle: function (layer) {
12449		var stroke = layer._stroke,
12450		    fill = layer._fill,
12451		    options = layer.options,
12452		    container = layer._container;
12453
12454		container.stroked = !!options.stroke;
12455		container.filled = !!options.fill;
12456
12457		if (options.stroke) {
12458			if (!stroke) {
12459				stroke = layer._stroke = vmlCreate('stroke');
12460			}
12461			container.appendChild(stroke);
12462			stroke.weight = options.weight + 'px';
12463			stroke.color = options.color;
12464			stroke.opacity = options.opacity;
12465
12466			if (options.dashArray) {
12467				stroke.dashStyle = isArray(options.dashArray) ?
12468				    options.dashArray.join(' ') :
12469				    options.dashArray.replace(/( *, *)/g, ' ');
12470			} else {
12471				stroke.dashStyle = '';
12472			}
12473			stroke.endcap = options.lineCap.replace('butt', 'flat');
12474			stroke.joinstyle = options.lineJoin;
12475
12476		} else if (stroke) {
12477			container.removeChild(stroke);
12478			layer._stroke = null;
12479		}
12480
12481		if (options.fill) {
12482			if (!fill) {
12483				fill = layer._fill = vmlCreate('fill');
12484			}
12485			container.appendChild(fill);
12486			fill.color = options.fillColor || options.color;
12487			fill.opacity = options.fillOpacity;
12488
12489		} else if (fill) {
12490			container.removeChild(fill);
12491			layer._fill = null;
12492		}
12493	},
12494
12495	_updateCircle: function (layer) {
12496		var p = layer._point.round(),
12497		    r = Math.round(layer._radius),
12498		    r2 = Math.round(layer._radiusY || r);
12499
12500		this._setPath(layer, layer._empty() ? 'M0 0' :
12501			'AL ' + p.x + ',' + p.y + ' ' + r + ',' + r2 + ' 0,' + (65535 * 360));
12502	},
12503
12504	_setPath: function (layer, path) {
12505		layer._path.v = path;
12506	},
12507
12508	_bringToFront: function (layer) {
12509		toFront(layer._container);
12510	},
12511
12512	_bringToBack: function (layer) {
12513		toBack(layer._container);
12514	}
12515};
12516
12517var create$2 = vml ? vmlCreate : svgCreate;
12518
12519/*
12520 * @class SVG
12521 * @inherits Renderer
12522 * @aka L.SVG
12523 *
12524 * Allows vector layers to be displayed with [SVG](https://developer.mozilla.org/docs/Web/SVG).
12525 * Inherits `Renderer`.
12526 *
12527 * Due to [technical limitations](http://caniuse.com/#search=svg), SVG is not
12528 * available in all web browsers, notably Android 2.x and 3.x.
12529 *
12530 * Although SVG is not available on IE7 and IE8, these browsers support
12531 * [VML](https://en.wikipedia.org/wiki/Vector_Markup_Language)
12532 * (a now deprecated technology), and the SVG renderer will fall back to VML in
12533 * this case.
12534 *
12535 * @example
12536 *
12537 * Use SVG by default for all paths in the map:
12538 *
12539 * ```js
12540 * var map = L.map('map', {
12541 * 	renderer: L.svg()
12542 * });
12543 * ```
12544 *
12545 * Use a SVG renderer with extra padding for specific vector geometries:
12546 *
12547 * ```js
12548 * var map = L.map('map');
12549 * var myRenderer = L.svg({ padding: 0.5 });
12550 * var line = L.polyline( coordinates, { renderer: myRenderer } );
12551 * var circle = L.circle( center, { renderer: myRenderer } );
12552 * ```
12553 */
12554
12555var SVG = Renderer.extend({
12556
12557	getEvents: function () {
12558		var events = Renderer.prototype.getEvents.call(this);
12559		events.zoomstart = this._onZoomStart;
12560		return events;
12561	},
12562
12563	_initContainer: function () {
12564		this._container = create$2('svg');
12565
12566		// makes it possible to click through svg root; we'll reset it back in individual paths
12567		this._container.setAttribute('pointer-events', 'none');
12568
12569		this._rootGroup = create$2('g');
12570		this._container.appendChild(this._rootGroup);
12571	},
12572
12573	_destroyContainer: function () {
12574		remove(this._container);
12575		off(this._container);
12576		delete this._container;
12577		delete this._rootGroup;
12578		delete this._svgSize;
12579	},
12580
12581	_onZoomStart: function () {
12582		// Drag-then-pinch interactions might mess up the center and zoom.
12583		// In this case, the easiest way to prevent this is re-do the renderer
12584		//   bounds and padding when the zooming starts.
12585		this._update();
12586	},
12587
12588	_update: function () {
12589		if (this._map._animatingZoom && this._bounds) { return; }
12590
12591		Renderer.prototype._update.call(this);
12592
12593		var b = this._bounds,
12594		    size = b.getSize(),
12595		    container = this._container;
12596
12597		// set size of svg-container if changed
12598		if (!this._svgSize || !this._svgSize.equals(size)) {
12599			this._svgSize = size;
12600			container.setAttribute('width', size.x);
12601			container.setAttribute('height', size.y);
12602		}
12603
12604		// movement: update container viewBox so that we don't have to change coordinates of individual layers
12605		setPosition(container, b.min);
12606		container.setAttribute('viewBox', [b.min.x, b.min.y, size.x, size.y].join(' '));
12607
12608		this.fire('update');
12609	},
12610
12611	// methods below are called by vector layers implementations
12612
12613	_initPath: function (layer) {
12614		var path = layer._path = create$2('path');
12615
12616		// @namespace Path
12617		// @option className: String = null
12618		// Custom class name set on an element. Only for SVG renderer.
12619		if (layer.options.className) {
12620			addClass(path, layer.options.className);
12621		}
12622
12623		if (layer.options.interactive) {
12624			addClass(path, 'leaflet-interactive');
12625		}
12626
12627		this._updateStyle(layer);
12628		this._layers[stamp(layer)] = layer;
12629	},
12630
12631	_addPath: function (layer) {
12632		if (!this._rootGroup) { this._initContainer(); }
12633		this._rootGroup.appendChild(layer._path);
12634		layer.addInteractiveTarget(layer._path);
12635	},
12636
12637	_removePath: function (layer) {
12638		remove(layer._path);
12639		layer.removeInteractiveTarget(layer._path);
12640		delete this._layers[stamp(layer)];
12641	},
12642
12643	_updatePath: function (layer) {
12644		layer._project();
12645		layer._update();
12646	},
12647
12648	_updateStyle: function (layer) {
12649		var path = layer._path,
12650		    options = layer.options;
12651
12652		if (!path) { return; }
12653
12654		if (options.stroke) {
12655			path.setAttribute('stroke', options.color);
12656			path.setAttribute('stroke-opacity', options.opacity);
12657			path.setAttribute('stroke-width', options.weight);
12658			path.setAttribute('stroke-linecap', options.lineCap);
12659			path.setAttribute('stroke-linejoin', options.lineJoin);
12660
12661			if (options.dashArray) {
12662				path.setAttribute('stroke-dasharray', options.dashArray);
12663			} else {
12664				path.removeAttribute('stroke-dasharray');
12665			}
12666
12667			if (options.dashOffset) {
12668				path.setAttribute('stroke-dashoffset', options.dashOffset);
12669			} else {
12670				path.removeAttribute('stroke-dashoffset');
12671			}
12672		} else {
12673			path.setAttribute('stroke', 'none');
12674		}
12675
12676		if (options.fill) {
12677			path.setAttribute('fill', options.fillColor || options.color);
12678			path.setAttribute('fill-opacity', options.fillOpacity);
12679			path.setAttribute('fill-rule', options.fillRule || 'evenodd');
12680		} else {
12681			path.setAttribute('fill', 'none');
12682		}
12683	},
12684
12685	_updatePoly: function (layer, closed) {
12686		this._setPath(layer, pointsToPath(layer._parts, closed));
12687	},
12688
12689	_updateCircle: function (layer) {
12690		var p = layer._point,
12691		    r = Math.max(Math.round(layer._radius), 1),
12692		    r2 = Math.max(Math.round(layer._radiusY), 1) || r,
12693		    arc = 'a' + r + ',' + r2 + ' 0 1,0 ';
12694
12695		// drawing a circle with two half-arcs
12696		var d = layer._empty() ? 'M0 0' :
12697			'M' + (p.x - r) + ',' + p.y +
12698			arc + (r * 2) + ',0 ' +
12699			arc + (-r * 2) + ',0 ';
12700
12701		this._setPath(layer, d);
12702	},
12703
12704	_setPath: function (layer, path) {
12705		layer._path.setAttribute('d', path);
12706	},
12707
12708	// SVG does not have the concept of zIndex so we resort to changing the DOM order of elements
12709	_bringToFront: function (layer) {
12710		toFront(layer._path);
12711	},
12712
12713	_bringToBack: function (layer) {
12714		toBack(layer._path);
12715	}
12716});
12717
12718if (vml) {
12719	SVG.include(vmlMixin);
12720}
12721
12722// @namespace SVG
12723// @factory L.svg(options?: Renderer options)
12724// Creates a SVG renderer with the given options.
12725function svg$1(options) {
12726	return svg || vml ? new SVG(options) : null;
12727}
12728
12729Map.include({
12730	// @namespace Map; @method getRenderer(layer: Path): Renderer
12731	// Returns the instance of `Renderer` that should be used to render the given
12732	// `Path`. It will ensure that the `renderer` options of the map and paths
12733	// are respected, and that the renderers do exist on the map.
12734	getRenderer: function (layer) {
12735		// @namespace Path; @option renderer: Renderer
12736		// Use this specific instance of `Renderer` for this path. Takes
12737		// precedence over the map's [default renderer](#map-renderer).
12738		var renderer = layer.options.renderer || this._getPaneRenderer(layer.options.pane) || this.options.renderer || this._renderer;
12739
12740		if (!renderer) {
12741			renderer = this._renderer = this._createRenderer();
12742		}
12743
12744		if (!this.hasLayer(renderer)) {
12745			this.addLayer(renderer);
12746		}
12747		return renderer;
12748	},
12749
12750	_getPaneRenderer: function (name) {
12751		if (name === 'overlayPane' || name === undefined) {
12752			return false;
12753		}
12754
12755		var renderer = this._paneRenderers[name];
12756		if (renderer === undefined) {
12757			renderer = this._createRenderer({pane: name});
12758			this._paneRenderers[name] = renderer;
12759		}
12760		return renderer;
12761	},
12762
12763	_createRenderer: function (options) {
12764		// @namespace Map; @option preferCanvas: Boolean = false
12765		// Whether `Path`s should be rendered on a `Canvas` renderer.
12766		// By default, all `Path`s are rendered in a `SVG` renderer.
12767		return (this.options.preferCanvas && canvas$1(options)) || svg$1(options);
12768	}
12769});
12770
12771/*
12772 * L.Rectangle extends Polygon and creates a rectangle when passed a LatLngBounds object.
12773 */
12774
12775/*
12776 * @class Rectangle
12777 * @aka L.Rectangle
12778 * @inherits Polygon
12779 *
12780 * A class for drawing rectangle overlays on a map. Extends `Polygon`.
12781 *
12782 * @example
12783 *
12784 * ```js
12785 * // define rectangle geographical bounds
12786 * var bounds = [[54.559322, -5.767822], [56.1210604, -3.021240]];
12787 *
12788 * // create an orange rectangle
12789 * L.rectangle(bounds, {color: "#ff7800", weight: 1}).addTo(map);
12790 *
12791 * // zoom the map to the rectangle bounds
12792 * map.fitBounds(bounds);
12793 * ```
12794 *
12795 */
12796
12797
12798var Rectangle = Polygon.extend({
12799	initialize: function (latLngBounds, options) {
12800		Polygon.prototype.initialize.call(this, this._boundsToLatLngs(latLngBounds), options);
12801	},
12802
12803	// @method setBounds(latLngBounds: LatLngBounds): this
12804	// Redraws the rectangle with the passed bounds.
12805	setBounds: function (latLngBounds) {
12806		return this.setLatLngs(this._boundsToLatLngs(latLngBounds));
12807	},
12808
12809	_boundsToLatLngs: function (latLngBounds) {
12810		latLngBounds = toLatLngBounds(latLngBounds);
12811		return [
12812			latLngBounds.getSouthWest(),
12813			latLngBounds.getNorthWest(),
12814			latLngBounds.getNorthEast(),
12815			latLngBounds.getSouthEast()
12816		];
12817	}
12818});
12819
12820
12821// @factory L.rectangle(latLngBounds: LatLngBounds, options?: Polyline options)
12822function rectangle(latLngBounds, options) {
12823	return new Rectangle(latLngBounds, options);
12824}
12825
12826SVG.create = create$2;
12827SVG.pointsToPath = pointsToPath;
12828
12829GeoJSON.geometryToLayer = geometryToLayer;
12830GeoJSON.coordsToLatLng = coordsToLatLng;
12831GeoJSON.coordsToLatLngs = coordsToLatLngs;
12832GeoJSON.latLngToCoords = latLngToCoords;
12833GeoJSON.latLngsToCoords = latLngsToCoords;
12834GeoJSON.getFeature = getFeature;
12835GeoJSON.asFeature = asFeature;
12836
12837/*
12838 * L.Handler.BoxZoom is used to add shift-drag zoom interaction to the map
12839 * (zoom to a selected bounding box), enabled by default.
12840 */
12841
12842// @namespace Map
12843// @section Interaction Options
12844Map.mergeOptions({
12845	// @option boxZoom: Boolean = true
12846	// Whether the map can be zoomed to a rectangular area specified by
12847	// dragging the mouse while pressing the shift key.
12848	boxZoom: true
12849});
12850
12851var BoxZoom = Handler.extend({
12852	initialize: function (map) {
12853		this._map = map;
12854		this._container = map._container;
12855		this._pane = map._panes.overlayPane;
12856		this._resetStateTimeout = 0;
12857		map.on('unload', this._destroy, this);
12858	},
12859
12860	addHooks: function () {
12861		on(this._container, 'mousedown', this._onMouseDown, this);
12862	},
12863
12864	removeHooks: function () {
12865		off(this._container, 'mousedown', this._onMouseDown, this);
12866	},
12867
12868	moved: function () {
12869		return this._moved;
12870	},
12871
12872	_destroy: function () {
12873		remove(this._pane);
12874		delete this._pane;
12875	},
12876
12877	_resetState: function () {
12878		this._resetStateTimeout = 0;
12879		this._moved = false;
12880	},
12881
12882	_clearDeferredResetState: function () {
12883		if (this._resetStateTimeout !== 0) {
12884			clearTimeout(this._resetStateTimeout);
12885			this._resetStateTimeout = 0;
12886		}
12887	},
12888
12889	_onMouseDown: function (e) {
12890		if (!e.shiftKey || ((e.which !== 1) && (e.button !== 1))) { return false; }
12891
12892		// Clear the deferred resetState if it hasn't executed yet, otherwise it
12893		// will interrupt the interaction and orphan a box element in the container.
12894		this._clearDeferredResetState();
12895		this._resetState();
12896
12897		disableTextSelection();
12898		disableImageDrag();
12899
12900		this._startPoint = this._map.mouseEventToContainerPoint(e);
12901
12902		on(document, {
12903			contextmenu: stop,
12904			mousemove: this._onMouseMove,
12905			mouseup: this._onMouseUp,
12906			keydown: this._onKeyDown
12907		}, this);
12908	},
12909
12910	_onMouseMove: function (e) {
12911		if (!this._moved) {
12912			this._moved = true;
12913
12914			this._box = create$1('div', 'leaflet-zoom-box', this._container);
12915			addClass(this._container, 'leaflet-crosshair');
12916
12917			this._map.fire('boxzoomstart');
12918		}
12919
12920		this._point = this._map.mouseEventToContainerPoint(e);
12921
12922		var bounds = new Bounds(this._point, this._startPoint),
12923		    size = bounds.getSize();
12924
12925		setPosition(this._box, bounds.min);
12926
12927		this._box.style.width  = size.x + 'px';
12928		this._box.style.height = size.y + 'px';
12929	},
12930
12931	_finish: function () {
12932		if (this._moved) {
12933			remove(this._box);
12934			removeClass(this._container, 'leaflet-crosshair');
12935		}
12936
12937		enableTextSelection();
12938		enableImageDrag();
12939
12940		off(document, {
12941			contextmenu: stop,
12942			mousemove: this._onMouseMove,
12943			mouseup: this._onMouseUp,
12944			keydown: this._onKeyDown
12945		}, this);
12946	},
12947
12948	_onMouseUp: function (e) {
12949		if ((e.which !== 1) && (e.button !== 1)) { return; }
12950
12951		this._finish();
12952
12953		if (!this._moved) { return; }
12954		// Postpone to next JS tick so internal click event handling
12955		// still see it as "moved".
12956		this._clearDeferredResetState();
12957		this._resetStateTimeout = setTimeout(bind(this._resetState, this), 0);
12958
12959		var bounds = new LatLngBounds(
12960		        this._map.containerPointToLatLng(this._startPoint),
12961		        this._map.containerPointToLatLng(this._point));
12962
12963		this._map
12964			.fitBounds(bounds)
12965			.fire('boxzoomend', {boxZoomBounds: bounds});
12966	},
12967
12968	_onKeyDown: function (e) {
12969		if (e.keyCode === 27) {
12970			this._finish();
12971		}
12972	}
12973});
12974
12975// @section Handlers
12976// @property boxZoom: Handler
12977// Box (shift-drag with mouse) zoom handler.
12978Map.addInitHook('addHandler', 'boxZoom', BoxZoom);
12979
12980/*
12981 * L.Handler.DoubleClickZoom is used to handle double-click zoom on the map, enabled by default.
12982 */
12983
12984// @namespace Map
12985// @section Interaction Options
12986
12987Map.mergeOptions({
12988	// @option doubleClickZoom: Boolean|String = true
12989	// Whether the map can be zoomed in by double clicking on it and
12990	// zoomed out by double clicking while holding shift. If passed
12991	// `'center'`, double-click zoom will zoom to the center of the
12992	//  view regardless of where the mouse was.
12993	doubleClickZoom: true
12994});
12995
12996var DoubleClickZoom = Handler.extend({
12997	addHooks: function () {
12998		this._map.on('dblclick', this._onDoubleClick, this);
12999	},
13000
13001	removeHooks: function () {
13002		this._map.off('dblclick', this._onDoubleClick, this);
13003	},
13004
13005	_onDoubleClick: function (e) {
13006		var map = this._map,
13007		    oldZoom = map.getZoom(),
13008		    delta = map.options.zoomDelta,
13009		    zoom = e.originalEvent.shiftKey ? oldZoom - delta : oldZoom + delta;
13010
13011		if (map.options.doubleClickZoom === 'center') {
13012			map.setZoom(zoom);
13013		} else {
13014			map.setZoomAround(e.containerPoint, zoom);
13015		}
13016	}
13017});
13018
13019// @section Handlers
13020//
13021// Map properties include interaction handlers that allow you to control
13022// interaction behavior in runtime, enabling or disabling certain features such
13023// as dragging or touch zoom (see `Handler` methods). For example:
13024//
13025// ```js
13026// map.doubleClickZoom.disable();
13027// ```
13028//
13029// @property doubleClickZoom: Handler
13030// Double click zoom handler.
13031Map.addInitHook('addHandler', 'doubleClickZoom', DoubleClickZoom);
13032
13033/*
13034 * L.Handler.MapDrag is used to make the map draggable (with panning inertia), enabled by default.
13035 */
13036
13037// @namespace Map
13038// @section Interaction Options
13039Map.mergeOptions({
13040	// @option dragging: Boolean = true
13041	// Whether the map be draggable with mouse/touch or not.
13042	dragging: true,
13043
13044	// @section Panning Inertia Options
13045	// @option inertia: Boolean = *
13046	// If enabled, panning of the map will have an inertia effect where
13047	// the map builds momentum while dragging and continues moving in
13048	// the same direction for some time. Feels especially nice on touch
13049	// devices. Enabled by default unless running on old Android devices.
13050	inertia: !android23,
13051
13052	// @option inertiaDeceleration: Number = 3000
13053	// The rate with which the inertial movement slows down, in pixels/second².
13054	inertiaDeceleration: 3400, // px/s^2
13055
13056	// @option inertiaMaxSpeed: Number = Infinity
13057	// Max speed of the inertial movement, in pixels/second.
13058	inertiaMaxSpeed: Infinity, // px/s
13059
13060	// @option easeLinearity: Number = 0.2
13061	easeLinearity: 0.2,
13062
13063	// TODO refactor, move to CRS
13064	// @option worldCopyJump: Boolean = false
13065	// With this option enabled, the map tracks when you pan to another "copy"
13066	// of the world and seamlessly jumps to the original one so that all overlays
13067	// like markers and vector layers are still visible.
13068	worldCopyJump: false,
13069
13070	// @option maxBoundsViscosity: Number = 0.0
13071	// If `maxBounds` is set, this option will control how solid the bounds
13072	// are when dragging the map around. The default value of `0.0` allows the
13073	// user to drag outside the bounds at normal speed, higher values will
13074	// slow down map dragging outside bounds, and `1.0` makes the bounds fully
13075	// solid, preventing the user from dragging outside the bounds.
13076	maxBoundsViscosity: 0.0
13077});
13078
13079var Drag = Handler.extend({
13080	addHooks: function () {
13081		if (!this._draggable) {
13082			var map = this._map;
13083
13084			this._draggable = new Draggable(map._mapPane, map._container);
13085
13086			this._draggable.on({
13087				dragstart: this._onDragStart,
13088				drag: this._onDrag,
13089				dragend: this._onDragEnd
13090			}, this);
13091
13092			this._draggable.on('predrag', this._onPreDragLimit, this);
13093			if (map.options.worldCopyJump) {
13094				this._draggable.on('predrag', this._onPreDragWrap, this);
13095				map.on('zoomend', this._onZoomEnd, this);
13096
13097				map.whenReady(this._onZoomEnd, this);
13098			}
13099		}
13100		addClass(this._map._container, 'leaflet-grab leaflet-touch-drag');
13101		this._draggable.enable();
13102		this._positions = [];
13103		this._times = [];
13104	},
13105
13106	removeHooks: function () {
13107		removeClass(this._map._container, 'leaflet-grab');
13108		removeClass(this._map._container, 'leaflet-touch-drag');
13109		this._draggable.disable();
13110	},
13111
13112	moved: function () {
13113		return this._draggable && this._draggable._moved;
13114	},
13115
13116	moving: function () {
13117		return this._draggable && this._draggable._moving;
13118	},
13119
13120	_onDragStart: function () {
13121		var map = this._map;
13122
13123		map._stop();
13124		if (this._map.options.maxBounds && this._map.options.maxBoundsViscosity) {
13125			var bounds = toLatLngBounds(this._map.options.maxBounds);
13126
13127			this._offsetLimit = toBounds(
13128				this._map.latLngToContainerPoint(bounds.getNorthWest()).multiplyBy(-1),
13129				this._map.latLngToContainerPoint(bounds.getSouthEast()).multiplyBy(-1)
13130					.add(this._map.getSize()));
13131
13132			this._viscosity = Math.min(1.0, Math.max(0.0, this._map.options.maxBoundsViscosity));
13133		} else {
13134			this._offsetLimit = null;
13135		}
13136
13137		map
13138		    .fire('movestart')
13139		    .fire('dragstart');
13140
13141		if (map.options.inertia) {
13142			this._positions = [];
13143			this._times = [];
13144		}
13145	},
13146
13147	_onDrag: function (e) {
13148		if (this._map.options.inertia) {
13149			var time = this._lastTime = +new Date(),
13150			    pos = this._lastPos = this._draggable._absPos || this._draggable._newPos;
13151
13152			this._positions.push(pos);
13153			this._times.push(time);
13154
13155			this._prunePositions(time);
13156		}
13157
13158		this._map
13159		    .fire('move', e)
13160		    .fire('drag', e);
13161	},
13162
13163	_prunePositions: function (time) {
13164		while (this._positions.length > 1 && time - this._times[0] > 50) {
13165			this._positions.shift();
13166			this._times.shift();
13167		}
13168	},
13169
13170	_onZoomEnd: function () {
13171		var pxCenter = this._map.getSize().divideBy(2),
13172		    pxWorldCenter = this._map.latLngToLayerPoint([0, 0]);
13173
13174		this._initialWorldOffset = pxWorldCenter.subtract(pxCenter).x;
13175		this._worldWidth = this._map.getPixelWorldBounds().getSize().x;
13176	},
13177
13178	_viscousLimit: function (value, threshold) {
13179		return value - (value - threshold) * this._viscosity;
13180	},
13181
13182	_onPreDragLimit: function () {
13183		if (!this._viscosity || !this._offsetLimit) { return; }
13184
13185		var offset = this._draggable._newPos.subtract(this._draggable._startPos);
13186
13187		var limit = this._offsetLimit;
13188		if (offset.x < limit.min.x) { offset.x = this._viscousLimit(offset.x, limit.min.x); }
13189		if (offset.y < limit.min.y) { offset.y = this._viscousLimit(offset.y, limit.min.y); }
13190		if (offset.x > limit.max.x) { offset.x = this._viscousLimit(offset.x, limit.max.x); }
13191		if (offset.y > limit.max.y) { offset.y = this._viscousLimit(offset.y, limit.max.y); }
13192
13193		this._draggable._newPos = this._draggable._startPos.add(offset);
13194	},
13195
13196	_onPreDragWrap: function () {
13197		// TODO refactor to be able to adjust map pane position after zoom
13198		var worldWidth = this._worldWidth,
13199		    halfWidth = Math.round(worldWidth / 2),
13200		    dx = this._initialWorldOffset,
13201		    x = this._draggable._newPos.x,
13202		    newX1 = (x - halfWidth + dx) % worldWidth + halfWidth - dx,
13203		    newX2 = (x + halfWidth + dx) % worldWidth - halfWidth - dx,
13204		    newX = Math.abs(newX1 + dx) < Math.abs(newX2 + dx) ? newX1 : newX2;
13205
13206		this._draggable._absPos = this._draggable._newPos.clone();
13207		this._draggable._newPos.x = newX;
13208	},
13209
13210	_onDragEnd: function (e) {
13211		var map = this._map,
13212		    options = map.options,
13213
13214		    noInertia = !options.inertia || this._times.length < 2;
13215
13216		map.fire('dragend', e);
13217
13218		if (noInertia) {
13219			map.fire('moveend');
13220
13221		} else {
13222			this._prunePositions(+new Date());
13223
13224			var direction = this._lastPos.subtract(this._positions[0]),
13225			    duration = (this._lastTime - this._times[0]) / 1000,
13226			    ease = options.easeLinearity,
13227
13228			    speedVector = direction.multiplyBy(ease / duration),
13229			    speed = speedVector.distanceTo([0, 0]),
13230
13231			    limitedSpeed = Math.min(options.inertiaMaxSpeed, speed),
13232			    limitedSpeedVector = speedVector.multiplyBy(limitedSpeed / speed),
13233
13234			    decelerationDuration = limitedSpeed / (options.inertiaDeceleration * ease),
13235			    offset = limitedSpeedVector.multiplyBy(-decelerationDuration / 2).round();
13236
13237			if (!offset.x && !offset.y) {
13238				map.fire('moveend');
13239
13240			} else {
13241				offset = map._limitOffset(offset, map.options.maxBounds);
13242
13243				requestAnimFrame(function () {
13244					map.panBy(offset, {
13245						duration: decelerationDuration,
13246						easeLinearity: ease,
13247						noMoveStart: true,
13248						animate: true
13249					});
13250				});
13251			}
13252		}
13253	}
13254});
13255
13256// @section Handlers
13257// @property dragging: Handler
13258// Map dragging handler (by both mouse and touch).
13259Map.addInitHook('addHandler', 'dragging', Drag);
13260
13261/*
13262 * L.Map.Keyboard is handling keyboard interaction with the map, enabled by default.
13263 */
13264
13265// @namespace Map
13266// @section Keyboard Navigation Options
13267Map.mergeOptions({
13268	// @option keyboard: Boolean = true
13269	// Makes the map focusable and allows users to navigate the map with keyboard
13270	// arrows and `+`/`-` keys.
13271	keyboard: true,
13272
13273	// @option keyboardPanDelta: Number = 80
13274	// Amount of pixels to pan when pressing an arrow key.
13275	keyboardPanDelta: 80
13276});
13277
13278var Keyboard = Handler.extend({
13279
13280	keyCodes: {
13281		left:    [37],
13282		right:   [39],
13283		down:    [40],
13284		up:      [38],
13285		zoomIn:  [187, 107, 61, 171],
13286		zoomOut: [189, 109, 54, 173]
13287	},
13288
13289	initialize: function (map) {
13290		this._map = map;
13291
13292		this._setPanDelta(map.options.keyboardPanDelta);
13293		this._setZoomDelta(map.options.zoomDelta);
13294	},
13295
13296	addHooks: function () {
13297		var container = this._map._container;
13298
13299		// make the container focusable by tabbing
13300		if (container.tabIndex <= 0) {
13301			container.tabIndex = '0';
13302		}
13303
13304		on(container, {
13305			focus: this._onFocus,
13306			blur: this._onBlur,
13307			mousedown: this._onMouseDown
13308		}, this);
13309
13310		this._map.on({
13311			focus: this._addHooks,
13312			blur: this._removeHooks
13313		}, this);
13314	},
13315
13316	removeHooks: function () {
13317		this._removeHooks();
13318
13319		off(this._map._container, {
13320			focus: this._onFocus,
13321			blur: this._onBlur,
13322			mousedown: this._onMouseDown
13323		}, this);
13324
13325		this._map.off({
13326			focus: this._addHooks,
13327			blur: this._removeHooks
13328		}, this);
13329	},
13330
13331	_onMouseDown: function () {
13332		if (this._focused) { return; }
13333
13334		var body = document.body,
13335		    docEl = document.documentElement,
13336		    top = body.scrollTop || docEl.scrollTop,
13337		    left = body.scrollLeft || docEl.scrollLeft;
13338
13339		this._map._container.focus();
13340
13341		window.scrollTo(left, top);
13342	},
13343
13344	_onFocus: function () {
13345		this._focused = true;
13346		this._map.fire('focus');
13347	},
13348
13349	_onBlur: function () {
13350		this._focused = false;
13351		this._map.fire('blur');
13352	},
13353
13354	_setPanDelta: function (panDelta) {
13355		var keys = this._panKeys = {},
13356		    codes = this.keyCodes,
13357		    i, len;
13358
13359		for (i = 0, len = codes.left.length; i < len; i++) {
13360			keys[codes.left[i]] = [-1 * panDelta, 0];
13361		}
13362		for (i = 0, len = codes.right.length; i < len; i++) {
13363			keys[codes.right[i]] = [panDelta, 0];
13364		}
13365		for (i = 0, len = codes.down.length; i < len; i++) {
13366			keys[codes.down[i]] = [0, panDelta];
13367		}
13368		for (i = 0, len = codes.up.length; i < len; i++) {
13369			keys[codes.up[i]] = [0, -1 * panDelta];
13370		}
13371	},
13372
13373	_setZoomDelta: function (zoomDelta) {
13374		var keys = this._zoomKeys = {},
13375		    codes = this.keyCodes,
13376		    i, len;
13377
13378		for (i = 0, len = codes.zoomIn.length; i < len; i++) {
13379			keys[codes.zoomIn[i]] = zoomDelta;
13380		}
13381		for (i = 0, len = codes.zoomOut.length; i < len; i++) {
13382			keys[codes.zoomOut[i]] = -zoomDelta;
13383		}
13384	},
13385
13386	_addHooks: function () {
13387		on(document, 'keydown', this._onKeyDown, this);
13388	},
13389
13390	_removeHooks: function () {
13391		off(document, 'keydown', this._onKeyDown, this);
13392	},
13393
13394	_onKeyDown: function (e) {
13395		if (e.altKey || e.ctrlKey || e.metaKey) { return; }
13396
13397		var key = e.keyCode,
13398		    map = this._map,
13399		    offset;
13400
13401		if (key in this._panKeys) {
13402			if (!map._panAnim || !map._panAnim._inProgress) {
13403				offset = this._panKeys[key];
13404				if (e.shiftKey) {
13405					offset = toPoint(offset).multiplyBy(3);
13406				}
13407
13408				map.panBy(offset);
13409
13410				if (map.options.maxBounds) {
13411					map.panInsideBounds(map.options.maxBounds);
13412				}
13413			}
13414		} else if (key in this._zoomKeys) {
13415			map.setZoom(map.getZoom() + (e.shiftKey ? 3 : 1) * this._zoomKeys[key]);
13416
13417		} else if (key === 27 && map._popup && map._popup.options.closeOnEscapeKey) {
13418			map.closePopup();
13419
13420		} else {
13421			return;
13422		}
13423
13424		stop(e);
13425	}
13426});
13427
13428// @section Handlers
13429// @section Handlers
13430// @property keyboard: Handler
13431// Keyboard navigation handler.
13432Map.addInitHook('addHandler', 'keyboard', Keyboard);
13433
13434/*
13435 * L.Handler.ScrollWheelZoom is used by L.Map to enable mouse scroll wheel zoom on the map.
13436 */
13437
13438// @namespace Map
13439// @section Interaction Options
13440Map.mergeOptions({
13441	// @section Mousewheel options
13442	// @option scrollWheelZoom: Boolean|String = true
13443	// Whether the map can be zoomed by using the mouse wheel. If passed `'center'`,
13444	// it will zoom to the center of the view regardless of where the mouse was.
13445	scrollWheelZoom: true,
13446
13447	// @option wheelDebounceTime: Number = 40
13448	// Limits the rate at which a wheel can fire (in milliseconds). By default
13449	// user can't zoom via wheel more often than once per 40 ms.
13450	wheelDebounceTime: 40,
13451
13452	// @option wheelPxPerZoomLevel: Number = 60
13453	// How many scroll pixels (as reported by [L.DomEvent.getWheelDelta](#domevent-getwheeldelta))
13454	// mean a change of one full zoom level. Smaller values will make wheel-zooming
13455	// faster (and vice versa).
13456	wheelPxPerZoomLevel: 60
13457});
13458
13459var ScrollWheelZoom = Handler.extend({
13460	addHooks: function () {
13461		on(this._map._container, 'mousewheel', this._onWheelScroll, this);
13462
13463		this._delta = 0;
13464	},
13465
13466	removeHooks: function () {
13467		off(this._map._container, 'mousewheel', this._onWheelScroll, this);
13468	},
13469
13470	_onWheelScroll: function (e) {
13471		var delta = getWheelDelta(e);
13472
13473		var debounce = this._map.options.wheelDebounceTime;
13474
13475		this._delta += delta;
13476		this._lastMousePos = this._map.mouseEventToContainerPoint(e);
13477
13478		if (!this._startTime) {
13479			this._startTime = +new Date();
13480		}
13481
13482		var left = Math.max(debounce - (+new Date() - this._startTime), 0);
13483
13484		clearTimeout(this._timer);
13485		this._timer = setTimeout(bind(this._performZoom, this), left);
13486
13487		stop(e);
13488	},
13489
13490	_performZoom: function () {
13491		var map = this._map,
13492		    zoom = map.getZoom(),
13493		    snap = this._map.options.zoomSnap || 0;
13494
13495		map._stop(); // stop panning and fly animations if any
13496
13497		// map the delta with a sigmoid function to -4..4 range leaning on -1..1
13498		var d2 = this._delta / (this._map.options.wheelPxPerZoomLevel * 4),
13499		    d3 = 4 * Math.log(2 / (1 + Math.exp(-Math.abs(d2)))) / Math.LN2,
13500		    d4 = snap ? Math.ceil(d3 / snap) * snap : d3,
13501		    delta = map._limitZoom(zoom + (this._delta > 0 ? d4 : -d4)) - zoom;
13502
13503		this._delta = 0;
13504		this._startTime = null;
13505
13506		if (!delta) { return; }
13507
13508		if (map.options.scrollWheelZoom === 'center') {
13509			map.setZoom(zoom + delta);
13510		} else {
13511			map.setZoomAround(this._lastMousePos, zoom + delta);
13512		}
13513	}
13514});
13515
13516// @section Handlers
13517// @property scrollWheelZoom: Handler
13518// Scroll wheel zoom handler.
13519Map.addInitHook('addHandler', 'scrollWheelZoom', ScrollWheelZoom);
13520
13521/*
13522 * L.Map.Tap is used to enable mobile hacks like quick taps and long hold.
13523 */
13524
13525// @namespace Map
13526// @section Interaction Options
13527Map.mergeOptions({
13528	// @section Touch interaction options
13529	// @option tap: Boolean = true
13530	// Enables mobile hacks for supporting instant taps (fixing 200ms click
13531	// delay on iOS/Android) and touch holds (fired as `contextmenu` events).
13532	tap: true,
13533
13534	// @option tapTolerance: Number = 15
13535	// The max number of pixels a user can shift his finger during touch
13536	// for it to be considered a valid tap.
13537	tapTolerance: 15
13538});
13539
13540var Tap = Handler.extend({
13541	addHooks: function () {
13542		on(this._map._container, 'touchstart', this._onDown, this);
13543	},
13544
13545	removeHooks: function () {
13546		off(this._map._container, 'touchstart', this._onDown, this);
13547	},
13548
13549	_onDown: function (e) {
13550		if (!e.touches) { return; }
13551
13552		preventDefault(e);
13553
13554		this._fireClick = true;
13555
13556		// don't simulate click or track longpress if more than 1 touch
13557		if (e.touches.length > 1) {
13558			this._fireClick = false;
13559			clearTimeout(this._holdTimeout);
13560			return;
13561		}
13562
13563		var first = e.touches[0],
13564		    el = first.target;
13565
13566		this._startPos = this._newPos = new Point(first.clientX, first.clientY);
13567
13568		// if touching a link, highlight it
13569		if (el.tagName && el.tagName.toLowerCase() === 'a') {
13570			addClass(el, 'leaflet-active');
13571		}
13572
13573		// simulate long hold but setting a timeout
13574		this._holdTimeout = setTimeout(bind(function () {
13575			if (this._isTapValid()) {
13576				this._fireClick = false;
13577				this._onUp();
13578				this._simulateEvent('contextmenu', first);
13579			}
13580		}, this), 1000);
13581
13582		this._simulateEvent('mousedown', first);
13583
13584		on(document, {
13585			touchmove: this._onMove,
13586			touchend: this._onUp
13587		}, this);
13588	},
13589
13590	_onUp: function (e) {
13591		clearTimeout(this._holdTimeout);
13592
13593		off(document, {
13594			touchmove: this._onMove,
13595			touchend: this._onUp
13596		}, this);
13597
13598		if (this._fireClick && e && e.changedTouches) {
13599
13600			var first = e.changedTouches[0],
13601			    el = first.target;
13602
13603			if (el && el.tagName && el.tagName.toLowerCase() === 'a') {
13604				removeClass(el, 'leaflet-active');
13605			}
13606
13607			this._simulateEvent('mouseup', first);
13608
13609			// simulate click if the touch didn't move too much
13610			if (this._isTapValid()) {
13611				this._simulateEvent('click', first);
13612			}
13613		}
13614	},
13615
13616	_isTapValid: function () {
13617		return this._newPos.distanceTo(this._startPos) <= this._map.options.tapTolerance;
13618	},
13619
13620	_onMove: function (e) {
13621		var first = e.touches[0];
13622		this._newPos = new Point(first.clientX, first.clientY);
13623		this._simulateEvent('mousemove', first);
13624	},
13625
13626	_simulateEvent: function (type, e) {
13627		var simulatedEvent = document.createEvent('MouseEvents');
13628
13629		simulatedEvent._simulated = true;
13630		e.target._simulatedClick = true;
13631
13632		simulatedEvent.initMouseEvent(
13633		        type, true, true, window, 1,
13634		        e.screenX, e.screenY,
13635		        e.clientX, e.clientY,
13636		        false, false, false, false, 0, null);
13637
13638		e.target.dispatchEvent(simulatedEvent);
13639	}
13640});
13641
13642// @section Handlers
13643// @property tap: Handler
13644// Mobile touch hacks (quick tap and touch hold) handler.
13645if (touch && !pointer) {
13646	Map.addInitHook('addHandler', 'tap', Tap);
13647}
13648
13649/*
13650 * L.Handler.TouchZoom is used by L.Map to add pinch zoom on supp
vendor: 4,276 bytes, lines 13650-13790
13650orted mobile browsers.
13651 */
13652
13653// @namespace Map
13654// @section Interaction Options
13655Map.mergeOptions({
13656	// @section Touch interaction options
13657	// @option touchZoom: Boolean|String = *
13658	// Whether the map can be zoomed by touch-dragging with two fingers. If
13659	// passed `'center'`, it will zoom to the center of the view regardless of
13660	// where the touch events (fingers) were. Enabled for touch-capable web
13661	// browsers except for old Androids.
13662	touchZoom: touch && !android23,
13663
13664	// @option bounceAtZoomLimits: Boolean = true
13665	// Set it to false if you don't want the map to zoom beyond min/max zoom
13666	// and then bounce back when pinch-zooming.
13667	bounceAtZoomLimits: true
13668});
13669
13670var TouchZoom = Handler.extend({
13671	addHooks: function () {
13672		addClass(this._map._container, 'leaflet-touch-zoom');
13673		on(this._map._container, 'touchstart', this._onTouchStart, this);
13674	},
13675
13676	removeHooks: function () {
13677		removeClass(this._map._container, 'leaflet-touch-zoom');
13678		off(this._map._container, 'touchstart', this._onTouchStart, this);
13679	},
13680
13681	_onTouchStart: function (e) {
13682		var map = this._map;
13683		if (!e.touches || e.touches.length !== 2 || map._animatingZoom || this._zooming) { return; }
13684
13685		var p1 = map.mouseEventToContainerPoint(e.touches[0]),
13686		    p2 = map.mouseEventToContainerPoint(e.touches[1]);
13687
13688		this._centerPoint = map.getSize()._divideBy(2);
13689		this._startLatLng = map.containerPointToLatLng(this._centerPoint);
13690		if (map.options.touchZoom !== 'center') {
13691			this._pinchStartLatLng = map.containerPointToLatLng(p1.add(p2)._divideBy(2));
13692		}
13693
13694		this._startDist = p1.distanceTo(p2);
13695		this._startZoom = map.getZoom();
13696
13697		this._moved = false;
13698		this._zooming = true;
13699
13700		map._stop();
13701
13702		on(document, 'touchmove', this._onTouchMove, this);
13703		on(document, 'touchend', this._onTouchEnd, this);
13704
13705		preventDefault(e);
13706	},
13707
13708	_onTouchMove: function (e) {
13709		if (!e.touches || e.touches.length !== 2 || !this._zooming) { return; }
13710
13711		var map = this._map,
13712		    p1 = map.mouseEventToContainerPoint(e.touches[0]),
13713		    p2 = map.mouseEventToContainerPoint(e.touches[1]),
13714		    scale = p1.distanceTo(p2) / this._startDist;
13715
13716		this._zoom = map.getScaleZoom(scale, this._startZoom);
13717
13718		if (!map.options.bounceAtZoomLimits && (
13719			(this._zoom < map.getMinZoom() && scale < 1) ||
13720			(this._zoom > map.getMaxZoom() && scale > 1))) {
13721			this._zoom = map._limitZoom(this._zoom);
13722		}
13723
13724		if (map.options.touchZoom === 'center') {
13725			this._center = this._startLatLng;
13726			if (scale === 1) { return; }
13727		} else {
13728			// Get delta from pinch to center, so centerLatLng is delta applied to initial pinchLatLng
13729			var delta = p1._add(p2)._divideBy(2)._subtract(this._centerPoint);
13730			if (scale === 1 && delta.x === 0 && delta.y === 0) { return; }
13731			this._center = map.unproject(map.project(this._pinchStartLatLng, this._zoom).subtract(delta), this._zoom);
13732		}
13733
13734		if (!this._moved) {
13735			map._moveStart(true, false);
13736			this._moved = true;
13737		}
13738
13739		cancelAnimFrame(this._animRequest);
13740
13741		var moveFn = bind(map._move, map, this._center, this._zoom, {pinch: true, round: false});
13742		this._animRequest = requestAnimFrame(moveFn, this, true);
13743
13744		preventDefault(e);
13745	},
13746
13747	_onTouchEnd: function () {
13748		if (!this._moved || !this._zooming) {
13749			this._zooming = false;
13750			return;
13751		}
13752
13753		this._zooming = false;
13754		cancelAnimFrame(this._animRequest);
13755
13756		off(document, 'touchmove', this._onTouchMove);
13757		off(document, 'touchend', this._onTouchEnd);
13758
13759		// Pinch updates GridLayers' levels only when zoomSnap is off, so zoomSnap becomes noUpdate.
13760		if (this._map.options.zoomAnimation) {
13761			this._map._animateZoom(this._center, this._map._limitZoom(this._zoom), true, this._map.options.zoomSnap);
13762		} else {
13763			this._map._resetView(this._center, this._map._limitZoom(this._zoom));
13764		}
13765	}
13766});
13767
13768// @section Handlers
13769// @property touchZoom: Handler
13770// Touch zoom handler.
13771Map.addInitHook('addHandler', 'touchZoom', TouchZoom);
13772
13773Map.BoxZoom = BoxZoom;
13774Map.DoubleClickZoom = DoubleClickZoom;
13775Map.Drag = Drag;
13776Map.Keyboard = Keyboard;
13777Map.ScrollWheelZoom = ScrollWheelZoom;
13778Map.Tap = Tap;
13779Map.TouchZoom = TouchZoom;
13780
13781Object.freeze = freeze;
13782
13783exports.version = version;
13784exports.Control = Control;
13785exports.control = control;
13786exports.Browser = Browser;
13787exports.Evented = Evented;
13788exports.Mixin = Mixin;
13789exports.Util = Util;
13790exports.Class = Class;
13791exports.Handler = Handler;
13792exports.extend = extend;
13793exports.bind = bind;
13794exports.stamp = stamp;
13795exports.setOptions = setOptions;
13796exports.DomEvent = DomEvent;
13797exports.DomUtil = DomUtil;
13798exports.PosAnimation = PosAnimation;
13799exports.Draggable = Draggable;
13800exports.LineUtil = LineUtil;
13801exports.PolyUtil = PolyUtil;
13802exports.Point = Point;
13803exports.point = toPoint;
13804exports.Bounds = Bounds;
13805exports.bounds = toBounds;
13806exports.Transformation = Transformation;
13807exports.transformation = toTransformation;
13808exports.Projection = index;
13809exports.LatLng = LatLng;
13810exports.latLng = toLatLng;
13811exports.LatLngBounds = LatLngBounds;
13812exports.latLngBounds = toLatLngBounds;
13813exports.CRS = CRS;
13814exports.GeoJSON = GeoJSON;
13815exports.geoJSON = geoJSON;
13816exports.geoJson = geoJson;
13817exports.Layer = Layer;
13818exports.LayerGroup = LayerGroup;
13819exports.layerGroup = layerGroup;
13820exports.FeatureGroup = FeatureGroup;
13821exports.featureGroup = featureGroup;
13822exports.ImageOverlay = ImageOverlay;
13823exports.imageOverlay = imageOverlay;
13824exports.VideoOverlay = VideoOverlay;
13825exports.videoOverlay = videoOverlay;
13826exports.DivOverlay = DivOverlay;
13827exports.Popup = Popup;
13828exports.popup = popup;
13829exports.Tooltip = Tooltip;
13830exports.tooltip = tooltip;
13831exports.Icon = Icon;
13832exports.icon = icon;
13833exports.DivIcon = DivIcon;
13834exports.divIcon = divIcon;
13835exports.Marker = Marker;
13836exports.marker = marker;
13837exports.TileLayer = TileLayer;
13838exports.tileLayer = tileLayer;
13839exports.GridLayer = GridLayer;
13840exports.gridLayer = gridLayer;
13841exports.SVG = SVG;
13842exports.svg = svg$1;
13843exports.Renderer = Renderer;
13844exports.Canvas = Canvas;
13845exports.canvas = canvas$1;
13846exports.Path = Path;
13847exports.CircleMarker = CircleMarker;
13848exports.circleMarker = circleMarker;
13849exports.Circle = Circle;
13850exports.circle = circle;
13851exports.Polyline = Polyline;
13852exports.polyline = polyline;
13853exports.Polygon = Polygon;
13854exports.polygon = polygon;
13855exports.Rectangle = Rectangle;
13856exports.rectangle = rectangle;
13857exports.Map = Map;
13858exports.map = createMap;
13859
13860var oldL = window.L;
13861exports.noConflict = function() {
13862	window.L = oldL;
13863	return this;
13864}
13865
13866// Always export us to window global (see #2364)
13867window.L = exports;
13868
13869})));
13870//# 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.