PageSourceSearch

https://logins.bidtrack.co.za/fleetview/external_components/leaflet/dist/leaflet-src.js

js bidtrack.co.za collected 2026-10-01 14:26:24 UTC 381,622 bytes, 13,251 lines download raw bytes

1/*
2 Leaflet 1.0.3, a JS library for interactive maps. http://leafletjs.com
3 (c) 2010-2016 Vladimir Agafonkin, (c) 2010-2011 CloudMade
4*/
5(function (window, document, undefined) {
6var L = {
7	version: "1.0.3"
8};
9
10function expose() {
11	var oldL = window.L;
12
13	L.noConflict = function () {
14		window.L = oldL;
15		return this;
16	};
17
18	window.L = L;
19}
20
21// define Leaflet for Node module pattern loaders, including Browserify
22if (typeof module === 'object' && typeof module.exports === 'object') {
23	module.exports = L;
24
25// define Leaflet as an AMD module
26} else if (typeof define === 'function' && define.amd) {
27	define(L);
28}
29
30// define Leaflet as a global L variable, saving the original L to restore later if needed
31if (typeof window !== 'undefined') {
32	expose();
33}
34
35
36
37/*
38 * @namespace Util
39 *
40 * Various utility functions, used by Leaflet internally.
41 */
42
43L.Util = {
44
45	// @function extend(dest: Object, src?: Object): Object
46	// Merges the properties of the `src` object (or multiple objects) into `dest` object and returns the latter. Has an `L.extend` shortcut.
47	extend: function (dest) {
48		var i, j, len, src;
49
50		for (j = 1, len = arguments.length; j < len; j++) {
51			src = arguments[j];
52			for (i in src) {
53				dest[i] = src[i];
54			}
55		}
56		return dest;
57	},
58
59	// @function create(proto: Object, properties?: Object): Object
60	// Compatibility polyfill for [Object.create](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Object/create)
61	create: Object.create || (function () {
62		function F() {}
63		return function (proto) {
64			F.prototype = proto;
65			return new F();
66		};
67	})(),
68
69	// @function bind(fn: Function, …): Function
70	// 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).
71	// Has a `L.bind()` shortcut.
72	bind: function (fn, obj) {
73		var slice = Array.prototype.slice;
74
75		if (fn.bind) {
76			return fn.bind.apply(fn, slice.call(arguments, 1));
77		}
78
79		var args = slice.call(arguments, 2);
80
81		return function () {
82			return fn.apply(obj, args.length ? args.concat(slice.call(arguments)) : arguments);
83		};
84	},
85
86	// @function stamp(obj: Object): Number
87	// Returns the unique ID of an object, assiging it one if it doesn't have it.
88	stamp: function (obj) {
89		/*eslint-disable */
90		obj._leaflet_id = obj._leaflet_id || ++L.Util.lastId;
91		return obj._leaflet_id;
92		/*eslint-enable */
93	},
94
95	// @property lastId: Number
96	// Last unique ID used by [`stamp()`](#util-stamp)
97	lastId: 0,
98
99	// @function throttle(fn: Function, time: Number, context: Object): Function
100	// Returns a function which executes function `fn` with the given scope `context`
101	// (so that the `this` keyword refers to `context` inside `fn`'s code). The function
102	// `fn` will be called no more than one time per given amount of `time`. The arguments
103	// received by the bound function will be any arguments passed when binding the
104	// function, followed by any arguments passed when invoking the bound function.
105	// Has an `L.bind` shortcut.
106	throttle: function (fn, time, context) {
107		var lock, args, wrapperFn, later;
108
109		later = function () {
110			// reset lock and call if queued
111			lock = false;
112			if (args) {
113				wrapperFn.apply(context, args);
114				args = false;
115			}
116		};
117
118		wrapperFn = function () {
119			if (lock) {
120				// called too soon, queue to call later
121				args = arguments;
122
123			} else {
124				// call and lock until later
125				fn.apply(context, arguments);
126				setTimeout(later, time);
127				lock = true;
128			}
129		};
130
131		return wrapperFn;
132	},
133
134	// @function wrapNum(num: Number, range: Number[], includeMax?: Boolean): Number
135	// Returns the number `num` modulo `range` in such a way so it lies within
136	// `range[0]` and `range[1]`. The returned value will be always smaller than
137	// `range[1]` unless `includeMax` is set to `true`.
138	wrapNum: function (x, range, includeMax) {
139		var max = range[1],
140		    min = range[0],
141		    d = max - min;
142		return x === max && includeMax ? x : ((x - min) % d + d) % d + min;
143	},
144
145	// @function falseFn(): Function
146	// Returns a function which always returns `false`.
147	falseFn: function () { return false; },
148
149	// @function formatNum(num: Number, digits?: Number): Number
150	// Returns the number `num` rounded to `digits` decimals, or to 5 decimals by default.
151	formatNum: function (num, digits) {
152		var pow = Math.pow(10, digits || 5);
153		return Math.round(num * pow) / pow;
154	},
155
156	// @function trim(str: String): String
157	// Compatibility polyfill for [String.prototype.trim](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String/Trim)
158	trim: function (str) {
159		return str.trim ? str.trim() : str.replace(/^\s+|\s+$/g, '');
160	},
161
162	// @function splitWords(str: String): String[]
163	// Trims and splits the string on whitespace and returns the array of parts.
164	splitWords: function (str) {
165		return L.Util.trim(str).split(/\s+/);
166	},
167
168	// @function setOptions(obj: Object, options: Object): Object
169	// Merges the given properties to the `options` of the `obj` object, returning the resulting options. See `Class options`. Has an `L.setOptions` shortcut.
170	setOptions: function (obj, options) {
171		if (!obj.hasOwnProperty('options')) {
172			obj.options = obj.options ? L.Util.create(obj.options) : {};
173		}
174		for (var i in options) {
175			obj.options[i] = options[i];
176		}
177		return obj.options;
178	},
179
180	// @function getParamString(obj: Object, existingUrl?: String, uppercase?: Boolean): String
181	// Converts an object into a parameter URL string, e.g. `{a: "foo", b: "bar"}`
182	// translates to `'?a=foo&b=bar'`. If `existingUrl` is set, the parameters will
183	// be appended at the end. If `uppercase` is `true`, the parameter names will
184	// be uppercased (e.g. `'?A=foo&B=bar'`)
185	getParamString: function (obj, existingUrl, uppercase) {
186		var params = [];
187		for (var i in obj) {
188			params.push(encodeURIComponent(uppercase ? i.toUpperCase() : i) + '=' + encodeURIComponent(obj[i]));
189		}
190		return ((!existingUrl || existingUrl.indexOf('?') === -1) ? '?' : '&') + params.join('&');
191	},
192
193	// @function template(str: String, data: Object): String
194	// Simple templating facility, accepts a template string of the form `'Hello {a}, {b}'`
195	// and a data object like `{a: 'foo', b: 'bar'}`, returns evaluated string
196	// `('Hello foo, bar')`. You can also specify functions instead of strings for
197	// data values — they will be evaluated passing `data` as an argument.
198	template: function (str, data) {
199		return str.replace(L.Util.templateRe, function (str, key) {
200			var value = data[key];
201
202			if (value === undefined) {
203				throw new Error('No value provided for variable ' + str);
204
205			} else if (typeof value === 'function') {
206				value = value(data);
207			}
208			return value;
209		});
210	},
211
212	templateRe: /\{ *([\w_\-]+) *\}/g,
213
214	// @function isArray(obj): Boolean
215	// Compatibility polyfill for [Array.isArray](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Array/isArray)
216	isArray: Array.isArray || function (obj) {
217		return (Object.prototype.toString.call(obj) === '[object Array]');
218	},
219
220	// @function indexOf(array: Array, el: Object): Number
221	// Compatibility polyfill for [Array.prototype.indexOf](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Array/indexOf)
222	indexOf: function (array, el) {
223		for (var i = 0; i < array.length; i++) {
224			if (array[i] === el) { return i; }
225		}
226		return -1;
227	},
228
229	// @property emptyImageUrl: String
230	// Data URI string containing a base64-encoded empty GIF image.
231	// Used as a hack to free memory from unused images on WebKit-powered
232	// mobile devices (by setting image `src` to this string).
233	emptyImageUrl: 'data:image/gif;base64,R0lGODlhAQABAAD/ACwAAAAAAQABAAACADs='
234};
235
236(function () {
237	// inspired by http://paulirish.com/2011/requestanimationframe-for-smart-animating/
238
239	function getPrefixed(name) {
240		return window['webkit' + name] || window['moz' + name] || window['ms' + name];
241	}
242
243	var lastTime = 0;
244
245	// fallback for IE 7-8
246	function timeoutDefer(fn) {
247		var time = +new Date(),
248		    timeToCall = Math.max(0, 16 - (time - lastTime));
249
250		lastTime = time + timeToCall;
251		return window.setTimeout(fn, timeToCall);
252	}
253
254	var requestFn = window.requestAnimationFrame || getPrefixed('RequestAnimationFrame') || timeoutDefer,
255	    cancelFn = window.cancelAnimationFrame || getPrefixed('CancelAnimationFrame') ||
256	               getPrefixed('CancelRequestAnimationFrame') || function (id) { window.clearTimeout(id); };
257
258
259	// @function requestAnimFrame(fn: Function, context?: Object, immediate?: Boolean): Number
260	// Schedules `fn` to be executed when the browser repaints. `fn` is bound to
261	// `context` if given. When `immediate` is set, `fn` is called immediately if
262	// the browser doesn't have native support for
263	// [`window.requestAnimationFrame`](https://developer.mozilla.org/docs/Web/API/window/requestAnimationFrame),
264	// otherwise it's delayed. Returns a request ID that can be used to cancel the request.
265	L.Util.requestAnimFrame = function (fn, context, immediate) {
266		if (immediate && requestFn === timeoutDefer) {
267			fn.call(context);
268		} else {
269			return requestFn.call(window, L.bind(fn, context));
270		}
271	};
272
273	// @function cancelAnimFrame(id: Number): undefined
274	// Cancels a previous `requestAnimFrame`. See also [window.cancelAnimationFrame](https://developer.mozilla.org/docs/Web/API/window/cancelAnimationFrame).
275	L.Util.cancelAnimFrame = function (id) {
276		if (id) {
277			cancelFn.call(window, id);
278		}
279	};
280})();
281
282// shortcuts for most used utility functions
283L.extend = L.Util.extend;
284L.bind = L.Util.bind;
285L.stamp = L.Util.stamp;
286L.setOptions = L.Util.setOptions;
287
288
289
290
291// @class Class
292// @aka L.Class
293
294// @section
295// @uninheritable
296
297// Thanks to John Resig and Dean Edwards for inspiration!
298
299L.Class = function () {};
300
301L.Class.extend = function (props) {
302
303	// @function extend(props: Object): Function
304	// [Extends the current class](#class-inheritance) given the properties to be included.
305	// Returns a Javascript function that is a class constructor (to be called with `new`).
306	var NewClass = function () {
307
308		// call the constructor
309		if (this.initialize) {
310			this.initialize.apply(this, arguments);
311		}
312
313		// call all constructor hooks
314		this.callInitHooks();
315	};
316
317	var parentProto = NewClass.__super__ = this.prototype;
318
319	var proto = L.Util.create(parentProto);
320	proto.constructor = NewClass;
321
322	NewClass.prototype = proto;
323
324	// inherit parent's statics
325	for (var i in this) {
326		if (this.hasOwnProperty(i) && i !== 'prototype') {
327			NewClass[i] = this[i];
328		}
329	}
330
331	// mix static properties into the class
332	if (props.statics) {
333		L.extend(NewClass, props.statics);
334		delete props.statics;
335	}
336
337	// mix includes into the prototype
338	if (props.includes) {
339		L.Util.extend.apply(null, [proto].concat(props.includes));
340		delete props.includes;
341	}
342
343	// merge options
344	if (proto.options) {
345		props.options = L.Util.extend(L.Util.create(proto.options), props.options);
346	}
347
348	// mix given properties into the prototype
349	L.extend(proto, props);
350
351	proto._initHooks = [];
352
353	// add method for calling all hooks
354	proto.callInitHooks = function () {
355
356		if (this._initHooksCalled) { return; }
357
358		if (parentProto.callInitHooks) {
359			parentProto.callInitHooks.call(this);
360		}
361
362		this._initHooksCalled = true;
363
364		for (var i = 0, len = proto._initHooks.length; i < len; i++) {
365			proto._initHooks[i].call(this);
366		}
367	};
368
369	return NewClass;
370};
371
372
373// @function include(properties: Object): this
374// [Includes a mixin](#class-includes) into the current class.
375L.Class.include = function (props) {
376	L.extend(this.prototype, props);
377	return this;
378};
379
380// @function mergeOptions(options: Object): this
381// [Merges `options`](#class-options) into the defaults of the class.
382L.Class.mergeOptions = function (options) {
383	L.extend(this.prototype.options, options);
384	return this;
385};
386
387// @function addInitHook(fn: Function): this
388// Adds a [constructor hook](#class-constructor-hooks) to the class.
389L.Class.addInitHook = function (fn) { // (Function) || (String, args...)
390	var args = Array.prototype.slice.call(arguments, 1);
391
392	var init = typeof fn === 'function' ? fn : function () {
393		this[fn].apply(this, args);
394	};
395
396	this.prototype._initHooks = this.prototype._initHooks || [];
397	this.prototype._initHooks.push(init);
398	return this;
399};
400
401
402
403/*
404 * @class Evented
405 * @aka L.Evented
406 * @inherits Class
407 *
408 * 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).
409 *
410 * @example
411 *
412 * ```js
413 * map.on('click', function(e) {
414 * 	alert(e.latlng);
415 * } );
416 * ```
417 *
418 * Leaflet deals with event listeners by reference, so if you want to add a listener and then remove it, define it as a function:
419 *
420 * ```js
421 * function onClick(e) { ... }
422 *
423 * map.on('click', onClick);
424 * map.off('click', onClick);
425 * ```
426 */
427
428
429L.Evented = L.Class.extend({
430
431	/* @method on(type: String, fn: Function, context?: Object): this
432	 * 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'`).
433	 *
434	 * @alternative
435	 * @method on(eventMap: Object): this
436	 * Adds a set of type/listener pairs, e.g. `{click: onClick, mousemove: onMouseMove}`
437	 */
438	on: function (types, fn, context) {
439
440		// types can be a map of types/handlers
441		if (typeof types === 'object') {
442			for (var type in types) {
443				// we don't process space-separated events here for performance;
444				// it's a hot path since Layer uses the on(obj) syntax
445				this._on(type, types[type], fn);
446			}
447
448		} else {
449			// types can be a string of space-separated words
450			types = L.Util.splitWords(types);
451
452			for (var i = 0, len = types.length; i < len; i++) {
453				this._on(types[i], fn, context);
454			}
455		}
456
457		return this;
458	},
459
460	/* @method off(type: String, fn?: Function, context?: Object): this
461	 * 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.
462	 *
463	 * @alternative
464	 * @method off(eventMap: Object): this
465	 * Removes a set of type/listener pairs.
466	 *
467	 * @alternative
468	 * @method off: this
469	 * Removes all listeners to all events on the object.
470	 */
471	off: function (types, fn, context) {
472
473		if (!types) {
474			// clear all listeners if called without arguments
475			delete this._events;
476
477		} else if (typeof types === 'object') {
478			for (var type in types) {
479				this._off(type, types[type], fn);
480			}
481
482		} else {
483			types = L.Util.splitWords(types);
484
485			for (var i = 0, len = types.length; i < len; i++) {
486				this._off(types[i], fn, context);
487			}
488		}
489
490		return this;
491	},
492
493	// attach listener (without syntactic sugar now)
494	_on: function (type, fn, context) {
495		this._events = this._events || {};
496
497		/* get/init listeners for type */
498		var typeListeners = this._events[type];
499		if (!typeListeners) {
500			typeListeners = [];
501			this._events[type] = typeListeners;
502		}
503
504		if (context === this) {
505			// Less memory footprint.
506			context = undefined;
507		}
508		var newListener = {fn: fn, ctx: context},
509		    listeners = typeListeners;
510
511		// check if fn already there
512		for (var i = 0, len = listeners.length; i < len; i++) {
513			if (listeners[i].fn === fn && listeners[i].ctx === context) {
514				return;
515			}
516		}
517
518		listeners.push(newListener);
519	},
520
521	_off: function (type, fn, context) {
522		var listeners,
523		    i,
524		    len;
525
526		if (!this._events) { return; }
527
528		listeners = this._events[type];
529
530		if (!listeners) {
531			return;
532		}
533
534		if (!fn) {
535			// Set all removed listeners to noop so they are not called if remove happens in fire
536			for (i = 0, len = listeners.length; i < len; i++) {
537				listeners[i].fn = L.Util.falseFn;
538			}
539			// clear all listeners for a type if function isn't specified
540			delete this._events[type];
541			return;
542		}
543
544		if (context === this) {
545			context = undefined;
546		}
547
548		if (listeners) {
549
550			// find fn and remove it
551			for (i = 0, len = listeners.length; i < len; i++) {
552				var l = listeners[i];
553				if (l.ctx !== context) { continue; }
554				if (l.fn === fn) {
555
556					// set the removed listener to noop so that's not called if remove happens in fire
557					l.fn = L.Util.falseFn;
558
559					if (this._firingCount) {
560						/* copy array in case events are being fired */
561						this._events[type] = listeners = listeners.slice();
562					}
563					listeners.splice(i, 1);
564
565					return;
566				}
567			}
568		}
569	},
570
571	// @method fire(type: String, data?: Object, propagate?: Boolean): this
572	// Fires an event of the specified type. You can optionally provide an data
573	// object — the first argument of the listener function will contain its
574	// properties. The event can optionally be propagated to event parents.
575	fire: function (type, data, propagate) {
576		if (!this.listens(type, propagate)) { return this; }
577
578		var event = L.Util.extend({}, data, {type: type, target: this});
579
580		if (this._events) {
581			var listeners = this._events[type];
582
583			if (listeners) {
584				this._firingCount = (this._firingCount + 1) || 1;
585				for (var i = 0, len = listeners.length; i < len; i++) {
586					var l = listeners[i];
587					l.fn.call(l.ctx || this, event);
588				}
589
590				this._firingCount--;
591			}
592		}
593
594		if (propagate) {
595			// propagate the event to parents (set with addEventParent)
596			this._propagateEvent(event);
597		}
598
599		return this;
600	},
601
602	// @method listens(type: String): Boolean
603	// Returns `true` if a particular event type has any listeners attached to it.
604	listens: function (type, propagate) {
605		var listeners = this._events && this._events[type];
606		if (listeners && listeners.length) { return true; }
607
608		if (propagate) {
609			// also check parents for listeners if event propagates
610			for (var id in this._eventParents) {
611				if (this._eventParents[id].listens(type, propagate)) { return true; }
612			}
613		}
614		return false;
615	},
616
617	// @method once(…): this
618	// Behaves as [`on(…)`](#evented-on), except the listener will only get fired once and then removed.
619	once: function (types, fn, context) {
620
621		if (typeof types === 'object') {
622			for (var type in types) {
623				this.once(type, types[type], fn);
624			}
625			return this;
626		}
627
628		var handler = L.bind(function () {
629			this
630			    .off(types, fn, context)
631			    .off(types, handler, context);
632		}, this);
633
634		// add a listener that's executed once and removed after that
635		return this
636		    .on(types, fn, context)
637		    .on(types, handler, context);
638	},
639
640	// @method addEventParent(obj: Evented): this
641	// Adds an event parent - an `Evented` that will receive propagated events
642	addEventParent: function (obj) {
643		this._eventParents = this._eventParents || {};
644		this._eventParents[L.stamp(obj)] = obj;
645		return this;
646	},
647
648	// @method removeEventParent(obj: Evented): this
649	// Removes an event parent, so it will stop receiving propagated events
650	removeEventParent: function (obj) {
651		if (this._eventParents) {
652			delete this._eventParents[L.stamp(obj)];
653		}
654		return this;
655	},
656
657	_propagateEvent: function (e) {
658		for (var id in this._eventParents) {
659			this._eventParents[id].fire(e.type, L.extend({layer: e.target}, e), true);
660		}
661	}
662});
663
664var proto = L.Evented.prototype;
665
666// aliases; we should ditch those eventually
667
668// @method addEventListener(…): this
669// Alias to [`on(…)`](#evented-on)
670proto.addEventListener = proto.on;
671
672// @method removeEventListener(…): this
673// Alias to [`off(…)`](#evented-off)
674
675// @method clearAllEventListeners(…): this
676// Alias to [`off()`](#evented-off)
677proto.removeEventListener = proto.clearAllEventListeners = proto.off;
678
679// @method addOneTimeEventListener(…): this
680// Alias to [`once(…)`](#evented-once)
681proto.addOneTimeEventListener = proto.once;
682
683// @method fireEvent(…): this
684// Alias to [`fire(…)`](#evented-fire)
685proto.fireEvent = proto.fire;
686
687// @method hasEventListeners(…): Boolean
688// Alias to [`listens(…)`](#evented-listens)
689proto.hasEventListeners = proto.listens;
690
691L.Mixin = {Events: proto};
692
693
694
695/*
696 * @namespace Browser
697 * @aka L.Browser
698 *
699 * A namespace with static properties for browser/feature detection used by Leaflet internally.
700 *
701 * @example
702 *
703 * ```js
704 * if (L.Browser.ielt9) {
705 *   alert('Upgrade your browser, dude!');
706 * }
707 * ```
708 */
709
710(function () {
711
712	var ua = navigator.userAgent.toLowerCase(),
713	    doc = document.documentElement,
714
715	    ie = 'ActiveXObject' in window,
716
717	    webkit    = ua.indexOf('webkit') !== -1,
718	    phantomjs = ua.indexOf('phantom') !== -1,
719	    android23 = ua.search('android [23]') !== -1,
720	    chrome    = ua.indexOf('chrome') !== -1,
721	    gecko     = ua.indexOf('gecko') !== -1  && !webkit && !window.opera && !ie,
722
723	    win = navigator.platform.indexOf('Win') === 0,
724
725	    mobile = typeof orientation !== 'undefined' || ua.indexOf('mobile') !== -1,
726	    msPointer = !window.PointerEvent && window.MSPointerEvent,
727	    pointer = window.PointerEvent || msPointer,
728
729	    ie3d = ie && ('transition' in doc.style),
730	    webkit3d = ('WebKitCSSMatrix' in window) && ('m11' in new window.WebKitCSSMatrix()) && !android23,
731	    gecko3d = 'MozPerspective' in doc.style,
732	    opera12 = 'OTransition' in doc.style;
733
734
735	var touch = !window.L_NO_TOUCH && (pointer || 'ontouchstart' in window ||
736			(window.DocumentTouch && document instanceof window.DocumentTouch));
737
738	L.Browser = {
739
740		// @property ie: Boolean
741		// `true` for all Internet Explorer versions (not Edge).
742		ie: ie,
743
744		// @property ielt9: Boolean
745		// `true` for Internet Explorer versions less than 9.
746		ielt9: ie && !document.addEventListener,
747
748		// @property edge: Boolean
749		// `true` for the Edge web browser.
750		edge: 'msLaunchUri' in navigator && !('documentMode' in document),
751
752		// @property webkit: Boolean
753		// `true` for webkit-based browsers like Chrome and Safari (including mobile versions).
754		webkit: webkit,
755
756		// @property gecko: Boolean
757		// `true` for gecko-based browsers like Firefox.
758		gecko: gecko,
759
760		// @property android: Boolean
761		// `true` for any browser running on an Android platform.
762		android: ua.indexOf('android') !== -1,
763
764		// @property android23: Boolean
765		// `true` for browsers running on Android 2 or Android 3.
766		android23: android23,
767
768		// @property chrome: Boolean
769		// `true` for the Chrome browser.
770		chrome: chrome,
771
772		// @property safari: Boolean
773		// `true` for the Safari browser.
774		safari: !chrome && ua.indexOf('safari') !== -1,
775
776
777		// @property win: Boolean
778		// `true` when the browser is running in a Windows platform
779		win: win,
780
781
782		// @property ie3d: Boolean
783		// `true` for all Internet Explorer versions supporting CSS transforms.
784		ie3d: ie3d,
785
786		// @property webkit3d: Boolean
787		// `true` for webkit-based browsers supporting CSS transforms.
788		webkit3d: webkit3d,
789
790		// @property gecko3d: Boolean
791		// `true` for gecko-based browsers supporting CSS transforms.
792		gecko3d: gecko3d,
793
794		// @property opera12: Boolean
795		// `true` for the Opera browser supporting CSS transforms (version 12 or later).
796		opera12: opera12,
797
798		// @property any3d: Boolean
799		// `true` for all browsers supporting CSS transforms.
800		any3d: !window.L_DISABLE_3D && (ie3d || webkit3d || gecko3d) && !opera12 && !phantomjs,
801
802
803		// @property mobile: Boolean
804		// `true` for all browsers running in a mobile device.
805		mobile: mobile,
806
807		// @property mobileWebkit: Boolean
808		// `true` for all webkit-based browsers in a mobile device.
809		mobileWebkit: mobile && webkit,
810
811		// @property mobileWebkit3d: Boolean
812		// `true` for all webkit-based browsers in a mobile device supporting CSS transforms.
813		mobileWebkit3d: mobile && webkit3d,
814
815		// @property mobileOpera: Boolean
816		// `true` for the Opera browser in a mobile device.
817		mobileOpera: mobile && window.opera,
818
819		// @property mobileGecko: Boolean
820		// `true` for gecko-based browsers running in a mobile device.
821		mobileGecko: mobile && gecko,
822
823
824		// @property touch: Boolean
825		// `true` for all browsers supporting [touch events](https://developer.mozilla.org/docs/Web/API/Touch_events).
826		// This does not necessarily mean that the browser is running in a computer with
827		// a touchscreen, it only means that the browser is capable of understanding
828		// touch events.
829		touch: !!touch,
830
831		// @property msPointer: Boolean
832		// `true` for browsers implementing the Microsoft touch events model (notably IE10).
833		msPointer: !!msPointer,
834
835		// @property pointer: Boolean
836		// `true` for all browsers supporting [pointer events](https://msdn.microsoft.com/en-us
vendor: 4,961 bytes, lines 836-1018
836/library/dn433244%28v=vs.85%29.aspx).
837		pointer: !!pointer,
838
839
840		// @property retina: Boolean
841		// `true` for browsers on a high-resolution "retina" screen.
842		retina: (window.devicePixelRatio || (window.screen.deviceXDPI / window.screen.logicalXDPI)) > 1
843	};
844
845}());
846
847
848
849/*
850 * @class Point
851 * @aka L.Point
852 *
853 * Represents a point with `x` and `y` coordinates in pixels.
854 *
855 * @example
856 *
857 * ```js
858 * var point = L.point(200, 300);
859 * ```
860 *
861 * 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:
862 *
863 * ```js
864 * map.panBy([200, 300]);
865 * map.panBy(L.point(200, 300));
866 * ```
867 */
868
869L.Point = function (x, y, round) {
870	// @property x: Number; The `x` coordinate of the point
871	this.x = (round ? Math.round(x) : x);
872	// @property y: Number; The `y` coordinate of the point
873	this.y = (round ? Math.round(y) : y);
874};
875
876L.Point.prototype = {
877
878	// @method clone(): Point
879	// Returns a copy of the current point.
880	clone: function () {
881		return new L.Point(this.x, this.y);
882	},
883
884	// @method add(otherPoint: Point): Point
885	// Returns the result of addition of the current and the given points.
886	add: function (point) {
887		// non-destructive, returns a new point
888		return this.clone()._add(L.point(point));
889	},
890
891	_add: function (point) {
892		// destructive, used directly for performance in situations where it's safe to modify existing point
893		this.x += point.x;
894		this.y += point.y;
895		return this;
896	},
897
898	// @method subtract(otherPoint: Point): Point
899	// Returns the result of subtraction of the given point from the current.
900	subtract: function (point) {
901		return this.clone()._subtract(L.point(point));
902	},
903
904	_subtract: function (point) {
905		this.x -= point.x;
906		this.y -= point.y;
907		return this;
908	},
909
910	// @method divideBy(num: Number): Point
911	// Returns the result of division of the current point by the given number.
912	divideBy: function (num) {
913		return this.clone()._divideBy(num);
914	},
915
916	_divideBy: function (num) {
917		this.x /= num;
918		this.y /= num;
919		return this;
920	},
921
922	// @method multiplyBy(num: Number): Point
923	// Returns the result of multiplication of the current point by the given number.
924	multiplyBy: function (num) {
925		return this.clone()._multiplyBy(num);
926	},
927
928	_multiplyBy: function (num) {
929		this.x *= num;
930		this.y *= num;
931		return this;
932	},
933
934	// @method scaleBy(scale: Point): Point
935	// Multiply each coordinate of the current point by each coordinate of
936	// `scale`. In linear algebra terms, multiply the point by the
937	// [scaling matrix](https://en.wikipedia.org/wiki/Scaling_%28geometry%29#Matrix_representation)
938	// defined by `scale`.
939	scaleBy: function (point) {
940		return new L.Point(this.x * point.x, this.y * point.y);
941	},
942
943	// @method unscaleBy(scale: Point): Point
944	// Inverse of `scaleBy`. Divide each coordinate of the current point by
945	// each coordinate of `scale`.
946	unscaleBy: function (point) {
947		return new L.Point(this.x / point.x, this.y / point.y);
948	},
949
950	// @method round(): Point
951	// Returns a copy of the current point with rounded coordinates.
952	round: function () {
953		return this.clone()._round();
954	},
955
956	_round: function () {
957		this.x = Math.round(this.x);
958		this.y = Math.round(this.y);
959		return this;
960	},
961
962	// @method floor(): Point
963	// Returns a copy of the current point with floored coordinates (rounded down).
964	floor: function () {
965		return this.clone()._floor();
966	},
967
968	_floor: function () {
969		this.x = Math.floor(this.x);
970		this.y = Math.floor(this.y);
971		return this;
972	},
973
974	// @method ceil(): Point
975	// Returns a copy of the current point with ceiled coordinates (rounded up).
976	ceil: function () {
977		return this.clone()._ceil();
978	},
979
980	_ceil: function () {
981		this.x = Math.ceil(this.x);
982		this.y = Math.ceil(this.y);
983		return this;
984	},
985
986	// @method distanceTo(otherPoint: Point): Number
987	// Returns the cartesian distance between the current and the given points.
988	distanceTo: function (point) {
989		point = L.point(point);
990
991		var x = point.x - this.x,
992		    y = point.y - this.y;
993
994		return Math.sqrt(x * x + y * y);
995	},
996
997	// @method equals(otherPoint: Point): Boolean
998	// Returns `true` if the given point has the same coordinates.
999	equals: function (point) {
1000		point = L.point(point);
1001
1002		return point.x === this.x &&
1003		       point.y === this.y;
1004	},
1005
1006	// @method contains(otherPoint: Point): Boolean
1007	// Returns `true` if both coordinates of the given point are less than the corresponding current point coordinates (in absolute values).
1008	contains: function (point) {
1009		point = L.point(point);
1010
1011		return Math.abs(point.x) <= Math.abs(this.x) &&
1012		       Math.abs(point.y) <= Math.abs(this.y);
1013	},
1014
1015	// @method toString(): String
1016	// Returns a string representation of the point for debugging purposes.
1017	toString: function () {
1018		return 'Point(' +
1019		        L.Util.formatNum(this.x) + ', ' +
1020		        L.Util.formatNum(this.y) + ')';
1021	}
1022};
1023
1024// @factory L.point(x: Number, y: Number, round?: Boolean)
1025// Creates a Point object with the given `x` and `y` coordinates. If optional `round` is set to true, rounds the `x` and `y` values.
1026
1027// @alternative
1028// @factory L.point(coords: Number[])
1029// Expects an array of the form `[x, y]` instead.
1030
1031// @alternative
1032// @factory L.point(coords: Object)
1033// Expects a plain object of the form `{x: Number, y: Number}` instead.
1034L.point = function (x, y, round) {
1035	if (x instanceof L.Point) {
1036		return x;
1037	}
1038	if (L.Util.isArray(x)) {
1039		return new L.Point(x[0], x[1]);
1040	}
1041	if (x === undefined || x === null) {
1042		return x;
1043	}
1044	if (typeof x === 'object' && 'x' in x && 'y' in x) {
1045		return new L.Point(x.x, x.y);
1046	}
1047	return new L.Point(x, y, round);
1048};
1049
1050
1051
1052/*
1053 * @class Bounds
1054 * @aka L.Bounds
1055 *
1056 * Represents a rectangular area in pixel coordinates.
1057 *
1058 * @example
1059 *
1060 * ```js
1061 * var p1 = L.point(10, 10),
1062 * p2 = L.point(40, 60),
1063 * bounds = L.bounds(p1, p2);
1064 * ```
1065 *
1066 * 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:
1067 *
1068 * ```js
1069 * otherBounds.intersects([[10, 10], [40, 60]]);
1070 * ```
1071 */
1072
1073L.Bounds = function (a, b) {
1074	if (!a) { return; }
1075
1076	var points = b ? [a, b] : a;
1077
1078	for (var i = 0, len = points.length; i < len; i++) {
1079		this.extend(points[i]);
1080	}
1081};
1082
1083L.Bounds.prototype = {
1084	// @method extend(point: Point): this
1085	// Extends the bounds to contain the given point.
1086	extend: function (point) { // (Point)
1087		point = L.point(point);
1088
1089		// @property min: Point
1090		// The top left corner of the rectangle.
1091		// @property max: Point
1092		// The bottom right corner of the rectangle.
1093		if (!this.min && !this.max) {
1094			this.min = point.clone();
1095			this.max = point.clone();
1096		} else {
1097			this.min.x = Math.min(point.x, this.min.x);
1098			this.max.x = Math.max(point.x, this.max.x);
1099			this.min.y = Math.min(point.y, this.min.y);
1100			this.max.y = Math.max(point.y, this.max.y);
1101		}
1102		return this;
1103	},
1104
1105	// @method getCenter(round?: Boolean): Point
1106	// Returns the center point of the bounds.
1107	getCenter: function (round) {
1108		return new L.Point(
1109		        (this.min.x + this.max.x) / 2,
1110		        (this.min.y + this.max.y) / 2, round);
1111	},
1112
1113	// @method getBottomLeft(): Point
1114	// Returns the bottom-left point of the bounds.
1115	getBottomLeft: function () {
1116		return new L.Point(this.min.x, this.max.y);
1117	},
1118
1119	// @method getTopRight(): Point
1120	// Returns the top-right point of the bounds.
1121	getTopRight: function () { // -> Point
1122		return new L.Point(this.max.x, this.min.y);
1123	},
1124
1125	// @method getSize(): Point
1126	// Returns the size of the given bounds
1127	getSize: function () {
1128		return this.max.subtract(this.min);
1129	},
1130
1131	// @method contains(otherBounds: Bounds): Boolean
1132	// Returns `true` if the rectangle contains the given one.
1133	// @alternative
1134	// @method contains(point: Point): Boolean
1135	// Returns `true` if the rectangle contains the given point.
1136	contains: function (obj) {
1137		var min, max;
1138
1139		if (typeof obj[0] === 'number' || obj instanceof L.Point) {
1140			obj = L.point(obj);
1141		} else {
1142			obj = L.bounds(obj);
1143		}
1144
1145		if (obj instanceof L.Bounds) {
1146			min = obj.min;
1147			max = obj.max;
1148		} else {
1149			min = max = obj;
1150		}
1151
1152		return (min.x >= this.min.x) &&
1153		       (max.x <= this.max.x) &&
1154		       (min.y >= this.min.y) &&
1155		       (max.y <= this.max.y);
1156	},
1157
1158	// @method intersects(otherBounds: Bounds): Boolean
1159	// Returns `true` if the rectangle intersects the given bounds. Two bounds
1160	// intersect if they have at least one point in common.
1161	intersects: function (bounds) { // (Bounds) -> Boolean
1162		bounds = L.bounds(bounds);
1163
1164		var min = this.min,
1165		    max = this.max,
1166		    min2 = bounds.min,
1167		    max2 = bounds.max,
1168		    xIntersects = (max2.x >= min.x) && (min2.x <= max.x),
1169		    yIntersects = (max2.y >= min.y) && (min2.y <= max.y);
1170
1171		return xIntersects && yIntersects;
1172	},
1173
1174	// @method overlaps(otherBounds: Bounds): Boolean
1175	// Returns `true` if the rectangle overlaps the given bounds. Two bounds
1176	// overlap if their intersection is an area.
1177	overlaps: function (bounds) { // (Bounds) -> Boolean
1178		bounds = L.bounds(bounds);
1179
1180		var min = this.min,
1181		    max = this.max,
1182		    min2 = bounds.min,
1183		    max2 = bounds.max,
1184		    xOverlaps = (max2.x > min.x) && (min2.x < max.x),
1185		    yOverlaps = (max2.y > min.y) && (min2.y < max.y);
1186
1187		return xOverlaps && yOverlaps;
1188	},
1189
1190	isValid: function () {
1191		return !!(this.min && this.max);
1192	}
1193};
1194
1195
1196// @factory L.bounds(topLeft: Point, bottomRight: Point)
1197// Creates a Bounds object from two coordinates (usually top-left and bottom-right corners).
1198// @alternative
1199// @factory L.bounds(points: Point[])
1200// Creates a Bounds object from the points it contains
1201L.bounds = function (a, b) {
1202	if (!a || a instanceof L.Bounds) {
1203		return a;
1204	}
1205	return new L.Bounds(a, b);
1206};
1207
1208
1209
1210/*
1211 * @class Transformation
1212 * @aka L.Transformation
1213 *
1214 * Represents an affine transformation: a set of coefficients `a`, `b`, `c`, `d`
1215 * for transforming a point of a form `(x, y)` into `(a*x + b, c*y + d)` and doing
1216 * the reverse. Used by Leaflet in its projections code.
1217 *
1218 * @example
1219 *
1220 * ```js
1221 * var transformation = new L.Transformation(2, 5, -1, 10),
1222 * 	p = L.point(1, 2),
1223 * 	p2 = transformation.transform(p), //  L.point(7, 8)
1224 * 	p3 = transformation.untransform(p2); //  L.point(1, 2)
1225 * ```
1226 */
1227
1228
1229// factory new L.Transformation(a: Number, b: Number, c: Number, d: Number)
1230// Creates a `Transformation` object with the given coefficients.
1231L.Transformation = function (a, b, c, d) {
1232	this._a = a;
1233	this._b = b;
1234	this._c = c;
1235	this._d = d;
1236};
1237
1238L.Transformation.prototype = {
1239	// @method transform(point: Point, scale?: Number): Point
1240	// Returns a transformed point, optionally multiplied by the given scale.
1241	// Only accepts actual `L.Point` instances, not arrays.
1242	transform: function (point, scale) { // (Point, Number) -> Point
1243		return this._transform(point.clone(), scale);
1244	},
1245
1246	// destructive transform (faster)
1247	_transform: function (point, scale) {
1248		scale = scale || 1;
1249		point.x = scale * (this._a * point.x + this._b);
1250		point.y = scale * (this._c * point.y + this._d);
1251		return point;
1252	},
1253
1254	// @method untransform(point: Point, scale?: Number): Point
1255	// Returns the reverse transformation of the given point, optionally divided
1256	// by the given scale. Only accepts actual `L.Point` instances, not arrays.
1257	untransform: function (point, scale) {
1258		scale = scale || 1;
1259		return new L.Point(
1260		        (point.x / scale - this._b) / this._a,
1261		        (point.y / scale - this._d) / this._c);
1262	}
1263};
1264
1265
1266
1267/*
1268 * @namespace DomUtil
1269 *
1270 * Utility functions to work with the [DOM](https://developer.mozilla.org/docs/Web/API/Document_Object_Model)
1271 * tree, used by Leaflet internally.
1272 *
1273 * Most functions expecting or returning a `HTMLElement` also work for
1274 * SVG elements. The only difference is that classes refer to CSS classes
1275 * in HTML and SVG classes in SVG.
1276 */
1277
1278L.DomUtil = {
1279
1280	// @function get(id: String|HTMLElement): HTMLElement
1281	// Returns an element given its DOM id, or returns the element itself
1282	// if it was passed directly.
1283	get: function (id) {
1284		return typeof id === 'string' ? document.getElementById(id) : id;
1285	},
1286
1287	// @function getStyle(el: HTMLElement, styleAttrib: String): String
1288	// Returns the value for a certain style attribute on an element,
1289	// including computed values or values set through CSS.
1290	getStyle: function (el, style) {
1291
1292		var value = el.style[style] || (el.currentStyle && el.currentStyle[style]);
1293
1294		if ((!value || value === 'auto') && document.defaultView) {
1295			var css = document.defaultView.getComputedStyle(el, null);
1296			value = css ? css[style] : null;
1297		}
1298
1299		return value === 'auto' ? null : value;
1300	},
1301
1302	// @function create(tagName: String, className?: String, container?: HTMLElement): HTMLElement
1303	// Creates an HTML element with `tagName`, sets its class to `className`, and optionally appends it to `container` element.
1304	create: function (tagName, className, container) {
1305
1306		var el = document.createElement(tagName);
1307		el.className = className || '';
1308
1309		if (container) {
1310			container.appendChild(el);
1311		}
1312
1313		return el;
1314	},
1315
1316	// @function remove(el: HTMLElement)
1317	// Removes `el` from its parent element
1318	remove: function (el) {
1319		var parent = el.parentNode;
1320		if (parent) {
1321			parent.removeChild(el);
1322		}
1323	},
1324
1325	// @function empty(el: HTMLElement)
1326	// Removes all of `el`'s children elements from `el`
1327	empty: function (el) {
1328		while (el.firstChild) {
1329			el.removeChild(el.firstChild);
1330		}
1331	},
1332
1333	// @function toFront(el: HTMLElement)
1334	// Makes `el` the last children of its parent, so it renders in front of the other children.
1335	toFront: function (el) {
1336		el.parentNode.appendChild(el);
1337	},
1338
1339	// @function toBack(el: HTMLElement)
1340	// Makes `el` the first children of its parent, so it renders back from the other children.
1341	toBack: function (el) {
1342		var parent = el.parentNode;
1343		parent.insertBefore(el, parent.firstChild);
1344	},
1345
1346	// @function hasClass(el: HTMLElement, name: String): Boolean
1347	// Returns `true` if the element's class attribute contains `name`.
1348	hasClass: function (el, name) {
1349		if (el.classList !== undefined) {
1350			return el.classList.contains(name);
1351		}
1352		var className = L.DomUtil.getClass(el);
1353		return className.length > 0 && new RegExp('(^|\\s)' + name + '(\\s|$)').test(className);
1354	},
1355
1356	// @function addClass(el: HTMLElement, name: String)
1357	// Adds `name` to the element's class attribute.
1358	addClass: function (el, name) {
1359		if (el.classList !== undefined) {
1360			var classes = L.Util.splitWords(name);
1361			for (var i = 0, len = classes.length; i < len; i++) {
1362				el.classList.add(classes[i]);
1363			}
1364		} else if (!L.DomUtil.hasClass(el, name)) {
1365			var className = L.DomUtil.getClass(el);
1366			L.DomUtil.setClass(el, (className ? className + ' ' : '') + name);
1367		}
1368	},
1369
1370	// @function removeClass(el: HTMLElement, name: String)
1371	// Removes `name` from the element's class attribute.
1372	removeClass: function (el, name) {
1373		if (el.classList !== undefined) {
1374			el.classList.remove(name);
1375		} else {
1376			L.DomUtil.setClass(el, L.Util.trim((' ' + L.DomUtil.getClass(el) + ' ').replace(' ' + name + ' ', ' ')));
1377		}
1378	},
1379
1380	// @function setClass(el: HTMLElement, name: String)
1381	// Sets the element's class.
1382	setClass: function (el, name) {
1383		if (el.className.baseVal === undefined) {
1384			el.className = name;
1385		} else {
1386			// in case of SVG element
1387			el.className.baseVal = name;
1388		}
1389	},
1390
1391	// @function getClass(el: HTMLElement): String
1392	// Returns the element's class.
1393	getClass: function (el) {
1394		return el.className.baseVal === undefined ? el.className : el.className.baseVal;
1395	},
1396
1397	// @function setOpacity(el: HTMLElement, opacity: Number)
1398	// Set the opacity of an element (including old IE support).
1399	// `opacity` must be a number from `0` to `1`.
1400	setOpacity: function (el, value) {
1401
1402		if ('opacity' in el.style) {
1403			el.style.opacity = value;
1404
1405		} else if ('filter' in el.style) {
1406			L.DomUtil._setOpacityIE(el, value);
1407		}
1408	},
1409
1410	_setOpacityIE: function (el, value) {
1411		var filter = false,
1412		    filterName = 'DXImageTransform.Microsoft.Alpha';
1413
1414		// filters collection throws an error if we try to retrieve a filter that doesn't exist
1415		try {
1416			filter = el.filters.item(filterName);
1417		} catch (e) {
1418			// don't set opacity to 1 if we haven't already set an opacity,
1419			// it isn't needed and breaks transparent pngs.
1420			if (value === 1) { return; }
1421		}
1422
1423		value = Math.round(value * 100);
1424
1425		if (filter) {
1426			filter.Enabled = (value !== 100);
1427			filter.Opacity = value;
1428		} else {
1429			el.style.filter += ' progid:' + filterName + '(opacity=' + value + ')';
1430		}
1431	},
1432
1433	// @function testProp(props: String[]): String|false
1434	// Goes through the array of style names and returns the first name
1435	// that is a valid style name for an element. If no such name is found,
1436	// it returns false. Useful for vendor-prefixed styles like `transform`.
1437	testProp: function (props) {
1438
1439		var style = document.documentElement.style;
1440
1441		for (var i = 0; i < props.length; i++) {
1442			if (props[i] in style) {
1443				return props[i];
1444			}
1445		}
1446		return false;
1447	},
1448
1449	// @function setTransform(el: HTMLElement, offset: Point, scale?: Number)
1450	// Resets the 3D CSS transform of `el` so it is translated by `offset` pixels
1451	// and optionally scaled by `scale`. Does not have an effect if the
1452	// browser doesn't support 3D CSS transforms.
1453	setTransform: function (el, offset, scale) {
1454		var pos = offset || new L.Point(0, 0);
1455
1456		el.style[L.DomUtil.TRANSFORM] =
1457			(L.Browser.ie3d ?
1458				'translate(' + pos.x + 'px,' + pos.y + 'px)' :
1459				'translate3d(' + pos.x + 'px,' + pos.y + 'px,0)') +
1460			(scale ? ' scale(' + scale + ')' : '');
1461	},
1462
1463	// @function setPosition(el: HTMLElement, position: Point)
1464	// Sets the position of `el` to coordinates specified by `position`,
1465	// using CSS translate or top/left positioning depending on the browser
1466	// (used by Leaflet internally to position its layers).
1467	setPosition: function (el, point) { // (HTMLElement, Point[, Boolean])
1468
1469		/*eslint-disable */
1470		el._leaflet_pos = point;
1471		/*eslint-enable */
1472
1473		if (L.Browser.any3d) {
1474			L.DomUtil.setTransform(el, point);
1475		} else {
1476			el.style.left = point.x + 'px';
1477			el.style.top = point.y + 'px';
1478		}
1479	},
1480
1481	// @function getPosition(el: HTMLElement): Point
1482	// Returns the coordinates of an element previously positioned with setPosition.
1483	getPosition: function (el) {
1484		// this method is only used for elements previously positioned using setPosition,
1485		// so it's safe to cache the position for performance
1486
1487		return el._leaflet_pos || new L.Point(0, 0);
1488	}
1489};
1490
1491
1492(function () {
1493	// prefix style property names
1494
1495	// @property TRANSFORM: String
1496	// Vendor-prefixed fransform style name (e.g. `'webkitTransform'` for WebKit).
1497	L.DomUtil.TRANSFORM = L.DomUtil.testProp(
1498			['transform', 'WebkitTransform', 'OTransform', 'MozTransform', 'msTransform']);
1499
1500
1501	// webkitTransition comes first because some browser versions that drop vendor prefix don't do
1502	// the same for the transitionend event, in particular the Android 4.1 stock browser
1503
1504	// @property TRANSITION: String
1505	// Vendor-prefixed transform style name.
1506	var transition = L.DomUtil.TRANSITION = L.DomUtil.testProp(
1507			['webkitTransition', 'transition', 'OTransition', 'MozTransition', 'msTransition']);
1508
1509	L.DomUtil.TRANSITION_END =
1510			transition === 'webkitTransition' || transition === 'OTransition' ? transition + 'End' : 'transitionend';
1511
1512	// @function disableTextSelection()
1513	// Prevents the user from generating `selectstart` DOM events, usually generated
1514	// when the user drags the mouse through a page with text. Used internally
1515	// by Leaflet to override the behaviour of any click-and-drag interaction on
1516	// the map. Affects drag interactions on the whole document.
1517
1518	// @function enableTextSelection()
1519	// Cancels the effects of a previous [`L.DomUtil.disableTextSelection`](#domutil-disabletextselection).
1520	if ('onselectstart' in document) {
1521		L.DomUtil.disableTextSelection = function () {
1522			L.DomEvent.on(window, 'selectstart', L.DomEvent.preventDefault);
1523		};
1524		L.DomUtil.enableTextSelection = function () {
1525			L.DomEvent.off(window, 'selectstart', L.DomEvent.preventDefault);
1526		};
1527
1528	} else {
1529		var userSelectProperty = L.DomUtil.testProp(
1530			['userSelect', 'WebkitUserSelect', 'OUserSelect', 'MozUserSelect', 'msUserSelect']);
1531
1532		L.DomUtil.disableTextSelection = function () {
1533			if (userSelectProperty) {
1534				var style = document.documentElement.style;
1535				this._userSelect = style[userSelectProperty];
1536				style[userSelectProperty] = 'none';
1537			}
1538		};
1539		L.DomUtil.enableTextSelection = function () {
1540			if (userSelectProperty) {
1541				document.documentElement.style[userSelectProperty] = this._userSelect;
1542				delete this._userSelect;
1543			}
1544		};
1545	}
1546
1547	// @function disableImageDrag()
1548	// As [`L.DomUtil.disableTextSelection`](#domutil-disabletextselection), but
1549	// for `dragstart` DOM events, usually generated when the user drags an image.
1550	L.DomUtil.disableImageDrag = function () {
1551		L.DomEvent.on(window, 'dragstart', L.DomEvent.preventDefault);
1552	};
1553
1554	// @function enableImageDrag()
1555	// Cancels the effects of a previous [`L.DomUtil.disableImageDrag`](#domutil-disabletextselection).
1556	L.DomUtil.enableImageDrag = function () {
1557		L.DomEvent.off(window, 'dragstart', L.DomEvent.preventDefault);
1558	};
1559
1560	// @function preventOutline(el: HTMLElement)
1561	// Makes the [outline](https://developer.mozilla.org/docs/Web/CSS/outline)
1562	// of the element `el` invisible. Used internally by Leaflet to prevent
1563	// focusable elements from displaying an outline when the user performs a
1564	// drag interaction on them.
1565	L.DomUtil.preventOutline = function (element) {
1566		while (element.tabIndex === -1) {
1567			element = element.parentNode;
1568		}
1569		if (!element || !element.style) { return; }
1570		L.DomUtil.restoreOutline();
1571		this._outlineElement = element;
1572		this._outlineStyle = element.style.outline;
1573		element.style.outline = 'none';
1574		L.DomEvent.on(window, 'keydown', L.DomUtil.restoreOutline, this);
1575	};
1576
1577	// @function restoreOutline()
1578	// Cancels the effects of a previous [`L.DomUtil.preventOutline`]().
1579	L.DomUtil.restoreOutline = function () {
1580		if (!this._outlineElement) { return; }
1581		this._outlineElement.style.outline = this._outlineStyle;
1582		delete this._outlineElement;
1583		delete this._outlineStyle;
1584		L.DomEvent.off(window, 'keydown', L.DomUtil.restoreOutline, this);
1585	};
1586})();
1587
1588
1589
1590/* @class LatLng
1591 * @aka L.LatLng
1592 *
1593 * Represents a geographical point with a certain latitude and longitude.
1594 *
1595 * @example
1596 *
1597 * ```
1598 * var latlng = L.latLng(50.5, 30.5);
1599 * ```
1600 *
1601 * 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:
1602 *
1603 * ```
1604 * map.panTo([50, 30]);
1605 * map.panTo({lon: 30, lat: 50});
1606 * map.panTo({lat: 50, lng: 30});
1607 * map.panTo(L.latLng(50, 30));
1608 * ```
1609 */
1610
1611L.LatLng = function (lat, lng, alt) {
1612	if (isNaN(lat) || isNaN(lng)) {
1613		throw new Error('Invalid LatLng object: (' + lat + ', ' + lng + ')');
1614	}
1615
1616	// @property lat: Number
1617	// Latitude in degrees
1618	this.lat = +lat;
1619
1620	// @property lng: Number
1621	// Longitude in degrees
1622	this.lng = +lng;
1623
1624	// @property alt: Number
1625	// Altitude in meters (optional)
1626	if (alt !== undefined) {
1627		this.alt = +alt;
1628	}
1629};
1630
1631L.LatLng.prototype = {
1632	// @method equals(otherLatLng: LatLng, maxMargin?: Number): Boolean
1633	// Returns `true` if the given `LatLng` point is at the same position (within a small margin of error). The margin of error can be overriden by setting `maxMargin` to a small number.
1634	equals: function (obj, maxMargin) {
1635		if (!obj) { return false; }
1636
1637		obj = L.latLng(obj);
1638
1639		var margin = Math.max(
1640		        Math.abs(this.lat - obj.lat),
1641		        Math.abs(this.lng - obj.lng));
1642
1643		return margin <= (maxMargin === undefined ? 1.0E-9 : maxMargin);
1644	},
1645
1646	// @method toString(): String
1647	// Returns a string representation of the point (for debugging purposes).
1648	toString: function (precision) {
1649		return 'LatLng(' +
1650		        L.Util.formatNum(this.lat, precision) + ', ' +
1651		        L.Util.formatNum(this.lng, precision) + ')';
1652	},
1653
1654	// @method distanceTo(otherLatLng: LatLng): Number
1655	// Returns the distance (in meters) to the given `LatLng` calculated using the [Haversine formula](http://en.wikipedia.org/wiki/Haversine_formula).
1656	distanceTo: function (other) {
1657		return L.CRS.Earth.distance(this, L.latLng(other));
1658	},
1659
1660	// @method wrap(): LatLng
1661	// Returns a new `LatLng` object with the longitude wrapped so it's always between -180 and +180 degrees.
1662	wrap: function () {
1663		return L.CRS.Earth.wrapLatLng(this);
1664	},
1665
1666	// @method toBounds(sizeInMeters: Number): LatLngBounds
1667	// Returns a new `LatLngBounds` object in which each boundary is `sizeInMeters/2` meters apart from the `LatLng`.
1668	toBounds: function (sizeInMeters) {
1669		var latAccuracy = 180 * sizeInMeters / 40075017,
1670		    lngAccuracy = latAccuracy / Math.cos((Math.PI / 180) * this.lat);
1671
1672		return L.latLngBounds(
1673		        [this.lat - latAccuracy, this.lng - lngAccuracy],
1674		        [this.lat + latAccuracy, this.lng + lngAccuracy]);
1675	},
1676
1677	clone: function () {
1678		return new L.LatLng(this.lat, this.lng, this.alt);
1679	}
1680};
1681
1682
1683
1684// @factory L.latLng(latitude: Number, longitude: Number, altitude?: Number): LatLng
1685// Creates an object representing a geographical point with the given latitude and longitude (and optionally altitude).
1686
1687// @alternative
1688// @factory L.latLng(coords: Array): LatLng
1689// Expects an array of the form `[Number, Number]` or `[Number, Number, Number]` instead.
1690
1691// @alternative
1692// @factory L.latLng(coords: Object): LatLng
1693// Expects an plain object of the form `{lat: Number, lng: Number}` or `{lat: Number, lng: Number, alt: Number}` instead.
1694
1695L.latLng = function (a, b, c) {
1696	if (a instanceof L.LatLng) {
1697		return a;
1698	}
1699	if (L.Util.isArray(a) && typeof a[0] !== 'object') {
1700		if (a.length === 3) {
1701			return new L.LatLng(a[0], a[1], a[2]);
1702		}
1703		if (a.length === 2) {
1704			return new L.LatLng(a[0], a[1]);
1705		}
1706		return null;
1707	}
1708	if (a === undefined || a === null) {
1709		return a;
1710	}
1711	if (typeof a === 'object' && 'lat' in a) {
1712		return new L.LatLng(a.lat, 'lng' in a ? a.lng : a.lon, a.alt);
1713	}
1714	if (b === undefined) {
1715		return null;
1716	}
1717	return new L.LatLng(a, b, c);
1718};
1719
1720
1721
1722/*
1723 * @class LatLngBounds
1724 * @aka L.LatLngBounds
1725 *
1726 * Represents a rectangular geographical area on a map.
1727 *
1728 * @example
1729 *
1730 * ```js
1731 * var corner1 = L.latLng(40.712, -74.227),
1732 * corner2 = L.latLng(40.774, -74.125),
1733 * bounds = L.latLngBounds(corner1, corner2);
1734 * ```
1735 *
1736 * 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:
1737 *
1738 * ```js
1739 * map.fitBounds([
1740 * 	[40.712, -74.227],
1741 * 	[40.774, -74.125]
1742 * ]);
1743 * ```
1744 *
1745 * 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.
1746 */
1747
1748L.LatLngBounds = function (corner1, corner2) { // (LatLng, LatLng) or (LatLng[])
1749	if (!corner1) { return; }
1750
1751	var latlngs = corner2 ? [corner1, corner2] : corner1;
1752
1753	for (var i = 0, len = latlngs.length; i < len; i++) {
1754		this.extend(latlngs[i]);
1755	}
1756};
1757
1758L.LatLngBounds.prototype = {
1759
1760	// @method extend(latlng: LatLng): this
1761	// Extend the bounds to contain the given point
1762
1763	// @alternative
1764	// @method extend(otherBounds: LatLngBounds): this
1765	// Extend the bounds to contain the given bounds
1766	extend: function (obj) {
1767		var sw = this._southWest,
1768		    ne = this._northEast,
1769		    sw2, ne2;
1770
1771		if (obj instanceof L.LatLng) {
1772			sw2 = obj;
1773			ne2 = obj;
1774
1775		} else if (obj instanceof L.LatLngBounds) {
1776			sw2 = obj._southWest;
1777			ne2 = obj._northEast;
1778
1779			if (!sw2 || !ne2) { return this; }
1780
1781		} else {
1782			return obj ? this.extend(L.latLng(obj) || L.latLngBounds(obj)) : this;
1783		}
1784
1785		if (!sw && !ne) {
1786			this._southWest = new L.LatLng(sw2.lat, sw2.lng);
1787			this._northEast = new L.LatLng(ne2.lat, ne2.lng);
1788		} else {
1789			sw.lat = Math.min(sw2.lat, sw.lat);
1790			sw.lng = Math.min(sw2.lng, sw.lng);
1791			ne.lat = Math.max(ne2.lat, ne.lat);
1792			ne.lng = Math.max(ne2.lng, ne.lng);
1793		}
1794
1795		return this;
1796	},
1797
1798	// @method pad(bufferRatio: Number): LatLngBounds
1799	// Returns bigger bounds created by extending the current bounds by a given percentage in each direction.
1800	pad: function (bufferRatio) {
1801		var sw = this._southWest,
1802		    ne = this._northEast,
1803		    heightBuffer = Math.abs(sw.lat - ne.lat) * bufferRatio,
1804		    widthBuffer = Math.abs(sw.lng - ne.lng) * bufferRatio;
1805
1806		return new L.LatLngBounds(
1807		        new L.LatLng(sw.lat - heightBuffer, sw.lng - widthBuffer),
1808		        new L.LatLng(ne.lat + heightBuffer, ne.lng + widthBuffer));
1809	},
1810
1811	// @method getCenter(): LatLng
1812	// Returns the center point of the bounds.
1813	getCenter: function () {
1814		return new L.LatLng(
1815		        (this._southWest.lat + this._northEast.lat) / 2,
1816		        (this._southWest.lng + this._northEast.lng) / 2);
1817	},
1818
1819	// @method getSouthWest(): LatLng
1820	// Returns the south-west point of the bounds.
1821	getSouthWest: function () {
1822		return this._southWest;
1823	},
1824
1825	// @method getNorthEast(): LatLng
1826	// Returns the north-east point of the bounds.
1827	getNorthEast: function () {
1828		return this._northEast;
1829	},
1830
1831	// @method getNorthWest(): LatLng
1832	// Returns the north-west point of the bounds.
1833	getNorthWest: function () {
1834		return new L.LatLng(this.getNorth(), this.getWest());
1835	},
1836
1837	// @method getSouthEast(): LatLng
1838	// Returns the south-east point of the bounds.
1839	getSouthEast: function () {
1840		return new L.LatLng(this.getSouth(), this.getEast());
1841	},
1842
1843	// @method getWest(): Number
1844	// Returns the west longitude of the bounds
1845	getWest: function () {
1846		return this._southWest.lng;
1847	},
1848
1849	// @method getSouth(): Number
1850	// Returns the south latitude of the bounds
1851	getSouth: function () {
1852		return this._southWest.lat;
1853	},
1854
1855	// @method getEast(): Number
1856	// Returns the east longitude of the bounds
1857	getEast: function () {
1858		return this._northEast.lng;
1859	},
1860
1861	// @method getNorth(): Number
1862	// Returns the north latitude of the bounds
1863	getNorth: function () {
1864		return this._northEast.lat;
1865	},
1866
1867	// @method contains(otherBounds: LatLngBounds): Boolean
1868	// Returns `true` if the rectangle contains the given one.
1869
1870	// @alternative
1871	// @method contains (latlng: LatLng): Boolean
1872	// Returns `true` if the rectangle contains the given point.
1873	contains: function (obj) { // (LatLngBounds) or (LatLng) -> Boolean
1874		if (typeof obj[0] === 'number' || obj instanceof L.LatLng || 'lat' in obj) {
1875			obj = L.latLng(obj);
1876		} else {
1877			obj = L.latLngBounds(obj);
1878		}
1879
1880		var sw = this._southWest,
1881		    ne = this._northEast,
1882		    sw2, ne2;
1883
1884		if (obj instanceof L.LatLngBounds) {
vendor: 1,093 bytes, lines 1885-1917
1885			sw2 = obj.getSouthWest();
1886			ne2 = obj.getNorthEast();
1887		} else {
1888			sw2 = ne2 = obj;
1889		}
1890
1891		return (sw2.lat >= sw.lat) && (ne2.lat <= ne.lat) &&
1892		       (sw2.lng >= sw.lng) && (ne2.lng <= ne.lng);
1893	},
1894
1895	// @method intersects(otherBounds: LatLngBounds): Boolean
1896	// Returns `true` if the rectangle intersects the given bounds. Two bounds intersect if they have at least one point in common.
1897	intersects: function (bounds) {
1898		bounds = L.latLngBounds(bounds);
1899
1900		var sw = this._southWest,
1901		    ne = this._northEast,
1902		    sw2 = bounds.getSouthWest(),
1903		    ne2 = bounds.getNorthEast(),
1904
1905		    latIntersects = (ne2.lat >= sw.lat) && (sw2.lat <= ne.lat),
1906		    lngIntersects = (ne2.lng >= sw.lng) && (sw2.lng <= ne.lng);
1907
1908		return latIntersects && lngIntersects;
1909	},
1910
1911	// @method overlaps(otherBounds: Bounds): Boolean
1912	// Returns `true` if the rectangle overlaps the given bounds. Two bounds overlap if their intersection is an area.
1913	overlaps: function (bounds) {
1914		bounds = L.latLngBounds(bounds);
1915
1916		var sw = this._southWest,
1917		    ne = this._northEast,
1918		    sw2 = bounds.getSouthWest(),
1919		    ne2 = bounds.getNorthEast(),
1920
1921		    latOverlaps = (ne2.lat > sw.lat) && (sw2.lat < ne.lat),
1922		    lngOverlaps = (ne2.lng > sw.lng) && (sw2.lng < ne.lng);
1923
1924		return latOverlaps && lngOverlaps;
1925	},
1926
1927	// @method toBBoxString(): String
1928	// 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.
1929	toBBoxString: function () {
1930		return [this.getWest(), this.getSouth(), this.getEast(), this.getNorth()].join(',');
1931	},
1932
1933	// @method equals(otherBounds: LatLngBounds): Boolean
1934	// Returns `true` if the rectangle is equivalent (within a small margin of error) to the given bounds.
1935	equals: function (bounds) {
1936		if (!bounds) { return false; }
1937
1938		bounds = L.latLngBounds(bounds);
1939
1940		return this._southWest.equals(bounds.getSouthWest()) &&
1941		       this._northEast.equals(bounds.getNorthEast());
1942	},
1943
1944	// @method isValid(): Boolean
1945	// Returns `true` if the bounds are properly initialized.
1946	isValid: function () {
1947		return !!(this._southWest && this._northEast);
1948	}
1949};
1950
1951// TODO International date line?
1952
1953// @factory L.latLngBounds(corner1: LatLng, corner2: LatLng)
1954// Creates a `LatLngBounds` object by defining two diagonally opposite corners of the rectangle.
1955
1956// @alternative
1957// @factory L.latLngBounds(latlngs: LatLng[])
1958// 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).
1959L.latLngBounds = function (a, b) {
1960	if (a instanceof L.LatLngBounds) {
1961		return a;
1962	}
1963	return new L.LatLngBounds(a, b);
1964};
1965
1966
1967
1968/*
1969 * @namespace Projection
1970 * @section
1971 * Leaflet comes with a set of already defined Projections out of the box:
1972 *
1973 * @projection L.Projection.LonLat
1974 *
1975 * Equirectangular, or Plate Carree projection — the most simple projection,
1976 * mostly used by GIS enthusiasts. Directly maps `x` as longitude, and `y` as
1977 * latitude. Also suitable for flat worlds, e.g. game maps. Used by the
1978 * `EPSG:3395` and `Simple` CRS.
1979 */
1980
1981L.Projection = {};
1982
1983L.Projection.LonLat = {
1984	project: function (latlng) {
1985		return new L.Point(latlng.lng, latlng.lat);
1986	},
1987
1988	unproject: function (point) {
1989		return new L.LatLng(point.y, point.x);
1990	},
1991
1992	bounds: L.bounds([-180, -90], [180, 90])
1993};
1994
1995
1996
1997/*
1998 * @namespace Projection
1999 * @projection L.Projection.SphericalMercator
2000 *
2001 * Spherical Mercator projection — the most common projection for online maps,
2002 * used by almost all free and commercial tile providers. Assumes that Earth is
2003 * a sphere. Used by the `EPSG:3857` CRS.
2004 */
2005
2006L.Projection.SphericalMercator = {
2007
2008	R: 6378137,
2009	MAX_LATITUDE: 85.0511287798,
2010
2011	project: function (latlng) {
2012		var d = Math.PI / 180,
2013		    max = this.MAX_LATITUDE,
2014		    lat = Math.max(Math.min(max, latlng.lat), -max),
2015		    sin = Math.sin(lat * d);
2016
2017		return new L.Point(
2018				this.R * latlng.lng * d,
2019				this.R * Math.log((1 + sin) / (1 - sin)) / 2);
2020	},
2021
2022	unproject: function (point) {
2023		var d = 180 / Math.PI;
2024
2025		return new L.LatLng(
2026			(2 * Math.atan(Math.exp(point.y / this.R)) - (Math.PI / 2)) * d,
2027			point.x * d / this.R);
2028	},
2029
2030	bounds: (function () {
2031		var d = 6378137 * Math.PI;
2032		return L.bounds([-d, -d], [d, d]);
2033	})()
2034};
2035
2036
2037
2038/*
2039 * @class CRS
2040 * @aka L.CRS
2041 * Abstract class that defines coordinate reference systems for projecting
2042 * geographical points into pixel (screen) coordinates and back (and to
2043 * coordinates in other units for [WMS](https://en.wikipedia.org/wiki/Web_Map_Service) services). See
2044 * [spatial reference system](http://en.wikipedia.org/wiki/Coordinate_reference_system).
2045 *
2046 * Leaflet defines the most usual CRSs by default. If you want to use a
2047 * CRS not defined by default, take a look at the
2048 * [Proj4Leaflet](https://github.com/kartena/Proj4Leaflet) plugin.
2049 */
2050
2051L.CRS = {
2052	// @method latLngToPoint(latlng: LatLng, zoom: Number): Point
2053	// Projects geographical coordinates into pixel coordinates for a given zoom.
2054	latLngToPoint: function (latlng, zoom) {
2055		var projectedPoint = this.projection.project(latlng),
2056		    scale = this.scale(zoom);
2057
2058		return this.transformation._transform(projectedPoint, scale);
2059	},
2060
2061	// @method pointToLatLng(point: Point, zoom: Number): LatLng
2062	// The inverse of `latLngToPoint`. Projects pixel coordinates on a given
2063	// zoom into geographical coordinates.
2064	pointToLatLng: function (point, zoom) {
2065		var scale = this.scale(zoom),
2066		    untransformedPoint = this.transformation.untransform(point, scale);
2067
2068		return this.projection.unproject(untransformedPoint);
2069	},
2070
2071	// @method project(latlng: LatLng): Point
2072	// Projects geographical coordinates into coordinates in units accepted for
2073	// this CRS (e.g. meters for EPSG:3857, for passing it to WMS services).
2074	project: function (latlng) {
2075		return this.projection.project(latlng);
2076	},
2077
2078	// @method unproject(point: Point): LatLng
2079	// Given a projected coordinate returns the corresponding LatLng.
2080	// The inverse of `project`.
2081	unproject: function (point) {
2082		return this.projection.unproject(point);
2083	},
2084
2085	// @method scale(zoom: Number): Number
2086	// Returns the scale used when transforming projected coordinates into
2087	// pixel coordinates for a particular zoom. For example, it returns
2088	// `256 * 2^zoom` for Mercator-based CRS.
2089	scale: function (zoom) {
2090		return 256 * Math.pow(2, zoom);
2091	},
2092
2093	// @method zoom(scale: Number): Number
2094	// Inverse of `scale()`, returns the zoom level corresponding to a scale
2095	// factor of `scale`.
2096	zoom: function (scale) {
2097		return Math.log(scale / 256) / Math.LN2;
2098	},
2099
2100	// @method getProjectedBounds(zoom: Number): Bounds
2101	// Returns the projection's bounds scaled and transformed for the provided `zoom`.
2102	getProjectedBounds: function (zoom) {
2103		if (this.infinite) { return null; }
2104
2105		var b = this.projection.bounds,
2106		    s = this.scale(zoom),
2107		    min = this.transformation.transform(b.min, s),
2108		    max = this.transformation.transform(b.max, s);
2109
2110		return L.bounds(min, max);
2111	},
2112
2113	// @method distance(latlng1: LatLng, latlng2: LatLng): Number
2114	// Returns the distance between two geographical coordinates.
2115
2116	// @property code: String
2117	// Standard code name of the CRS passed into WMS services (e.g. `'EPSG:3857'`)
2118	//
2119	// @property wrapLng: Number[]
2120	// An array of two numbers defining whether the longitude (horizontal) coordinate
2121	// axis wraps around a given range and how. Defaults to `[-180, 180]` in most
2122	// geographical CRSs. If `undefined`, the longitude axis does not wrap around.
2123	//
2124	// @property wrapLat: Number[]
2125	// Like `wrapLng`, but for the latitude (vertical) axis.
2126
2127	// wrapLng: [min, max],
2128	// wrapLat: [min, max],
2129
2130	// @property infinite: Boolean
2131	// If true, the coordinate space will be unbounded (infinite in both axes)
2132	infinite: false,
2133
2134	// @method wrapLatLng(latlng: LatLng): LatLng
2135	// Returns a `LatLng` where lat and lng has been wrapped according to the
2136	// CRS's `wrapLat` and `wrapLng` properties, if they are outside the CRS's bounds.
2137	// Only accepts actual `L.LatLng` instances, not arrays.
2138	wrapLatLng: function (latlng) {
2139		var lng = this.wrapLng ? L.Util.wrapNum(latlng.lng, this.wrapLng, true) : latlng.lng,
2140		    lat = this.wrapLat ? L.Util.wrapNum(latlng.lat, this.wrapLat, true) : latlng.lat,
2141		    alt = latlng.alt;
2142
2143		return L.latLng(lat, lng, alt);
2144	},
2145
2146	// @method wrapLatLngBounds(bounds: LatLngBounds): LatLngBounds
2147	// Returns a `LatLngBounds` with the same size as the given one, ensuring
2148	// that its center is within the CRS's bounds.
2149	// Only accepts actual `L.LatLngBounds` instances, not arrays.
2150	wrapLatLngBounds: function (bounds) {
2151		var center = bounds.getCenter(),
2152		    newCenter = this.wrapLatLng(center),
2153		    latShift = center.lat - newCenter.lat,
2154		    lngShift = center.lng - newCenter.lng;
2155
2156		if (latShift === 0 && lngShift === 0) {
2157			return bounds;
2158		}
2159
2160		var sw = bounds.getSouthWest(),
2161		    ne = bounds.getNorthEast(),
2162		    newSw = L.latLng({lat: sw.lat - latShift, lng: sw.lng - lngShift}),
2163		    newNe = L.latLng({lat: ne.lat - latShift, lng: ne.lng - lngShift});
2164
2165		return new L.LatLngBounds(newSw, newNe);
2166	}
2167};
2168
2169
2170
2171/*
2172 * @namespace CRS
2173 * @crs L.CRS.Simple
2174 *
2175 * A simple CRS that maps longitude and latitude into `x` and `y` directly.
2176 * May be used for maps of flat surfaces (e.g. game maps). Note that the `y`
2177 * axis should still be inverted (going from bottom to top). `distance()` returns
2178 * simple euclidean distance.
2179 */
2180
2181L.CRS.Simple = L.extend({}, L.CRS, {
2182	projection: L.Projection.LonLat,
2183	transformation: new L.Transformation(1, 0, -1, 0),
2184
2185	scale: function (zoom) {
2186		return Math.pow(2, zoom);
2187	},
2188
2189	zoom: function (scale) {
2190		return Math.log(scale) / Math.LN2;
2191	},
2192
2193	distance: function (latlng1, latlng2) {
2194		var dx = latlng2.lng - latlng1.lng,
2195		    dy = latlng2.lat - latlng1.lat;
2196
2197		return Math.sqrt(dx * dx + dy * dy);
2198	},
2199
2200	infinite: true
2201});
2202
2203
2204
2205/*
2206 * @namespace CRS
2207 * @crs L.CRS.Earth
2208 *
2209 * Serves as the base for CRS that are global such that they cover the earth.
2210 * Can only be used as the base for other CRS and cannot be used directly,
2211 * since it does not have a `code`, `projection` or `transformation`. `distance()` returns
2212 * meters.
2213 */
2214
2215L.CRS.Earth = L.extend({}, L.CRS, {
2216	wrapLng: [-180, 180],
2217
2218	// Mean Earth Radius, as recommended for use by
2219	// the International Union of Geodesy and Geophysics,
2220	// see http://rosettacode.org/wiki/Haversine_formula
2221	R: 6371000,
2222
2223	// distance between two geographical points using spherical law of cosines approximation
2224	distance: function (latlng1, latlng2) {
2225		var rad = Math.PI / 180,
2226		    lat1 = latlng1.lat * rad,
2227		    lat2 = latlng2.lat * rad,
2228		    a = Math.sin(lat1) * Math.sin(lat2) +
2229		        Math.cos(lat1) * Math.cos(lat2) * Math.cos((latlng2.lng - latlng1.lng) * rad);
2230
2231		return this.R * Math.acos(Math.min(a, 1));
2232	}
2233});
2234
2235
2236
2237/*
2238 * @namespace CRS
2239 * @crs L.CRS.EPSG3857
2240 *
2241 * The most common CRS for online maps, used by almost all free and commercial
2242 * tile providers. Uses Spherical Mercator projection. Set in by default in
2243 * Map's `crs` option.
2244 */
2245
2246L.CRS.EPSG3857 = L.extend({}, L.CRS.Earth, {
2247	code: 'EPSG:3857',
2248	projection: L.Projection.SphericalMercator,
2249
2250	transformation: (function () {
2251		var scale = 0.5 / (Math.PI * L.Projection.SphericalMercator.R);
2252		return new L.Transformation(scale, 0.5, -scale, 0.5);
2253	}())
2254});
2255
2256L.CRS.EPSG900913 = L.extend({}, L.CRS.EPSG3857, {
2257	code: 'EPSG:900913'
2258});
2259
2260
2261
2262/*
2263 * @namespace CRS
2264 * @crs L.CRS.EPSG4326
2265 *
2266 * A common CRS among GIS enthusiasts. Uses simple Equirectangular projection.
2267 *
2268 * Leaflet 1.0.x complies with the [TMS coordinate scheme for EPSG:4326](https://wiki.osgeo.org/wiki/Tile_Map_Service_Specification#global-geodetic),
2269 * which is a breaking change from 0.7.x behaviour.  If you are using a `TileLayer`
2270 * with this CRS, ensure that there are two 256x256 pixel tiles covering the
2271 * whole earth at zoom level zero, and that the tile coordinate origin is (-180,+90),
2272 * or (-180,-90) for `TileLayer`s with [the `tms` option](#tilelayer-tms) set.
2273 */
2274
2275L.CRS.EPSG4326 = L.extend({}, L.CRS.Earth, {
2276	code: 'EPSG:4326',
2277	projection: L.Projection.LonLat,
2278	transformation: new L.Transformation(1 / 180, 1, -1 / 180, 0.5)
2279});
2280
2281
2282
2283/*
2284 * @class Map
2285 * @aka L.Map
2286 * @inherits Evented
2287 *
2288 * The central class of the API — it is used to create a map on a page and manipulate it.
2289 *
2290 * @example
2291 *
2292 * ```js
2293 * // initialize the map on the "map" div with a given center and zoom
2294 * var map = L.map('map', {
2295 * 	center: [51.505, -0.09],
2296 * 	zoom: 13
2297 * });
2298 * ```
2299 *
2300 */
2301
2302L.Map = L.Evented.extend({
2303
2304	options: {
2305		// @section Map State Options
2306		// @option crs: CRS = L.CRS.EPSG3857
2307		// The [Coordinate Reference System](#crs) to use. Don't change this if you're not
2308		// sure what it means.
2309		crs: L.CRS.EPSG3857,
2310
2311		// @option center: LatLng = undefined
2312		// Initial geographic center of the map
2313		center: undefined,
2314
2315		// @option zoom: Number = undefined
2316		// Initial map zoom level
2317		zoom: undefined,
2318
2319		// @option minZoom: Number = undefined
2320		// Minimum zoom level of the map. Overrides any `minZoom` option set on map layers.
2321		minZoom: undefined,
2322
2323		// @option maxZoom: Number = undefined
2324		// Maximum zoom level of the map. Overrides any `maxZoom` option set on map layers.
2325		maxZoom: undefined,
2326
2327		// @option layers: Layer[] = []
2328		// Array of layers that will be added to the map initially
2329		layers: [],
2330
2331		// @option maxBounds: LatLngBounds = null
2332		// When this option is set, the map restricts the view to the given
2333		// geographical bounds, bouncing the user back if the user tries to pan
2334		// outside the view. To set the restriction dynamically, use
2335		// [`setMaxBounds`](#map-setmaxbounds) method.
2336		maxBounds: undefined,
2337
2338		// @option renderer: Renderer = *
2339		// The default method for drawing vector layers on the map. `L.SVG`
2340		// or `L.Canvas` by default depending on browser support.
2341		renderer: undefined,
2342
2343
2344		// @section Animation Options
2345		// @option zoomAnimation: Boolean = true
2346		// Whether the map zoom animation is enabled. By default it's enabled
2347		// in all browsers that support CSS3 Transitions except Android.
2348		zoomAnimation: true,
2349
2350		// @option zoomAnimationThreshold: Number = 4
2351		// Won't animate zoom if the zoom difference exceeds this value.
2352		zoomAnimationThreshold: 4,
2353
2354		// @option fadeAnimation: Boolean = true
2355		// Whether the tile fade animation is enabled. By default it's enabled
2356		// in all browsers that support CSS3 Transitions except Android.
2357		fadeAnimation: true,
2358
2359		// @option markerZoomAnimation: Boolean = true
2360		// Whether markers animate their zoom with the zoom animation, if disabled
2361		// they will disappear for the length of the animation. By default it's
2362		// enabled in all browsers that support CSS3 Transitions except Android.
2363		markerZoomAnimation: true,
2364
2365		// @option transform3DLimit: Number = 2^23
2366		// Defines the maximum size of a CSS translation transform. The default
2367		// value should not be changed unless a web browser positions layers in
2368		// the wrong place after doing a large `panBy`.
2369		transform3DLimit: 8388608, // Precision limit of a 32-bit float
2370
2371		// @section Interaction Options
2372		// @option zoomSnap: Number = 1
2373		// Forces the map's zoom level to always be a multiple of this, particularly
2374		// right after a [`fitBounds()`](#map-fitbounds) or a pinch-zoom.
2375		// By default, the zoom level snaps to the nearest integer; lower values
2376		// (e.g. `0.5` or `0.1`) allow for greater granularity. A value of `0`
2377		// means the zoom level will not be snapped after `fitBounds` or a pinch-zoom.
2378		zoomSnap: 1,
2379
2380		// @option zoomDelta: Number = 1
2381		// Controls how much the map's zoom level will change after a
2382		// [`zoomIn()`](#map-zoomin), [`zoomOut()`](#map-zoomout), pressing `+`
2383		// or `-` on the keyboard, or using the [zoom controls](#control-zoom).
2384		// Values smaller than `1` (e.g. `0.5`) allow for greater granularity.
2385		zoomDelta: 1,
2386
2387		// @option trackResize: Boolean = true
2388		// Whether the map automatically handles browser window resize to update itself.
2389		trackResize: true
2390	},
2391
2392	initialize: function (id, options) { // (HTMLElement or String, Object)
2393		options = L.setOptions(this, options);
2394
2395		this._initContainer(id);
2396		this._initLayout();
2397
2398		// hack for https://github.com/Leaflet/Leaflet/issues/1980
2399		this._onResize = L.bind(this._onResize, this);
2400
2401		this._initEvents();
2402
2403		if (options.maxBounds) {
2404			this.setMaxBounds(options.maxBounds);
2405		}
2406
2407		if (options.zoom !== undefined) {
2408			this._zoom = this._limitZoom(options.zoom);
2409		}
2410
2411		if (options.center && options.zoom !== undefined) {
2412			this.setView(L.latLng(options.center), options.zoom, {reset: true});
2413		}
2414
2415		this._handlers = [];
2416		this._layers = {};
2417		this._zoomBoundLayers = {};
2418		this._sizeChanged = true;
2419
2420		this.callInitHooks();
2421
2422		// don't animate on browsers without hardware-accelerated transitions or old Android/Opera
2423		this._zoomAnimated = L.DomUtil.TRANSITION && L.Browser.any3d && !L.Browser.mobileOpera &&
2424				this.options.zoomAnimation;
2425
2426		// zoom transitions run with the same duration for all layers, so if one of transitionend events
2427		// happens after starting zoom animation (propagating to the map pane), we know that it ended globally
2428		if (this._zoomAnimated) {
2429			this._createAnimProxy();
2430			L.DomEvent.on(this._proxy, L.DomUtil.TRANSITION_END, this._catchTransitionEnd, this);
2431		}
2432
2433		this._addLayers(this.options.layers);
2434	},
2435
2436
2437	// @section Methods for modifying map state
2438
2439	// @method setView(center: LatLng, zoom: Number, options?: Zoom/pan options): this
2440	// Sets the view of the map (geographical center and zoom) with the given
2441	// animation options.
2442	setView: function (center, zoom, options) {
2443
2444		zoom = zoom === undefined ? this._zoom : this._limitZoom(zoom);
2445		center = this._limitCenter(L.latLng(center), zoom, this.options.maxBounds);
2446		options = options || {};
2447
2448		this._stop();
2449
2450		if (this._loaded && !options.reset && options !== true) {
2451
2452			if (options.animate !== undefined) {
2453				options.zoom = L.extend({animate: options.animate}, options.zoom);
2454				options.pan = L.extend({animate: options.animate, duration: options.duration}, options.pan);
2455			}
2456
2457			// try animating pan or zoom
2458			var moved = (this._zoom !== zoom) ?
vendor: 18,281 bytes, lines 2459-3039
2459				this._tryAnimatedZoom && this._tryAnimatedZoom(center, zoom, options.zoom) :
2460				this._tryAnimatedPan(center, options.pan);
2461
2462			if (moved) {
2463				// prevent resize handler call, the view will refresh after animation anyway
2464				clearTimeout(this._sizeTimer);
2465				return this;
2466			}
2467		}
2468
2469		// animation didn't start, just reset the map view
2470		this._resetView(center, zoom);
2471
2472		return this;
2473	},
2474
2475	// @method setZoom(zoom: Number, options: Zoom/pan options): this
2476	// Sets the zoom of the map.
2477	setZoom: function (zoom, options) {
2478		if (!this._loaded) {
2479			this._zoom = zoom;
2480			return this;
2481		}
2482		return this.setView(this.getCenter(), zoom, {zoom: options});
2483	},
2484
2485	// @method zoomIn(delta?: Number, options?: Zoom options): this
2486	// Increases the zoom of the map by `delta` ([`zoomDelta`](#map-zoomdelta) by default).
2487	zoomIn: function (delta, options) {
2488		delta = delta || (L.Browser.any3d ? this.options.zoomDelta : 1);
2489		return this.setZoom(this._zoom + delta, options);
2490	},
2491
2492	// @method zoomOut(delta?: Number, options?: Zoom options): this
2493	// Decreases the zoom of the map by `delta` ([`zoomDelta`](#map-zoomdelta) by default).
2494	zoomOut: function (delta, options) {
2495		delta = delta || (L.Browser.any3d ? this.options.zoomDelta : 1);
2496		return this.setZoom(this._zoom - delta, options);
2497	},
2498
2499	// @method setZoomAround(latlng: LatLng, zoom: Number, options: Zoom options): this
2500	// Zooms the map while keeping a specified geographical point on the map
2501	// stationary (e.g. used internally for scroll zoom and double-click zoom).
2502	// @alternative
2503	// @method setZoomAround(offset: Point, zoom: Number, options: Zoom options): this
2504	// Zooms the map while keeping a specified pixel on the map (relative to the top-left corner) stationary.
2505	setZoomAround: function (latlng, zoom, options) {
2506		var scale = this.getZoomScale(zoom),
2507		    viewHalf = this.getSize().divideBy(2),
2508		    containerPoint = latlng instanceof L.Point ? latlng : this.latLngToContainerPoint(latlng),
2509
2510		    centerOffset = containerPoint.subtract(viewHalf).multiplyBy(1 - 1 / scale),
2511		    newCenter = this.containerPointToLatLng(viewHalf.add(centerOffset));
2512
2513		return this.setView(newCenter, zoom, {zoom: options});
2514	},
2515
2516	_getBoundsCenterZoom: function (bounds, options) {
2517
2518		options = options || {};
2519		bounds = bounds.getBounds ? bounds.getBounds() : L.latLngBounds(bounds);
2520
2521		var paddingTL = L.point(options.paddingTopLeft || options.padding || [0, 0]),
2522		    paddingBR = L.point(options.paddingBottomRight || options.padding || [0, 0]),
2523
2524		    zoom = this.getBoundsZoom(bounds, false, paddingTL.add(paddingBR));
2525
2526		zoom = (typeof options.maxZoom === 'number') ? Math.min(options.maxZoom, zoom) : zoom;
2527
2528		var paddingOffset = paddingBR.subtract(paddingTL).divideBy(2),
2529
2530		    swPoint = this.project(bounds.getSouthWest(), zoom),
2531		    nePoint = this.project(bounds.getNorthEast(), zoom),
2532		    center = this.unproject(swPoint.add(nePoint).divideBy(2).add(paddingOffset), zoom);
2533
2534		return {
2535			center: center,
2536			zoom: zoom
2537		};
2538	},
2539
2540	// @method fitBounds(bounds: LatLngBounds, options?: fitBounds options): this
2541	// Sets a map view that contains the given geographical bounds with the
2542	// maximum zoom level possible.
2543	fitBounds: function (bounds, options) {
2544
2545		bounds = L.latLngBounds(bounds);
2546
2547		if (!bounds.isValid()) {
2548			throw new Error('Bounds are not valid.');
2549		}
2550
2551		var target = this._getBoundsCenterZoom(bounds, options);
2552		return this.setView(target.center, target.zoom, options);
2553	},
2554
2555	// @method fitWorld(options?: fitBounds options): this
2556	// Sets a map view that mostly contains the whole world with the maximum
2557	// zoom level possible.
2558	fitWorld: function (options) {
2559		return this.fitBounds([[-90, -180], [90, 180]], options);
2560	},
2561
2562	// @method panTo(latlng: LatLng, options?: Pan options): this
2563	// Pans the map to a given center.
2564	panTo: function (center, options) { // (LatLng)
2565		return this.setView(center, this._zoom, {pan: options});
2566	},
2567
2568	// @method panBy(offset: Point): this
2569	// Pans the map by a given number of pixels (animated).
2570	panBy: function (offset, options) {
2571		offset = L.point(offset).round();
2572		options = options || {};
2573
2574		if (!offset.x && !offset.y) {
2575			return this.fire('moveend');
2576		}
2577		// If we pan too far, Chrome gets issues with tiles
2578		// and makes them disappear or appear in the wrong place (slightly offset) #2602
2579		if (options.animate !== true && !this.getSize().contains(offset)) {
2580			this._resetView(this.unproject(this.project(this.getCenter()).add(offset)), this.getZoom());
2581			return this;
2582		}
2583
2584		if (!this._panAnim) {
2585			this._panAnim = new L.PosAnimation();
2586
2587			this._panAnim.on({
2588				'step': this._onPanTransitionStep,
2589				'end': this._onPanTransitionEnd
2590			}, this);
2591		}
2592
2593		// don't fire movestart if animating inertia
2594		if (!options.noMoveStart) {
2595			this.fire('movestart');
2596		}
2597
2598		// animate pan unless animate: false specified
2599		if (options.animate !== false) {
2600			L.DomUtil.addClass(this._mapPane, 'leaflet-pan-anim');
2601
2602			var newPos = this._getMapPanePos().subtract(offset).round();
2603			this._panAnim.run(this._mapPane, newPos, options.duration || 0.25, options.easeLinearity);
2604		} else {
2605			this._rawPanBy(offset);
2606			this.fire('move').fire('moveend');
2607		}
2608
2609		return this;
2610	},
2611
2612	// @method flyTo(latlng: LatLng, zoom?: Number, options?: Zoom/pan options): this
2613	// Sets the view of the map (geographical center and zoom) performing a smooth
2614	// pan-zoom animation.
2615	flyTo: function (targetCenter, targetZoom, options) {
2616
2617		options = options || {};
2618		if (options.animate === false || !L.Browser.any3d) {
2619			return this.setView(targetCenter, targetZoom, options);
2620		}
2621
2622		this._stop();
2623
2624		var from = this.project(this.getCenter()),
2625		    to = this.project(targetCenter),
2626		    size = this.getSize(),
2627		    startZoom = this._zoom;
2628
2629		targetCenter = L.latLng(targetCenter);
2630		targetZoom = targetZoom === undefined ? startZoom : targetZoom;
2631
2632		var w0 = Math.max(size.x, size.y),
2633		    w1 = w0 * this.getZoomScale(startZoom, targetZoom),
2634		    u1 = (to.distanceTo(from)) || 1,
2635		    rho = 1.42,
2636		    rho2 = rho * rho;
2637
2638		function r(i) {
2639			var s1 = i ? -1 : 1,
2640			    s2 = i ? w1 : w0,
2641			    t1 = w1 * w1 - w0 * w0 + s1 * rho2 * rho2 * u1 * u1,
2642			    b1 = 2 * s2 * rho2 * u1,
2643			    b = t1 / b1,
2644			    sq = Math.sqrt(b * b + 1) - b;
2645
2646			    // workaround for floating point precision bug when sq = 0, log = -Infinite,
2647			    // thus triggering an infinite loop in flyTo
2648			    var log = sq < 0.000000001 ? -18 : Math.log(sq);
2649
2650			return log;
2651		}
2652
2653		function sinh(n) { return (Math.exp(n) - Math.exp(-n)) / 2; }
2654		function cosh(n) { return (Math.exp(n) + Math.exp(-n)) / 2; }
2655		function tanh(n) { return sinh(n) / cosh(n); }
2656
2657		var r0 = r(0);
2658
2659		function w(s) { return w0 * (cosh(r0) / cosh(r0 + rho * s)); }
2660		function u(s) { return w0 * (cosh(r0) * tanh(r0 + rho * s) - sinh(r0)) / rho2; }
2661
2662		function easeOut(t) { return 1 - Math.pow(1 - t, 1.5); }
2663
2664		var start = Date.now(),
2665		    S = (r(1) - r0) / rho,
2666		    duration = options.duration ? 1000 * options.duration : 1000 * S * 0.8;
2667
2668		function frame() {
2669			var t = (Date.now() - start) / duration,
2670			    s = easeOut(t) * S;
2671
2672			if (t <= 1) {
2673				this._flyToFrame = L.Util.requestAnimFrame(frame, this);
2674
2675				this._move(
2676					this.unproject(from.add(to.subtract(from).multiplyBy(u(s) / u1)), startZoom),
2677					this.getScaleZoom(w0 / w(s), startZoom),
2678					{flyTo: true});
2679
2680			} else {
2681				this
2682					._move(targetCenter, targetZoom)
2683					._moveEnd(true);
2684			}
2685		}
2686
2687		this._moveStart(true);
2688
2689		frame.call(this);
2690		return this;
2691	},
2692
2693	// @method flyToBounds(bounds: LatLngBounds, options?: fitBounds options): this
2694	// Sets the view of the map with a smooth animation like [`flyTo`](#map-flyto),
2695	// but takes a bounds parameter like [`fitBounds`](#map-fitbounds).
2696	flyToBounds: function (bounds, options) {
2697		var target = this._getBoundsCenterZoom(bounds, options);
2698		return this.flyTo(target.center, target.zoom, options);
2699	},
2700
2701	// @method setMaxBounds(bounds: Bounds): this
2702	// Restricts the map view to the given bounds (see the [maxBounds](#map-maxbounds) option).
2703	setMaxBounds: function (bounds) {
2704		bounds = L.latLngBounds(bounds);
2705
2706		if (!bounds.isValid()) {
2707			this.options.maxBounds = null;
2708			return this.off('moveend', this._panInsideMaxBounds);
2709		} else if (this.options.maxBounds) {
2710			this.off('moveend', this._panInsideMaxBounds);
2711		}
2712
2713		this.options.maxBounds = bounds;
2714
2715		if (this._loaded) {
2716			this._panInsideMaxBounds();
2717		}
2718
2719		return this.on('moveend', this._panInsideMaxBounds);
2720	},
2721
2722	// @method setMinZoom(zoom: Number): this
2723	// Sets the lower limit for the available zoom levels (see the [minZoom](#map-minzoom) option).
2724	setMinZoom: function (zoom) {
2725		this.options.minZoom = zoom;
2726
2727		if (this._loaded && this.getZoom() < this.options.minZoom) {
2728			return this.setZoom(zoom);
2729		}
2730
2731		return this;
2732	},
2733
2734	// @method setMaxZoom(zoom: Number): this
2735	// Sets the upper limit for the available zoom levels (see the [maxZoom](#map-maxzoom) option).
2736	setMaxZoom: function (zoom) {
2737		this.options.maxZoom = zoom;
2738
2739		if (this._loaded && (this.getZoom() > this.options.maxZoom)) {
2740			return this.setZoom(zoom);
2741		}
2742
2743		return this;
2744	},
2745
2746	// @method panInsideBounds(bounds: LatLngBounds, options?: Pan options): this
2747	// 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.
2748	panInsideBounds: function (bounds, options) {
2749		this._enforcingBounds = true;
2750		var center = this.getCenter(),
2751		    newCenter = this._limitCenter(center, this._zoom, L.latLngBounds(bounds));
2752
2753		if (!center.equals(newCenter)) {
2754			this.panTo(newCenter, options);
2755		}
2756
2757		this._enforcingBounds = false;
2758		return this;
2759	},
2760
2761	// @method invalidateSize(options: Zoom/Pan options): this
2762	// Checks if the map container size changed and updates the map if so —
2763	// call it after you've changed the map size dynamically, also animating
2764	// pan by default. If `options.pan` is `false`, panning will not occur.
2765	// If `options.debounceMoveend` is `true`, it will delay `moveend` event so
2766	// that it doesn't happen often even if the method is called many
2767	// times in a row.
2768
2769	// @alternative
2770	// @method invalidateSize(animate: Boolean): this
2771	// Checks if the map container size changed and updates the map if so —
2772	// call it after you've changed the map size dynamically, also animating
2773	// pan by default.
2774	invalidateSize: function (options) {
2775		if (!this._loaded) { return this; }
2776
2777		options = L.extend({
2778			animate: false,
2779			pan: true
2780		}, options === true ? {animate: true} : options);
2781
2782		var oldSize = this.getSize();
2783		this._sizeChanged = true;
2784		this._lastCenter = null;
2785
2786		var newSize = this.getSize(),
2787		    oldCenter = oldSize.divideBy(2).round(),
2788		    newCenter = newSize.divideBy(2).round(),
2789		    offset = oldCenter.subtract(newCenter);
2790
2791		if (!offset.x && !offset.y) { return this; }
2792
2793		if (options.animate && options.pan) {
2794			this.panBy(offset);
2795
2796		} else {
2797			if (options.pan) {
2798				this._rawPanBy(offset);
2799			}
2800
2801			this.fire('move');
2802
2803			if (options.debounceMoveend) {
2804				clearTimeout(this._sizeTimer);
2805				this._sizeTimer = setTimeout(L.bind(this.fire, this, 'moveend'), 200);
2806			} else {
2807				this.fire('moveend');
2808			}
2809		}
2810
2811		// @section Map state change events
2812		// @event resize: ResizeEvent
2813		// Fired when the map is resized.
2814		return this.fire('resize', {
2815			oldSize: oldSize,
2816			newSize: newSize
2817		});
2818	},
2819
2820	// @section Methods for modifying map state
2821	// @method stop(): this
2822	// Stops the currently running `panTo` or `flyTo` animation, if any.
2823	stop: function () {
2824		this.setZoom(this._limitZoom(this._zoom));
2825		if (!this.options.zoomSnap) {
2826			this.fire('viewreset');
2827		}
2828		return this._stop();
2829	},
2830
2831	// @section Geolocation methods
2832	// @method locate(options?: Locate options): this
2833	// Tries to locate the user using the Geolocation API, firing a [`locationfound`](#map-locationfound)
2834	// event with location data on success or a [`locationerror`](#map-locationerror) event on failure,
2835	// and optionally sets the map view to the user's location with respect to
2836	// detection accuracy (or to the world view if geolocation failed).
2837	// Note that, if your page doesn't use HTTPS, this method will fail in
2838	// modern browsers ([Chrome 50 and newer](https://sites.google.com/a/chromium.org/dev/Home/chromium-security/deprecating-powerful-features-on-insecure-origins))
2839	// See `Locate options` for more details.
2840	locate: function (options) {
2841
2842		options = this._locateOptions = L.extend({
2843			timeout: 10000,
2844			watch: false
2845			// setView: false
2846			// maxZoom: <Number>
2847			// maximumAge: 0
2848			// enableHighAccuracy: false
2849		}, options);
2850
2851		if (!('geolocation' in navigator)) {
2852			this._handleGeolocationError({
2853				code: 0,
2854				message: 'Geolocation not supported.'
2855			});
2856			return this;
2857		}
2858
2859		var onResponse = L.bind(this._handleGeolocationResponse, this),
2860		    onError = L.bind(this._handleGeolocationError, this);
2861
2862		if (options.watch) {
2863			this._locationWatchId =
2864			        navigator.geolocation.watchPosition(onResponse, onError, options);
2865		} else {
2866			navigator.geolocation.getCurrentPosition(onResponse, onError, options);
2867		}
2868		return this;
2869	},
2870
2871	// @method stopLocate(): this
2872	// Stops watching location previously initiated by `map.locate({watch: true})`
2873	// and aborts resetting the map view if map.locate was called with
2874	// `{setView: true}`.
2875	stopLocate: function () {
2876		if (navigator.geolocation && navigator.geolocation.clearWatch) {
2877			navigator.geolocation.clearWatch(this._locationWatchId);
2878		}
2879		if (this._locateOptions) {
2880			this._locateOptions.setView = false;
2881		}
2882		return this;
2883	},
2884
2885	_handleGeolocationError: function (error) {
2886		var c = error.code,
2887		    message = error.message ||
2888		            (c === 1 ? 'permission denied' :
2889		            (c === 2 ? 'position unavailable' : 'timeout'));
2890
2891		if (this._locateOptions.setView && !this._loaded) {
2892			this.fitWorld();
2893		}
2894
2895		// @section Location events
2896		// @event locationerror: ErrorEvent
2897		// Fired when geolocation (using the [`locate`](#map-locate) method) failed.
2898		this.fire('locationerror', {
2899			code: c,
2900			message: 'Geolocation error: ' + message + '.'
2901		});
2902	},
2903
2904	_handleGeolocationResponse: function (pos) {
2905		var lat = pos.coords.latitude,
2906		    lng = pos.coords.longitude,
2907		    latlng = new L.LatLng(lat, lng),
2908		    bounds = latlng.toBounds(pos.coords.accuracy),
2909		    options = this._locateOptions;
2910
2911		if (options.setView) {
2912			var zoom = this.getBoundsZoom(bounds);
2913			this.setView(latlng, options.maxZoom ? Math.min(zoom, options.maxZoom) : zoom);
2914		}
2915
2916		var data = {
2917			latlng: latlng,
2918			bounds: bounds,
2919			timestamp: pos.timestamp
2920		};
2921
2922		for (var i in pos.coords) {
2923			if (typeof pos.coords[i] === 'number') {
2924				data[i] = pos.coords[i];
2925			}
2926		}
2927
2928		// @event locationfound: LocationEvent
2929		// Fired when geolocation (using the [`locate`](#map-locate) method)
2930		// went successfully.
2931		this.fire('locationfound', data);
2932	},
2933
2934	// TODO handler.addTo
2935	// TODO Appropiate docs section?
2936	// @section Other Methods
2937	// @method addHandler(name: String, HandlerClass: Function): this
2938	// Adds a new `Handler` to the map, given its name and constructor function.
2939	addHandler: function (name, HandlerClass) {
2940		if (!HandlerClass) { return this; }
2941
2942		var handler = this[name] = new HandlerClass(this);
2943
2944		this._handlers.push(handler);
2945
2946		if (this.options[name]) {
2947			handler.enable();
2948		}
2949
2950		return this;
2951	},
2952
2953	// @method remove(): this
2954	// Destroys the map and clears all related event listeners.
2955	remove: function () {
2956
2957		this._initEvents(true);
2958
2959		if (this._containerId !== this._container._leaflet_id) {
2960			throw new Error('Map container is being reused by another instance');
2961		}
2962
2963		try {
2964			// throws error in IE6-8
2965			delete this._container._leaflet_id;
2966			delete this._containerId;
2967		} catch (e) {
2968			/*eslint-disable */
2969			this._container._leaflet_id = undefined;
2970			/*eslint-enable */
2971			this._containerId = undefined;
2972		}
2973
2974		L.DomUtil.remove(this._mapPane);
2975
2976		if (this._clearControlPos) {
2977			this._clearControlPos();
2978		}
2979
2980		this._clearHandlers();
2981
2982		if (this._loaded) {
2983			// @section Map state change events
2984			// @event unload: Event
2985			// Fired when the map is destroyed with [remove](#map-remove) method.
2986			this.fire('unload');
2987		}
2988
2989		for (var i in this._layers) {
2990			this._layers[i].remove();
2991		}
2992
2993		return this;
2994	},
2995
2996	// @section Other Methods
2997	// @method createPane(name: String, container?: HTMLElement): HTMLElement
2998	// Creates a new [map pane](#map-pane) with the given name if it doesn't exist already,
2999	// then returns it. The pane is created as a children of `container`, or
3000	// as a children of the main map pane if not set.
3001	createPane: function (name, container) {
3002		var className = 'leaflet-pane' + (name ? ' leaflet-' + name.replace('Pane', '') + '-pane' : ''),
3003		    pane = L.DomUtil.create('div', className, container || this._mapPane);
3004
3005		if (name) {
3006			this._panes[name] = pane;
3007		}
3008		return pane;
3009	},
3010
3011	// @section Methods for Getting Map State
3012
3013	// @method getCenter(): LatLng
3014	// Returns the geographical center of the map view
3015	getCenter: function () {
3016		this._checkIfLoaded();
3017
3018		if (this._lastCenter && !this._moved()) {
3019			return this._lastCenter;
3020		}
3021		return this.layerPointToLatLng(this._getCenterLayerPoint());
3022	},
3023
3024	// @method getZoom(): Number
3025	// Returns the current zoom level of the map view
3026	getZoom: function () {
3027		return this._zoom;
3028	},
3029
3030	// @method getBounds(): LatLngBounds
3031	// Returns the geographical bounds visible in the current map view
3032	getBounds: function () {
3033		var bounds = this.getPixelBounds(),
3034		    sw = this.unproject(bounds.getBottomLeft()),
3035		    ne = this.unproject(bounds.getTopRight());
3036
3037		return new L.LatLngBounds(sw, ne);
3038	},
3039
3040	// @method getMinZoom(): Number
3041	// 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.
3042	getMinZoom: function () {
3043		return this.options.minZoom === undefined ? this._layersMinZoom || 0 : this.options.minZoom;
3044	},
3045
3046	// @method getMaxZoom(): Number
3047	// Returns the maximum zoom level of the map (if set in the `maxZoom` option of the map or of any layers).
3048	getMaxZoom: function () {
3049		return this.options.maxZoom === undefined ?
3050			(this._layersMaxZoom === undefined ? Infinity : this._layersMaxZoom) :
3051			this.options.maxZoom;
3052	},
3053
3054	// @method getBoundsZoom(bounds: LatLngBounds, inside?: Boolean): Number
3055	// Returns the maximum zoom level on which the given bounds fit to the map
3056	// view in its entirety. If `inside` (optional) is set to `true`, the method
3057	// instead returns the minimum zoom level on which the map view fits into
3058	// the given bounds in its entirety.
3059	getBoundsZoom: function (bounds, inside, padding) { // (LatLngBounds[, Boolean, Point]) -> Number
3060		bounds = L.latLngBounds(bounds);
3061		padding = L.point(padding || [0, 0]);
3062
3063		var zoom = this.getZoom() || 0,
3064		    min = this.getMinZoom(),
3065		    max = this.getMaxZoom(),
3066		    nw = bounds.getNorthWest(),
3067		    se = bounds.getSouthEast(),
3068		    size = this.getSize().subtract(padding),
3069		    boundsSize = L.bounds(this.project(se, zoom), this.project(nw, zoom)).getSize(),
3070		    snap = L.Browser.any3d ? this.options.zoomSnap : 1;
3071
3072		var scale = Math.min(size.x / boundsSize.x, size.y / boundsSize.y);
3073		zoom = this.getScaleZoom(scale, zoom);
3074
3075		if (snap) {
3076			zoom = Math.round(zoom / (snap / 100)) * (snap / 100); // don't jump if within 1% of a snap level
3077			zoom = inside ? Math.ceil(zoom / snap) * snap : Math.floor(zoom / snap) * snap;
3078		}
3079
3080		return Math.max(min, Math.min(max, zoom));
3081	},
3082
3083	// @method getSize(): Point
3084	// Returns the current size of the map container (in pixels).
3085	getSize: function () {
3086		if (!this._size || this._sizeChanged) {
3087			this._size = new L.Point(
3088				this._container.clientWidth || 0,
3089				this._container.clientHeight || 0);
3090
3091			this._sizeChanged = false;
3092		}
3093		return this._size.clone();
3094	},
3095
3096	// @method getPixelBounds(): Bounds
3097	// Returns the bounds of the current map view in projected pixel
3098	// coordinates (sometimes useful in layer and overlay implementations).
3099	getPixelBounds: function (center, zoom) {
3100		var topLeftPoint = this._getTopLeftPoint(center, zoom);
3101		return new L.Bounds(topLeftPoint, topLeftPoint.add(this.getSize()));
3102	},
3103
3104	// TODO: Check semantics - isn't the pixel origin the 0,0 coord relative to
3105	// the map pane? "left point of the map layer" can be confusing, specially
3106	// since there can be negative offsets.
3107	// @method getPixelOrigin(): Point
3108	// Returns the projected pixel coordinates of the top left point of
3109	// the map layer (useful in custom layer and overlay implementations).
3110	getPixelOrigin: function () {
3111		this._checkIfLoaded();
3112		return this._pixelOrigin;
3113	},
3114
3115	// @method getPixelWorldBounds(zoom?: Number): Bounds
3116	// Returns the world's bounds in pixel coordinates for zoom level `zoom`.
3117	// If `zoom` is omitted, the map's current zoom level is used.
3118	getPixelWorldBounds: function (zoom) {
3119		return this.options.crs.getProjectedBounds(zoom === undefined ? this.getZoom() : zoom);
3120	},
3121
3122	// @section Other Methods
3123
3124	// @method getPane(pane: String|HTMLElement): HTMLElement
3125	// Returns a [map pane](#map-pane), given its name or its HTML element (its identity).
3126	getPane: function (pane) {
3127		return typeof pane === 'string' ? this._panes[pane] : pane;
3128	},
3129
3130	// @method getPanes(): Object
vendor: 6,095 bytes, lines 3131-3267
3131	// Returns a plain object containing the names of all [panes](#map-pane) as keys and
3132	// the panes as values.
3133	getPanes: function () {
3134		return this._panes;
3135	},
3136
3137	// @method getContainer: HTMLElement
3138	// Returns the HTML element that contains the map.
3139	getContainer: function () {
3140		return this._container;
3141	},
3142
3143
3144	// @section Conversion Methods
3145
3146	// @method getZoomScale(toZoom: Number, fromZoom: Number): Number
3147	// Returns the scale factor to be applied to a map transition from zoom level
3148	// `fromZoom` to `toZoom`. Used internally to help with zoom animations.
3149	getZoomScale: function (toZoom, fromZoom) {
3150		// TODO replace with universal implementation after refactoring projections
3151		var crs = this.options.crs;
3152		fromZoom = fromZoom === undefined ? this._zoom : fromZoom;
3153		return crs.scale(toZoom) / crs.scale(fromZoom);
3154	},
3155
3156	// @method getScaleZoom(scale: Number, fromZoom: Number): Number
3157	// Returns the zoom level that the map would end up at, if it is at `fromZoom`
3158	// level and everything is scaled by a factor of `scale`. Inverse of
3159	// [`getZoomScale`](#map-getZoomScale).
3160	getScaleZoom: function (scale, fromZoom) {
3161		var crs = this.options.crs;
3162		fromZoom = fromZoom === undefined ? this._zoom : fromZoom;
3163		var zoom = crs.zoom(scale * crs.scale(fromZoom));
3164		return isNaN(zoom) ? Infinity : zoom;
3165	},
3166
3167	// @method project(latlng: LatLng, zoom: Number): Point
3168	// Projects a geographical coordinate `LatLng` according to the projection
3169	// of the map's CRS, then scales it according to `zoom` and the CRS's
3170	// `Transformation`. The result is pixel coordinate relative to
3171	// the CRS origin.
3172	project: function (latlng, zoom) {
3173		zoom = zoom === undefined ? this._zoom : zoom;
3174		return this.options.crs.latLngToPoint(L.latLng(latlng), zoom);
3175	},
3176
3177	// @method unproject(point: Point, zoom: Number): LatLng
3178	// Inverse of [`project`](#map-project).
3179	unproject: function (point, zoom) {
3180		zoom = zoom === undefined ? this._zoom : zoom;
3181		return this.options.crs.pointToLatLng(L.point(point), zoom);
3182	},
3183
3184	// @method layerPointToLatLng(point: Point): LatLng
3185	// Given a pixel coordinate relative to the [origin pixel](#map-getpixelorigin),
3186	// returns the corresponding geographical coordinate (for the current zoom level).
3187	layerPointToLatLng: function (point) {
3188		var projectedPoint = L.point(point).add(this.getPixelOrigin());
3189		return this.unproject(projectedPoint);
3190	},
3191
3192	// @method latLngToLayerPoint(latlng: LatLng): Point
3193	// Given a geographical coordinate, returns the corresponding pixel coordinate
3194	// relative to the [origin pixel](#map-getpixelorigin).
3195	latLngToLayerPoint: function (latlng) {
3196		var projectedPoint = this.project(L.latLng(latlng))._round();
3197		return projectedPoint._subtract(this.getPixelOrigin());
3198	},
3199
3200	// @method wrapLatLng(latlng: LatLng): LatLng
3201	// Returns a `LatLng` where `lat` and `lng` has been wrapped according to the
3202	// map's CRS's `wrapLat` and `wrapLng` properties, if they are outside the
3203	// CRS's bounds.
3204	// By default this means longitude is wrapped around the dateline so its
3205	// value is between -180 and +180 degrees.
3206	wrapLatLng: function (latlng) {
3207		return this.options.crs.wrapLatLng(L.latLng(latlng));
3208	},
3209
3210	// @method wrapLatLngBounds(bounds: LatLngBounds): LatLngBounds
3211	// Returns a `LatLngBounds` with the same size as the given one, ensuring that
3212	// its center is within the CRS's bounds.
3213	// By default this means the center longitude is wrapped around the dateline so its
3214	// value is between -180 and +180 degrees, and the majority of the bounds
3215	// overlaps the CRS's bounds.
3216	wrapLatLngBounds: function (latlng) {
3217		return this.options.crs.wrapLatLngBounds(L.latLngBounds(latlng));
3218	},
3219
3220	// @method distance(latlng1: LatLng, latlng2: LatLng): Number
3221	// Returns the distance between two geographical coordinates according to
3222	// the map's CRS. By default this measures distance in meters.
3223	distance: function (latlng1, latlng2) {
3224		return this.options.crs.distance(L.latLng(latlng1), L.latLng(latlng2));
3225	},
3226
3227	// @method containerPointToLayerPoint(point: Point): Point
3228	// Given a pixel coordinate relative to the map container, returns the corresponding
3229	// pixel coordinate relative to the [origin pixel](#map-getpixelorigin).
3230	containerPointToLayerPoint: function (point) { // (Point)
3231		return L.point(point).subtract(this._getMapPanePos());
3232	},
3233
3234	// @method layerPointToContainerPoint(point: Point): Point
3235	// Given a pixel coordinate relative to the [origin pixel](#map-getpixelorigin),
3236	// returns the corresponding pixel coordinate relative to the map container.
3237	layerPointToContainerPoint: function (point) { // (Point)
3238		return L.point(point).add(this._getMapPanePos());
3239	},
3240
3241	// @method containerPointToLatLng(point: Point): LatLng
3242	// Given a pixel coordinate relative to the map container, returns
3243	// the corresponding geographical coordinate (for the current zoom level).
3244	containerPointToLatLng: function (point) {
3245		var layerPoint = this.containerPointToLayerPoint(L.point(point));
3246		return this.layerPointToLatLng(layerPoint);
3247	},
3248
3249	// @method latLngToContainerPoint(latlng: LatLng): Point
3250	// Given a geographical coordinate, returns the corresponding pixel coordinate
3251	// relative to the map container.
3252	latLngToContainerPoint: function (latlng) {
3253		return this.layerPointToContainerPoint(this.latLngToLayerPoint(L.latLng(latlng)));
3254	},
3255
3256	// @method mouseEventToContainerPoint(ev: MouseEvent): Point
3257	// Given a MouseEvent object, returns the pixel coordinate relative to the
3258	// map container where the event took place.
3259	mouseEventToContainerPoint: function (e) {
3260		return L.DomEvent.getMousePosition(e, this._container);
3261	},
3262
3263	// @method mouseEventToLayerPoint(ev: MouseEvent): Point
3264	// Given a MouseEvent object, returns the pixel coordinate relative to
3265	// the [origin pixel](#map-getpixelorigin) where the event took place.
3266	mouseEventToLayerPoint: function (e) {
3267		return this.containerPointToLayerPoint(this.mouseEventToContainerPoint(e));
3268	},
3269
3270	// @method mouseEventToLatLng(ev: MouseEvent): LatLng
3271	// Given a MouseEvent object, returns geographical coordinate where the
3272	// event took place.
3273	mouseEventToLatLng: function (e) { // (MouseEvent)
3274		return this.layerPointToLatLng(this.mouseEventToLayerPoint(e));
3275	},
3276
3277
3278	// map initialization methods
3279
3280	_initContainer: function (id) {
3281		var container = this._container = L.DomUtil.get(id);
3282
3283		if (!container) {
3284			throw new Error('Map container not found.');
3285		} else if (container._leaflet_id) {
3286			throw new Error('Map container is already initialized.');
3287		}
3288
3289		L.DomEvent.addListener(container, 'scroll', this._onScroll, this);
3290		this._containerId = L.Util.stamp(container);
3291	},
3292
3293	_initLayout: function () {
3294		var container = this._container;
3295
3296		this._fadeAnimated = this.options.fadeAnimation && L.Browser.any3d;
3297
3298		L.DomUtil.addClass(container, 'leaflet-container' +
3299			(L.Browser.touch ? ' leaflet-touch' : '') +
3300			(L.Browser.retina ? ' leaflet-retina' : '') +
3301			(L.Browser.ielt9 ? ' leaflet-oldie' : '') +
3302			(L.Browser.safari ? ' leaflet-safari' : '') +
3303			(this._fadeAnimated ? ' leaflet-fade-anim' : ''));
3304
3305		var position = L.DomUtil.getStyle(container, 'position');
3306
3307		if (position !== 'absolute' && position !== 'relative' && position !== 'fixed') {
3308			container.style.position = 'relative';
3309		}
3310
3311		this._initPanes();
3312
3313		if (this._initControlPos) {
3314			this._initControlPos();
3315		}
3316	},
3317
3318	_initPanes: function () {
3319		var panes = this._panes = {};
3320		this._paneRenderers = {};
3321
3322		// @section
3323		//
3324		// Panes are DOM elements used to control the ordering of layers on the map. You
3325		// can access panes with [`map.getPane`](#map-getpane) or
3326		// [`map.getPanes`](#map-getpanes) methods. New panes can be created with the
3327		// [`map.createPane`](#map-createpane) method.
3328		//
3329		// Every map has the following default panes that differ only in zIndex.
3330		//
3331		// @pane mapPane: HTMLElement = 'auto'
3332		// Pane that contains all other map panes
3333
3334		this._mapPane = this.createPane('mapPane', this._container);
3335		L.DomUtil.setPosition(this._mapPane, new L.Point(0, 0));
3336
3337		// @pane tilePane: HTMLElement = 200
3338		// Pane for `GridLayer`s and `TileLayer`s
3339		this.createPane('tilePane');
3340		// @pane overlayPane: HTMLElement = 400
3341		// Pane for vector overlays (`Path`s), like `Polyline`s and `Polygon`s
3342		this.createPane('shadowPane');
3343		// @pane shadowPane: HTMLElement = 500
3344		// Pane for overlay shadows (e.g. `Marker` shadows)
3345		this.createPane('overlayPane');
3346		// @pane markerPane: HTMLElement = 600
3347		// Pane for `Icon`s of `Marker`s
3348		this.createPane('markerPane');
3349		// @pane tooltipPane: HTMLElement = 650
3350		// Pane for tooltip.
3351		this.createPane('tooltipPane');
3352		// @pane popupPane: HTMLElement = 700
3353		// Pane for `Popup`s.
3354		this.createPane('popupPane');
3355
3356		if (!this.options.markerZoomAnimation) {
3357			L.DomUtil.addClass(panes.markerPane, 'leaflet-zoom-hide');
3358			L.DomUtil.addClass(panes.shadowPane, 'leaflet-zoom-hide');
3359		}
3360	},
3361
3362
3363	// private methods that modify map state
3364
3365	// @section Map state change events
3366	_resetView: function (center, zoom) {
3367		L.DomUtil.setPosition(this._mapPane, new L.Point(0, 0));
3368
3369		var loading = !this._loaded;
3370		this._loaded = true;
3371		zoom = this._limitZoom(zoom);
3372
3373		this.fire('viewprereset');
3374
3375		var zoomChanged = this._zoom !== zoom;
3376		this
3377			._moveStart(zoomChanged)
3378			._move(center, zoom)
3379			._moveEnd(zoomChanged);
3380
3381		// @event viewreset: Event
3382		// Fired when the map needs to redraw its content (this usually happens
3383		// on map zoom or load). Very useful for creating custom overlays.
3384		this.fire('viewreset');
3385
3386		// @event load: Event
3387		// Fired when the map is initialized (when its center and zoom are set
3388		// for the first time).
3389		if (loading) {
3390			this.fire('load');
3391		}
3392	},
3393
3394	_moveStart: function (zoomChanged) {
3395		// @event zoomstart: Event
3396		// Fired when the map zoom is about to change (e.g. before zoom animation).
3397		// @event movestart: Event
3398		// Fired when the view of the map starts changing (e.g. user starts dragging the map).
3399		if (zoomChanged) {
3400			this.fire('zoomstart');
3401		}
3402		return this.fire('movestart');
3403	},
3404
3405	_move: function (center, zoom, data) {
3406		if (zoom === undefined) {
3407			zoom = this._zoom;
3408		}
3409		var zoomChanged = this._zoom !== zoom;
3410
3411		this._zoom = zoom;
3412		this._lastCenter = center;
3413		this._pixelOrigin = this._getNewPixelOrigin(center);
3414
3415		// @event zoom: Event
3416		// Fired repeatedly during any change in zoom level, including zoom
3417		// and fly animations.
3418		if (zoomChanged || (data && data.pinch)) {	// Always fire 'zoom' if pinching because #3530
3419			this.fire('zoom', data);
3420		}
3421
3422		// @event move: Event
3423		// Fired repeatedly during any movement of the map, including pan and
3424		// fly animations.
3425		return this.fire('move', data);
3426	},
3427
3428	_moveEnd: function (zoomChanged) {
3429		// @event zoomend: Event
3430		// Fired when the map has changed, after any animations.
3431		if (zoomChanged) {
3432			this.fire('zoomend');
3433		}
3434
3435		// @event moveend: Event
3436		// Fired when the center of the map stops changing (e.g. user stopped
3437		// dragging the map).
3438		return this.fire('moveend');
3439	},
3440
3441	_stop: function () {
3442		L.Util.cancelAnimFrame(this._flyToFrame);
3443		if (this._panAnim) {
3444			this._panAnim.stop();
3445		}
3446		return this;
3447	},
3448
3449	_rawPanBy: function (offset) {
3450		L.DomUtil.setPosition(this._mapPane, this._getMapPanePos().subtract(offset));
3451	},
3452
3453	_getZoomSpan: function () {
3454		return this.getMaxZoom() - this.getMinZoom();
3455	},
3456
3457	_panInsideMaxBounds: function () {
3458		if (!this._enforcingBounds) {
3459			this.panInsideBounds(this.options.maxBounds);
3460		}
3461	},
3462
3463	_checkIfLoaded: function () {
3464		if (!this._loaded) {
3465			throw new Error('Set map center and zoom first.');
3466		}
3467	},
3468
3469	// DOM event handling
3470
3471	// @section Interaction events
3472	_initEvents: function (remove) {
3473		if (!L.DomEvent) { return; }
3474
3475		this._targets = {};
3476		this._targets[L.stamp(this._container)] = this;
3477
3478		var onOff = remove ? 'off' : 'on';
3479
3480		// @event click: MouseEvent
3481		// Fired when the user clicks (or taps) the map.
3482		// @event dblclick: MouseEvent
3483		// Fired when the user double-clicks (or double-taps) the map.
3484		// @event mousedown: MouseEvent
3485		// Fired when the user pushes the mouse button on the map.
3486		// @event mouseup: MouseEvent
3487		// Fired when the user releases the mouse button on the map.
3488		// @event mouseover: MouseEvent
3489		// Fired when the mouse enters the map.
3490		// @event mouseout: MouseEvent
3491		// Fired when the mouse leaves the map.
3492		// @event mousemove: MouseEvent
3493		// Fired while the mouse moves over the map.
3494		// @event contextmenu: MouseEvent
3495		// Fired when the user pushes the right mouse button on the map, prevents
3496		// default browser context menu from showing if there are listeners on
3497		// this event. Also fired on mobile when the user holds a single touch
3498		// for a second (also called long press).
3499		// @event keypress: KeyboardEvent
3500		// Fired when the user presses a key from the keyboard while the map is focused.
3501		L.DomEvent[onOff](this._container, 'click dblclick mousedown mouseup ' +
3502			'mouseover mouseout mousemove contextmenu keypress', this._handleDOMEvent, this);
3503
3504		if (this.options.trackResize) {
3505			L.DomEvent[onOff](window, 'resize', this._onResize, this);
3506		}
3507
3508		if (L.Browser.any3d && this.options.transform3DLimit) {
3509			this[onOff]('moveend', this._onMoveEnd);
3510		}
3511	},
3512
3513	_onResize: function () {
3514		L.Util.cancelAnimFrame(this._resizeRequest);
3515		this._resizeRequest = L.Util.requestAnimFrame(
3516		        function () { this.invalidateSize({debounceMoveend: true}); }, this);
3517	},
3518
3519	_onScroll: function () {
3520		this._container.scrollTop  = 0;
3521		this._container.scrollLeft = 0;
3522	},
3523
3524	_onMoveEnd: function () {
3525		var pos = this._getMapPanePos();
3526		if (Math.max(Math.abs(pos.x), Math.abs(pos.y)) >= this.options.transform3DLimit) {
3527			// https://bugzilla.mozilla.org/show_bug.cgi?id=1203873 but Webkit also have
3528			// a pixel offset on very high values, see: http://jsfiddle.net/dg6r5hhb/
3529			this._resetView(this.getCenter(), this.getZoom());
3530		}
3531	},
3532
3533	_findEventTargets: function (e, type) {
3534		var targets = [],
3535		    target,
3536		    isHover = type === 'mouseout' || type === 'mouseover',
3537		    src = e.target || e.srcElement,
3538		    dragging = false;
3539
3540		while (src) {
3541			target = this._targets[L.stamp(src)];
3542			if (target && (type === 'click' || type === 'preclick') && !e._simulated && this._draggableMoved(target)) {
3543				// Prevent firing click after you just dragged an object.
3544				dragging = true;
3545				break;
3546			}
3547			if (target && target.listens(type, true)) {
3548				if (isHover && !L.DomEvent._isExternalTarget(src, e)) { break; }
3549				targets.push(target);
3550				if (isHover) { break; }
3551			}
3552			if (src === this._container) { break; }
3553			src = src.parentNode;
3554		}
3555		if (!targets.length && !dragging && !isHover && L.DomEvent._isExternalTarget(src, e)) {
3556			targets = [this];
3557		}
3558		return targets;
3559	},
3560
3561	_handleDOMEvent: function (e) {
3562		if (!this._loaded || L.DomEvent._skipped(e)) { return; }
3563
3564		var type = e.type === 'keypress' && e.keyCode === 13 ? 'click' : e.type;
3565
3566		if (type === 'mousedown') {
3567			// prevents outline when clicking on keyboard-focusable element
3568			L.DomUtil.preventOutline(e.target || e.srcElement);
3569		}
3570
3571		this._fireDOMEvent(e, type);
3572	},
3573
3574	_fireDOMEvent: function (e, type, targets) {
3575
3576		if (e.type === 'click') {
3577			// Fire a synthetic 'preclick' event which propagates up (mainly for closing popups).
3578			// @event preclick: MouseEvent
3579			// Fired before mouse click on the map (sometimes useful when you
3580			// want something to happen on click before any existing click
3581			// handlers start running).
3582			var synth = L.Util.extend({}, e);
3583			synth.type = 'preclick';
3584			this._fireDOMEvent(synth, synth.type, targets);
3585		}
3586
3587		if (e._stopped) { return; }
3588
3589		// Find the layer the event is propagating from and its parents.
3590		targets = (targets || []).concat(this._findEventTargets(e, type));
3591
3592		if (!targets.length) { return; }
3593
3594		var target = targets[0];
3595		if (type === 'contextmenu' && target.listens(type, true)) {
3596			L.DomEvent.preventDefault(e);
3597		}
3598
3599		var data = {
3600			originalEvent: e
3601		};
3602
3603		if (e.type !== 'keypress') {
3604			var isMarker = target instanceof L.Marker;
3605			data.containerPoint = isMarker ?
3606					this.latLngToContainerPoint(target.getLatLng()) : this.mouseEventToContainerPoint(e);
3607			data.layerPoint = this.containerPointToLayerPoint(data.containerPoint);
3608			data.latlng = isMarker ? target.getLatLng() : this.layerPointToLatLng(data.layerPoint);
3609		}
3610
3611		for (var i = 0; i < targets.length; i++) {
3612			targets[i].fire(type, data, true);
3613			if (data.originalEvent._stopped ||
3614				(targets[i].options.nonBubblingEvents && L.Util.indexOf(targets[i].options.nonBubblingEvents, type) !== -1)) { return; }
3615		}
3616	},
3617
3618	_draggableMoved: function (obj) {
3619		obj = obj.dragging && obj.dragging.enabled() ? obj : this;
3620		return (obj.dragging && obj.dragging.moved()) || (this.boxZoom && this.boxZoom.moved());
3621	},
3622
3623	_clearHandlers: function () {
3624		for (var i = 0, len = this._handlers.length; i < len; i++) {
3625			this._handlers[i].disable();
3626		}
3627	},
3628
3629	// @section Other Methods
3630
3631	// @method whenReady(fn: Function, context?: Object): this
3632	// Runs the given function `fn` when the map gets initialized with
3633	// a view (center and zoom) and at least one layer, or immediately
3634	// if it's already initialized, optionally passing a function context.
3635	whenReady: function (callback, context) {
3636		if (this._loaded) {
3637			callback.call(context || this, {target: this});
3638		} else {
3639			this.on('load', callback, context);
3640		}
3641		return this;
3642	},
3643
3644
3645	// private methods for getting map state
3646
3647	_getMapPanePos: function () {
3648		return L.DomUtil.getPosition(this._mapPane) || new L.Point(0, 0);
3649	},
3650
3651	_moved: function () {
3652		var pos = this._getMapPanePos();
3653		return pos && !pos.equals([0, 0]);
3654	},
3655
3656	_getTopLeftPoint: function (center, zoom) {
3657		var pixelOrigin = center && zoom !== undefined ?
3658			this._getNewPixelOrigin(center, zoom) :
3659			this.getPixelOrigin();
3660		return pixelOrigin.subtract(this._getMapPanePos());
3661	},
3662
3663	_getNewPixelOrigin: function (center, zoom) {
3664		var viewHalf = this.getSize()._divideBy(2);
3665		return this.project(center, zoom)._subtract(viewHalf)._add(this._getMapPanePos())._round();
3666	},
3667
3668	_latLngToNewLayerPoint: function (latlng, zoom, center) {
3669		var topLeft = this._getNewPixelOrigin(center, zoom);
3670		return this.project(latlng, zoom)._subtract(topLeft);
3671	},
3672
3673	_latLngBoundsToNewLayerBounds: function (latLngBounds, zoom, center) {
3674		var topLeft = this._getNewPixelOrigin(center, zoom);
3675		return L.bounds([
3676			this.project(latLngBounds.getSouthWest(), zoom)._subtract(topLeft),
3677			this.project(latLngBounds.getNorthWest(), zoom)._subtract(topLeft),
3678			this.project(latLngBounds.getSouthEast(), zoom)._subtract(topLeft),
3679			this.project(latLngBounds.getNorthEast(), zoom)._subtract(topLeft)
3680		]);
3681	},
3682
3683	// layer point of the current center
3684	_getCenterLayerPoint: function () {
3685		return this.containerPointToLayerPoint(this.getSize()._divideBy(2));
3686	},
3687
3688	// offset of the specified place to the current center in pixels
3689	_getCenterOffset: function (latlng) {
3690		return this.latLngToLayerPoint(latlng).subtract(this._getCenterLayerPoint());
3691	},
3692
3693	// adjust center for view to get inside bounds
3694	_limitCenter: function (center, zoom, bounds) {
3695
3696		if (!bounds) { return center; }
3697
3698		var centerPoint = this.project(center, zoom),
3699		    viewHalf = this.getSize().divideBy(2),
3700		    viewBounds = new L.Bounds(centerPoint.subtract(viewHalf), centerPoint.add(viewHalf)),
3701		    offset = this._getBoundsOffset(viewBounds, bounds, zoom);
3702
3703		// If offset is less than a pixel, ignore.
3704		// This prevents unstable projections from getting into
3705		// an infinite loop of tiny offsets.
3706		if (offset.round().equals([0, 0])) {
3707			return center;
3708		}
3709
3710		return this.unproject(centerPoint.add(offset), zoom);
3711	},
3712
3713	// adjust offset for view to get inside bounds
3714	_limitOffset: function (offset, bounds) {
3715		if (!bounds) { return offset; }
3716
3717		var viewBounds = this.getPixelBounds(),
3718		    newBounds = new L.Bounds(viewBounds.min.add(offset), viewBounds.max.add(offset));
3719
3720		return offset.add(this._getBoundsOffset(newBounds, bounds));
3721	},
3722
3723	// returns offset needed for pxBounds to get inside maxBounds at a specified zoom
3724	_getBoundsOffset: function (pxBounds, maxBounds, zoom) {
3725		var projectedMaxBounds = L.bounds(
3726		        this.project(maxBounds.getNorthEast(), zoom),
vendor: 4,411 bytes, lines 3727-3867
3727		        this.project(maxBounds.getSouthWest(), zoom)
3728		    ),
3729		    minOffset = projectedMaxBounds.min.subtract(pxBounds.min),
3730		    maxOffset = projectedMaxBounds.max.subtract(pxBounds.max),
3731
3732		    dx = this._rebound(minOffset.x, -maxOffset.x),
3733		    dy = this._rebound(minOffset.y, -maxOffset.y);
3734
3735		return new L.Point(dx, dy);
3736	},
3737
3738	_rebound: function (left, right) {
3739		return left + right > 0 ?
3740			Math.round(left - right) / 2 :
3741			Math.max(0, Math.ceil(left)) - Math.max(0, Math.floor(right));
3742	},
3743
3744	_limitZoom: function (zoom) {
3745		var min = this.getMinZoom(),
3746		    max = this.getMaxZoom(),
3747		    snap = L.Browser.any3d ? this.options.zoomSnap : 1;
3748		if (snap) {
3749			zoom = Math.round(zoom / snap) * snap;
3750		}
3751		return Math.max(min, Math.min(max, zoom));
3752	},
3753
3754	_onPanTransitionStep: function () {
3755		this.fire('move');
3756	},
3757
3758	_onPanTransitionEnd: function () {
3759		L.DomUtil.removeClass(this._mapPane, 'leaflet-pan-anim');
3760		this.fire('moveend');
3761	},
3762
3763	_tryAnimatedPan: function (center, options) {
3764		// difference between the new and current centers in pixels
3765		var offset = this._getCenterOffset(center)._floor();
3766
3767		// don't animate too far unless animate: true specified in options
3768		if ((options && options.animate) !== true && !this.getSize().contains(offset)) { return false; }
3769
3770		this.panBy(offset, options);
3771
3772		return true;
3773	},
3774
3775	_createAnimProxy: function () {
3776
3777		var proxy = this._proxy = L.DomUtil.create('div', 'leaflet-proxy leaflet-zoom-animated');
3778		this._panes.mapPane.appendChild(proxy);
3779
3780		this.on('zoomanim', function (e) {
3781			var prop = L.DomUtil.TRANSFORM,
3782			    transform = proxy.style[prop];
3783
3784			L.DomUtil.setTransform(proxy, this.project(e.center, e.zoom), this.getZoomScale(e.zoom, 1));
3785
3786			// workaround for case when transform is the same and so transitionend event is not fired
3787			if (transform === proxy.style[prop] && this._animatingZoom) {
3788				this._onZoomTransitionEnd();
3789			}
3790		}, this);
3791
3792		this.on('load moveend', function () {
3793			var c = this.getCenter(),
3794			    z = this.getZoom();
3795			L.DomUtil.setTransform(proxy, this.project(c, z), this.getZoomScale(z, 1));
3796		}, this);
3797	},
3798
3799	_catchTransitionEnd: function (e) {
3800		if (this._animatingZoom && e.propertyName.indexOf('transform') >= 0) {
3801			this._onZoomTransitionEnd();
3802		}
3803	},
3804
3805	_nothingToAnimate: function () {
3806		return !this._container.getElementsByClassName('leaflet-zoom-animated').length;
3807	},
3808
3809	_tryAnimatedZoom: function (center, zoom, options) {
3810
3811		if (this._animatingZoom) { return true; }
3812
3813		options = options || {};
3814
3815		// don't animate if disabled, not supported or zoom difference is too large
3816		if (!this._zoomAnimated || options.animate === false || this._nothingToAnimate() ||
3817		        Math.abs(zoom - this._zoom) > this.options.zoomAnimationThreshold) { return false; }
3818
3819		// offset is the pixel coords of the zoom origin relative to the current center
3820		var scale = this.getZoomScale(zoom),
3821		    offset = this._getCenterOffset(center)._divideBy(1 - 1 / scale);
3822
3823		// don't animate if the zoom origin isn't within one screen from the current center, unless forced
3824		if (options.animate !== true && !this.getSize().contains(offset)) { return false; }
3825
3826		L.Util.requestAnimFrame(function () {
3827			this
3828			    ._moveStart(true)
3829			    ._animateZoom(center, zoom, true);
3830		}, this);
3831
3832		return true;
3833	},
3834
3835	_animateZoom: function (center, zoom, startAnim, noUpdate) {
3836		if (startAnim) {
3837			this._animatingZoom = true;
3838
3839			// remember what center/zoom to set after animation
3840			this._animateToCenter = center;
3841			this._animateToZoom = zoom;
3842
3843			L.DomUtil.addClass(this._mapPane, 'leaflet-zoom-anim');
3844		}
3845
3846		// @event zoomanim: ZoomAnimEvent
3847		// Fired on every frame of a zoom animation
3848		this.fire('zoomanim', {
3849			center: center,
3850			zoom: zoom,
3851			noUpdate: noUpdate
3852		});
3853
3854		// Work around webkit not firing 'transitionend', see https://github.com/Leaflet/Leaflet/issues/3689, 2693
3855		setTimeout(L.bind(this._onZoomTransitionEnd, this), 250);
3856	},
3857
3858	_onZoomTransitionEnd: function () {
3859		if (!this._animatingZoom) { return; }
3860
3861		L.DomUtil.removeClass(this._mapPane, 'leaflet-zoom-anim');
3862
3863		this._animatingZoom = false;
3864
3865		this._move(this._animateToCenter, this._animateToZoom);
3866
3867		// This anim frame should prevent an obscure iOS webkit tile loading race condition.
3868		L.Util.requestAnimFrame(function () {
3869			this._moveEnd(true);
3870		}, this);
3871	}
3872});
3873
3874// @section
3875
3876// @factory L.map(id: String, options?: Map options)
3877// Instantiates a map object given the DOM ID of a `<div>` element
3878// and optionally an object literal with `Map options`.
3879//
3880// @alternative
3881// @factory L.map(el: HTMLElement, options?: Map options)
3882// Instantiates a map object given an instance of a `<div>` HTML element
3883// and optionally an object literal with `Map options`.
3884L.map = function (id, options) {
3885	return new L.Map(id, options);
3886};
3887
3888
3889
3890
3891/*
3892 * @class Layer
3893 * @inherits Evented
3894 * @aka L.Layer
3895 * @aka ILayer
3896 *
3897 * A set of methods from the Layer base class that all Leaflet layers use.
3898 * Inherits all methods, options and events from `L.Evented`.
3899 *
3900 * @example
3901 *
3902 * ```js
3903 * var layer = L.Marker(latlng).addTo(map);
3904 * layer.addTo(map);
3905 * layer.remove();
3906 * ```
3907 *
3908 * @event add: Event
3909 * Fired after the layer is added to a map
3910 *
3911 * @event remove: Event
3912 * Fired after the layer is removed from a map
3913 */
3914
3915
3916L.Layer = L.Evented.extend({
3917
3918	// Classes extending `L.Layer` will inherit the following options:
3919	options: {
3920		// @option pane: String = 'overlayPane'
3921		// 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.
3922		pane: 'overlayPane',
3923		nonBubblingEvents: [],  // Array of events that should not be bubbled to DOM parents (like the map),
3924
3925		// @option attribution: String = null
3926		// String to be shown in the attribution control, describes the layer data, e.g. "© Mapbox".
3927		attribution: null
3928	},
3929
3930	/* @section
3931	 * Classes extending `L.Layer` will inherit the following methods:
3932	 *
3933	 * @method addTo(map: Map): this
3934	 * Adds the layer to the given map
3935	 */
3936	addTo: function (map) {
3937		map.addLayer(this);
3938		return this;
3939	},
3940
3941	// @method remove: this
3942	// Removes the layer from the map it is currently active on.
3943	remove: function () {
3944		return this.removeFrom(this._map || this._mapToAdd);
3945	},
3946
3947	// @method removeFrom(map: Map): this
3948	// Removes the layer from the given map
3949	removeFrom: function (obj) {
3950		if (obj) {
3951			obj.removeLayer(this);
3952		}
3953		return this;
3954	},
3955
3956	// @method getPane(name? : String): HTMLElement
3957	// Returns the `HTMLElement` representing the named pane on the map. If `name` is omitted, returns the pane for this layer.
3958	getPane: function (name) {
3959		return this._map.getPane(name ? (this.options[name] || name) : this.options.pane);
3960	},
3961
3962	addInteractiveTarget: function (targetEl) {
3963		this._map._targets[L.stamp(targetEl)] = this;
3964		return this;
3965	},
3966
3967	removeInteractiveTarget: function (targetEl) {
3968		delete this._map._targets[L.stamp(targetEl)];
3969		return this;
3970	},
3971
3972	// @method getAttribution: String
3973	// Used by the `attribution control`, returns the [attribution option](#gridlayer-attribution).
3974	getAttribution: function () {
3975		return this.options.attribution;
3976	},
3977
3978	_layerAdd: function (e) {
3979		var map = e.target;
3980
3981		// check in case layer gets added and then removed before the map is ready
3982		if (!map.hasLayer(this)) { return; }
3983
3984		this._map = map;
3985		this._zoomAnimated = map._zoomAnimated;
3986
3987		if (this.getEvents) {
3988			var events = this.getEvents();
3989			map.on(events, this);
3990			this.once('remove', function () {
3991				map.off(events, this);
3992			}, this);
3993		}
3994
3995		this.onAdd(map);
3996
3997		if (this.getAttribution && map.attributionControl) {
3998			map.attributionControl.addAttribution(this.getAttribution());
3999		}
4000
4001		this.fire('add');
4002		map.fire('layeradd', {layer: this});
4003	}
4004});
4005
4006/* @section Extension methods
4007 * @uninheritable
4008 *
4009 * Every layer should extend from `L.Layer` and (re-)implement the following methods.
4010 *
4011 * @method onAdd(map: Map): this
4012 * 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).
4013 *
4014 * @method onRemove(map: Map): this
4015 * 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).
4016 *
4017 * @method getEvents(): Object
4018 * 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.
4019 *
4020 * @method getAttribution(): String
4021 * This optional method should return a string containing HTML to be shown on the `Attribution control` whenever the layer is visible.
4022 *
4023 * @method beforeAdd(map: Map): this
4024 * 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.
4025 */
4026
4027
4028/* @namespace Map
4029 * @section Layer events
4030 *
4031 * @event layeradd: LayerEvent
4032 * Fired when a new layer is added to the map.
4033 *
4034 * @event layerremove: LayerEvent
4035 * Fired when some layer is removed from the map
4036 *
4037 * @section Methods for Layers and Controls
4038 */
4039L.Map.include({
4040	// @method addLayer(layer: Layer): this
4041	// Adds the given layer to the map
4042	addLayer: function (layer) {
4043		var id = L.stamp(layer);
4044		if (this._layers[id]) { return this; }
4045		this._layers[id] = layer;
4046
4047		layer._mapToAdd = this;
4048
4049		if (layer.beforeAdd) {
4050			layer.beforeAdd(this);
4051		}
4052
4053		this.whenReady(layer._layerAdd, layer);
4054
4055		return this;
4056	},
4057
4058	// @method removeLayer(layer: Layer): this
4059	// Removes the given layer from the map.
4060	removeLayer: function (layer) {
4061		var id = L.stamp(layer);
4062
4063		if (!this._layers[id]) { return this; }
4064
4065		if (this._loaded) {
4066			layer.onRemove(this);
4067		}
4068
4069		if (layer.getAttribution && this.attributionControl) {
4070			this.attributionControl.removeAttribution(layer.getAttribution());
4071		}
4072
4073		delete this._layers[id];
4074
4075		if (this._loaded) {
4076			this.fire('layerremove', {layer: layer});
4077			layer.fire('remove');
4078		}
4079
4080		layer._map = layer._mapToAdd = null;
4081
4082		return this;
4083	},
4084
4085	// @method hasLayer(layer: Layer): Boolean
4086	// Returns `true` if the given layer is currently added to the map
4087	hasLayer: function (layer) {
4088		return !!layer && (L.stamp(layer) in this._layers);
4089	},
4090
4091	/* @method eachLayer(fn: Function, context?: Object): this
4092	 * Iterates over the layers of the map, optionally specifying context of the iterator function.
4093	 * ```
4094	 * map.eachLayer(function(layer){
4095	 *     layer.bindPopup('Hello');
4096	 * });
4097	 * ```
4098	 */
4099	eachLayer: function (method, context) {
4100		for (var i in this._layers) {
4101			method.call(context, this._layers[i]);
4102		}
4103		return this;
4104	},
4105
4106	_addLayers: function (layers) {
4107		layers = layers ? (L.Util.isArray(layers) ? layers : [layers]) : [];
4108
4109		for (var i = 0, len = layers.length; i < len; i++) {
4110			this.addLayer(layers[i]);
4111		}
4112	},
4113
4114	_addZoomLimit: function (layer) {
4115		if (isNaN(layer.options.maxZoom) || !isNaN(layer.options.minZoom)) {
4116			this._zoomBoundLayers[L.stamp(layer)] = layer;
4117			this._updateZoomLevels();
4118		}
4119	},
4120
4121	_removeZoomLimit: function (layer) {
4122		var id = L.stamp(layer);
4123
4124		if (this._zoomBoundLayers[id]) {
4125			delete this._zoomBoundLayers[id];
4126			this._updateZoomLevels();
4127		}
4128	},
4129
4130	_updateZoomLevels: function () {
4131		var minZoom = Infinity,
4132		    maxZoom = -Infinity,
4133		    oldZoomSpan = this._getZoomSpan();
4134
4135		for (var i in this._zoomBoundLayers) {
4136			var options = this._zoomBoundLayers[i].options;
4137
4138			minZoom = options.minZoom === undefined ? minZoom : Math.min(minZoom, options.minZoom);
4139			maxZoom = options.maxZoom === undefined ? maxZoom : Math.max(maxZoom, options.maxZoom);
4140		}
4141
4142		this._layersMaxZoom = maxZoom === -Infinity ? undefined : maxZoom;
4143		this._layersMinZoom = minZoom === Infinity ? undefined : minZoom;
4144
4145		// @section Map state change events
4146		// @event zoomlevelschange: Event
4147		// Fired when the number of zoomlevels on the map is changed due
4148		// to adding or removing a layer.
4149		if (oldZoomSpan !== this._getZoomSpan()) {
4150			this.fire('zoomlevelschange');
4151		}
4152
4153		if (this.options.maxZoom === undefined && this._layersMaxZoom && this.getZoom() > this._layersMaxZoom) {
4154			this.setZoom(this._layersMaxZoom);
4155		}
4156		if (this.options.minZoom === undefined && this._layersMinZoom && this.getZoom() < this._layersMinZoom) {
4157			this.setZoom(this._layersMinZoom);
4158		}
4159	}
4160});
4161
4162
4163
4164/*
4165 * @namespace DomEvent
4166 * Utility functions to work with the [DOM events](https://developer.mozilla.org/docs/Web/API/Event), used by Leaflet internally.
4167 */
4168
4169// Inspired by John Resig, Dean Edwards and YUI addEvent implementations.
4170
4171
4172
4173var eventsKey = '_leaflet_events';
4174
4175L.DomEvent = {
4176
4177	// @function on(el: HTMLElement, types: String, fn: Function, context?: Object): this
4178	// Adds a listener function (`fn`) to a particular DOM event type of the
4179	// element `el`. You can optionally specify the context of the listener
4180	// (object the `this` keyword will point to). You can also pass several
4181	// space-separated types (e.g. `'click dblclick'`).
4182
4183	// @alternative
4184	// @function on(el: HTMLElement, eventMap: Object, context?: Object): this
4185	// Adds a set of type/listener pairs, e.g. `{click: onClick, mousemove: onMouseMove}`
4186	on: function (obj, types, fn, context) {
4187
4188		if (typeof types === 'object') {
4189			for (var type in types) {
4190				this._on(obj, type, types[type], fn);
4191			}
4192		} else {
4193			types = L.Util.splitWords(types);
4194
4195			for (var i = 0, len = types.length; i < len; i++) {
4196				this._on(obj, types[i], fn, context);
4197			}
4198		}
4199
4200		return this;
4201	},
4202
4203	// @function off(el: HTMLElement, types: String, fn: Function, context?: Object): this
4204	// Removes a previously added listener function. If no function is specified,
4205	// it will remove all the listeners of that particular DOM event from the element.
4206	// Note that if you passed a custom context to on, you must pass the same
4207	// context to `off` in order to remove the listener.
4208
4209	// @alternative
4210	// @function off(el: HTMLElement, eventMap: Object, context?: Object): this
4211	// Removes a set of type/listener pairs, e.g. `{click: onClick, mousemove: onMouseMove}`
4212	off: function (obj, types, fn, context) {
4213
4214		if (typeof types === 'object') {
4215			for (var type in types) {
4216				this._off(obj, type, types[type], fn);
4217			}
4218		} else {
4219			types = L.Util.splitWords(types);
4220
4221			for (var i = 0, len = types.length; i < len; i++) {
4222				this._off(obj, types[i], fn, context);
4223			}
4224		}
4225
4226		return this;
4227	},
4228
4229	_on: function (obj, type, fn, context) {
4230		var id = type + L.stamp(fn) + (context ? '_' + L.stamp(context) : '');
4231
4232		if (obj[eventsKey] && obj[eventsKey][id]) { return this; }
4233
4234		var handler = function (e) {
4235			return fn.call(context || obj, e || window.event);
4236		};
4237
4238		var originalHandler = handler;
4239
4240		if (L.Browser.pointer && type.indexOf('touch') === 0) {
4241			this.addPointerListener(obj, type, handler, id);
4242
4243		} else if (L.Browser.touch && (type === 'dblclick') && this.addDoubleTapListener &&
4244		           !(L.Browser.pointer && L.Browser.chrome)) {
4245			// Chrome >55 does not need the synthetic dblclicks from addDoubleTapListener
4246			// See #5180
4247			this.addDoubleTapListener(obj, handler, id);
4248
4249		} else if ('addEventListener' in obj) {
4250
4251			if (type === 'mousewheel') {
4252				obj.addEventListener('onwheel' in obj ? 'wheel' : 'mousewheel', handler, false);
4253
4254			} else if ((type === 'mouseenter') || (type === 'mouseleave')) {
4255				handler = function (e) {
4256					e = e || window.event;
4257					if (L.DomEvent._isExternalTarget(obj, e)) {
4258						originalHandler(e);
4259					}
4260				};
4261				obj.addEventListener(type === 'mouseenter' ? 'mouseover' : 'mouseout', handler, false);
4262
4263			} else {
4264				if (type === 'click' && L.Browser.android) {
4265					handler = function (e) {
4266						return L.DomEvent._filterClick(e, originalHandler);
4267					};
4268				}
4269				obj.addEventListener(type, handler, false);
4270			}
4271
4272		} else if ('attachEvent' in obj) {
4273			obj.attachEvent('on' + type, handler);
4274		}
4275
4276		obj[eventsKey] = obj[eventsKey] || {};
4277		obj[eventsKey][id] = handler;
4278
4279		return this;
4280	},
4281
4282	_off: function (obj, type, fn, context) {
4283
4284		var id = type + L.stamp(fn) + (context ? '_' + L.stamp(context) : ''),
4285		    handler = obj[eventsKey] && obj[eventsKey][id];
4286
4287		if (!handler) { return this; }
4288
4289		if (L.Browser.pointer && type.indexOf('touch') === 0) {
4290			this.removePointerListener(obj, type, id);
4291
4292		} else if (L.Browser.touch && (type === 'dblclick') && this.removeDoubleTapListener) {
4293			this.removeDoubleTapListener(obj, id);
4294
4295		} else if ('removeEventListener' in obj) {
4296
4297			if (type === 'mousewheel') {
4298				obj.removeEventListener('onwheel' in obj ? 'wheel' : 'mousewheel', handler, false);
4299
4300			} else {
4301				obj.removeEventListener(
4302					type === 'mouseenter' ? 'mouseover' :
4303					type === 'mouseleave' ? 'mouseout' : type, handler, false);
4304			}
4305
4306		} else if ('detachEvent' in obj) {
4307			obj.detachEvent('on' + type, handler);
4308		}
4309
4310		obj[eventsKey][id] = null;
4311
4312		return this;
4313	},
4314
4315	// @function stopPropagation(ev: DOMEvent): this
4316	// Stop the given event from propagation to parent elements. Used inside the listener functions:
4317	// ```js
4318	// L.DomEvent.on(div, 'click', function (ev) {
4319	// 	L.DomEvent.stopPropagation(ev);
4320	// });
4321	// ```
4322	stopPropagation: function (e) {
4323
4324		if (e.stopPropagation) {
4325			e.stopPropagation();
4326		} else if (e.originalEvent) {  // In case of Leaflet event.
4327			e.originalEvent._stopped = true;
4328		} else {
4329			e.cancelBubble = true;
4330		}
4331		L.DomEvent._skipped(e);
4332
4333		return this;
4334	},
4335
4336	// @function disableScrollPropagation(el: HTMLElement): this
4337	// Adds `stopPropagation` to the element's `'mousewheel'` events (plus browser variants).
4338	disableScrollPropagation: function (el) {
4339		return L.DomEvent.on(el, 'mousewheel', L.DomEvent.stopPropagation);
4340	},
4341
4342	// @function disableClickPropagation(el: HTMLElement): this
4343	// Adds `stopPropagation` to the element's `'click'`, `'doubleclick'`,
4344	// `'mousedown'` and `'touchstart'` events (plus browser variants).
4345	disableClickPropagation: function (el) {
4346		var stop = L.DomEvent.stopPropagation;
4347
4348		L.DomEvent.on(el, L.Draggable.START.join(' '), stop);
4349
4350		return L.DomEvent.on(el, {
4351			click: L.DomEvent._fakeStop,
4352			dblclick: stop
4353		});
4354	},
4355
4356	// @function preventDefault(ev: DOMEvent): this
4357	// Prevents the default action of the DOM Event `ev` from happening (such as
4358	// following a link in the href of the a element, or doing a POST request
4359	// with page reload when a `<form>` is submitted).
4360	// Use it inside listener functions.
4361	preventDefault: function (e) {
4362
4363		if (e.preventDefault) {
4364			e.preventDefault();
4365		} else {
4366			e.returnValue = false;
4367		}
4368		return this;
4369	},
4370
4371	// @function stop(ev): this
4372	// Does `stopPropagation` and `preventDefault` at the same time.
4373	stop: function (e) {
4374		return L.DomEvent
4375			.preventDefault(e)
4376			.stopPropagation(e);
4377	},
4378
4379	// @function getMousePosition(ev: DOMEvent, container?: HTMLElement): Point
4380	// Gets normalized mouse position from a DOM event relative to the
4381	// `container` or to the whole page if not specified.
4382	getMousePosition: function (e, container) {
4383		if (!container) {
4384			return new L.Point(e.clientX, e.clientY);
4385		}
4386
4387		var rect = container.getBoundingClientRect();
4388
4389		return new L.Point(
4390			e.clientX - rect.left - container.clientLeft,
4391			e.clientY - rect.top - container.clientTop);
4392	},
4393
4394	// Chrome on Win scrolls double the pixels as in other platforms (see #4538),
4395	// and Firefox scrolls device pixels, not CSS pixels
4396	_wheelPxFactor: (L.Browser.win && L.Browser.chrome) ? 2 :
4397	                L.Browser.gecko ? window.devicePixelRatio :
4398	                1,
4399
4400	// @function getWheelDelta(ev: DOMEvent): Number
4401	// Gets normalized wheel delta from a mousewheel DOM event, in vertical
4402	// pixels scrolled (negative if scrolling down).
4403	// Events from pointing devices without precise scrolling are mapped to
4404	// a best guess of 60 pixels.
4405	getWheelDelta: function (e) {
4406		return (L.Browser.edge) ? e.wheelDeltaY / 2 : // Don't trust window-geometry-based delta
4407		       (e.deltaY && e.deltaMode === 0) ? -e.deltaY / L.DomEvent._wheelPxFactor : // Pixels
4408		       (e.deltaY && e.deltaMode === 1) ? -e.deltaY * 20 : // Lines
4409		       (e.deltaY && e.deltaMode === 2) ? -e.deltaY * 60 : // Pages
4410		       (e.deltaX || e.deltaZ) ? 0 :	// Skip horizontal/depth wheel events
4411		       e.wheelDelta ? (e.wheelDeltaY || e.wheelDelta) / 2 : // Legacy IE pixels
4412		       (e.detail && Math.abs(e.detail) < 32765) ? -e.detail * 20 : // Legacy Moz lines
4413		       e.detail ? e.detail / -32765 * 60 : // Legacy Moz pages
4414		       0;
4415	},
4416
4417	_skipEvents: {},
4418
4419	_fakeStop: function (e) {
4420		// fakes stopPropagation by setting a special event flag, checked/reset with L.DomEvent._skipped(e)
4421		L.DomEvent._skipEvents[e.type] = true;
4422	},
4423
4424	_skipped: function (e) {
4425		var skipped = this._skipEvents[e.type];
4426		// reset when checking, as it's only used in map container and propagates outside of the map
4427		this._skipEvents[e.type] = false;
4428		return skipped;
4429	},
4430
4431	// check if element really left/entered the event target (for mouseenter/mouseleave)
4432	_isExternalTarget: function (el, e) {
4433
4434		var related = e.relatedTarget;
4435
4436		if (!related) { return true; }
4437
4438		try {
4439			while (related && (related !== el)) {
4440				related = related.parentNode;
4441			}
4442		} catch (err) {
4443			return false;
4444		}
4445		return (related !== el);
4446	},
4447
4448	// this is a horrible workaround for a bug in Android where a single touch triggers two click events
4449	_filterClick: function (e, handler) {
4450		var timeStamp = (e.timeStamp || (e.originalEvent && e.originalEvent.timeStamp)),
4451		    elapsed = L.DomEvent._lastClick && (timeStamp - L.DomEvent._lastClick);
4452
4453		// are they closer together than 500ms yet more than 100ms?
4454		// Android typically triggers them ~300ms apart while multiple listeners
4455		// on the same event should be triggered far faster;
4456		// or check if click is simulated on the element, and if it is, reject any non-simulated events
4457
4458		if ((elapsed && elapsed > 100 && elapsed < 500) || (e.target._simulatedClick && !e._simulated)) {
4459			L.DomEvent.stop(e);
4460			return;
4461		}
4462		L.DomEvent._lastClick = timeStamp;
4463
4464		handler(e);
4465	}
4466};
4467
4468// @function addListener(…): this
4469// Alias to [`L.DomEvent.on`](#domevent-on)
4470L.DomEvent.addListener = L.DomEvent.on;
4471
4472// @function removeListener(…): this
4473// Alias to [`L.DomEvent.off`](#domevent-off)
4474L.DomEvent.removeListener = L.DomEvent.off;
4475
4476
4477
4478/*
4479 * @class PosAnimation
4480 * @aka L.PosAnimation
4481 * @inherits Evented
4482 * Used internally for panning animations, utilizing CSS3 Transitions for modern browsers and a timer fallback for IE6-9.
4483 *
4484 * @example
4485 * ```js
4486 * var fx = new L.PosAnimation();
4487 * fx.run(el, [300, 500], 0.5);
4488 * ```
4489 *
4490 * @constructor L.PosAnimation()
4491 * Creates a `PosAnimation` object.
4492 *
4493 */
4494
4495L.PosAnimation = L.Evented.extend({
4496
4497	// @method run(el: HTMLElement, newPos: Point, duration?: Number, easeLinearity?: 
vendor: 24,665 bytes, lines 4497-5398
4497Number)
4498	// Run an animation of a given element to a new position, optionally setting
4499	// duration in seconds (`0.25` by default) and easing linearity factor (3rd
4500	// argument of the [cubic bezier curve](http://cubic-bezier.com/#0,0,.5,1),
4501	// `0.5` by default).
4502	run: function (el, newPos, duration, easeLinearity) {
4503		this.stop();
4504
4505		this._el = el;
4506		this._inProgress = true;
4507		this._duration = duration || 0.25;
4508		this._easeOutPower = 1 / Math.max(easeLinearity || 0.5, 0.2);
4509
4510		this._startPos = L.DomUtil.getPosition(el);
4511		this._offset = newPos.subtract(this._startPos);
4512		this._startTime = +new Date();
4513
4514		// @event start: Event
4515		// Fired when the animation starts
4516		this.fire('start');
4517
4518		this._animate();
4519	},
4520
4521	// @method stop()
4522	// Stops the animation (if currently running).
4523	stop: function () {
4524		if (!this._inProgress) { return; }
4525
4526		this._step(true);
4527		this._complete();
4528	},
4529
4530	_animate: function () {
4531		// animation loop
4532		this._animId = L.Util.requestAnimFrame(this._animate, this);
4533		this._step();
4534	},
4535
4536	_step: function (round) {
4537		var elapsed = (+new Date()) - this._startTime,
4538		    duration = this._duration * 1000;
4539
4540		if (elapsed < duration) {
4541			this._runFrame(this._easeOut(elapsed / duration), round);
4542		} else {
4543			this._runFrame(1);
4544			this._complete();
4545		}
4546	},
4547
4548	_runFrame: function (progress, round) {
4549		var pos = this._startPos.add(this._offset.multiplyBy(progress));
4550		if (round) {
4551			pos._round();
4552		}
4553		L.DomUtil.setPosition(this._el, pos);
4554
4555		// @event step: Event
4556		// Fired continuously during the animation.
4557		this.fire('step');
4558	},
4559
4560	_complete: function () {
4561		L.Util.cancelAnimFrame(this._animId);
4562
4563		this._inProgress = false;
4564		// @event end: Event
4565		// Fired when the animation ends.
4566		this.fire('end');
4567	},
4568
4569	_easeOut: function (t) {
4570		return 1 - Math.pow(1 - t, this._easeOutPower);
4571	}
4572});
4573
4574
4575
4576/*
4577 * @namespace Projection
4578 * @projection L.Projection.Mercator
4579 *
4580 * 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.
4581 */
4582
4583L.Projection.Mercator = {
4584	R: 6378137,
4585	R_MINOR: 6356752.314245179,
4586
4587	bounds: L.bounds([-20037508.34279, -15496570.73972], [20037508.34279, 18764656.23138]),
4588
4589	project: function (latlng) {
4590		var d = Math.PI / 180,
4591		    r = this.R,
4592		    y = latlng.lat * d,
4593		    tmp = this.R_MINOR / r,
4594		    e = Math.sqrt(1 - tmp * tmp),
4595		    con = e * Math.sin(y);
4596
4597		var ts = Math.tan(Math.PI / 4 - y / 2) / Math.pow((1 - con) / (1 + con), e / 2);
4598		y = -r * Math.log(Math.max(ts, 1E-10));
4599
4600		return new L.Point(latlng.lng * d * r, y);
4601	},
4602
4603	unproject: function (point) {
4604		var d = 180 / Math.PI,
4605		    r = this.R,
4606		    tmp = this.R_MINOR / r,
4607		    e = Math.sqrt(1 - tmp * tmp),
4608		    ts = Math.exp(-point.y / r),
4609		    phi = Math.PI / 2 - 2 * Math.atan(ts);
4610
4611		for (var i = 0, dphi = 0.1, con; i < 15 && Math.abs(dphi) > 1e-7; i++) {
4612			con = e * Math.sin(phi);
4613			con = Math.pow((1 - con) / (1 + con), e / 2);
4614			dphi = Math.PI / 2 - 2 * Math.atan(ts * con) - phi;
4615			phi += dphi;
4616		}
4617
4618		return new L.LatLng(phi * d, point.x * d / r);
4619	}
4620};
4621
4622
4623
4624/*
4625 * @namespace CRS
4626 * @crs L.CRS.EPSG3395
4627 *
4628 * Rarely used by some commercial tile providers. Uses Elliptical Mercator projection.
4629 */
4630
4631L.CRS.EPSG3395 = L.extend({}, L.CRS.Earth, {
4632	code: 'EPSG:3395',
4633	projection: L.Projection.Mercator,
4634
4635	transformation: (function () {
4636		var scale = 0.5 / (Math.PI * L.Projection.Mercator.R);
4637		return new L.Transformation(scale, 0.5, -scale, 0.5);
4638	}())
4639});
4640
4641
4642
4643/*
4644 * @class GridLayer
4645 * @inherits Layer
4646 * @aka L.GridLayer
4647 *
4648 * Generic class for handling a tiled grid of HTML elements. This is the base class for all tile layers and replaces `TileLayer.Canvas`.
4649 * 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.
4650 *
4651 *
4652 * @section Synchronous usage
4653 * @example
4654 *
4655 * 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.
4656 *
4657 * ```js
4658 * var CanvasLayer = L.GridLayer.extend({
4659 *     createTile: function(coords){
4660 *         // create a <canvas> element for drawing
4661 *         var tile = L.DomUtil.create('canvas', 'leaflet-tile');
4662 *
4663 *         // setup tile width and height according to the options
4664 *         var size = this.getTileSize();
4665 *         tile.width = size.x;
4666 *         tile.height = size.y;
4667 *
4668 *         // get a canvas context and draw something on it using coords.x, coords.y and coords.z
4669 *         var ctx = tile.getContext('2d');
4670 *
4671 *         // return the tile so it can be rendered on screen
4672 *         return tile;
4673 *     }
4674 * });
4675 * ```
4676 *
4677 * @section Asynchronous usage
4678 * @example
4679 *
4680 * 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.
4681 *
4682 * ```js
4683 * var CanvasLayer = L.GridLayer.extend({
4684 *     createTile: function(coords, done){
4685 *         var error;
4686 *
4687 *         // create a <canvas> element for drawing
4688 *         var tile = L.DomUtil.create('canvas', 'leaflet-tile');
4689 *
4690 *         // setup tile width and height according to the options
4691 *         var size = this.getTileSize();
4692 *         tile.width = size.x;
4693 *         tile.height = size.y;
4694 *
4695 *         // draw something asynchronously and pass the tile to the done() callback
4696 *         setTimeout(function() {
4697 *             done(error, tile);
4698 *         }, 1000);
4699 *
4700 *         return tile;
4701 *     }
4702 * });
4703 * ```
4704 *
4705 * @section
4706 */
4707
4708
4709L.GridLayer = L.Layer.extend({
4710
4711	// @section
4712	// @aka GridLayer options
4713	options: {
4714		// @option tileSize: Number|Point = 256
4715		// Width and height of tiles in the grid. Use a number if width and height are equal, or `L.point(width, height)` otherwise.
4716		tileSize: 256,
4717
4718		// @option opacity: Number = 1.0
4719		// Opacity of the tiles. Can be used in the `createTile()` function.
4720		opacity: 1,
4721
4722		// @option updateWhenIdle: Boolean = depends
4723		// If `false`, new tiles are loaded during panning, otherwise only after it (for better performance). `true` by default on mobile browsers, otherwise `false`.
4724		updateWhenIdle: L.Browser.mobile,
4725
4726		// @option updateWhenZooming: Boolean = true
4727		// 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.
4728		updateWhenZooming: true,
4729
4730		// @option updateInterval: Number = 200
4731		// Tiles will not update more than once every `updateInterval` milliseconds when panning.
4732		updateInterval: 200,
4733
4734		// @option zIndex: Number = 1
4735		// The explicit zIndex of the tile layer.
4736		zIndex: 1,
4737
4738		// @option bounds: LatLngBounds = undefined
4739		// If set, tiles will only be loaded inside the set `LatLngBounds`.
4740		bounds: null,
4741
4742		// @option minZoom: Number = 0
4743		// The minimum zoom level that tiles will be loaded at. By default the entire map.
4744		minZoom: 0,
4745
4746		// @option maxZoom: Number = undefined
4747		// The maximum zoom level that tiles will be loaded at.
4748		maxZoom: undefined,
4749
4750		// @option noWrap: Boolean = false
4751		// Whether the layer is wrapped around the antimeridian. If `true`, the
4752		// GridLayer will only be displayed once at low zoom levels. Has no
4753		// effect when the [map CRS](#map-crs) doesn't wrap around. Can be used
4754		// in combination with [`bounds`](#gridlayer-bounds) to prevent requesting
4755		// tiles outside the CRS limits.
4756		noWrap: false,
4757
4758		// @option pane: String = 'tilePane'
4759		// `Map pane` where the grid layer will be added.
4760		pane: 'tilePane',
4761
4762		// @option className: String = ''
4763		// A custom class name to assign to the tile layer. Empty by default.
4764		className: '',
4765
4766		// @option keepBuffer: Number = 2
4767		// When panning the map, keep this many rows and columns of tiles before unloading them.
4768		keepBuffer: 2
4769	},
4770
4771	initialize: function (options) {
4772		L.setOptions(this, options);
4773	},
4774
4775	onAdd: function () {
4776		this._initContainer();
4777
4778		this._levels = {};
4779		this._tiles = {};
4780
4781		this._resetView();
4782		this._update();
4783	},
4784
4785	beforeAdd: function (map) {
4786		map._addZoomLimit(this);
4787	},
4788
4789	onRemove: function (map) {
4790		this._removeAllTiles();
4791		L.DomUtil.remove(this._container);
4792		map._removeZoomLimit(this);
4793		this._container = null;
4794		this._tileZoom = null;
4795	},
4796
4797	// @method bringToFront: this
4798	// Brings the tile layer to the top of all tile layers.
4799	bringToFront: function () {
4800		if (this._map) {
4801			L.DomUtil.toFront(this._container);
4802			this._setAutoZIndex(Math.max);
4803		}
4804		return this;
4805	},
4806
4807	// @method bringToBack: this
4808	// Brings the tile layer to the bottom of all tile layers.
4809	bringToBack: function () {
4810		if (this._map) {
4811			L.DomUtil.toBack(this._container);
4812			this._setAutoZIndex(Math.min);
4813		}
4814		return this;
4815	},
4816
4817	// @method getContainer: HTMLElement
4818	// Returns the HTML element that contains the tiles for this layer.
4819	getContainer: function () {
4820		return this._container;
4821	},
4822
4823	// @method setOpacity(opacity: Number): this
4824	// Changes the [opacity](#gridlayer-opacity) of the grid layer.
4825	setOpacity: function (opacity) {
4826		this.options.opacity = opacity;
4827		this._updateOpacity();
4828		return this;
4829	},
4830
4831	// @method setZIndex(zIndex: Number): this
4832	// Changes the [zIndex](#gridlayer-zindex) of the grid layer.
4833	setZIndex: function (zIndex) {
4834		this.options.zIndex = zIndex;
4835		this._updateZIndex();
4836
4837		return this;
4838	},
4839
4840	// @method isLoading: Boolean
4841	// Returns `true` if any tile in the grid layer has not finished loading.
4842	isLoading: function () {
4843		return this._loading;
4844	},
4845
4846	// @method redraw: this
4847	// Causes the layer to clear all the tiles and request them again.
4848	redraw: function () {
4849		if (this._map) {
4850			this._removeAllTiles();
4851			this._update();
4852		}
4853		return this;
4854	},
4855
4856	getEvents: function () {
4857		var events = {
4858			viewprereset: this._invalidateAll,
4859			viewreset: this._resetView,
4860			zoom: this._resetView,
4861			moveend: this._onMoveEnd
4862		};
4863
4864		if (!this.options.updateWhenIdle) {
4865			// update tiles on move, but not more often than once per given interval
4866			if (!this._onMove) {
4867				this._onMove = L.Util.throttle(this._onMoveEnd, this.options.updateInterval, this);
4868			}
4869
4870			events.move = this._onMove;
4871		}
4872
4873		if (this._zoomAnimated) {
4874			events.zoomanim = this._animateZoom;
4875		}
4876
4877		return events;
4878	},
4879
4880	// @section Extension methods
4881	// Layers extending `GridLayer` shall reimplement the following method.
4882	// @method createTile(coords: Object, done?: Function): HTMLElement
4883	// Called only internally, must be overriden by classes extending `GridLayer`.
4884	// Returns the `HTMLElement` corresponding to the given `coords`. If the `done` callback
4885	// is specified, it must be called when the tile has finished loading and drawing.
4886	createTile: function () {
4887		return document.createElement('div');
4888	},
4889
4890	// @section
4891	// @method getTileSize: Point
4892	// Normalizes the [tileSize option](#gridlayer-tilesize) into a point. Used by the `createTile()` method.
4893	getTileSize: function () {
4894		var s = this.options.tileSize;
4895		return s instanceof L.Point ? s : new L.Point(s, s);
4896	},
4897
4898	_updateZIndex: function () {
4899		if (this._container && this.options.zIndex !== undefined && this.options.zIndex !== null) {
4900			this._container.style.zIndex = this.options.zIndex;
4901		}
4902	},
4903
4904	_setAutoZIndex: function (compare) {
4905		// go through all other layers of the same pane, set zIndex to max + 1 (front) or min - 1 (back)
4906
4907		var layers = this.getPane().children,
4908		    edgeZIndex = -compare(-Infinity, Infinity); // -Infinity for max, Infinity for min
4909
4910		for (var i = 0, len = layers.length, zIndex; i < len; i++) {
4911
4912			zIndex = layers[i].style.zIndex;
4913
4914			if (layers[i] !== this._container && zIndex) {
4915				edgeZIndex = compare(edgeZIndex, +zIndex);
4916			}
4917		}
4918
4919		if (isFinite(edgeZIndex)) {
4920			this.options.zIndex = edgeZIndex + compare(-1, 1);
4921			this._updateZIndex();
4922		}
4923	},
4924
4925	_updateOpacity: function () {
4926		if (!this._map) { return; }
4927
4928		// IE doesn't inherit filter opacity properly, so we're forced to set it on tiles
4929		if (L.Browser.ielt9) { return; }
4930
4931		L.DomUtil.setOpacity(this._container, this.options.opacity);
4932
4933		var now = +new Date(),
4934		    nextFrame = false,
4935		    willPrune = false;
4936
4937		for (var key in this._tiles) {
4938			var tile = this._tiles[key];
4939			if (!tile.current || !tile.loaded) { continue; }
4940
4941			var fade = Math.min(1, (now - tile.loaded) / 200);
4942
4943			L.DomUtil.setOpacity(tile.el, fade);
4944			if (fade < 1) {
4945				nextFrame = true;
4946			} else {
4947				if (tile.active) { willPrune = true; }
4948				tile.active = true;
4949			}
4950		}
4951
4952		if (willPrune && !this._noPrune) { this._pruneTiles(); }
4953
4954		if (nextFrame) {
4955			L.Util.cancelAnimFrame(this._fadeFrame);
4956			this._fadeFrame = L.Util.requestAnimFrame(this._updateOpacity, this);
4957		}
4958	},
4959
4960	_initContainer: function () {
4961		if (this._container) { return; }
4962
4963		this._container = L.DomUtil.create('div', 'leaflet-layer ' + (this.options.className || ''));
4964		this._updateZIndex();
4965
4966		if (this.options.opacity < 1) {
4967			this._updateOpacity();
4968		}
4969
4970		this.getPane().appendChild(this._container);
4971	},
4972
4973	_updateLevels: function () {
4974
4975		var zoom = this._tileZoom,
4976		    maxZoom = this.options.maxZoom;
4977
4978		if (zoom === undefined) { return undefined; }
4979
4980		for (var z in this._levels) {
4981			if (this._levels[z].el.children.length || z === zoom) {
4982				this._levels[z].el.style.zIndex = maxZoom - Math.abs(zoom - z);
4983			} else {
4984				L.DomUtil.remove(this._levels[z].el);
4985				this._removeTilesAtZoom(z);
4986				delete this._levels[z];
4987			}
4988		}
4989
4990		var level = this._levels[zoom],
4991		    map = this._map;
4992
4993		if (!level) {
4994			level = this._levels[zoom] = {};
4995
4996			level.el = L.DomUtil.create('div', 'leaflet-tile-container leaflet-zoom-animated', this._container);
4997			level.el.style.zIndex = maxZoom;
4998
4999			level.origin = map.project(map.unproject(map.getPixelOrigin()), zoom).round();
5000			level.zoom = zoom;
5001
5002			this._setZoomTransform(level, map.getCenter(), map.getZoom());
5003
5004			// force the browser to consider the newly added element for transition
5005			L.Util.falseFn(level.el.offsetWidth);
5006		}
5007
5008		this._level = level;
5009
5010		return level;
5011	},
5012
5013	_pruneTiles: function () {
5014		if (!this._map) {
5015			return;
5016		}
5017
5018		var key, tile;
5019
5020		var zoom = this._map.getZoom();
5021		if (zoom > this.options.maxZoom ||
5022			zoom < this.options.minZoom) {
5023			this._removeAllTiles();
5024			return;
5025		}
5026
5027		for (key in this._tiles) {
5028			tile = this._tiles[key];
5029			tile.retain = tile.current;
5030		}
5031
5032		for (key in this._tiles) {
5033			tile = this._tiles[key];
5034			if (tile.current && !tile.active) {
5035				var coords = tile.coords;
5036				if (!this._retainParent(coords.x, coords.y, coords.z, coords.z - 5)) {
5037					this._retainChildren(coords.x, coords.y, coords.z, coords.z + 2);
5038				}
5039			}
5040		}
5041
5042		for (key in this._tiles) {
5043			if (!this._tiles[key].retain) {
5044				this._removeTile(key);
5045			}
5046		}
5047	},
5048
5049	_removeTilesAtZoom: function (zoom) {
5050		for (var key in this._tiles) {
5051			if (this._tiles[key].coords.z !== zoom) {
5052				continue;
5053			}
5054			this._removeTile(key);
5055		}
5056	},
5057
5058	_removeAllTiles: function () {
5059		for (var key in this._tiles) {
5060			this._removeTile(key);
5061		}
5062	},
5063
5064	_invalidateAll: function () {
5065		for (var z in this._levels) {
5066			L.DomUtil.remove(this._levels[z].el);
5067			delete this._levels[z];
5068		}
5069		this._removeAllTiles();
5070
5071		this._tileZoom = null;
5072	},
5073
5074	_retainParent: function (x, y, z, minZoom) {
5075		var x2 = Math.floor(x / 2),
5076		    y2 = Math.floor(y / 2),
5077		    z2 = z - 1,
5078		    coords2 = new L.Point(+x2, +y2);
5079		coords2.z = +z2;
5080
5081		var key = this._tileCoordsToKey(coords2),
5082		    tile = this._tiles[key];
5083
5084		if (tile && tile.active) {
5085			tile.retain = true;
5086			return true;
5087
5088		} else if (tile && tile.loaded) {
5089			tile.retain = true;
5090		}
5091
5092		if (z2 > minZoom) {
5093			return this._retainParent(x2, y2, z2, minZoom);
5094		}
5095
5096		return false;
5097	},
5098
5099	_retainChildren: function (x, y, z, maxZoom) {
5100
5101		for (var i = 2 * x; i < 2 * x + 2; i++) {
5102			for (var j = 2 * y; j < 2 * y + 2; j++) {
5103
5104				var coords = new L.Point(i, j);
5105				coords.z = z + 1;
5106
5107				var key = this._tileCoordsToKey(coords),
5108				    tile = this._tiles[key];
5109
5110				if (tile && tile.active) {
5111					tile.retain = true;
5112					continue;
5113
5114				} else if (tile && tile.loaded) {
5115					tile.retain = true;
5116				}
5117
5118				if (z + 1 < maxZoom) {
5119					this._retainChildren(i, j, z + 1, maxZoom);
5120				}
5121			}
5122		}
5123	},
5124
5125	_resetView: function (e) {
5126		var animating = e && (e.pinch || e.flyTo);
5127		this._setView(this._map.getCenter(), this._map.getZoom(), animating, animating);
5128	},
5129
5130	_animateZoom: function (e) {
5131		this._setView(e.center, e.zoom, true, e.noUpdate);
5132	},
5133
5134	_setView: function (center, zoom, noPrune, noUpdate) {
5135		var tileZoom = Math.round(zoom);
5136		if ((this.options.maxZoom !== undefined && tileZoom > this.options.maxZoom) ||
5137		    (this.options.minZoom !== undefined && tileZoom < this.options.minZoom)) {
5138			tileZoom = undefined;
5139		}
5140
5141		var tileZoomChanged = this.options.updateWhenZooming && (tileZoom !== this._tileZoom);
5142
5143		if (!noUpdate || tileZoomChanged) {
5144
5145			this._tileZoom = tileZoom;
5146
5147			if (this._abortLoading) {
5148				this._abortLoading();
5149			}
5150
5151			this._updateLevels();
5152			this._resetGrid();
5153
5154			if (tileZoom !== undefined) {
5155				this._update(center);
5156			}
5157
5158			if (!noPrune) {
5159				this._pruneTiles();
5160			}
5161
5162			// Flag to prevent _updateOpacity from pruning tiles during
5163			// a zoom anim or a pinch gesture
5164			this._noPrune = !!noPrune;
5165		}
5166
5167		this._setZoomTransforms(center, zoom);
5168	},
5169
5170	_setZoomTransforms: function (center, zoom) {
5171		for (var i in this._levels) {
5172			this._setZoomTransform(this._levels[i], center, zoom);
5173		}
5174	},
5175
5176	_setZoomTransform: function (level, center, zoom) {
5177		var scale = this._map.getZoomScale(zoom, level.zoom),
5178		    translate = level.origin.multiplyBy(scale)
5179		        .subtract(this._map._getNewPixelOrigin(center, zoom)).round();
5180
5181		if (L.Browser.any3d) {
5182			L.DomUtil.setTransform(level.el, translate, scale);
5183		} else {
5184			L.DomUtil.setPosition(level.el, translate);
5185		}
5186	},
5187
5188	_resetGrid: function () {
5189		var map = this._map,
5190		    crs = map.options.crs,
5191		    tileSize = this._tileSize = this.getTileSize(),
5192		    tileZoom = this._tileZoom;
5193
5194		var bounds = this._map.getPixelWorldBounds(this._tileZoom);
5195		if (bounds) {
5196			this._globalTileRange = this._pxBoundsToTileRange(bounds);
5197		}
5198
5199		this._wrapX = crs.wrapLng && !this.options.noWrap && [
5200			Math.floor(map.project([0, crs.wrapLng[0]], tileZoom).x / tileSize.x),
5201			Math.ceil(map.project([0, crs.wrapLng[1]], tileZoom).x / tileSize.y)
5202		];
5203		this._wrapY = crs.wrapLat && !this.options.noWrap && [
5204			Math.floor(map.project([crs.wrapLat[0], 0], tileZoom).y / tileSize.x),
5205			Math.ceil(map.project([crs.wrapLat[1], 0], tileZoom).y / tileSize.y)
5206		];
5207	},
5208
5209	_onMoveEnd: function () {
5210		if (!this._map || this._map._animatingZoom) { return; }
5211
5212		this._update();
5213	},
5214
5215	_getTiledPixelBounds: function (center) {
5216		var map = this._map,
5217		    mapZoom = map._animatingZoom ? Math.max(map._animateToZoom, map.getZoom()) : map.getZoom(),
5218		    scale = map.getZoomScale(mapZoom, this._tileZoom),
5219		    pixelCenter = map.project(center, this._tileZoom).floor(),
5220		    halfSize = map.getSize().divideBy(scale * 2);
5221
5222		return new L.Bounds(pixelCenter.subtract(halfSize), pixelCenter.add(halfSize));
5223	},
5224
5225	// Private method to load tiles in the grid's active zoom level according to map bounds
5226	_update: function (center) {
5227		var map = this._map;
5228		if (!map) { return; }
5229		var zoom = map.getZoom();
5230
5231		if (center === undefined) { center = map.getCenter(); }
5232		if (this._tileZoom === undefined) { return; }	// if out of minzoom/maxzoom
5233
5234		var pixelBounds = this._getTiledPixelBounds(center),
5235		    tileRange = this._pxBoundsToTileRange(pixelBounds),
5236		    tileCenter = tileRange.getCenter(),
5237		    queue = [],
5238		    margin = this.options.keepBuffer,
5239		    noPruneRange = new L.Bounds(tileRange.getBottomLeft().subtract([margin, -margin]),
5240		                              tileRange.getTopRight().add([margin, -margin]));
5241
5242		for (var key in this._tiles) {
5243			var c = this._tiles[key].coords;
5244			if (c.z !== this._tileZoom || !noPruneRange.contains(L.point(c.x, c.y))) {
5245				this._tiles[key].current = false;
5246			}
5247		}
5248
5249		// _update just loads more tiles. If the tile zoom level differs too much
5250		// from the map's, let _setView reset levels and prune old tiles.
5251		if (Math.abs(zoom - this._tileZoom) > 1) { this._setView(center, zoom); return; }
5252
5253		// create a queue of coordinates to load tiles from
5254		for (var j = tileRange.min.y; j <= tileRange.max.y; j++) {
5255			for (var i = tileRange.min.x; i <= tileRange.max.x; i++) {
5256				var coords = new L.Point(i, j);
5257				coords.z = this._tileZoom;
5258
5259				if (!this._isValidTile(coords)) { continue; }
5260
5261				var tile = this._tiles[this._tileCoordsToKey(coords)];
5262				if (tile) {
5263					tile.current = true;
5264				} else {
5265					queue.push(coords);
5266				}
5267			}
5268		}
5269
5270		// sort tile queue to load tiles in order of their distance to center
5271		queue.sort(function (a, b) {
5272			return a.distanceTo(tileCenter) - b.distanceTo(tileCenter);
5273		});
5274
5275		if (queue.length !== 0) {
5276			// if it's the first batch of tiles to load
5277			if (!this._loading) {
5278				this._loading = true;
5279				// @event loading: Event
5280				// Fired when the grid layer starts loading tiles.
5281				this.fire('loading');
5282			}
5283
5284			// create DOM fragment to append tiles in one batch
5285			var fragment = document.createDocumentFragment();
5286
5287			for (i = 0; i < queue.length; i++) {
5288				this._addTile(queue[i], fragment);
5289			}
5290
5291			this._level.el.appendChild(fragment);
5292		}
5293	},
5294
5295	_isValidTile: function (coords) {
5296		var crs = this._map.options.crs;
5297
5298		if (!crs.infinite) {
5299			// don't load tile if it's out of bounds and not wrapped
5300			var bounds = this._globalTileRange;
5301			if ((!crs.wrapLng && (coords.x < bounds.min.x || coords.x > bounds.max.x)) ||
5302			    (!crs.wrapLat && (coords.y < bounds.min.y || coords.y > bounds.max.y))) { return false; }
5303		}
5304
5305		if (!this.options.bounds) { return true; }
5306
5307		// don't load tile if it doesn't intersect the bounds in options
5308		var tileBounds = this._tileCoordsToBounds(coords);
5309		return L.latLngBounds(this.options.bounds).overlaps(tileBounds);
5310	},
5311
5312	_keyToBounds: function (key) {
5313		return this._tileCoordsToBounds(this._keyToTileCoords(key));
5314	},
5315
5316	// converts tile coordinates to its geographical bounds
5317	_tileCoordsToBounds: function (coords) {
5318
5319		var map = this._map,
5320		    tileSize = this.getTileSize(),
5321
5322		    nwPoint = coords.scaleBy(tileSize),
5323		    sePoint = nwPoint.add(tileSize),
5324
5325		    nw = map.unproject(nwPoint, coords.z),
5326		    se = map.unproject(sePoint, coords.z),
5327		    bounds = new L.LatLngBounds(nw, se);
5328
5329		if (!this.options.noWrap) {
5330			map.wrapLatLngBounds(bounds);
5331		}
5332
5333		return bounds;
5334	},
5335
5336	// converts tile coordinates to key for the tile cache
5337	_tileCoordsToKey: function (coords) {
5338		return coords.x + ':' + coords.y + ':' + coords.z;
5339	},
5340
5341	// converts tile cache key to coordinates
5342	_keyToTileCoords: function (key) {
5343		var k = key.split(':'),
5344		    coords = new L.Point(+k[0], +k[1]);
5345		coords.z = +k[2];
5346		return coords;
5347	},
5348
5349	_removeTile: function (key) {
5350		var tile = this._tiles[key];
5351		if (!tile) { return; }
5352
5353		L.DomUtil.remove(tile.el);
5354
5355		delete this._tiles[key];
5356
5357		// @event tileunload: TileEvent
5358		// Fired when a tile is removed (e.g. when a tile goes off the screen).
5359		this.fire('tileunload', {
5360			tile: tile.el,
5361			coords: this._keyToTileCoords(key)
5362		});
5363	},
5364
5365	_initTile: function (tile) {
5366		L.DomUtil.addClass(tile, 'leaflet-tile');
5367
5368		var tileSize = this.getTileSize();
5369		tile.style.width = tileSize.x + 'px';
5370		tile.style.height = tileSize.y + 'px';
5371
5372		tile.onselectstart = L.Util.falseFn;
5373		tile.onmousemove = L.Util.falseFn;
5374
5375		// update opacity on tiles in IE7-8 because of filter inheritance problems
5376		if (L.Browser.ielt9 && this.options.opacity < 1) {
5377			L.DomUtil.setOpacity(tile, this.options.opacity);
5378		}
5379
5380		// without this hack, tiles disappear after zoom on Chrome for Android
5381		// https://github.com/Leaflet/Leaflet/issues/2078
5382		if (L.Browser.android && !L.Browser.android23) {
5383			tile.style.WebkitBackfaceVisibility = 'hidden';
5384		}
5385	},
5386
5387	_addTile: function (coords, container) {
5388		var tilePos = this._getTilePos(coords),
5389		    key = this._tileCoordsToKey(coords);
5390
5391		var tile = this.createTile(this._wrapCoords(coords), L.bind(this._tileReady, this, coords));
5392
5393		this._initTile(tile);
5394
5395		// if createTile is defined with a second argument ("done" callback),
5396		// we know that tile is async and will be ready later; otherwise
5397		if (this.createTile.length < 2) {
5398			// mark tile as ready, but delay one frame for opacity animation to happen
5399			L.Util.requestAnimFrame(L.bind(this._tileReady, this, coords, null, tile));
5400		}
5401
5402		L.DomUtil.setPosition(tile, tilePos);
5403
5404		// save tile in cache
5405		this._tiles[key] = {
5406			el: tile,
5407			coords: coords,
5408			current: true
5409		};
5410
5411		container.appendChild(tile);
5412		// @event tileloadstart: TileEvent
5413		// Fired when a tile is requested and starts loading.
5414		this.fire('tileloadstart', {
5415			tile: tile,
5416			coords: coords
5417		});
5418	},
5419
5420	_tileReady: function (coords, err, tile) {
5421		if (!this._map) { return; }
5422
5423		if (err) {
5424			// @event tileerror: TileErrorEvent
5425			// Fired when there is an error loading a tile.
5426			this.fire('tileerror', {
5427				error: err,
5428				tile: tile,
5429				coords: coords
5430			});
5431		}
5432
5433		var key = this._tileCoordsToKey(coords);
5434
5435		tile = this._tiles[key];
5436		if (!tile) { return; }
5437
5438		tile.loaded = +new Date();
5439		if (this._map._fadeAnimated) {
5440			L.DomUtil.setOpacity(tile.el, 0);
5441			L.Util.cancelAnimFrame(this._fadeFrame);
5442			this._fadeFrame = L.Util.requestAnimFrame(this._updateOpacity, this);
5443		} else {
5444			tile.active = true;
5445			this._pruneTiles();
5446		}
5447
5448		if (!err) {
5449			L.DomUtil.addClass(tile.el, 'leaflet-tile-loaded');
5450
5451			// @event tileload: TileEvent
5452			// Fired when a tile loads.
5453			this.fire('tileload', {
5454				tile: tile.el,
5455				coords: coords
5456			});
5457		}
5458
5459		if (this._noTilesToLoad()) {
5460			this._loading = false;
5461			// @event load: Event
5462			// Fired when the grid layer loaded all visible tiles.
5463			this.fire('load');
5464
5465			if (L.Browser.ielt9 || !this._map._fadeAnimated) {
5466				L.Util.requestAnimFrame(this._pruneTiles, this);
5467			} else {
5468				// Wait a bit more than 0.2 secs (the duration of the tile fade-in)
5469				// to trigger a pruning.
5470				setTimeout(L.bind(this._pruneTiles, this), 250);
5471			}
5472		}
5473	},
5474
5475	_getTilePos: function (coords) {
5476		return coords.scaleBy(this.getTileSize()).subtract(this._level.origin);
5477	},
5478
5479	_wrapCoords: function (coords) {
5480		var newCoords = new L.Point(
5481			this._wrapX ? L.Util.wrapNum(coords.x, this._wrapX) : coords.x,
5482			this._wrapY ? L.Util.wrapNum(coords.y, this._wrapY) : coords.y);
5483		newCoords.z = coords.z;
5484		return newCoords;
5485	},
5486
5487	_pxBoundsToTileRange: function (bounds) {
5488		var tileSize = this.getTileSize();
5489		return new L.Bounds(
5490			bounds.min.unscaleBy(tileSize).floor(),
5491			bounds.max.unscaleBy(tileSize).ceil().subtract([1, 1]));
5492	},
5493
5494	_noTilesToLoad: function () {
5495		for (var key in this._tiles) {
5496			if (!this._tiles[key].loaded) { return false; }
5497		}
5498		return true;
5499	}
5500});
5501
5502// @factory L.gridLayer(options?: GridLayer options)
5503// Creates a new instance of GridLayer with the supplied options.
5504L.gridLayer = function (options) {
5505	return new L.GridLayer(options);
5506};
5507
5508
5509
5510/*
5511 * @class TileLayer
5512 * @inherits GridLayer
5513 * @aka L.TileLayer
5514 * Used to load and display tile layers on the map. Extends `GridLayer`.
5515 *
5516 * @example
5517 *
5518 * ```js
5519 * L.tileLayer('http://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png?{foo}', {foo: 'bar'}).addTo(map);
5520 * ```
5521 *
5522 * @section URL template
5523 * @example
5524 *
5525 * A string of the following form:
5526 *
5527 * ```
5528 * 'http://{s}.somedomain.com/blabla/{z}/{x}/{y}{r}.png'
5529 * ```
5530 *
5531 * `{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 @2x to the URL to load retina tiles.
5532 *
5533 * You can use custom keys in the template, which will be [evaluated](#util-template) from TileLayer options, like this:
5534 *
5535 * ```
5536 * L.tileLayer('http://{s}.somedomain.com/{foo}/{z}/{x}/{y}.png', {foo: 'bar'});
5537 * ```
5538 */
5539
5540
5541L.TileLayer = L.GridLayer.extend({
5542
5543	// @section
5544	// @aka TileLayer options
5545	options: {
5546		// @option minZoom: Number = 0
5547		// Minimum zoom number.
5548		minZoom: 0,
5549
5550		// @option maxZoom: Number = 18
5551		// Maximum zoom number.
5552		maxZoom: 18,
5553
5554		// @option maxNativeZoom: Number = null
5555		// Maximum zoom number the tile source has available. If it is specified,
5556		// the tiles on all zoom levels higher than `maxNativeZoom` will be loaded
5557		// from `maxNativeZoom` level and auto-scaled.
5558		maxNativeZoom: null,
5559
5560		// @option minNativeZoom: Number = null
5561		// Minimum zoom number the tile source has available. If it is specified,
5562		// the tiles on all zoom levels lower than `minNativeZoom` will be loaded
5563		// from `minNativeZoom` level and auto-scaled.
5564		minNativeZoom: null,
5565
5566		// @option subdomains: String|String[] = 'abc'
5567		// 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.
5568		subdomains: 'abc',
5569
5570		// @option errorTileUrl: String = ''
5571		// URL to the tile image to show in place of the tile that failed to load.
5572		errorTileUrl: '',
5573
5574		// @option zoomOffset: Number = 0
5575		// The zoom number used in tile URLs will be offset with this value.
5576		zoomOffset: 0,
5577
5578		// @option tms: Boolean = false
5579		// If `true`, inverses Y axis numbering for tiles (turn this on for [TMS](https://en.wikipedia.org/wiki/Tile_Map_Service) services).
5580		tms: false,
5581
5582		// @option zoomReverse: Boolean = false
5583		// If set to true, the zoom number used in tile URLs will be reversed (`maxZoom - zoom` instead of `zoom`)
5584		zoomReverse: false,
5585
5586		// @option detectRetina: Boolean = false
5587		// 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.
5588		detectRetina: false,
5589
5590		// @option crossOrigin: Boolean = false
5591		// If true, all tiles will have their crossOrigin attribute set to ''. This is needed if you want to access tile pixel data.
5592		crossOrigin: false
5593	},
5594
5595	initialize: function (url, options) {
5596
5597		this._url = url;
5598
5599		options = L.setOptions(this, options);
5600
5601		// detecting retina displays, adjusting tileSize and zoom levels
5602		if (options.detectRetina && L.Browser.retina && options.maxZoom > 0) {
5603
5604			options.tileSize = Math.floor(options.tileSize / 2);
5605
5606			if (!options.zoomReverse) {
5607				options.zoomOffset++;
5608				options.maxZoom--;
5609			} else {
5610				options.zoomOffset--;
5611				options.minZoom++;
5612			}
5613
5614			options.minZoom = Math.max(0, options.minZoom);
5615		}
5616
5617		if (typeof options.subdomains === 'string') {
5618			options.subdomains = options.subdomains.split('');
5619		}
5620
5621		// for https://github.com/Leaflet/Leaflet/issues/137
5622		if (!L.Browser.android) {
5623			this.on('tileunload', this._onTileRemove);
5624		}
5625	},
5626
5627	// @method setUrl(url: String, noRedraw?: Boolean): this
5628	// Updates the layer's URL template and redraws it (unless `noRedraw` is set to `true`).
5629	setUrl: function (url, noRedraw) {
5630		this._url = url;
5631
5632		if (!noRedraw) {
5633			this.redraw();
5634		}
5635		return this;
5636	},
5637
5638	// @method createTile(coords: Object, done?: Function): HTMLElement
5639	// Called only internally, overrides GridLayer's [`createTile()`](#gridlayer-createtile)
5640	// to return an `<img>` HTML element with the appropiate image URL given `coords`. The `done`
5641	// callback is called when the tile has been loaded.
5642	createTile: function (coords, done) {
5643		var tile = document.createElement('img');
5644
5645		L.DomEvent.on(tile, 'load', L.bind(this._tileOnLoad, this, done, tile));
5646		L.DomEvent.on(tile, 'error', L.bind(this._tileOnError, this, done, tile));
5647
5648		if (this.options.crossOrigin) {
5649			tile.crossOrigin = '';
5650		}
5651
5652		/*
5653		 Alt tag is set to empty string to keep screen readers from reading URL and for compliance reasons
5654		 http://www.w3.org/TR/WCAG20-TECHS/H67
5655		*/
5656		tile.alt = '';
5657
5658		/*
5659		 Set role="presentation" to force screen readers to ignore this
5660		 https://www.w3.org/TR/wai-aria/roles#textalternativecomputation
5661		*/
5662		tile.setAttribute('role', 'presentation');
5663
5664		tile.src = this.getTileUrl(coords);
5665
5666		return tile;
5667	},
5668
5669	// @section Extension methods
5670	// @uninheritable
5671	// Layers extending `TileLayer` might reimplement the following method.
5672	// @method getTileUrl(coords: Object): String
5673	// Called only internally, returns the URL for a tile given its coordinates.
5674	// Classes extending `TileLayer` can override this function to provide custom tile URL naming schemes.
5675	getTileUrl: function (coords) {
5676		var data = {
5677			r: L.Browser.retina ? '@2x' : '',
5678			s: this._getSubdomain(coords),
5679			x: coords.x,
5680			y: coords.y,
5681			z: this._getZoomForUrl()
5682		};
5683		if (this._map && !this._map.options.crs.infinite) {
5684			var invertedY = this._globalTileRange.max.y - coords.y;
5685			if (this.options.tms) {
5686				data['y'] = invertedY;
5687			}
5688			data['-y'] = invertedY;
5689		}
5690
5691		return L.Util.template(this._url, L.extend(data, this.options));
5692	},
5693
5694	_tileOnLoad: function (done, tile) {
5695		// For https://github.com/Leaflet/Leaflet/issues/3332
5696		if (L.Browser.ielt9) {
5697			setTimeout(L.bind(done, this, null, tile), 0);
5698		} else {
5699			done(null, tile);
5700		}
5701	},
5702
5703	_tileOnError: function (done, tile, e) {
5704		var errorUrl = this.options.errorTileUrl;
5705		if (errorUrl && tile.src !== errorUrl) {
5706			tile.src = errorUrl;
5707		}
5708		done(e, tile);
5709	},
5710
5711	getTileSize: function () {
5712		var map = this._map,
5713		tileSize = L.GridLayer.prototype.getTileSize.call(this),
5714		zoom = this._tileZoom + this.options.zoomOffset,
5715		minNativeZoom = this.options.minNativeZoom,
5716		maxNativeZoom = this.options.maxNativeZoom;
5717
5718		// decrease tile size when scaling below minNativeZoom
5719		if (minNativeZoom !== null && zoom < minNativeZoom) {
5720			return tileSize.divideBy(map.getZoomScale(minNativeZoom, zoom)).round();
5721		}
5722
5723		// increase tile size when scaling above maxNativeZoom
5724		if (maxNativeZoom !== null && zoom > maxNativeZoom) {
5725			return tileSize.divideBy(map.getZoomScale(maxNativeZoom, zoom)).round();
5726		}
5727
5728		return tileSize;
5729	},
5730
5731	_onTileRemove: function (e) {
5732		e.tile.onload = null;
5733	},
5734
5735	_getZoomForUrl: function () {
5736		var zoom = this._tileZoom,
5737		maxZoom = this.options.maxZoom,
5738		zoomReverse = this.options.zoomReverse,
5739		zoomOffset = this.options.zoomOffset,
5740		minNativeZoom = this.options.minNativeZoom,
5741		maxNativeZoom = this.options.maxNativeZoom;
5742
5743		if (zoomReverse) {
5744			zoom = maxZoom - zoom;
5745		}
5746
5747		zoom += zoomOffset;
5748
5749		if (minNativeZoom !== null && zoom < minNativeZoom) {
5750			return minNativeZoom;
5751		}
5752
5753		if (maxNativeZoom !== null && zoom > maxNativeZoom) {
5754			return maxNativeZoom;
5755		}
5756
5757		return zoom;
5758	},
5759
5760	_getSubdomain: function (tilePoint) {
5761		var index = Math.abs(tilePoint.x + tilePoint.y) % this.options.subdomains.length;
5762		return this.options.subdomains[index];
5763	},
5764
5765	// stops loading all tiles in the background layer
5766	_abortLoading: function () {
5767		var i, tile;
5768		for (i in this._tiles) {
5769			if (this._tiles[i].coords.z !== this._tileZoom) {
5770				tile = this._tiles[i].el;
5771
5772				tile.onload = L.Util.falseFn;
5773				tile.onerror = L.Util.falseFn;
5774
5775				if (!tile.complete) {
5776					tile.src = L.Util.emptyImageUrl;
5777					L.DomUtil.remove(tile);
5778				}
5779			}
5780		}
5781	}
5782});
5783
5784
5785// @factory L.tilelayer(urlTemplate: String, options?: TileLayer options)
5786// Instantiates a tile layer object given a `URL template` and optionally an options object.
5787
5788L.tileLayer = function (url, options) {
5789	return new L.TileLayer(url, options);
5790};
5791
5792
5793
5794/*
5795 * @class TileLayer.WMS
5796 * @inherits TileLayer
5797 * @aka L.TileLayer.WMS
5798 * Used to display [WMS](https://en.wikipedia.org/wiki/Web_Map_Service) services as tile layers on the map. Extends `TileLayer`.
5799 *
5800 * @example
5801 *
5802 * ```js
5803 * var nexrad = L.tileLayer.wms("http://mesonet.agron.iastate.edu/cgi-bin/wms/nexrad/n0r.cgi", {
5804 * 	layers: 'nexrad-n0r-900913',
5805 * 	format: 'image/png',
5806 * 	transparent: true,
5807 * 	attribution: "Weather data © 2012 IEM Nexrad"
5808 * });
5809 * ```
5810 */
5811
5812L.TileLayer.WMS = L.TileLayer.extend({
5813
5814	// @section
5815	// @aka TileLayer.WMS options
5816	// If any custom options not documented here are used, they will be sent to the
5817	// WMS server as extra parameters in each request URL. This can be useful for
5818	// [non-standard vendor WMS parameters](http://docs.geoserver.org/stable/en/user/services/wms/vendor.html).
5819	defaultWmsParams: {
5820		service: 'WMS',
5821		request: 'GetMap',
5822
5823		// @option layers: String = ''
5824		// **(required)** Comma-separated list of WMS layers to show.
5825		layers: '',
5826
5827		// @option styles: String = ''
5828		// Comma-separated list of WMS styles.
5829		styles: '',
5830
5831		// @option format: String = 'image/jpeg'
5832		// WMS image format (use `'image/png'` for layers with transparency).
5833		format: 'image/jpeg',
5834
5835		// @option transparent: Boolean = false
5836		// If `true`, the WMS service will return images with transparency.
5837		transparent: false,
5838
5839		// @option version: String = '1.1.1'
5840		// Version of the WMS service to use
5841		version: '1.1.1'
5842	},
5843
5844	options: {
5845		// @option crs: CRS = null
5846		// Coordinate Reference System to use for the WMS requests, defaults to
5847		// map CRS. Don't change this if you're not sure what it means.
5848		crs: null,
5849
5850		// @option uppercase: Boolean = false
5851		// If `true`, WMS request parameter keys will be uppercase.
5852		uppercase: false
5853	},
5854
5855	initialize: function (url, options) {
5856
5857		this._url = url;
5858
5859		var wmsParams = L.extend({}, this.defaultWmsParams);
5860
5861		// all keys that are not TileLayer options go to WMS params
5862		for (var i in options) {
5863			if (!(i in this.options)) {
5864				wmsParams[i] = options[i];
5865			}
5866		}
5867
5868		options = L.setOptions(this, options);
5869
5870		wmsParams.width = wmsParams.height = options.tileSize * (options.detectRetina && L.Browser.retina ? 2 : 1);
5871
5872		this.wmsParams = wmsParams;
5873	},
5874
5875	onAdd: function (map) {
5876
5877		this._crs = this.options.crs || map.options.crs;
5878		this._wmsVersion = parseFloat(this.wmsParams.version);
5879
5880		var projectionKey = this._wmsVersion >= 1.3 ? 'crs' : 'srs';
5881		this.wmsParams[projectionKey] = this._crs.code;
5882
5883		L.TileLayer.prototype.onAdd.call(this, map);
5884	},
5885
5886	getTileUrl: function (coords) {
5887
5888		var tileBounds = this._tileCoordsToBounds(coords),
5889		    nw = this._crs.project(tileBounds.getNorthWest()),
5890		    se = this._crs.project(tileBounds.getSouthEast()),
5891
5892		    bbox = (this._wmsVersion >= 1.3 && this._crs === L.CRS.EPSG4326 ?
5893			    [se.y, nw.x, nw.y, se.x] :
5894			    [nw.x, se.y, se.x, nw.y]).join(','),
5895
5896		    url = L.TileLayer.prototype.getTileUrl.call(this, coords);
5897
5898		return url +
5899			L.Util.getParamString(this.wmsParams, url, this.options.uppercase) +
5900			(this.options.uppercase ? '&BBOX=' : '&bbox=') + bbox;
5901	},
5902
5903	// @method setParams(params: Object, noRedraw?: Boolean): this
5904	// Merges an object with the new parameters and re-requests tiles on the current screen (unless `noRedraw` was set to true).
5905	setParams: function (params, noRedraw) {
5906
5907		L.extend(this.wmsParams, params);
5908
5909		if (!noRedraw) {
5910			this.redraw();
5911		}
5912
5913		return this;
5914	}
5915});
5916
5917
5918// @factory L.tileLayer.wms(baseUrl: String, options: TileLayer.WMS options)
5919// Instantiates a WMS tile layer object given a base URL of the WMS service and a WMS parameters/options object.
5920L.tileLayer.wms = function (url, options) {
5921	return new L.TileLayer.WMS(url, options);
5922};
5923
5924
5925
5926/*
5927 * @class ImageOverlay
5928 * @aka L.ImageOverlay
5929 * @inherits Interactive layer
5930 *
5931 * Used to load and display a single image over specific bounds of the map. Extends `Layer`.
5932 *
5933 * @example
5934 *
5935 * ```js
5936 * var imageUrl = 'http://www.lib.utexas.edu/maps/historical/newark_nj_1922.jpg',
5937 * 	imageBounds = [[40.712216, -74.22655], [40.773941, -74.12544]];
5938 * L.imageOverlay(imageUrl, imageBounds).addTo(map);
5939 * ```
5940 */
5941
5942L.ImageOverlay = L.Layer.extend({
5943
5944	// @section
5945	// @aka ImageOverlay options
5946	options: {
5947		// @option opacity: Number = 1.0
5948		// The opacity of the image overlay.
5949		opacity: 1,
5950
5951		// @option alt: String = ''
5952		// Text for the `alt` attribute of the image (useful for accessibility).
5953		alt: '',
5954
5955		// @option interactive: Boolean = false
5956		// If `true`, the image overlay will emit [mouse events](#interactive-layer) when clicked or hovered.
5957		interactive: false,
5958
5959		// @option crossOrigin: Boolean = false
5960		// If true, the image will have its crossOrigin attribute set to ''. This is needed if you want to access image pixel data.
5961		crossOrigin: false
5962	},
5963
5964	initialize: function (url, bounds, options) { // (String, LatLngBounds, Object)
5965		this._url = url;
5966		this._bounds = L.latLngBounds(bounds);
5967
5968		L.setOptions(this, options);
5969	},
5970
5971	onAdd: function () {
5972		if (!this._image) {
5973			this._initImage();
5974
5975			if (this.options.opacity < 1) {
5976				this._updateOpacity();
5977			}
5978		}
5979
5980		if (this.options.interactive) {
5981			L.DomUtil.addClass(this._image, 'leaflet-interactive');
5982			this.addInteractiveTarget(this._image);
5983		}
5984
5985		this.getPane().appendChild(this._image);
5986		this._reset();
5987	},
5988
5989	onRemove: function () {
5990		L.DomUtil.remove(this._image);
5991		if (this.options.interactive) {
5992			this.removeInteractiveTarget(this._image);
5993		}
5994	},
5995
5996	// @method setOpacity(opacity: Number): this
5997	// Sets the opacity of the overlay.
5998	setOpacity: function (opacity) {
5999		this.options.opacity = opacity;
6000
6001		if (this._image) {
6002			this._updateOpacity();
6003		}
6004		return this;
6005	},
6006
6007	setStyle: function (styleOpts) {
6008		if (styleOpts.opacity) {
6009			this.setOpacity(styleOpts.opacity);
6010		}
6011		return this;
6012	},
6013
6014	// @method bringToFront(): this
6015	// Brings the layer to the top of all overlays.
6016	bringToFront: function () {
6017		if (this._map) {
6018			L.DomUtil.toFront(this._image);
6019		}
6020		return this;
6021	},
6022
6023	// @method bringToBack(): this
6024	// Brings the layer to the bottom of all overlays.
6025	bringToBack: function () {
6026		if (this._map) {
6027			L.DomUtil.toBack(this._image);
6028		}
6029		return this;
6030	},
6031
6032	// @method setUrl(url: String): this
6033	// Changes the URL of the image.
6034	setUrl: function (url) {
6035		this._url = url;
6036
6037		if (this._image) {
6038			this._image.src = url;
6039		}
6040		return this;
6041	},
6042
6043	// @method setBounds(bounds: LatLngBounds): this
6044	// Update the bounds that this ImageOverlay covers
6045	setBounds: function (bounds) {
6046		this._bounds = bounds;
6047
6048		if (this._map) {
6049			this._reset();
6050		}
6051		return this;
6052	},
6053
6054	getEvents: function () {
6055		var events = {
6056			zoom: this._reset,
6057			viewreset: this._reset
6058		};
6059
6060		if (this._zoomAnimated) {
6061			events.zoomanim = this._animateZoom;
6062		}
6063
6064		return events;
6065	},
6066
6067	// @method getBounds(): LatLngBounds
6068	// Get the bounds that this ImageOverlay covers
6069	getBounds: function () {
6070		return this._bounds;
6071	},
6072
6073	// @method getElement(): HTMLElement
6074	// Get the img element that represents the ImageOverlay on the map
6075	getElement: function () {
6076		return this._image;
6077	},
6078
6079	_initImage: function () {
6080		var img = this._image = L.DomUtil.create('img',
6081				'leaflet-image-layer ' + (this._zoomAnimated ? 'leaflet-zoom-animated' : ''));
6082
6083		img.onselectstart = L.Util.falseFn;
6084		img.onmousemove = L.Util.falseFn;
6085
6086		img.onload = L.bind(this.fire, this, 'load');
6087
6088		if (this.options.crossOrigin) {
6089			img.crossOrigin = '';
6090		}
6091
6092		img.src = this._url;
6093		img.alt = this.options.alt;
6094	},
6095
6096	_animateZoom: function (e) {
6097		var scale = this._map.getZoomScale(e.zoom),
6098		    offset = this._map._latLngBoundsToNewLayerBounds(this._bounds, e.zoom, e.center).min;
6099
6100		L.DomUtil.setTransform(this._image, offset, scale);
6101	},
6102
6103	_reset: function () {
6104		var image = this._image,
6105		    bounds = new L.Bounds(
6106		        this._map.latLngToLayerPoint(this._bounds.getNorthWest()),
6107		        this._map.latLngToLayerPoint(this._bounds.getSouthEast())),
6108		    size = bounds.getSize();
6109
6110		L.DomUtil.setPosition(image, bounds.min);
6111
6112		image.style.width  = size.x + 'px';
6113		image.style.height = size.y + 'px';
6114	},
6115
6116	_updateOpacity: function () {
6117		L.DomUtil.setOpacity(this._image, this.options.opacity);
6118	}
6119});
6120
6121// @factory L.imageOverlay(imageUrl: String, bounds: LatLngBounds, options?: ImageOverlay options)
6122// Instantiates an image overlay object given the URL of the image and the
6123// geographical bounds it is tied to.
6124L.imageOverlay = function (url, bounds, options) {
6125	return new L.ImageOverlay(url, bounds, options);
6126};
6127
6128
6129
6130/*
6131 * @class Icon
6132 * @aka L.Icon
6133 * @inherits Layer
6134 *
6135 * Represents an icon to provide when creating a marker.
6136 *
6137 * @example
6138 *
6139 * ```js
6140 * var myIcon = L.icon({
6141 *     iconUrl: 'my-icon.png',
6142 *     iconRetinaUrl: '[email protected]',
6143 *     iconSize: [38, 95],
6144 *     iconAnchor: [22, 94],
6145 *     popupAnchor: [-3, -76],
6146 *     shadowUrl: 'my-icon-shadow.png',
6147 *     shadowRetinaUrl: '[email protected]',
6148 *     shadowSize: [68, 95],
6149 *     shadowAnchor: [22, 94]
6150 * });
6151 *
6152 * L.marker([50.505, 30.57], {icon: myIcon}).addTo(map);
6153 * ```
6154 *
6155 * `L.Icon.Default` extends `L.Icon` and is the blue icon Leaflet uses for markers by default.
6156 *
6157 */
6158
6159L.Icon = L.Class.extend({
6160
6161	/* @section
6162	 * @aka Icon options
6163	 *
6164	 * @option iconUrl: String = null
6165	 * **(required)** The URL to the icon image (absolute or relative to your script path).
6166	 *
6167	 * @option iconRetinaUrl: String = null
6168	 * The URL to a retina sized version of the icon image (absolute or relative to your
6169	 * script path). Used for Retina screen devices.
6170	 *
6171	 * @option iconSize: Point = null
6172	 * Size of the icon image in pixels.
6173	 *
6174	 * @option iconAnchor: Point = null
6175	 * The coordinates of the "tip" of the icon (relative to its top left corner). The icon
6176	 * will be aligned so that this point is at the marker's geographical location. Centered
6177	 * by default if size is specified, also can be set in CSS with negative margins.
6178	 *
6179	 * @option popupAnchor: Point = null
6180	 * The coordinates of the point from which popups will "open", relative to the icon anchor.
6181	 *
6182	 * @option shadowUrl: String = null
6183	 * The URL to the icon shadow image. If not specified, no shadow image will be created.
6184	 *
6185	 * @option shadowRetinaUrl: String = null
6186	 *
6187	 * @option shadowSize: Point = null
6188	 * Size of the shadow image in pixels.
6189	 *
6190	 * @option shadowAnchor: Point = null
6191	 * The coordinates of the "tip" of the shadow (relative to its top left corner) (the same
6192	 * as iconAnchor if not specified).
6193	 *
6194	 * @option className: String = ''
6195	 * A custom class name to assign to both icon and shadow images. Empty by default.
6196	 */
6197
6198	initialize: function (options) {
6199		L.setOptions(this, options);
6200	},
6201
6202	// @method createIcon(oldIcon?: HTMLElement): HTMLElement
6203	// Called internally when the icon has to be shown, returns a `<img>` HTML element
6204	// styled according to the options.
6205	createIcon: function (oldIcon) {
6206		return this._createIcon('icon', oldIcon);
6207	},
6208
6209	// @method createShadow(oldIcon?: HTMLElement): HTMLElement
6210	// As `createIcon`, but for the shadow beneath it.
6211	createShadow: function (oldIcon) {
6212		return this._createIcon('shadow', oldIcon);
6213	},
6214
6215	_createIcon: function (name, oldIcon) {
6216		var src = this._getIconUrl(name);
6217
6218		if (!src) {
6219			if (name === 'icon') {
6220				throw new Error('iconUrl not set in Icon options (see the docs).');
6221			}
6222			return null;
6223		}
6224
6225		var img = this._createImg(src, oldIcon && oldIcon.tagName === 'IMG' ? oldIcon : null);
6226		this._setIconStyles(img, name);
6227
6228		return img;
6229	},
6230
6231	_setIconStyles: function (img, name) {
6232		var options = this.options;
6233		var sizeOption = options[name + 'Size'];
6234
6235		if (typeof sizeOption === 'number') {
6236			sizeOption = [sizeOption, sizeOption];
6237		}
6238
6239		var size = L.point(sizeOption),
6240		    anchor = L.point(name === 'shadow' && options.shadowAnchor || options.iconAnchor ||
6241		            size && size.divideBy(2, true));
6242
6243		img.className = 'leaflet-marker-' + name + ' ' + (options.className || '');
6244
6245		if (anchor) {
6246			img.style.marginLeft = (-anchor.x) + 'px';
6247			img.style.marginTop  = (-anchor.y) + 'px';
6248		}
6249
6250		if (size) {
6251			img.style.width  = size.x + 'px';
6252			img.style.height = size.y + 'px';
6253		}
6254	},
6255
6256	_createImg: function (src, el) {
6257		el = el || document.createElement('img');
6258		el.src = src;
6259		return el;
6260	},
6261
6262	_getIconUrl: function (name) {
6263		return L.Browser.retina && this.options[name + 'RetinaUrl'] || this.options[name + 'Url'];
6264	}
6265});
6266
6267
6268// @factory L.icon(options: Icon options)
6269// Creates an icon instance with the given options.
6270L.icon = function (options) {
6271	return new L.Icon(options);
6272};
6273
6274
6275
6276/*
6277 * @miniclass Icon.Default (Icon)
6278 * @aka L.Icon.Default
6279 * @section
6280 *
6281 * A trivial subclass of `Icon`, represents the icon to use in `Marker`s when
6282 * no icon is specified. Points to the blue marker image distributed with Leaflet
6283 * releases.
6284 *
6285 * In order to customize the default icon, just change the properties of `L.Icon.Default.prototype.options`
6286 * (which is a set of `Icon options`).
6287 *
6288 * If you want to _completely_ replace the default icon, override the
6289 * `L.Marker.prototype.options.icon` with your own icon instead.
6290 */
6291
6292L.Icon.Default = L.Icon.extend({
6293
6294	options: {
6295		iconUrl:       'marker-icon.png',
6296		iconRetinaUrl: 'marker-icon-2x.png',
6297		shadowUrl:     'marker-shadow.png',
6298		iconSize:    [25, 41],
6299		iconAnchor:  [12, 41],
6300		popupAnchor: [1, -34],
6301		tooltipAnchor: [16, -28],
6302		shadowSize:  [41, 41]
6303	},
6304
6305	_getIconUrl: function (name) {
6306		if (!L.Icon.Default.imagePath) {	// Deprecated, backwards-compatibility only
6307			L.Icon.Default.imagePath = this._detectIconPath();
6308		}
6309
6310		// @option imagePath: String
6311		// `L.Icon.Default` will try to auto-detect the absolute location of the
6312		// blue icon images. If you are placing these images in a non-standard
6313		// way, set this option to point to the right absolute path.
6314		return (this.options.imagePath || L.Icon.Default.imagePath) + L.Icon.prototype._getIconUrl.call(this, name);
6315	},
6316
6317	_detectIconPath: function () {
6318		var el = L.DomUtil.create('div',  'leaflet-default-icon-path', document.body);
6319		var path = L.DomUtil.getStyle(el, 'background-image') ||
6320		           L.DomUtil.getStyle(el, 'backgroundImage');	// IE8
6321
6322		document.body.removeChild(el);
6323
6324		return path.indexOf('url') === 0 ?
6325			path.replace(/^url\([\"\']?/, '').replace(/marker-icon\.png[\"\']?\)$/, '') : '';
6326	}
6327});
6328
6329
6330
6331/*
6332 * @class Marker
6333 * @inherits Interactive layer
6334 * @aka L.Marker
6335 * L.Marker is used to display clickable/draggable icons on the map. Extends `Layer`.
6336 *
6337 * @example
6338 *
6339 * ```js
6340 * L.marker([50.5, 30.5]).addTo(map);
6341 * ```
6342 */
6343
6344L.Marker = L.Layer.extend({
6345
6346	// @section
6347	// @aka Marker options
6348	options: {
6349		// @option icon: Icon = *
6350		// Icon class to use for rendering the marker. See [Icon documentation](#L.Icon) for details on how to customize the marker icon. If not specified, a new `L.Icon.Default` is used.
6351		icon: new L.Icon.Default(),
6352
6353		// Option inherited from "Interactive layer" abstract class
6354		interactive: true,
6355
vendor: 15,270 bytes, lines 6356-6975
6356		// @option draggable: Boolean = false
6357		// Whether the marker is draggable with mouse/touch or not.
6358		draggable: false,
6359
6360		// @option keyboard: Boolean = true
6361		// Whether the marker can be tabbed to with a keyboard and clicked by pressing enter.
6362		keyboard: true,
6363
6364		// @option title: String = ''
6365		// Text for the browser tooltip that appear on marker hover (no tooltip by default).
6366		title: '',
6367
6368		// @option alt: String = ''
6369		// Text for the `alt` attribute of the icon image (useful for accessibility).
6370		alt: '',
6371
6372		// @option zIndexOffset: Number = 0
6373		// 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).
6374		zIndexOffset: 0,
6375
6376		// @option opacity: Number = 1.0
6377		// The opacity of the marker.
6378		opacity: 1,
6379
6380		// @option riseOnHover: Boolean = false
6381		// If `true`, the marker will get on top of others when you hover the mouse over it.
6382		riseOnHover: false,
6383
6384		// @option riseOffset: Number = 250
6385		// The z-index offset used for the `riseOnHover` feature.
6386		riseOffset: 250,
6387
6388		// @option pane: String = 'markerPane'
6389		// `Map pane` where the markers icon will be added.
6390		pane: 'markerPane',
6391
6392		// FIXME: shadowPane is no longer a valid option
6393		nonBubblingEvents: ['click', 'dblclick', 'mouseover', 'mouseout', 'contextmenu']
6394	},
6395
6396	/* @section
6397	 *
6398	 * In addition to [shared layer methods](#Layer) like `addTo()` and `remove()` and [popup methods](#Popup) like bindPopup() you can also use the following methods:
6399	 */
6400
6401	initialize: function (latlng, options) {
6402		L.setOptions(this, options);
6403		this._latlng = L.latLng(latlng);
6404	},
6405
6406	onAdd: function (map) {
6407		this._zoomAnimated = this._zoomAnimated && map.options.markerZoomAnimation;
6408
6409		if (this._zoomAnimated) {
6410			map.on('zoomanim', this._animateZoom, this);
6411		}
6412
6413		this._initIcon();
6414		this.update();
6415	},
6416
6417	onRemove: function (map) {
6418		if (this.dragging && this.dragging.enabled()) {
6419			this.options.draggable = true;
6420			this.dragging.removeHooks();
6421		}
6422
6423		if (this._zoomAnimated) {
6424			map.off('zoomanim', this._animateZoom, this);
6425		}
6426
6427		this._removeIcon();
6428		this._removeShadow();
6429	},
6430
6431	getEvents: function () {
6432		return {
6433			zoom: this.update,
6434			viewreset: this.update
6435		};
6436	},
6437
6438	// @method getLatLng: LatLng
6439	// Returns the current geographical position of the marker.
6440	getLatLng: function () {
6441		return this._latlng;
6442	},
6443
6444	// @method setLatLng(latlng: LatLng): this
6445	// Changes the marker position to the given point.
6446	setLatLng: function (latlng) {
6447		var oldLatLng = this._latlng;
6448		this._latlng = L.latLng(latlng);
6449		this.update();
6450
6451		// @event move: Event
6452		// 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`.
6453		return this.fire('move', {oldLatLng: oldLatLng, latlng: this._latlng});
6454	},
6455
6456	// @method setZIndexOffset(offset: Number): this
6457	// Changes the [zIndex offset](#marker-zindexoffset) of the marker.
6458	setZIndexOffset: function (offset) {
6459		this.options.zIndexOffset = offset;
6460		return this.update();
6461	},
6462
6463	// @method setIcon(icon: Icon): this
6464	// Changes the marker icon.
6465	setIcon: function (icon) {
6466
6467		this.options.icon = icon;
6468
6469		if (this._map) {
6470			this._initIcon();
6471			this.update();
6472		}
6473
6474		if (this._popup) {
6475			this.bindPopup(this._popup, this._popup.options);
6476		}
6477
6478		return this;
6479	},
6480
6481	getElement: function () {
6482		return this._icon;
6483	},
6484
6485	update: function () {
6486
6487		if (this._icon) {
6488			var pos = this._map.latLngToLayerPoint(this._latlng).round();
6489			this._setPos(pos);
6490		}
6491
6492		return this;
6493	},
6494
6495	_initIcon: function () {
6496		var options = this.options,
6497		    classToAdd = 'leaflet-zoom-' + (this._zoomAnimated ? 'animated' : 'hide');
6498
6499		var icon = options.icon.createIcon(this._icon),
6500		    addIcon = false;
6501
6502		// if we're not reusing the icon, remove the old one and init new one
6503		if (icon !== this._icon) {
6504			if (this._icon) {
6505				this._removeIcon();
6506			}
6507			addIcon = true;
6508
6509			if (options.title) {
6510				icon.title = options.title;
6511			}
6512			if (options.alt) {
6513				icon.alt = options.alt;
6514			}
6515		}
6516
6517		L.DomUtil.addClass(icon, classToAdd);
6518
6519		if (options.keyboard) {
6520			icon.tabIndex = '0';
6521		}
6522
6523		this._icon = icon;
6524
6525		if (options.riseOnHover) {
6526			this.on({
6527				mouseover: this._bringToFront,
6528				mouseout: this._resetZIndex
6529			});
6530		}
6531
6532		var newShadow = options.icon.createShadow(this._shadow),
6533		    addShadow = false;
6534
6535		if (newShadow !== this._shadow) {
6536			this._removeShadow();
6537			addShadow = true;
6538		}
6539
6540		if (newShadow) {
6541			L.DomUtil.addClass(newShadow, classToAdd);
6542			newShadow.alt = '';
6543		}
6544		this._shadow = newShadow;
6545
6546
6547		if (options.opacity < 1) {
6548			this._updateOpacity();
6549		}
6550
6551
6552		if (addIcon) {
6553			this.getPane().appendChild(this._icon);
6554		}
6555		this._initInteraction();
6556		if (newShadow && addShadow) {
6557			this.getPane('shadowPane').appendChild(this._shadow);
6558		}
6559	},
6560
6561	_removeIcon: function () {
6562		if (this.options.riseOnHover) {
6563			this.off({
6564				mouseover: this._bringToFront,
6565				mouseout: this._resetZIndex
6566			});
6567		}
6568
6569		L.DomUtil.remove(this._icon);
6570		this.removeInteractiveTarget(this._icon);
6571
6572		this._icon = null;
6573	},
6574
6575	_removeShadow: function () {
6576		if (this._shadow) {
6577			L.DomUtil.remove(this._shadow);
6578		}
6579		this._shadow = null;
6580	},
6581
6582	_setPos: function (pos) {
6583		L.DomUtil.setPosition(this._icon, pos);
6584
6585		if (this._shadow) {
6586			L.DomUtil.setPosition(this._shadow, pos);
6587		}
6588
6589		this._zIndex = pos.y + this.options.zIndexOffset;
6590
6591		this._resetZIndex();
6592	},
6593
6594	_updateZIndex: function (offset) {
6595		this._icon.style.zIndex = this._zIndex + offset;
6596	},
6597
6598	_animateZoom: function (opt) {
6599		var pos = this._map._latLngToNewLayerPoint(this._latlng, opt.zoom, opt.center).round();
6600
6601		this._setPos(pos);
6602	},
6603
6604	_initInteraction: function () {
6605
6606		if (!this.options.interactive) { return; }
6607
6608		L.DomUtil.addClass(this._icon, 'leaflet-interactive');
6609
6610		this.addInteractiveTarget(this._icon);
6611
6612		if (L.Handler.MarkerDrag) {
6613			var draggable = this.options.draggable;
6614			if (this.dragging) {
6615				draggable = this.dragging.enabled();
6616				this.dragging.disable();
6617			}
6618
6619			this.dragging = new L.Handler.MarkerDrag(this);
6620
6621			if (draggable) {
6622				this.dragging.enable();
6623			}
6624		}
6625	},
6626
6627	// @method setOpacity(opacity: Number): this
6628	// Changes the opacity of the marker.
6629	setOpacity: function (opacity) {
6630		this.options.opacity = opacity;
6631		if (this._map) {
6632			this._updateOpacity();
6633		}
6634
6635		return this;
6636	},
6637
6638	_updateOpacity: function () {
6639		var opacity = this.options.opacity;
6640
6641		L.DomUtil.setOpacity(this._icon, opacity);
6642
6643		if (this._shadow) {
6644			L.DomUtil.setOpacity(this._shadow, opacity);
6645		}
6646	},
6647
6648	_bringToFront: function () {
6649		this._updateZIndex(this.options.riseOffset);
6650	},
6651
6652	_resetZIndex: function () {
6653		this._updateZIndex(0);
6654	},
6655
6656	_getPopupAnchor: function () {
6657		return this.options.icon.options.popupAnchor || [0, 0];
6658	},
6659
6660	_getTooltipAnchor: function () {
6661		return this.options.icon.options.tooltipAnchor || [0, 0];
6662	}
6663});
6664
6665
6666// factory L.marker(latlng: LatLng, options? : Marker options)
6667
6668// @factory L.marker(latlng: LatLng, options? : Marker options)
6669// Instantiates a Marker object given a geographical point and optionally an options object.
6670L.marker = function (latlng, options) {
6671	return new L.Marker(latlng, options);
6672};
6673
6674
6675
6676/*
6677 * @class DivIcon
6678 * @aka L.DivIcon
6679 * @inherits Icon
6680 *
6681 * Represents a lightweight icon for markers that uses a simple `<div>`
6682 * element instead of an image. Inherits from `Icon` but ignores the `iconUrl` and shadow options.
6683 *
6684 * @example
6685 * ```js
6686 * var myIcon = L.divIcon({className: 'my-div-icon'});
6687 * // you can set .my-div-icon styles in CSS
6688 *
6689 * L.marker([50.505, 30.57], {icon: myIcon}).addTo(map);
6690 * ```
6691 *
6692 * By default, it has a 'leaflet-div-icon' CSS class and is styled as a little white square with a shadow.
6693 */
6694
6695L.DivIcon = L.Icon.extend({
6696	options: {
6697		// @section
6698		// @aka DivIcon options
6699		iconSize: [12, 12], // also can be set through CSS
6700
6701		// iconAnchor: (Point),
6702		// popupAnchor: (Point),
6703
6704		// @option html: String = ''
6705		// Custom HTML code to put inside the div element, empty by default.
6706		html: false,
6707
6708		// @option bgPos: Point = [0, 0]
6709		// Optional relative position of the background, in pixels
6710		bgPos: null,
6711
6712		className: 'leaflet-div-icon'
6713	},
6714
6715	createIcon: function (oldIcon) {
6716		var div = (oldIcon && oldIcon.tagName === 'DIV') ? oldIcon : document.createElement('div'),
6717		    options = this.options;
6718
6719		div.innerHTML = options.html !== false ? options.html : '';
6720
6721		if (options.bgPos) {
6722			var bgPos = L.point(options.bgPos);
6723			div.style.backgroundPosition = (-bgPos.x) + 'px ' + (-bgPos.y) + 'px';
6724		}
6725		this._setIconStyles(div, 'icon');
6726
6727		return div;
6728	},
6729
6730	createShadow: function () {
6731		return null;
6732	}
6733});
6734
6735// @factory L.divIcon(options: DivIcon options)
6736// Creates a `DivIcon` instance with the given options.
6737L.divIcon = function (options) {
6738	return new L.DivIcon(options);
6739};
6740
6741
6742
6743/*
6744 * @class DivOverlay
6745 * @inherits Layer
6746 * @aka L.DivOverlay
6747 * Base model for L.Popup and L.Tooltip. Inherit from it for custom popup like plugins.
6748 */
6749
6750// @namespace DivOverlay
6751L.DivOverlay = L.Layer.extend({
6752
6753	// @section
6754	// @aka DivOverlay options
6755	options: {
6756		// @option offset: Point = Point(0, 7)
6757		// The offset of the popup position. Useful to control the anchor
6758		// of the popup when opening it on some overlays.
6759		offset: [0, 7],
6760
6761		// @option className: String = ''
6762		// A custom CSS class name to assign to the popup.
6763		className: '',
6764
6765		// @option pane: String = 'popupPane'
6766		// `Map pane` where the popup will be added.
6767		pane: 'popupPane'
6768	},
6769
6770	initialize: function (options, source) {
6771		L.setOptions(this, options);
6772
6773		this._source = source;
6774	},
6775
6776	onAdd: function (map) {
6777		this._zoomAnimated = map._zoomAnimated;
6778
6779		if (!this._container) {
6780			this._initLayout();
6781		}
6782
6783		if (map._fadeAnimated) {
6784			L.DomUtil.setOpacity(this._container, 0);
6785		}
6786
6787		clearTimeout(this._removeTimeout);
6788		this.getPane().appendChild(this._container);
6789		this.update();
6790
6791		if (map._fadeAnimated) {
6792			L.DomUtil.setOpacity(this._container, 1);
6793		}
6794
6795		this.bringToFront();
6796	},
6797
6798	onRemove: function (map) {
6799		if (map._fadeAnimated) {
6800			L.DomUtil.setOpacity(this._container, 0);
6801			this._removeTimeout = setTimeout(L.bind(L.DomUtil.remove, L.DomUtil, this._container), 200);
6802		} else {
6803			L.DomUtil.remove(this._container);
6804		}
6805	},
6806
6807	// @namespace Popup
6808	// @method getLatLng: LatLng
6809	// Returns the geographical point of popup.
6810	getLatLng: function () {
6811		return this._latlng;
6812	},
6813
6814	// @method setLatLng(latlng: LatLng): this
6815	// Sets the geographical point where the popup will open.
6816	setLatLng: function (latlng) {
6817		this._latlng = L.latLng(latlng);
6818		if (this._map) {
6819			this._updatePosition();
6820			this._adjustPan();
6821		}
6822		return this;
6823	},
6824
6825	// @method getContent: String|HTMLElement
6826	// Returns the content of the popup.
6827	getContent: function () {
6828		return this._content;
6829	},
6830
6831	// @method setContent(htmlContent: String|HTMLElement|Function): this
6832	// 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.
6833	setContent: function (content) {
6834		this._content = content;
6835		this.update();
6836		return this;
6837	},
6838
6839	// @method getElement: String|HTMLElement
6840	// Alias for [getContent()](#popup-getcontent)
6841	getElement: function () {
6842		return this._container;
6843	},
6844
6845	// @method update: null
6846	// Updates the popup content, layout and position. Useful for updating the popup after something inside changed, e.g. image loaded.
6847	update: function () {
6848		if (!this._map) { return; }
6849
6850		this._container.style.visibility = 'hidden';
6851
6852		this._updateContent();
6853		this._updateLayout();
6854		this._updatePosition();
6855
6856		this._container.style.visibility = '';
6857
6858		this._adjustPan();
6859	},
6860
6861	getEvents: function () {
6862		var events = {
6863			zoom: this._updatePosition,
6864			viewreset: this._updatePosition
6865		};
6866
6867		if (this._zoomAnimated) {
6868			events.zoomanim = this._animateZoom;
6869		}
6870		return events;
6871	},
6872
6873	// @method isOpen: Boolean
6874	// Returns `true` when the popup is visible on the map.
6875	isOpen: function () {
6876		return !!this._map && this._map.hasLayer(this);
6877	},
6878
6879	// @method bringToFront: this
6880	// Brings this popup in front of other popups (in the same map pane).
6881	bringToFront: function () {
6882		if (this._map) {
6883			L.DomUtil.toFront(this._container);
6884		}
6885		return this;
6886	},
6887
6888	// @method bringToBack: this
6889	// Brings this popup to the back of other popups (in the same map pane).
6890	bringToBack: function () {
6891		if (this._map) {
6892			L.DomUtil.toBack(this._container);
6893		}
6894		return this;
6895	},
6896
6897	_updateContent: function () {
6898		if (!this._content) { return; }
6899
6900		var node = this._contentNode;
6901		var content = (typeof this._content === 'function') ? this._content(this._source || this) : this._content;
6902
6903		if (typeof content === 'string') {
6904			node.innerHTML = content;
6905		} else {
6906			while (node.hasChildNodes()) {
6907				node.removeChild(node.firstChild);
6908			}
6909			node.appendChild(content);
6910		}
6911		this.fire('contentupdate');
6912	},
6913
6914	_updatePosition: function () {
6915		if (!this._map) { return; }
6916
6917		var pos = this._map.latLngToLayerPoint(this._latlng),
6918		    offset = L.point(this.options.offset),
6919		    anchor = this._getAnchor();
6920
6921		if (this._zoomAnimated) {
6922			L.DomUtil.setPosition(this._container, pos.add(anchor));
6923		} else {
6924			offset = offset.add(pos).add(anchor);
6925		}
6926
6927		var bottom = this._containerBottom = -offset.y,
6928		    left = this._containerLeft = -Math.round(this._containerWidth / 2) + offset.x;
6929
6930		// bottom position the popup in case the height of the popup changes (images loading etc)
6931		this._container.style.bottom = bottom + 'px';
6932		this._container.style.left = left + 'px';
6933	},
6934
6935	_getAnchor: function () {
6936		return [0, 0];
6937	}
6938
6939});
6940
6941
6942
6943/*
6944 * @class Popup
6945 * @inherits DivOverlay
6946 * @aka L.Popup
6947 * Used to open popups in certain places of the map. Use [Map.openPopup](#map-openpopup) to
6948 * open popups while making sure that only one popup is open at one time
6949 * (recommended for usability), or use [Map.addLayer](#map-addlayer) to open as many as you want.
6950 *
6951 * @example
6952 *
6953 * If you want to just bind a popup to marker click and then open it, it's really easy:
6954 *
6955 * ```js
6956 * marker.bindPopup(popupContent).openPopup();
6957 * ```
6958 * Path overlays like polylines also have a `bindPopup` method.
6959 * Here's a more complicated way to open a popup on a map:
6960 *
6961 * ```js
6962 * var popup = L.popup()
6963 * 	.setLatLng(latlng)
6964 * 	.setContent('<p>Hello world!<br />This is a nice popup.</p>')
6965 * 	.openOn(map);
6966 * ```
6967 */
6968
6969
6970// @namespace Popup
6971L.Popup = L.DivOverlay.extend({
6972
6973	// @section
6974	// @aka Popup options
6975	options: {
6976		// @option maxWidth: Number = 300
6977		// Max width of the popup, in pixels.
6978		maxWidth: 300,
6979
6980		// @option minWidth: Number = 50
6981		// Min width of the popup, in pixels.
6982		minWidth: 50,
6983
6984		// @option maxHeight: Number = null
6985		// If set, creates a scrollable container of the given height
6986		// inside a popup if its content exceeds it.
6987		maxHeight: null,
6988
6989		// @option autoPan: Boolean = true
6990		// Set it to `false` if you don't want the map to do panning animation
6991		// to fit the opened popup.
6992		autoPan: true,
6993
6994		// @option autoPanPaddingTopLeft: Point = null
6995		// The margin between the popup and the top left corner of the map
6996		// view after autopanning was performed.
6997		autoPanPaddingTopLeft: null,
6998
6999		// @option autoPanPaddingBottomRight: Point = null
7000		// The margin between the popup and the bottom right corner of the map
7001		// view after autopanning was performed.
7002		autoPanPaddingBottomRight: null,
7003
7004		// @option autoPanPadding: Point = Point(5, 5)
7005		// Equivalent of setting both top left and bottom right autopan padding to the same value.
7006		autoPanPadding: [5, 5],
7007
7008		// @option keepInView: Boolean = false
7009		// Set it to `true` if you want to prevent users from panning the popup
7010		// off of the screen while it is open.
7011		keepInView: false,
7012
7013		// @option closeButton: Boolean = true
7014		// Controls the presence of a close button in the popup.
7015		closeButton: true,
7016
7017		// @option autoClose: Boolean = true
7018		// Set it to `false` if you want to override the default behavior of
7019		// the popup closing when user clicks the map (set globally by
7020		// the Map's [closePopupOnClick](#map-closepopuponclick) option).
7021		autoClose: true,
7022
7023		// @option className: String = ''
7024		// A custom CSS class name to assign to the popup.
7025		className: ''
7026	},
7027
7028	// @namespace Popup
7029	// @method openOn(map: Map): this
7030	// Adds the popup to the map and closes the previous one. The same as `map.openPopup(popup)`.
7031	openOn: function (map) {
7032		map.openPopup(this);
7033		return this;
7034	},
7035
7036	onAdd: function (map) {
7037		L.DivOverlay.prototype.onAdd.call(this, map);
7038
7039		// @namespace Map
7040		// @section Popup events
7041		// @event popupopen: PopupEvent
7042		// Fired when a popup is opened in the map
7043		map.fire('popupopen', {popup: this});
7044
7045		if (this._source) {
7046			// @namespace Layer
7047			// @section Popup events
7048			// @event popupopen: PopupEvent
7049			// Fired when a popup bound to this layer is opened
7050			this._source.fire('popupopen', {popup: this}, true);
7051			// For non-path layers, we toggle the popup when clicking
7052			// again the layer, so prevent the map to reopen it.
7053			if (!(this._source instanceof L.Path)) {
7054				this._source.on('preclick', L.DomEvent.stopPropagation);
7055			}
7056		}
7057	},
7058
7059	onRemove: function (map) {
7060		L.DivOverlay.prototype.onRemove.call(this, map);
7061
7062		// @namespace Map
7063		// @section Popup events
7064		// @event popupclose: PopupEvent
7065		// Fired when a popup in the map is closed
7066		map.fire('popupclose', {popup: this});
7067
7068		if (this._source) {
7069			// @namespace Layer
7070			// @section Popup events
7071			// @event popupclose: PopupEvent
7072			// Fired when a popup bound to this layer is closed
7073			this._source.fire('popupclose', {popup: this}, true);
7074			if (!(this._source instanceof L.Path)) {
7075				this._source.off('preclick', L.DomEvent.stopPropagation);
7076			}
7077		}
7078	},
7079
7080	getEvents: function () {
7081		var events = L.DivOverlay.prototype.getEvents.call(this);
7082
7083		if ('closeOnClick' in this.options ? this.options.closeOnClick : this._map.options.closePopupOnClick) {
7084			events.preclick = this._close;
7085		}
7086
7087		if (this.options.keepInView) {
7088			events.moveend = this._adjustPan;
7089		}
7090
7091		return events;
7092	},
7093
7094	_close: function () {
7095		if (this._map) {
7096			this._map.closePopup(this);
7097		}
7098	},
7099
7100	_initLayout: function () {
7101		var prefix = 'leaflet-popup',
7102		    container = this._container = L.DomUtil.create('div',
7103			prefix + ' ' + (this.options.className || '') +
7104			' leaflet-zoom-animated');
7105
7106		if (this.options.closeButton) {
7107			var closeButton = this._closeButton = L.DomUtil.create('a', prefix + '-close-button', container);
7108			closeButton.href = '#close';
7109			closeButton.innerHTML = '&#215;';
7110
7111			L.DomEvent.on(closeButton, 'click', this._onCloseButtonClick, this);
7112		}
7113
7114		var wrapper = this._wrapper = L.DomUtil.create('div', prefix + '-content-wrapper', container);
7115		this._contentNode = L.DomUtil.create('div', prefix + '-content', wrapper);
7116
7117		L.DomEvent
7118			.disableClickPropagation(wrapper)
7119			.disableScrollPropagation(this._contentNode)
7120			.on(wrapper, 'contextmenu', L.DomEvent.stopPropagation);
7121
7122		this._tipContainer = L.DomUtil.create('div', prefix + '-tip-container', container);
7123		this._tip = L.DomUtil.create('div', prefix + '-tip', this._tipContainer);
7124	},
7125
7126	_updateLayout: function () {
7127		var container = this._contentNode,
7128		    style = container.style;
7129
7130		style.width = '';
7131		style.whiteSpace = 'nowrap';
7132
7133		var width = container.offsetWidth;
7134		width = Math.min(width, this.options.maxWidth);
7135		width = Math.max(width, this.options.minWidth);
7136
7137		style.width = (width + 1) + 'px';
7138		style.whiteSpace = '';
7139
7140		style.height = '';
7141
7142		var height = container.offsetHeight,
7143		    maxHeight = this.options.maxHeight,
7144		    scrolledClass = 'leaflet-popup-scrolled';
7145
7146		if (maxHeight && height > maxHeight) {
7147			style.height = maxHeight + 'px';
7148			L.DomUtil.addClass(container, scrolledClass);
7149		} else {
7150			L.DomUtil.removeClass(container, scrolledClass);
7151		}
7152
7153		this._containerWidth = this._container.offsetWidth;
7154	},
7155
7156	_animateZoom: function (e) {
7157		var pos = this._map._latLngToNewLayerPoint(this._latlng, e.zoom, e.center),
7158		    anchor = this._getAnchor();
7159		L.DomUtil.setPosition(this._container, pos.add(anchor));
7160	},
7161
7162	_adjustPan: function () {
7163		if (!this.options.autoPan || (this._map._panAnim && this._map._panAnim._inProgress)) { return; }
7164
7165		var map = this._map,
7166		    marginBottom = parseInt(L.DomUtil.getStyle(this._container, 'marginBottom'), 10) || 0,
7167		    containerHeight = this._container.offsetHeight + marginBottom,
7168		    containerWidth = this._containerWidth,
7169		    layerPos = new L.Point(this._containerLeft, -containerHeight - this._containerBottom);
7170
7171		layerPos._add(L.DomUtil.getPosition(this._container));
7172
7173		var containerPos = map.layerPointToContainerPoint(layerPos),
7174		    padding = L.point(this.options.autoPanPadding),
7175		    paddingTL = L.point(this.options.autoPanPaddingTopLeft || padding),
7176		    paddingBR = L.point(this.options.autoPanPaddingBottomRight || padding),
7177		    size = map.getSize(),
7178		    dx = 0,
7179		    dy = 0;
7180
7181		if (containerPos.x + containerWidth + paddingBR.x > size.x) { // right
7182			dx = containerPos.x + containerWidth - size.x + paddingBR.x;
7183		}
7184		if (containerPos.x - dx - paddingTL.x < 0) { // left
7185			dx = containerPos.x - paddingTL.x;
7186		}
7187		if (containerPos.y + containerHeight + paddingBR.y > size.y) { // bottom
7188			dy = containerPos.y + containerHeight - size.y + paddingBR.y;
7189		}
7190		if (containerPos.y - dy - paddingTL.y < 0) { // top
7191			dy = containerPos.y - paddingTL.y;
7192		}
7193
7194		// @namespace Map
7195		// @section Popup events
7196		// @event autopanstart: Event
7197		// Fired when the map starts autopanning when opening a popup.
7198		if (dx || dy) {
7199			map
7200			    .fire('autopanstart')
7201			    .panBy([dx, dy]);
7202		}
7203	},
7204
7205	_onCloseButtonClick: function (e) {
7206		this._close();
7207		L.DomEvent.stop(e);
7208	},
7209
7210	_getAnchor: function () {
7211		// Where should we anchor the popup on the source layer?
7212		return L.point(this._source && this._source._getPopupAnchor ? this._source._getPopupAnchor() : [0, 0]);
7213	}
7214
7215});
7216
7217// @namespace Popup
7218// @factory L.popup(options?: Popup options, source?: Layer)
7219// 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.
7220L.popup = function (options, source) {
7221	return new L.Popup(options, source);
7222};
7223
7224
7225/* @namespace Map
7226 * @section Interaction Options
7227 * @option closePopupOnClick: Boolean = true
7228 * Set it to `false` if you don't want popups to close when user clicks the map.
7229 */
7230L.Map.mergeOptions({
7231	closePopupOnClick: true
7232});
7233
7234
7235// @namespace Map
7236// @section Methods for Layers and Controls
7237L.Map.include({
7238	// @method openPopup(popup: Popup): this
7239	// Opens the specified popup while closing the previously opened (to make sure only one is opened at one time for usability).
7240	// @alternative
7241	// @method openPopup(content: String|HTMLElement, latlng: LatLng, options?: Popup options): this
7242	// Creates a popup with the specified content and options and opens it in the given point on a map.
7243	openPopup: function (popup, latlng, options) {
7244		if (!(popup instanceof L.Popup)) {
7245			popup = new L.Popup(options).setContent(popup);
7246		}
7247
7248		if (latlng) {
7249			popup.setLatLng(latlng);
7250		}
7251
7252		if (this.hasLayer(popup)) {
7253			return this;
7254		}
7255
7256		if (this._popup && this._popup.options.autoClose) {
7257			this.closePopup();
7258		}
7259
7260		this._popup = popup;
7261		return this.addLayer(popup);
7262	},
7263
7264	// @method closePopup(popup?: Popup): this
7265	// Closes the popup previously opened with [openPopup](#map-openpopup) (or the given one).
7266	closePopup: function (popup) {
7267		if (!popup || popup === this._popup) {
7268			popup = this._popup;
7269			this._popup = null;
7270		}
7271		if (popup) {
7272			this.removeLayer(popup);
7273		}
7274		return this;
7275	}
7276});
7277
7278/*
7279 * @namespace Layer
7280 * @section Popup methods example
7281 *
7282 * All layers share a set of methods convenient for binding popups to it.
7283 *
7284 * ```js
7285 * var layer = L.Polygon(latlngs).bindPopup('Hi There!').addTo(map);
7286 * layer.openPopup();
7287 * layer.closePopup();
7288 * ```
7289 *
7290 * 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.
7291 */
7292
7293// @section Popup methods
7294L.Layer.include({
7295
7296	// @method bindPopup(content: String|HTMLElement|Function|Popup, options?: Popup options): this
7297	// Binds a popup to the layer with the passed `content` and sets up the
7298	// neccessary event listeners. If a `Function` is passed it will receive
7299	// the layer as the first argument and should return a `String` or `HTMLElement`.
7300	bindPopup: function (content, options) {
7301
7302		if (content instanceof L.Popup) {
7303			L.setOptions(content, options);
7304			this._popup = content;
7305			content._source = this;
7306		} else {
7307			if (!this._popup || options) {
7308				this._popup = new L.Popup(options, this);
7309			}
7310			this._popup.setContent(content);
7311		}
7312
7313		if (!this._popupHandlersAdded) {
7314			this.on({
7315				click: this._openPopup,
7316				remove: this.closePopup,
7317				move: this._movePopup
7318			});
7319			this._popupHandlersAdded = true;
7320		}
7321
7322		return this;
7323	},
7324
7325	// @method unbindPopup(): this
7326	// Removes the popup previously bound with `bindPopup`.
7327	unbindPopup: function () {
7328		if (this._popup) {
7329			this.off({
7330				click: this._openPopup,
7331				remove: this.closePopup,
7332				move: this._movePopup
7333			});
7334			this._popupHandlersAdded = false;
7335			this._popup = null;
7336		}
7337		return this;
7338	},
7339
7340	// @method openPopup(latlng?: LatLng): this
7341	// Opens the bound popup at the specificed `latlng` or at the default popup anchor if no `latlng` is passed.
7342	openPopup: function (layer, latlng) {
7343		if (!(layer instanceof L.Layer)) {
7344			latlng = layer;
7345			layer = this;
7346		}
7347
7348		if (layer instanceof L.FeatureGroup) {
7349			for (var id in this._layers) {
7350				layer = this._layers[id];
7351				break;
7352			}
7353		}
7354
7355		if (!latlng) {
7356			latlng = layer.getCenter ? layer.getCenter() : layer.getLatLng();
7357		}
7358
7359		if (this._popup && this._map) {
7360			// set popup source to this layer
7361			this._popup._source = layer;
7362
7363			// update the popup (content, layout, ect...)
7364			this._popup.update();
7365
7366			// open the popup on the map
7367			this._map.openPopup(this._popup, latlng);
7368		}
7369
7370		return this;
7371	},
7372
7373	// @method closePopup(): this
7374	// Closes the popup bound to this layer if it is open.
7375	closePopup: function () {
7376		if (this._popup) {
7377			this._popup._close();
7378		}
7379		return this;
7380	},
7381
7382	// @method togglePopup(): this
7383	// Opens or closes the popup bound to this layer depending on its current state.
7384	togglePopup: function (target) {
7385		if (this._popup) {
7386			if (this._popup._map) {
7387				this.closePopup();
7388			} else {
7389				this.openPopup(target);
7390			}
7391		}
7392		return this;
7393	},
7394
7395	// @method isPopupOpen(): boolean
7396	// Returns `true` if the popup bound to this layer is currently open.
7397	isPopupOpen: function () {
7398		return (this._popup ? this._popup.isOpen() : false);
7399	},
7400
7401	// @method setPopupContent(content: String|HTMLElement|Popup): this
7402	// Sets the content of the popup bound to this layer.
7403	setPopupContent: function (content) {
7404		if (this._popup) {
7405			this._popup.setContent(content);
7406		}
7407		return this;
7408	},
7409
7410	// @method getPopup(): Popup
7411	// Returns the popup bound to this layer.
7412	getPopup: function () {
7413		return this._popup;
7414	},
7415
7416	_openPopup: function (e) {
7417		var layer = e.layer || e.target;
7418
7419		if (!this._popup) {
7420			return;
7421		}
7422
7423		if (!this._map) {
7424			return;
7425		}
7426
7427		// prevent map click
7428		L.DomEvent.stop(e);
7429
7430		// if this inherits from Path its a vector and we can just
7431		// open the popup at the new location
7432		if (layer instanceof L.Path) {
7433			this.openPopup(e.layer || e.target, e.latlng);
7434			return;
7435		}
7436
7437		// otherwise treat it like a marker and figure out
7438		// if we should toggle it open/closed
7439		if (this._map.hasLayer(this._popup) && this._popup._source === layer) {
7440			this.closePopup();
7441		} else {
7442			this.openPopup(layer, e.latlng);
7443		}
7444	},
7445
7446	_movePopup: function (e) {
7447		this._popup.setLatLng(e.latlng);
7448	}
7449});
7450
7451
7452
7453/*
7454 * @class Tooltip
7455 * @inherits DivOverlay
7456 * @aka L.Tooltip
7457 * Used to display small texts on top of map layers.
7458 *
7459 * @example
7460 *
7461 * ```js
7462 * marker.bindTooltip("my tooltip text").openTooltip();
7463 * ```
7464 * Note about tooltip offset. Leaflet takes two options in consideration
7465 * for computing tooltip offseting:
7466 * - the `offset` Tooltip option: it defaults to [0, 0], and it's specific to one tooltip.
7467 *   Add a positive x offset to move the tooltip to the right, and a positive y offset to
7468 *   move it to the bottom. Negatives will move to the left and top.
7469 * - the `tooltipAnchor` Icon option: this will only be considered for Marker. You
7470 *   should adapt this value if you use a custom icon.
7471 */
7472
7473
7474// @namespace Tooltip
7475L.Tooltip = L.DivOverlay.extend({
7476
7477	// @section
7478	// @aka Tooltip options
7479	options: {
7480		// @option pane: String = 'tooltipPane'
7481		// `Map pane` where the tooltip will be added.
7482		pane: 'tooltipPane',
7483
7484		// @option offset: Point = Point(0, 0)
7485		// Optional offset of the tooltip position.
7486		offset: [0, 0],
7487
7488		// @option direction: String = 'auto'
7489		// Direction where to open the tooltip. Possible values are: `right`, `left`,
7490		// `top`, `bottom`, `center`, `auto`.
7491		// `auto` will dynamicaly switch between `right` and `left` according to the tooltip
7492		// position on the map.
7493		direction: 'auto',
7494
7495		// @option permanent: Boolean = false
7496		// Whether to open the tooltip permanently or only on mouseover.
7497		permanent: false,
7498
7499		// @option sticky: Boolean = false
7500		// If true, the tooltip will follow the mouse instead of being fixed at the feature center.
7501		sticky: false,
7502
7503		// @option interactive: Boolean = false
7504		// If true, the tooltip will listen to the feature events.
7505		interactive: false,
7506
7507		// @option opacity: Number = 0.9
7508		// Tooltip container opacity.
7509		opacity: 0.9
7510	},
7511
7512	onAdd: function (map) {
7513		L.DivOverlay.prototype.onAdd.call(this, map);
7514		this.setOpacity(this.options.opacity);
7515
7516		// @namespace Map
7517		// @section Tooltip events
7518		// @event tooltipopen: TooltipEvent
7519		// Fired when a tooltip is opened in the map.
7520		map.fire('tooltipopen', {tooltip: this});
7521
7522		if (this._source) {
7523			// @namespace Layer
7524			// @section Tooltip events
7525			// @event tooltipopen: TooltipEvent
7526			// Fired when a tooltip bound to this layer is opened.
7527			this._source.fire('tooltipopen', {tooltip: this}, true);
7528		}
7529	},
7530
7531	onRemove: function (map) {
7532		L.DivOverlay.prototype.onRemove.call(this, map);
7533
7534		// @namespace Map
7535		// @section Tooltip events
7536		// @event tooltipclose: TooltipEvent
7537		// Fired when a tooltip in the map is closed.
7538		map.fire('tooltipclose', {tooltip: this});
7539
7540		if (this._source) {
7541			// @namespace Layer
7542			// @section Tooltip events
7543			// @event tooltipclose: TooltipEvent
7544			// Fired when a tooltip bound to this layer is closed.
7545			this._source.fire('tooltipclose', {tooltip: this}, true);
7546		}
7547	},
7548
7549	getEvents: function () {
7550		var events = L.DivOverlay.prototype.getEvents.call(this);
7551
7552		if (L.Browser.touch && !this.options.permanent) {
7553			events.preclick = this._close;
7554		}
7555
7556		return events;
7557	},
7558
7559	_close: function () {
7560		if (this._map) {
7561			this._map.closeTooltip(this);
7562		}
7563	},
7564
7565	_initLayout: function () {
7566		var prefix = 'leaflet-tooltip',
7567		    className = prefix + ' ' + (this.options.className || '') + ' leaflet-zoom-' + (this._zoomAnimated ? 'animated' : 'hide');
7568
7569		this._contentNode = this._container = L.DomUtil.create('div', className);
7570	},
7571
7572	_updateLayout: function () {},
7573
7574	_adjustPan: function () {},
7575
7576	_setPosition: function (pos) {
7577		var map = this._map,
7578		    container = this._container,
7579		    centerPoint = map.latLngToContainerPoint(map.getCenter()),
7580		    tooltipPoint = map.layerPointToContainerPoint(pos),
7581		    direction = this.options.direction,
7582		    tooltipWidth = container.offsetWidth,
7583		    tooltipHeight = container.offsetHeight,
7584		    offset = L.point(this.options.offset),
7585		    anchor = this._getAnchor();
7586
7587		if (direction === 'top') {
7588			pos = pos.add(L.point(-tooltipWidth / 2 + offset.x, -tooltipHeight + offset.y + anchor.y, true));
7589		} else if (direction === 'bottom') {
7590			pos = pos.subtract(L.point(tooltipWidth / 2 - offset.x, -offset.y, true));
7591		} else if (direction === 'center') {
7592			pos = pos.subtract(L.point(tooltipWidth / 2 + offset.x, tooltipHeight / 2 - anchor.y + offset.y, true));
7593		} else if (direction === 'right' || direction === 'auto' && tooltipPoint.x < centerPoint.x) {
7594			direction = 'right';
7595			pos = pos.add(L.point(offset.x + anchor.x, anchor.y - tooltipHeight / 2 + offset.y, true));
7596		} else {
7597			direction = 'left';
7598			pos = pos.subtract(L.point(tooltipWidth + anchor.x - offset.x, tooltipHeight / 2 - anchor.y - offset.y, true));
7599		}
7600
7601		L.DomUtil.removeClass(container, 'leaflet-tooltip-right');
7602		L.DomUtil.removeClass(container, 'leaflet-tooltip-left');
7603		L.DomUtil.removeClass(container, 'leaflet-tooltip-top');
7604		L.DomUtil.removeClass(container, 'leaflet-tooltip-bottom');
7605		L.DomUtil.addClass(container, 'leaflet-tooltip-' + direction);
7606		L.DomUtil.setPosition(container, pos);
7607	},
7608
7609	_updatePosition: function () {
7610		var pos = this._map.latLngToLayerPoint(this._latlng);
7611		this._setPosition(pos);
7612	},
7613
7614	setOpacity: function (opacity) {
7615		this.options.opacity = opacity;
7616
7617		if (this._container) {
7618			L.DomUtil.setOpacity(this._container, opacity);
7619		}
7620	},
7621
7622	_animateZoom: function (e) {
7623		var pos = this._map._latLngToNewLayerPoint(this._latlng, e.zoom, e.center);
7624		this._setPosition(pos);
7625	},
7626
7627	_getAnchor: function () {
7628		// Where should we anchor the tooltip on the source layer?
7629		return L.point(this._source && this._source._getTooltipAnchor && !this.options.sticky ? this._source._getTooltipAnchor() : [0, 0]);
7630	}
7631
7632});
7633
7634// @namespace Tooltip
7635// @factory L.tooltip(options?: Tooltip options, source?: Layer)
7636// 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.
7637L.tooltip = function (options, source) {
7638	return new L.Tooltip(options, source);
7639};
7640
7641// @namespace Map
7642// @section Methods for Layers and Controls
7643L.Map.include({
7644
7645	// @method openTooltip(tooltip: Tooltip): this
7646	// Opens the specified tooltip.
7647	// @alternative
7648	// @method openTooltip(content: String|HTMLElement, latlng: LatLng, options?: Tooltip options): this
7649	// Creates a tooltip with the specified content and options and open it.
7650	openTooltip: function (tooltip, latlng, options) {
7651		if (!(tooltip instanceof L.Tooltip)) {
7652			tooltip = new L.Tooltip(options).setContent(tooltip);
7653		}
7654
7655		if (latlng) {
7656			tooltip.setLatLng(latlng);
7657		}
7658
7659		if (this.hasLayer(tooltip)) {
7660			return this;
7661		}
7662
7663		return this.addLayer(tooltip);
7664	},
7665
7666	// @method closeTooltip(tooltip?: Tooltip): this
7667	// Closes the tooltip given as parameter.
7668	closeTooltip: function (tooltip) {
7669		if (tooltip) {
7670			this.removeLayer(tooltip);
7671		}
7672		return this;
7673	}
7674
7675});
7676
7677/*
7678 * @namespace Layer
7679 * @section Tooltip methods example
7680 *
7681 * All layers share a set of methods convenient for binding tooltips to it.
7682 *
7683 * ```js
7684 * var layer = L.Polygon(latlngs).bindTooltip('Hi There!').addTo(map);
7685 * layer.openTooltip();
7686 * layer.closeTooltip();
7687 * ```
7688 */
7689
7690// @section Tooltip methods
7691L.Layer.include({
7692
7693	// @method bindTooltip(content: String|HTMLElement|Function|Tooltip, options?: Tooltip options): this
7694	// Binds a tooltip to the layer with the passed `content` and sets up the
7695	// neccessary event listeners. If a `Function` is passed it will receive
7696	// the layer as the first argument and should return a `String` or `HTMLElement`.
7697	bindTooltip: function (content, options) {
7698
7699		if (content instanceof L.Tooltip) {
7700			L.setOptions(content, options);
7701			this._tooltip = content;
7702			content._source = this;
7703		} else {
7704			if (!this._tooltip || options) {
7705				this._tooltip = L.tooltip(options, this);
7706			}
7707			this._tooltip.setContent(content);
7708
7709		}
7710
7711		this._initTooltipInteractions();
7712
7713		if (this._tooltip.options.permanent && this._map && this._map.hasLayer(this)) {
7714			this.openTooltip();
7715		}
7716
7717		return this;
7718	},
7719
7720	// @method unbindTooltip(): this
7721	// Removes the tooltip previously bound with `bindTooltip`.
7722	unbindTooltip: function () {
7723		if (this._tooltip) {
7724			this._initTooltipInteractions(true);
7725			this.closeTooltip();
7726			this._tooltip = null;
7727		}
7728		return this;
7729	},
7730
7731	_initTooltipInteractions: function (remove) {
7732		if (!remove && this._tooltipHandlersAdded) { return; }
7733		var onOff = remove ? 'off' : 'on',
7734		    events = {
7735			remove: this.closeTooltip,
7736			move: this._moveTooltip
7737		    };
7738		if (!this._tooltip.options.permanent) {
7739			events.mouseover = this._openTooltip;
7740			events.mouseout = this.closeTooltip;
7741			if (this._tooltip.options.sticky) {
7742				events.mousemove = this._moveTooltip;
7743			}
7744			if (L.Browser.touch) {
7745				events.click = this._openTooltip;
7746			}
7747		} else {
7748			events.add = this._openTooltip;
7749		}
7750		this[onOff](events);
7751		this._tooltipHandlersAdded = !remove;
7752	},
7753
7754	// @method openTooltip(latlng?: LatLng): this
7755	// Opens the bound tooltip at the specificed `latlng` or at the default tooltip anchor if no `latlng` is passed.
7756	openTooltip: function (layer, latlng) {
7757		if (!(layer instanceof L.Layer)) {
7758			latlng = layer;
7759			layer = this;
7760		}
7761
7762		if (layer instanceof L.FeatureGroup) {
7763			for (var id in this._layers) {
7764				layer = this._layers[id];
7765				break;
7766			}
7767		}
7768
7769		if (!latlng) {
7770			latlng = layer.getCenter ? layer.getCenter() : layer.getLatLng();
7771		}
7772
7773		if (this._tooltip && this._map) {
7774
7775			// set tooltip source to this layer
7776			this._tooltip._source = layer;
7777
7778			// update the tooltip (content, layout, ect...)
7779			this._tooltip.update();
7780
7781			// open the tooltip on the map
7782			this._map.openTooltip(this._tooltip, latlng);
7783
7784			// Tooltip container may not be defined if not permanent and never
7785			// opened.
7786			if (this._tooltip.options.interactive && this._tooltip._container) {
7787				L.DomUtil.addClass(this._tooltip._container, 'leaflet-clickable');
7788				this.addInteractiveTarget(this._tooltip._container);
7789			}
7790		}
7791
7792		return this;
7793	},
7794
7795	// @method closeTooltip(): this
7796	// Closes the tooltip bound to this layer if it is open.
7797	closeTooltip: function () {
7798		if (this._tooltip) {
7799			this._tooltip._close();
7800			if (this._tooltip.options.interactive && this._tooltip._container) {
7801				L.DomUtil.removeClass(this._tooltip._container, 'leaflet-clickable');
7802				this.removeInteractiveTarget(this._tooltip._container);
7803			}
7804		}
7805		return this;
7806	},
7807
7808	// @method toggleTooltip(): this
7809	// Opens or closes the tooltip bound to this layer depending on its current state.
7810	toggleTooltip: function (target) {
7811		if (this._tooltip) {
7812			if (this._tooltip._map) {
7813				this.closeTooltip();
7814			} else {
7815				this.openTooltip(target);
7816			}
7817		}
7818		return this;
7819	},
7820
7821	// @method isTooltipOpen(): boolean
7822	// Returns `true` if the tooltip bound to this layer is currently open.
7823	isTooltipOpen: function () {
7824		return this._tooltip.isOpen();
7825	},
7826
7827	// @method setTooltipContent(content: String|HTMLElement|Tooltip): this
vendor: 8,308 bytes, lines 7828-8144
7828	// Sets the content of the tooltip bound to this layer.
7829	setTooltipContent: function (content) {
7830		if (this._tooltip) {
7831			this._tooltip.setContent(content);
7832		}
7833		return this;
7834	},
7835
7836	// @method getTooltip(): Tooltip
7837	// Returns the tooltip bound to this layer.
7838	getTooltip: function () {
7839		return this._tooltip;
7840	},
7841
7842	_openTooltip: function (e) {
7843		var layer = e.layer || e.target;
7844
7845		if (!this._tooltip || !this._map) {
7846			return;
7847		}
7848		this.openTooltip(layer, this._tooltip.options.sticky ? e.latlng : undefined);
7849	},
7850
7851	_moveTooltip: function (e) {
7852		var latlng = e.latlng, containerPoint, layerPoint;
7853		if (this._tooltip.options.sticky && e.originalEvent) {
7854			containerPoint = this._map.mouseEventToContainerPoint(e.originalEvent);
7855			layerPoint = this._map.containerPointToLayerPoint(containerPoint);
7856			latlng = this._map.layerPointToLatLng(layerPoint);
7857		}
7858		this._tooltip.setLatLng(latlng);
7859	}
7860});
7861
7862
7863
7864/*
7865 * @class LayerGroup
7866 * @aka L.LayerGroup
7867 * @inherits Layer
7868 *
7869 * Used to group several layers and handle them as one. If you add it to the map,
7870 * any layers added or removed from the group will be added/removed on the map as
7871 * well. Extends `Layer`.
7872 *
7873 * @example
7874 *
7875 * ```js
7876 * L.layerGroup([marker1, marker2])
7877 * 	.addLayer(polyline)
7878 * 	.addTo(map);
7879 * ```
7880 */
7881
7882L.LayerGroup = L.Layer.extend({
7883
7884	initialize: function (layers) {
7885		this._layers = {};
7886
7887		var i, len;
7888
7889		if (layers) {
7890			for (i = 0, len = layers.length; i < len; i++) {
7891				this.addLayer(layers[i]);
7892			}
7893		}
7894	},
7895
7896	// @method addLayer(layer: Layer): this
7897	// Adds the given layer to the group.
7898	addLayer: function (layer) {
7899		var id = this.getLayerId(layer);
7900
7901		this._layers[id] = layer;
7902
7903		if (this._map) {
7904			this._map.addLayer(layer);
7905		}
7906
7907		return this;
7908	},
7909
7910	// @method removeLayer(layer: Layer): this
7911	// Removes the given layer from the group.
7912	// @alternative
7913	// @method removeLayer(id: Number): this
7914	// Removes the layer with the given internal ID from the group.
7915	removeLayer: function (layer) {
7916		var id = layer in this._layers ? layer : this.getLayerId(layer);
7917
7918		if (this._map && this._layers[id]) {
7919			this._map.removeLayer(this._layers[id]);
7920		}
7921
7922		delete this._layers[id];
7923
7924		return this;
7925	},
7926
7927	// @method hasLayer(layer: Layer): Boolean
7928	// Returns `true` if the given layer is currently added to the group.
7929	hasLayer: function (layer) {
7930		return !!layer && (layer in this._layers || this.getLayerId(layer) in this._layers);
7931	},
7932
7933	// @method clearLayers(): this
7934	// Removes all the layers from the group.
7935	clearLayers: function () {
7936		for (var i in this._layers) {
7937			this.removeLayer(this._layers[i]);
7938		}
7939		return this;
7940	},
7941
7942	// @method invoke(methodName: String, …): this
7943	// Calls `methodName` on every layer contained in this group, passing any
7944	// additional parameters. Has no effect if the layers contained do not
7945	// implement `methodName`.
7946	invoke: function (methodName) {
7947		var args = Array.prototype.slice.call(arguments, 1),
7948		    i, layer;
7949
7950		for (i in this._layers) {
7951			layer = this._layers[i];
7952
7953			if (layer[methodName]) {
7954				layer[methodName].apply(layer, args);
7955			}
7956		}
7957
7958		return this;
7959	},
7960
7961	onAdd: function (map) {
7962		for (var i in this._layers) {
7963			map.addLayer(this._layers[i]);
7964		}
7965	},
7966
7967	onRemove: function (map) {
7968		for (var i in this._layers) {
7969			map.removeLayer(this._layers[i]);
7970		}
7971	},
7972
7973	// @method eachLayer(fn: Function, context?: Object): this
7974	// Iterates over the layers of the group, optionally specifying context of the iterator function.
7975	// ```js
7976	// group.eachLayer(function (layer) {
7977	// 	layer.bindPopup('Hello');
7978	// });
7979	// ```
7980	eachLayer: function (method, context) {
7981		for (var i in this._layers) {
7982			method.call(context, this._layers[i]);
7983		}
7984		return this;
7985	},
7986
7987	// @method getLayer(id: Number): Layer
7988	// Returns the layer with the given internal ID.
7989	getLayer: function (id) {
7990		return this._layers[id];
7991	},
7992
7993	// @method getLayers(): Layer[]
7994	// Returns an array of all the layers added to the group.
7995	getLayers: function () {
7996		var layers = [];
7997
7998		for (var i in this._layers) {
7999			layers.push(this._layers[i]);
8000		}
8001		return layers;
8002	},
8003
8004	// @method setZIndex(zIndex: Number): this
8005	// Calls `setZIndex` on every layer contained in this group, passing the z-index.
8006	setZIndex: function (zIndex) {
8007		return this.invoke('setZIndex', zIndex);
8008	},
8009
8010	// @method getLayerId(layer: Layer): Number
8011	// Returns the internal ID for a layer
8012	getLayerId: function (layer) {
8013		return L.stamp(layer);
8014	}
8015});
8016
8017
8018// @factory L.layerGroup(layers: Layer[])
8019// Create a layer group, optionally given an initial set of layers.
8020L.layerGroup = function (layers) {
8021	return new L.LayerGroup(layers);
8022};
8023
8024
8025
8026/*
8027 * @class FeatureGroup
8028 * @aka L.FeatureGroup
8029 * @inherits LayerGroup
8030 *
8031 * Extended `LayerGroup` that makes it easier to do the same thing to all its member layers:
8032 *  * [`bindPopup`](#layer-bindpopup) binds a popup to all of the layers at once (likewise with [`bindTooltip`](#layer-bindtooltip))
8033 *  * Events are propagated to the `FeatureGroup`, so if the group has an event
8034 * handler, it will handle events from any of the layers. This includes mouse events
8035 * and custom events.
8036 *  * Has `layeradd` and `layerremove` events
8037 *
8038 * @example
8039 *
8040 * ```js
8041 * L.featureGroup([marker1, marker2, polyline])
8042 * 	.bindPopup('Hello world!')
8043 * 	.on('click', function() { alert('Clicked on a member of the group!'); })
8044 * 	.addTo(map);
8045 * ```
8046 */
8047
8048L.FeatureGroup = L.LayerGroup.extend({
8049
8050	addLayer: function (layer) {
8051		if (this.hasLayer(layer)) {
8052			return this;
8053		}
8054
8055		layer.addEventParent(this);
8056
8057		L.LayerGroup.prototype.addLayer.call(this, layer);
8058
8059		// @event layeradd: LayerEvent
8060		// Fired when a layer is added to this `FeatureGroup`
8061		return this.fire('layeradd', {layer: layer});
8062	},
8063
8064	removeLayer: function (layer) {
8065		if (!this.hasLayer(layer)) {
8066			return this;
8067		}
8068		if (layer in this._layers) {
8069			layer = this._layers[layer];
8070		}
8071
8072		layer.removeEventParent(this);
8073
8074		L.LayerGroup.prototype.removeLayer.call(this, layer);
8075
8076		// @event layerremove: LayerEvent
8077		// Fired when a layer is removed from this `FeatureGroup`
8078		return this.fire('layerremove', {layer: layer});
8079	},
8080
8081	// @method setStyle(style: Path options): this
8082	// Sets the given path options to each layer of the group that has a `setStyle` method.
8083	setStyle: function (style) {
8084		return this.invoke('setStyle', style);
8085	},
8086
8087	// @method bringToFront(): this
8088	// Brings the layer group to the top of all other layers
8089	bringToFront: function () {
8090		return this.invoke('bringToFront');
8091	},
8092
8093	// @method bringToBack(): this
8094	// Brings the layer group to the top of all other layers
8095	bringToBack: function () {
8096		return this.invoke('bringToBack');
8097	},
8098
8099	// @method getBounds(): LatLngBounds
8100	// Returns the LatLngBounds of the Feature Group (created from bounds and coordinates of its children).
8101	getBounds: function () {
8102		var bounds = new L.LatLngBounds();
8103
8104		for (var id in this._layers) {
8105			var layer = this._layers[id];
8106			bounds.extend(layer.getBounds ? layer.getBounds() : layer.getLatLng());
8107		}
8108		return bounds;
8109	}
8110});
8111
8112// @factory L.featureGroup(layers: Layer[])
8113// Create a feature group, optionally given an initial set of layers.
8114L.featureGroup = function (layers) {
8115	return new L.FeatureGroup(layers);
8116};
8117
8118
8119
8120/*
8121 * @class Renderer
8122 * @inherits Layer
8123 * @aka L.Renderer
8124 *
8125 * Base class for vector renderer implementations (`SVG`, `Canvas`). Handles the
8126 * DOM container of the renderer, its bounds, and its zoom animation.
8127 *
8128 * A `Renderer` works as an implicit layer group for all `Path`s - the renderer
8129 * itself can be added or removed to the map. All paths use a renderer, which can
8130 * be implicit (the map will decide the type of renderer and use it automatically)
8131 * or explicit (using the [`renderer`](#path-renderer) option of the path).
8132 *
8133 * Do not use this class directly, use `SVG` and `Canvas` instead.
8134 *
8135 * @event update: Event
8136 * Fired when the renderer updates its bounds, center and zoom, for example when
8137 * its map has moved
8138 */
8139
8140L.Renderer = L.Layer.extend({
8141
8142	// @section
8143	// @aka Renderer options
8144	options: {
8145		// @option padding: Number = 0.1
8146		// How much to extend the clip area around the map view (relative to its size)
8147		// e.g. 0.1 would be 10% of map view in each direction
8148		padding: 0.1
8149	},
8150
8151	initialize: function (options) {
8152		L.setOptions(this, options);
8153		L.stamp(this);
8154		this._layers = this._layers || {};
8155	},
8156
8157	onAdd: function () {
8158		if (!this._container) {
8159			this._initContainer(); // defined by renderer implementations
8160
8161			if (this._zoomAnimated) {
8162				L.DomUtil.addClass(this._container, 'leaflet-zoom-animated');
8163			}
8164		}
8165
8166		this.getPane().appendChild(this._container);
8167		this._update();
8168		this.on('update', this._updatePaths, this);
8169	},
8170
8171	onRemove: function () {
8172		L.DomUtil.remove(this._container);
8173		this.off('update', this._updatePaths, this);
8174	},
8175
8176	getEvents: function () {
8177		var events = {
8178			viewreset: this._reset,
8179			zoom: this._onZoom,
8180			moveend: this._update,
8181			zoomend: this._onZoomEnd
8182		};
8183		if (this._zoomAnimated) {
8184			events.zoomanim = this._onAnimZoom;
8185		}
8186		return events;
8187	},
8188
8189	_onAnimZoom: function (ev) {
8190		this._updateTransform(ev.center, ev.zoom);
8191	},
8192
8193	_onZoom: function () {
8194		this._updateTransform(this._map.getCenter(), this._map.getZoom());
8195	},
8196
8197	_updateTransform: function (center, zoom) {
8198		var scale = this._map.getZoomScale(zoom, this._zoom),
8199		    position = L.DomUtil.getPosition(this._container),
8200		    viewHalf = this._map.getSize().multiplyBy(0.5 + this.options.padding),
8201		    currentCenterPoint = this._map.project(this._center, zoom),
8202		    destCenterPoint = this._map.project(center, zoom),
8203		    centerOffset = destCenterPoint.subtract(currentCenterPoint),
8204
8205		    topLeftOffset = viewHalf.multiplyBy(-scale).add(position).add(viewHalf).subtract(centerOffset);
8206
8207		if (L.Browser.any3d) {
8208			L.DomUtil.setTransform(this._container, topLeftOffset, scale);
8209		} else {
8210			L.DomUtil.setPosition(this._container, topLeftOffset);
8211		}
8212	},
8213
8214	_reset: function () {
8215		this._update();
8216		this._updateTransform(this._center, this._zoom);
8217
8218		for (var id in this._layers) {
8219			this._layers[id]._reset();
8220		}
8221	},
8222
8223	_onZoomEnd: function () {
8224		for (var id in this._layers) {
8225			this._layers[id]._project();
8226		}
8227	},
8228
8229	_updatePaths: function () {
8230		for (var id in this._layers) {
8231			this._layers[id]._update();
8232		}
8233	},
8234
8235	_update: function () {
8236		// Update pixel bounds of renderer container (for positioning/sizing/clipping later)
8237		// Subclasses are responsible of firing the 'update' event.
8238		var p = this.options.padding,
8239		    size = this._map.getSize(),
8240		    min = this._map.containerPointToLayerPoint(size.multiplyBy(-p)).round();
8241
8242		this._bounds = new L.Bounds(min, min.add(size.multiplyBy(1 + p * 2)).round());
8243
8244		this._center = this._map.getCenter();
8245		this._zoom = this._map.getZoom();
8246	}
8247});
8248
8249
8250L.Map.include({
8251	// @namespace Map; @method getRenderer(layer: Path): Renderer
8252	// Returns the instance of `Renderer` that should be used to render the given
8253	// `Path`. It will ensure that the `renderer` options of the map and paths
8254	// are respected, and that the renderers do exist on the map.
8255	getRenderer: function (layer) {
8256		// @namespace Path; @option renderer: Renderer
8257		// Use this specific instance of `Renderer` for this path. Takes
8258		// precedence over the map's [default renderer](#map-renderer).
8259		var renderer = layer.options.renderer || this._getPaneRenderer(layer.options.pane) || this.options.renderer || this._renderer;
8260
8261		if (!renderer) {
8262			// @namespace Map; @option preferCanvas: Boolean = false
8263			// Whether `Path`s should be rendered on a `Canvas` renderer.
8264			// By default, all `Path`s are rendered in a `SVG` renderer.
8265			renderer = this._renderer = (this.options.preferCanvas && L.canvas()) || L.svg();
8266		}
8267
8268		if (!this.hasLayer(renderer)) {
8269			this.addLayer(renderer);
8270		}
8271		return renderer;
8272	},
8273
8274	_getPaneRenderer: function (name) {
8275		if (name === 'overlayPane' || name === undefined) {
8276			return false;
8277		}
8278
8279		var renderer = this._paneRenderers[name];
8280		if (renderer === undefined) {
8281			renderer = (L.SVG && L.svg({pane: name})) || (L.Canvas && L.canvas({pane: name}));
8282			this._paneRenderers[name] = renderer;
8283		}
8284		return renderer;
8285	}
8286});
8287
8288
8289
8290/*
8291 * @class Path
8292 * @aka L.Path
8293 * @inherits Interactive layer
8294 *
8295 * An abstract class that contains options and constants shared between vector
8296 * overlays (Polygon, Polyline, Circle). Do not use it directly. Extends `Layer`.
8297 */
8298
8299L.Path = L.Layer.extend({
8300
8301	// @section
8302	// @aka Path options
8303	options: {
8304		// @option stroke: Boolean = true
8305		// Whether to draw stroke along the path. Set it to `false` to disable borders on polygons or circles.
8306		stroke: true,
8307
8308		// @option color: String = '#3388ff'
8309		// Stroke color
8310		color: '#3388ff',
8311
8312		// @option weight: Number = 3
8313		// Stroke width in pixels
8314		weight: 3,
8315
8316		// @option opacity: Number = 1.0
8317		// Stroke opacity
8318		opacity: 1,
8319
8320		// @option lineCap: String= 'round'
8321		// A string that defines [shape to be used at the end](https://developer.mozilla.org/docs/Web/SVG/Attribute/stroke-linecap) of the stroke.
8322		lineCap: 'round',
8323
8324		// @option lineJoin: String = 'round'
8325		// A string that defines [shape to be used at the corners](https://developer.mozilla.org/docs/Web/SVG/Attribute/stroke-linejoin) of the stroke.
8326		lineJoin: 'round',
8327
8328		// @option dashArray: String = null
8329		// 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).
8330		dashArray: null,
8331
8332		// @option dashOffset: String = null
8333		// 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).
8334		dashOffset: null,
8335
8336		// @option fill: Boolean = depends
8337		// Whether to fill the path with color. Set it to `false` to disable filling on polygons or circles.
8338		fill: false,
8339
8340		// @option fillColor: String = *
8341		// Fill color. Defaults to the value of the [`color`](#path-color) option
8342		fillColor: null,
8343
8344		// @option fillOpacity: Number = 0.2
8345		// Fill opacity.
8346		fillOpacity: 0.2,
8347
8348		// @option fillRule: String = 'evenodd'
8349		// A string that defines [how the inside of a shape](https://developer.mozilla.org/docs/Web/SVG/Attribute/fill-rule) is determined.
8350		fillRule: 'evenodd',
8351
8352		// className: '',
8353
8354		// Option inherited from "Interactive layer" abstract class
8355		interactive: true
8356	},
8357
8358	beforeAdd: function (map) {
8359		// Renderer is set here because we need to call renderer.getEvents
8360		// before this.getEvents.
8361		this._renderer = map.getRenderer(this);
8362	},
8363
8364	onAdd: function () {
8365		this._renderer._initPath(this);
8366		this._reset();
8367		this._renderer._addPath(this);
8368	},
8369
8370	onRemove: function () {
8371		this._renderer._removePath(this);
8372	},
8373
8374	// @method redraw(): this
8375	// Redraws the layer. Sometimes useful after you changed the coordinates that the path uses.
8376	redraw: function () {
8377		if (this._map) {
8378			this._renderer._updatePath(this);
8379		}
8380		return this;
8381	},
8382
8383	// @method setStyle(style: Path options): this
8384	// Changes the appearance of a Path based on the options in the `Path options` object.
8385	setStyle: function (style) {
8386		L.setOptions(this, style);
8387		if (this._renderer) {
8388			this._renderer._updateStyle(this);
8389		}
8390		return this;
8391	},
8392
8393	// @method bringToFront(): this
8394	// Brings the layer to the top of all path layers.
8395	bringToFront: function () {
8396		if (this._renderer) {
8397			this._renderer._bringToFront(this);
8398		}
8399		return this;
8400	},
8401
8402	// @method bringToBack(): this
8403	// Brings the layer to the bottom of all path layers.
8404	bringToBack: function () {
8405		if (this._renderer) {
8406			this._renderer._bringToBack(this);
8407		}
8408		return this;
8409	},
8410
8411	getElement: function () {
8412		return this._path;
8413	},
8414
8415	_reset: function () {
8416		// defined in children classes
8417		this._project();
8418		this._update();
8419	},
8420
8421	_clickTolerance: function () {
8422		// used when doing hit detection for Canvas layers
8423		return (this.options.stroke ? this.options.weight / 2 : 0) + (L.Browser.touch ? 10 : 0);
8424	}
8425});
8426
8427
8428
8429/*
8430 * @namespace LineUtil
8431 *
8432 * Various utility functions for polyine points processing, used by Leaflet internally to make polylines lightning-fast.
8433 */
8434
8435L.LineUtil = {
8436
8437	// Simplify polyline with vertex reduction and Douglas-Peucker simplification.
8438	// Improves rendering performance dramatically by lessening the number of points to draw.
8439
8440	// @function simplify(points: Point[], tolerance: Number): Point[]
8441	// Dramatically reduces the number of points in a polyline while retaining
8442	// its shape and returns a new array of simplified points, using the
8443	// [Douglas-Peucker algorithm](http://en.wikipedia.org/wiki/Douglas-Peucker_algorithm).
8444	// Used for a huge performance boost when processing/displaying Leaflet polylines for
8445	// each zoom level and also reducing visual noise. tolerance affects the amount of
8446	// simplification (lesser value means higher quality but slower and with more points).
8447	// Also released as a separated micro-library [Simplify.js](http://mourner.github.com/simplify-js/).
8448	simplify: function (points, tolerance) {
8449		if (!tolerance || !points.length) {
8450			return points.slice();
8451		}
8452
8453		var sqTolerance = tolerance * tolerance;
8454
8455		// stage 1: vertex reduction
8456		points = this._reducePoints(points, sqTolerance);
8457
8458		// stage 2: Douglas-Peucker simplification
8459		points = this._simplifyDP(points, sqTolerance);
8460
8461		return points;
8462	},
8463
8464	// @function pointToSegmentDistance(p: Point, p1: Point, p2: Point): Number
8465	// Returns the distance between point `p` and segment `p1` to `p2`.
8466	pointToSegmentDistance:  function (p, p1, p2) {
8467		return Math.sqrt(this._sqClosestPointOnSegment(p, p1, p2, true));
8468	},
8469
8470	// @function closestPointOnSegment(p: Point, p1: Point, p2: Point): Number
8471	// Returns the closest point from a point `p` on a segment `p1` to `p2`.
8472	closestPointOnSegment: function (p, p1, p2) {
8473		return this._sqClosestPointOnSegment(p, p1, p2);
8474	},
8475
8476	// Douglas-Peucker simplification, see http://en.wikipedia.org/wiki/Douglas-Peucker_algorithm
8477	_simplifyDP: function (points, sqTolerance) {
8478
8479		var len = points.length,
8480		    ArrayConstructor = typeof Uint8Array !== undefined + '' ? Uint8Array : Array,
8481		    markers = new ArrayConstructor(len);
8482
8483		markers[0] = markers[len - 1] = 1;
8484
8485		this._simplifyDPStep(points, markers, sqTolerance, 0, len - 1);
8486
8487		var i,
8488		    newPoints = [];
8489
8490		for (i = 0; i < len; i++) {
8491			if (markers[i]) {
8492				newPoints.push(points[i]);
8493			}
8494		}
8495
8496		return newPoints;
8497	},
8498
8499	_simplifyDPStep: function (points, markers, sqTolerance, first, last) {
8500
8501		var maxSqDist = 0,
8502		    index, i, sqDist;
8503
8504		for (i = first + 1; i <= last - 1; i++) {
8505			sqDist = this._sqClosestPointOnSegment(points[i], points[first], points[last], true);
8506
8507			if (sqDist > maxSqDist) {
8508				index = i;
8509				maxSqDist = sqDist;
8510			}
8511		}
8512
8513		if (maxSqDist > sqTolerance) {
8514			markers[index] = 1;
8515
8516			this._simplifyDPStep(points, markers, sqTolerance, first, index);
8517			this._simplifyDPStep(points, markers, sqTolerance, index, last);
8518		}
8519	},
8520
8521	// reduce points that are too close to each other to a single point
8522	_reducePoints: function (points, sqTolerance) {
8523		var reducedPoints = [points[0]];
8524
8525		for (var i = 1, prev = 0, len = points.length; i < len; i++) {
8526			if (this._sqDist(points[i], points[prev]) > sqTolerance) {
8527				reducedPoints.push(points[i]);
8528				prev = i;
8529			}
8530		}
8531		if (prev < len - 1) {
8532			reducedPoints.push(points[len - 1]);
8533		}
8534		return reducedPoints;
8535	},
8536
8537
8538	// @function clipSegment(a: Point, b: Point, bounds: Bounds, useLastCode?: Boolean, roun
8538d?: Boolean): Point[]|Boolean
8539	// Clips the segment a to b by rectangular bounds with the
8540	// [Cohen-Sutherland algorithm](https://en.wikipedia.org/wiki/Cohen%E2%80%93Sutherland_algorithm)
8541	// (modifying the segment points directly!). Used by Leaflet to only show polyline
8542	// points that are on the screen or near, increasing performance.
8543	clipSegment: function (a, b, bounds, useLastCode, round) {
8544		var codeA = useLastCode ? this._lastCode : this._getBitCode(a, bounds),
8545		    codeB = this._getBitCode(b, bounds),
8546
8547		    codeOut, p, newCode;
8548
8549		// save 2nd code to avoid calculating it on the next segment
8550		this._lastCode = codeB;
8551
8552		while (true) {
8553			// if a,b is inside the clip window (trivial accept)
8554			if (!(codeA | codeB)) {
8555				return [a, b];
8556			}
8557
8558			// if a,b is outside the clip window (trivial reject)
8559			if (codeA & codeB) {
8560				return false;
8561			}
8562
8563			// other cases
8564			codeOut = codeA || codeB;
8565			p = this._getEdgeIntersection(a, b, codeOut, bounds, round);
8566			newCode = this._getBitCode(p, bounds);
8567
8568			if (codeOut === codeA) {
8569				a = p;
8570				codeA = newCode;
8571			} else {
8572				b = p;
8573				codeB = newCode;
8574			}
8575		}
8576	},
8577
vendor: 5,078 bytes, lines 8578-8795
8578	_getEdgeIntersection: function (a, b, code, bounds, round) {
8579		var dx = b.x - a.x,
8580		    dy = b.y - a.y,
8581		    min = bounds.min,
8582		    max = bounds.max,
8583		    x, y;
8584
8585		if (code & 8) { // top
8586			x = a.x + dx * (max.y - a.y) / dy;
8587			y = max.y;
8588
8589		} else if (code & 4) { // bottom
8590			x = a.x + dx * (min.y - a.y) / dy;
8591			y = min.y;
8592
8593		} else if (code & 2) { // right
8594			x = max.x;
8595			y = a.y + dy * (max.x - a.x) / dx;
8596
8597		} else if (code & 1) { // left
8598			x = min.x;
8599			y = a.y + dy * (min.x - a.x) / dx;
8600		}
8601
8602		return new L.Point(x, y, round);
8603	},
8604
8605	_getBitCode: function (p, bounds) {
8606		var code = 0;
8607
8608		if (p.x < bounds.min.x) { // left
8609			code |= 1;
8610		} else if (p.x > bounds.max.x) { // right
8611			code |= 2;
8612		}
8613
8614		if (p.y < bounds.min.y) { // bottom
8615			code |= 4;
8616		} else if (p.y > bounds.max.y) { // top
8617			code |= 8;
8618		}
8619
8620		return code;
8621	},
8622
8623	// square distance (to avoid unnecessary Math.sqrt calls)
8624	_sqDist: function (p1, p2) {
8625		var dx = p2.x - p1.x,
8626		    dy = p2.y - p1.y;
8627		return dx * dx + dy * dy;
8628	},
8629
8630	// return closest point on segment or distance to that point
8631	_sqClosestPointOnSegment: function (p, p1, p2, sqDist) {
8632		var x = p1.x,
8633		    y = p1.y,
8634		    dx = p2.x - x,
8635		    dy = p2.y - y,
8636		    dot = dx * dx + dy * dy,
8637		    t;
8638
8639		if (dot > 0) {
8640			t = ((p.x - x) * dx + (p.y - y) * dy) / dot;
8641
8642			if (t > 1) {
8643				x = p2.x;
8644				y = p2.y;
8645			} else if (t > 0) {
8646				x += dx * t;
8647				y += dy * t;
8648			}
8649		}
8650
8651		dx = p.x - x;
8652		dy = p.y - y;
8653
8654		return sqDist ? dx * dx + dy * dy : new L.Point(x, y);
8655	}
8656};
8657
8658
8659
8660/*
8661 * @class Polyline
8662 * @aka L.Polyline
8663 * @inherits Path
8664 *
8665 * A class for drawing polyline overlays on a map. Extends `Path`.
8666 *
8667 * @example
8668 *
8669 * ```js
8670 * // create a red polyline from an array of LatLng points
8671 * var latlngs = [
8672 * 	[45.51, -122.68],
8673 * 	[37.77, -122.43],
8674 * 	[34.04, -118.2]
8675 * ];
8676 *
8677 * var polyline = L.polyline(latlngs, {color: 'red'}).addTo(map);
8678 *
8679 * // zoom the map to the polyline
8680 * map.fitBounds(polyline.getBounds());
8681 * ```
8682 *
8683 * You can also pass a multi-dimensional array to represent a `MultiPolyline` shape:
8684 *
8685 * ```js
8686 * // create a red polyline from an array of arrays of LatLng points
8687 * var latlngs = [
8688 * 	[[45.51, -122.68],
8689 * 	 [37.77, -122.43],
8690 * 	 [34.04, -118.2]],
8691 * 	[[40.78, -73.91],
8692 * 	 [41.83, -87.62],
8693 * 	 [32.76, -96.72]]
8694 * ];
8695 * ```
8696 */
8697
8698L.Polyline = L.Path.extend({
8699
8700	// @section
8701	// @aka Polyline options
8702	options: {
8703		// @option smoothFactor: Number = 1.0
8704		// How much to simplify the polyline on each zoom level. More means
8705		// better performance and smoother look, and less means more accurate representation.
8706		smoothFactor: 1.0,
8707
8708		// @option noClip: Boolean = false
8709		// Disable polyline clipping.
8710		noClip: false
8711	},
8712
8713	initialize: function (latlngs, options) {
8714		L.setOptions(this, options);
8715		this._setLatLngs(latlngs);
8716	},
8717
8718	// @method getLatLngs(): LatLng[]
8719	// Returns an array of the points in the path, or nested arrays of points in case of multi-polyline.
8720	getLatLngs: function () {
8721		return this._latlngs;
8722	},
8723
8724	// @method setLatLngs(latlngs: LatLng[]): this
8725	// Replaces all the points in the polyline with the given array of geographical points.
8726	setLatLngs: function (latlngs) {
8727		this._setLatLngs(latlngs);
8728		return this.redraw();
8729	},
8730
8731	// @method isEmpty(): Boolean
8732	// Returns `true` if the Polyline has no LatLngs.
8733	isEmpty: function () {
8734		return !this._latlngs.length;
8735	},
8736
8737	closestLayerPoint: function (p) {
8738		var minDistance = Infinity,
8739		    minPoint = null,
8740		    closest = L.LineUtil._sqClosestPointOnSegment,
8741		    p1, p2;
8742
8743		for (var j = 0, jLen = this._parts.length; j < jLen; j++) {
8744			var points = this._parts[j];
8745
8746			for (var i = 1, len = points.length; i < len; i++) {
8747				p1 = points[i - 1];
8748				p2 = points[i];
8749
8750				var sqDist = closest(p, p1, p2, true);
8751
8752				if (sqDist < minDistance) {
8753					minDistance = sqDist;
8754					minPoint = closest(p, p1, p2);
8755				}
8756			}
8757		}
8758		if (minPoint) {
8759			minPoint.distance = Math.sqrt(minDistance);
8760		}
8761		return minPoint;
8762	},
8763
8764	// @method getCenter(): LatLng
8765	// Returns the center ([centroid](http://en.wikipedia.org/wiki/Centroid)) of the polyline.
8766	getCenter: function () {
8767		// throws error when not yet added to map as this center calculation requires projected coordinates
8768		if (!this._map) {
8769			throw new Error('Must add layer to map before using getCenter()');
8770		}
8771
8772		var i, halfDist, segDist, dist, p1, p2, ratio,
8773		    points = this._rings[0],
8774		    len = points.length;
8775
8776		if (!len) { return null; }
8777
8778		// polyline centroid algorithm; only uses the first ring if there are multiple
8779
8780		for (i = 0, halfDist = 0; i < len - 1; i++) {
8781			halfDist += points[i].distanceTo(points[i + 1]) / 2;
8782		}
8783
8784		// The line is so small in the current view that all points are on the same pixel.
8785		if (halfDist === 0) {
8786			return this._map.layerPointToLatLng(points[0]);
8787		}
8788
8789		for (i = 0, dist = 0; i < len - 1; i++) {
8790			p1 = points[i];
8791			p2 = points[i + 1];
8792			segDist = p1.distanceTo(p2);
8793			dist += segDist;
8794
8795			if (dist > halfDist) {
8796				ratio = (dist - halfDist) / segDist;
8797				return this._map.layerPointToLatLng([
8798					p2.x - ratio * (p2.x - p1.x),
8799					p2.y - ratio * (p2.y - p1.y)
8800				]);
8801			}
8802		}
8803	},
8804
8805	// @method getBounds(): LatLngBounds
8806	// Returns the `LatLngBounds` of the path.
8807	getBounds: function () {
8808		return this._bounds;
8809	},
8810
8811	// @method addLatLng(latlng: LatLng, latlngs? LatLng[]): this
8812	// Adds a given point to the polyline. By default, adds to the first ring of
8813	// the polyline in case of a multi-polyline, but can be overridden by passing
8814	// a specific ring as a LatLng array (that you can earlier access with [`getLatLngs`](#polyline-getlatlngs)).
8815	addLatLng: function (latlng, latlngs) {
8816		latlngs = latlngs || this._defaultShape();
8817		latlng = L.latLng(latlng);
8818		latlngs.push(latlng);
8819		this._bounds.extend(latlng);
8820		return this.redraw();
8821	},
8822
8823	_setLatLngs: function (latlngs) {
8824		this._bounds = new L.LatLngBounds();
8825		this._latlngs = this._convertLatLngs(latlngs);
8826	},
8827
8828	_defaultShape: function () {
8829		return L.Polyline._flat(this._latlngs) ? this._latlngs : this._latlngs[0];
8830	},
8831
8832	// recursively convert latlngs input into actual LatLng instances; calculate bounds along the way
8833	_convertLatLngs: function (latlngs) {
8834		var result = [],
8835		    flat = L.Polyline._flat(latlngs);
8836
8837		for (var i = 0, len = latlngs.length; i < len; i++) {
8838			if (flat) {
8839				result[i] = L.latLng(latlngs[i]);
8840				this._bounds.extend(result[i]);
8841			} else {
8842				result[i] = this._convertLatLngs(latlngs[i]);
8843			}
8844		}
8845
8846		return result;
8847	},
8848
8849	_project: function () {
8850		var pxBounds = new L.Bounds();
8851		this._rings = [];
8852		this._projectLatlngs(this._latlngs, this._rings, pxBounds);
8853
8854		var w = this._clickTolerance(),
8855		    p = new L.Point(w, w);
8856
8857		if (this._bounds.isValid() && pxBounds.isValid()) {
8858			pxBounds.min._subtract(p);
8859			pxBounds.max._add(p);
8860			this._pxBounds = pxBounds;
8861		}
8862	},
8863
8864	// recursively turns latlngs into a set of rings with projected coordinates
8865	_projectLatlngs: function (latlngs, result, projectedBounds) {
8866		var flat = latlngs[0] instanceof L.LatLng,
8867		    len = latlngs.length,
8868		    i, ring;
8869
8870		if (flat) {
8871			ring = [];
8872			for (i = 0; i < len; i++) {
8873				ring[i] = this._map.latLngToLayerPoint(latlngs[i]);
8874				projectedBounds.extend(ring[i]);
8875			}
8876			result.push(ring);
8877		} else {
8878			for (i = 0; i < len; i++) {
8879				this._projectLatlngs(latlngs[i], result, projectedBounds);
8880			}
8881		}
8882	},
8883
8884	// clip polyline by renderer bounds so that we have less to render for performance
8885	_clipPoints: function () {
8886		var bounds = this._renderer._bounds;
8887
8888		this._parts = [];
8889		if (!this._pxBounds || !this._pxBounds.intersects(bounds)) {
8890			return;
8891		}
8892
8893		if (this.options.noClip) {
8894			this._parts = this._rings;
8895			return;
8896		}
8897
8898		var parts = this._parts,
8899		    i, j, k, len, len2, segment, points;
8900
8901		for (i = 0, k = 0, len = this._rings.length; i < len; i++) {
8902			points = this._rings[i];
8903
8904			for (j = 0, len2 = points.length; j < len2 - 1; j++) {
8905				segment = L.LineUtil.clipSegment(points[j], points[j + 1], bounds, j, true);
8906
8907				if (!segment) { continue; }
8908
8909				parts[k] = parts[k] || [];
8910				parts[k].push(segment[0]);
8911
8912				// if segment goes out of screen, or it's the last one, it's the end of the line part
8913				if ((segment[1] !== points[j + 1]) || (j === len2 - 2)) {
8914					parts[k].push(segment[1]);
8915					k++;
8916				}
8917			}
8918		}
8919	},
8920
8921	// simplify each clipped part of the polyline for performance
8922	_simplifyPoints: function () {
8923		var parts = this._parts,
8924		    tolerance = this.options.smoothFactor;
8925
8926		for (var i = 0, len = parts.length; i < len; i++) {
8927			parts[i] = L.LineUtil.simplify(parts[i], tolerance);
8928		}
8929	},
8930
8931	_update: function () {
8932		if (!this._map) { return; }
8933
8934		this._clipPoints();
8935		this._simplifyPoints();
8936		this._updatePath();
8937	},
8938
8939	_updatePath: function () {
8940		this._renderer._updatePoly(this);
8941	}
8942});
8943
8944// @factory L.polyline(latlngs: LatLng[], options?: Polyline options)
8945// Instantiates a polyline object given an array of geographical points and
8946// optionally an options object. You can create a `Polyline` object with
8947// multiple separate lines (`MultiPolyline`) by passing an array of arrays
8948// of geographic points.
8949L.polyline = function (latlngs, options) {
8950	return new L.Polyline(latlngs, options);
8951};
8952
8953L.Polyline._flat = function (latlngs) {
8954	// true if it's a flat array of latlngs; false if nested
8955	return !L.Util.isArray(latlngs[0]) || (typeof latlngs[0][0] !== 'object' && typeof latlngs[0][0] !== 'undefined');
8956};
8957
8958
8959
8960/*
8961 * @namespace PolyUtil
8962 * Various utility functions for polygon geometries.
8963 */
8964
8965L.PolyUtil = {};
8966
8967/* @function clipPolygon(points: Point[], bounds: Bounds, round?: Boolean): Point[]
8968 * Clips the polygon geometry defined by the given `points` by the given bounds (using the [Sutherland-Hodgeman algorithm](https://en.wikipedia.org/wiki/Sutherland%E2%80%93Hodgman_algorithm)).
8969 * Used by Leaflet to only show polygon points that are on the screen or near, increasing
8970 * performance. Note that polygon points needs different algorithm for clipping
8971 * than polyline, so there's a seperate method for it.
8972 */
8973L.PolyUtil.clipPolygon = function (points, bounds, round) {
8974	var clippedPoints,
8975	    edges = [1, 4, 2, 8],
8976	    i, j, k,
8977	    a, b,
8978	    len, edge, p,
8979	    lu = L.LineUtil;
8980
8981	for (i = 0, len = points.length; i < len; i++) {
8982		points[i]._code = lu._getBitCode(points[i], bounds);
8983	}
8984
8985	// for each edge (left, bottom, right, top)
8986	for (k = 0; k < 4; k++) {
8987		edge = edges[k];
8988		clippedPoints = [];
8989
8990		for (i = 0, len = points.length, j = len - 1; i < len; j = i++) {
8991			a = points[i];
8992			b = points[j];
8993
8994			// if a is inside the clip window
8995			if (!(a._code & edge)) {
8996				// if b is outside the clip window (a->b goes out of screen)
8997				if (b._code & edge) {
8998					p = lu._getEdgeIntersection(b, a, edge, bounds, round);
8999					p._code = lu._getBitCode(p, bounds);
9000					clippedPoints.push(p);
9001				}
9002				clippedPoints.push(a);
9003
9004			// else if b is inside the clip window (a->b enters the screen)
9005			} else if (!(b._code & edge)) {
9006				p = lu._getEdgeIntersection(b, a, edge, bounds, round);
9007				p._code = lu._getBitCode(p, bounds);
9008				clippedPoints.push(p);
9009			}
9010		}
9011		points = clippedPoints;
9012	}
9013
9014	return points;
9015};
9016
9017
9018
9019/*
9020 * @class Polygon
9021 * @aka L.Polygon
9022 * @inherits Polyline
9023 *
9024 * A class for drawing polygon overlays on a map. Extends `Polyline`.
9025 *
9026 * 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.
9027 *
9028 *
9029 * @example
9030 *
9031 * ```js
9032 * // create a red polygon from an array of LatLng points
9033 * var latlngs = [[37, -109.05],[41, -109.03],[41, -102.05],[37, -102.04]];
9034 *
9035 * var polygon = L.polygon(latlngs, {color: 'red'}).addTo(map);
9036 *
9037 * // zoom the map to the polygon
9038 * map.fitBounds(polygon.getBounds());
9039 * ```
9040 *
9041 * 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:
9042 *
9043 * ```js
9044 * var latlngs = [
9045 *   [[37, -109.05],[41, -109.03],[41, -102.05],[37, -102.04]], // outer ring
9046 *   [[37.29, -108.58],[40.71, -108.58],[40.71, -102.50],[37.29, -102.50]] // hole
9047 * ];
9048 * ```
9049 *
9050 * Additionally, you can pass a multi-dimensional array to represent a MultiPolygon shape.
9051 *
9052 * ```js
9053 * var latlngs = [
9054 *   [ // first polygon
9055 *     [[37, -109.05],[41, -109.03],[41, -102.05],[37, -102.04]], // outer ring
9056 *     [[37.29, -108.58],[40.71, -108.58],[40.71, -102.50],[37.29, -102.50]] // hole
9057 *   ],
9058 *   [ // second polygon
9059 *     [[41, -111.03],[45, -111.04],[45, -104.05],[41, -104.05]]
9060 *   ]
9061 * ];
9062 * ```
9063 */
9064
9065L.Polygon = L.Polyline.extend({
9066
9067	options: {
9068		fill: true
9069	},
9070
9071	isEmpty: function () {
9072		return !this._latlngs.length || !this._latlngs[0].length;
9073	},
9074
9075	getCenter: function () {
9076		// throws error when not yet added to map as this center calculation requires projected coordinates
9077		if (!this._map) {
9078			throw new Error('Must add layer to map before using getCenter()');
9079		}
9080
9081		var i, j, p1, p2, f, area, x, y, center,
9082		    points = this._rings[0],
9083		    len = points.length;
9084
9085		if (!len) { return null; }
9086
9087		// polygon centroid algorithm; only uses the first ring if there are multiple
9088
9089		area = x = y = 0;
9090
9091		for (i = 0, j = len - 1; i < len; j = i++) {
9092			p1 = points[i];
9093			p2 = points[j];
9094
9095			f = p1.y * p2.x - p2.y * p1.x;
9096			x += (p1.x + p2.x) * f;
9097			y += (p1.y + p2.y) * f;
9098			area += f * 3;
9099		}
9100
9101		if (area === 0) {
9102			// Polygon is so small that all points are on same pixel.
9103			center = points[0];
9104		} else {
9105			center = [x / area, y / area];
9106		}
9107		return this._map.layerPointToLatLng(center);
9108	},
9109
9110	_convertLatLngs: function (latlngs) {
9111		var result = L.Polyline.prototype._convertLatLngs.call(this, latlngs),
9112		    len = result.length;
9113
9114		// remove last point if it equals first one
9115		if (len >= 2 && result[0] instanceof L.LatLng && result[0].equals(result[len - 1])) {
9116			result.pop();
9117		}
9118		return result;
9119	},
9120
9121	_setLatLngs: function (latlngs) {
9122		L.Polyline.prototype._setLatLngs.call(this, latlngs);
9123		if (L.Polyline._flat(this._latlngs)) {
9124			this._latlngs = [this._latlngs];
9125		}
9126	},
9127
9128	_defaultShape: function () {
9129		return L.Polyline._flat(this._latlngs[0]) ? this._latlngs[0] : this._latlngs[0][0];
9130	},
9131
9132	_clipPoints: function () {
9133		// polygons need a different clipping algorithm so we redefine that
9134
9135		var bounds = this._renderer._bounds,
9136		    w = this.options.weight,
9137		    p = new L.Point(w, w);
9138
9139		// increase clip padding by stroke width to avoid stroke on clip edges
9140		bounds = new L.Bounds(bounds.min.subtract(p), bounds.max.add(p));
9141
9142		this._parts = [];
9143		if (!this._pxBounds || !this._pxBounds.intersects(bounds)) {
9144			return;
9145		}
9146
9147		if (this.options.noClip) {
9148			this._parts = this._rings;
9149			return;
9150		}
9151
9152		for (var i = 0, len = this._rings.length, clipped; i < len; i++) {
9153			clipped = L.PolyUtil.clipPolygon(this._rings[i], bounds, true);
9154			if (clipped.length) {
9155				this._parts.push(clipped);
9156			}
9157		}
9158	},
9159
9160	_updatePath: function () {
9161		this._renderer._updatePoly(this, true);
9162	}
9163});
9164
9165
9166// @factory L.polygon(latlngs: LatLng[], options?: Polyline options)
9167L.polygon = function (latlngs, options) {
9168	return new L.Polygon(latlngs, options);
9169};
9170
9171
9172
9173/*
9174 * L.Rectangle extends Polygon and creates a rectangle when passed a 
9174LatLngBounds object.
9175 */
9176
9177/*
9178 * @class Rectangle
9179 * @aka L.Retangle
9180 * @inherits Polygon
9181 *
9182 * A class for drawing rectangle overlays on a map. Extends `Polygon`.
9183 *
9184 * @example
9185 *
9186 * ```js
9187 * // define rectangle geographical bounds
9188 * var bounds = [[54.559322, -5.767822], [56.1210604, -3.021240]];
9189 *
9190 * // create an orange rectangle
9191 * L.rectangle(bounds, {color: "#ff7800", weight: 1}).addTo(map);
9192 *
9193 * // zoom the map to the rectangle bounds
9194 * map.fitBounds(bounds);
9195 * ```
9196 *
9197 */
9198
9199
9200L.Rectangle = L.Polygon.extend({
9201	initialize: function (latLngBounds, options) {
9202		L.Polygon.prototype.initialize.call(this, this._boundsToLatLngs(latLngBounds), options);
9203	},
9204
9205	// @method setBounds(latLngBounds: LatLngBounds): this
9206	// Redraws the rectangle with the passed bounds.
9207	setBounds: function (latLngBounds) {
9208		return this.setLatLngs(this._boundsToLatLngs(latLngBounds));
9209	},
9210
9211	_boundsToLatLngs: function (latLngBounds) {
9212		latLngBounds = L.latLngBounds(latLngBounds);
9213		return [
9214			latLngBounds.getSouthWest(),
9215			latLngBounds.getNorthWest(),
9216			latLngBounds.getNorthEast(),
9217			latLngBounds.getSouthEast()
9218		];
9219	}
9220});
9221
9222
9223// @factory L.rectangle(latLngBounds: LatLngBounds, options?: Polyline options)
9224L.rectangle = function (latLngBounds, options) {
9225	return new L.Rectangle(latLngBounds, options);
9226};
9227
9228
9229
9230/*
9231 * @class CircleMarker
9232 * @aka L.CircleMarker
9233 * @inherits Path
9234 *
9235 * A circle of a fixed size with radius specified in pixels. Extends `Path`.
9236 */
9237
9238L.CircleMarker = L.Path.extend({
9239
9240	// @section
9241	// @aka CircleMarker options
9242	options: {
9243		fill: true,
9244
9245		// @option radius: Number = 10
9246		// Radius of the circle marker, in pixels
9247		radius: 10
9248	},
9249
9250	initialize: function (latlng, options) {
9251		L.setOptions(this, options);
9252		this._latlng = L.latLng(latlng);
9253		this._radius = this.options.radius;
9254	},
9255
9256	// @method setLatLng(latLng: LatLng): this
9257	// Sets the position of a circle marker to a new location.
9258	setLatLng: function (latlng) {
9259		this._latlng = L.latLng(latlng);
9260		this.redraw();
9261		return this.fire('move', {latlng: this._latlng});
9262	},
9263
9264	// @method getLatLng(): LatLng
9265	// Returns the current geographical position of the circle marker
9266	getLatLng: function () {
9267		return this._latlng;
9268	},
9269
9270	// @method setRadius(radius: Number): this
9271	// Sets the radius of a circle marker. Units are in pixels.
9272	setRadius: function (radius) {
9273		this.options.radius = this._radius = radius;
9274		return this.redraw();
9275	},
9276
9277	// @method getRadius(): Number
9278	// Returns the current radius of the circle
9279	getRadius: function () {
9280		return this._radius;
9281	},
9282
9283	setStyle : function (options) {
9284		var radius = options && options.radius || this._radius;
9285		L.Path.prototype.setStyle.call(this, options);
9286		this.setRadius(radius);
9287		return this;
9288	},
9289
9290	_project: function () {
9291		this._point = this._map.latLngToLayerPoint(this._latlng);
9292		this._updateBounds();
9293	},
9294
9295	_updateBounds: function () {
9296		var r = this._radius,
9297		    r2 = this._radiusY || r,
9298		    w = this._clickTolerance(),
9299		    p = [r + w, r2 + w];
9300		this._pxBounds = new L.Bounds(this._point.subtract(p), this._point.add(p));
9301	},
9302
9303	_update: function () {
9304		if (this._map) {
9305			this._updatePath();
9306		}
9307	},
9308
9309	_updatePath: function () {
9310		this._renderer._updateCircle(this);
9311	},
9312
9313	_empty: function () {
9314		return this._radius && !this._renderer._bounds.intersects(this._pxBounds);
9315	}
9316});
9317
9318
9319// @factory L.circleMarker(latlng: LatLng, options?: CircleMarker options)
9320// Instantiates a circle marker object given a geographical point, and an optional options object.
9321L.circleMarker = function (latlng, options) {
9322	return new L.CircleMarker(latlng, options);
9323};
9324
9325
9326
9327/*
9328 * @class Circle
9329 * @aka L.Circle
9330 * @inherits CircleMarker
9331 *
9332 * A class for drawing circle overlays on a map. Extends `CircleMarker`.
9333 *
9334 * It's an approximation and starts to diverge from a real circle closer to poles (due to projection distortion).
9335 *
9336 * @example
9337 *
9338 * ```js
9339 * L.circle([50.5, 30.5], {radius: 200}).addTo(map);
9340 * ```
9341 */
9342
9343L.Circle = L.CircleMarker.extend({
9344
9345	initialize: function (latlng, options, legacyOptions) {
9346		if (typeof options === 'number') {
9347			// Backwards compatibility with 0.7.x factory (latlng, radius, options?)
9348			options = L.extend({}, legacyOptions, {radius: options});
9349		}
9350		L.setOptions(this, options);
9351		this._latlng = L.latLng(latlng);
9352
9353		if (isNaN(this.options.radius)) { throw new Error('Circle radius cannot be NaN'); }
9354
9355		// @section
9356		// @aka Circle options
9357		// @option radius: Number; Radius of the circle, in meters.
9358		this._mRadius = this.options.radius;
9359	},
9360
9361	// @method setRadius(radius: Number): this
9362	// Sets the radius of a circle. Units are in meters.
9363	setRadius: function (radius) {
9364		this._mRadius = radius;
9365		return this.redraw();
9366	},
9367
9368	// @method getRadius(): Number
9369	// Returns the current radius of a circle. Units are in meters.
9370	getRadius: function () {
9371		return this._mRadius;
9372	},
9373
9374	// @method getBounds(): LatLngBounds
9375	// Returns the `LatLngBounds` of the path.
9376	getBounds: function () {
9377		var half = [this._radius, this._radiusY || this._radius];
9378
9379		return new L.LatLngBounds(
9380			this._map.layerPointToLatLng(this._point.subtract(half)),
9381			this._map.layerPointToLatLng(this._point.add(half)));
9382	},
9383
9384	setStyle: L.Path.prototype.setStyle,
9385
9386	_project: function () {
9387
9388		var lng = this._latlng.lng,
9389		    lat = this._latlng.lat,
9390		    map = this._map,
9391		    crs = map.options.crs;
9392
9393		if (crs.distance === L.CRS.Earth.distance) {
9394			var d = Math.PI / 180,
9395			    latR = (this._mRadius / L.CRS.Earth.R) / d,
9396			    top = map.project([lat + latR, lng]),
9397			    bottom = map.project([lat - latR, lng]),
9398			    p = top.add(bottom).divideBy(2),
9399			    lat2 = map.unproject(p).lat,
9400			    lngR = Math.acos((Math.cos(latR * d) - Math.sin(lat * d) * Math.sin(lat2 * d)) /
9401			            (Math.cos(lat * d) * Math.cos(lat2 * d))) / d;
9402
9403			if (isNaN(lngR) || lngR === 0) {
9404				lngR = latR / Math.cos(Math.PI / 180 * lat); // Fallback for edge case, #2425
9405			}
9406
9407			this._point = p.subtract(map.getPixelOrigin());
9408			this._radius = isNaN(lngR) ? 0 : Math.max(Math.round(p.x - map.project([lat2, lng - lngR]).x), 1);
9409			this._radiusY = Math.max(Math.round(p.y - top.y), 1);
9410
9411		} else {
9412			var latlng2 = crs.unproject(crs.project(this._latlng).subtract([this._mRadius, 0]));
9413
9414			this._point = map.latLngToLayerPoint(this._latlng);
9415			this._radius = this._point.x - map.latLngToLayerPoint(latlng2).x;
9416		}
9417
9418		this._updateBounds();
9419	}
9420});
9421
9422// @factory L.circle(latlng: LatLng, options?: Circle options)
9423// Instantiates a circle object given a geographical point, and an options object
9424// which contains the circle radius.
9425// @alternative
9426// @factory L.circle(latlng: LatLng, radius: Number, options?: Circle options)
9427// Obsolete way of instantiating a circle, for compatibility with 0.7.x code.
9428// Do not use in new applications or plugins.
9429L.circle = function (latlng, options, legacyOptions) {
9430	return new L.Circle(latlng, options, legacyOptions);
9431};
9432
9433
9434
9435/*
9436 * @class SVG
9437 * @inherits Renderer
9438 * @aka L.SVG
9439 *
9440 * Allows vector layers to be displayed with [SVG](https://developer.mozilla.org/docs/Web/SVG).
9441 * Inherits `Renderer`.
9442 *
9443 * Due to [technical limitations](http://caniuse.com/#search=svg), SVG is not
9444 * available in all web browsers, notably Android 2.x and 3.x.
9445 *
9446 * Although SVG is not available on IE7 and IE8, these browsers support
9447 * [VML](https://en.wikipedia.org/wiki/Vector_Markup_Language)
9448 * (a now deprecated technology), and the SVG renderer will fall back to VML in
9449 * this case.
9450 *
9451 * @example
9452 *
9453 * Use SVG by default for all paths in the map:
9454 *
9455 * ```js
9456 * var map = L.map('map', {
9457 * 	renderer: L.svg()
9458 * });
9459 * ```
9460 *
9461 * Use a SVG renderer with extra padding for specific vector geometries:
9462 *
9463 * ```js
9464 * var map = L.map('map');
9465 * var myRenderer = L.svg({ padding: 0.5 });
9466 * var line = L.polyline( coordinates, { renderer: myRenderer } );
9467 * var circle = L.circle( center, { renderer: myRenderer } );
9468 * ```
9469 */
9470
9471L.SVG = L.Renderer.extend({
9472
9473	getEvents: function () {
9474		var events = L.Renderer.prototype.getEvents.call(this);
9475		events.zoomstart = this._onZoomStart;
9476		return events;
9477	},
9478
9479	_initContainer: function () {
9480		this._container = L.SVG.create('svg');
9481
9482		// makes it possible to click through svg root; we'll reset it back in individual paths
9483		this._container.setAttribute('pointer-events', 'none');
9484
9485		this._rootGroup = L.SVG.create('g');
9486		this._container.appendChild(this._rootGroup);
9487	},
9488
9489	_onZoomStart: function () {
9490		// Drag-then-pinch interactions might mess up the center and zoom.
9491		// In this case, the easiest way to prevent this is re-do the renderer
9492		//   bounds and padding when the zooming starts.
9493		this._update();
9494	},
9495
9496	_update: function () {
9497		if (this._map._animatingZoom && this._bounds) { return; }
9498
9499		L.Renderer.prototype._update.call(this);
9500
9501		var b = this._bounds,
9502		    size = b.getSize(),
9503		    container = this._container;
9504
9505		// set size of svg-container if changed
9506		if (!this._svgSize || !this._svgSize.equals(size)) {
9507			this._svgSize = size;
9508			container.setAttribute('width', size.x);
9509			container.setAttribute('height', size.y);
9510		}
9511
9512		// movement: update container viewBox so that we don't have to change coordinates of individual layers
9513		L.DomUtil.setPosition(container, b.min);
9514		container.setAttribute('viewBox', [b.min.x, b.min.y, size.x, size.y].join(' '));
9515
9516		this.fire('update');
9517	},
9518
9519	// methods below are called by vector layers implementations
9520
9521	_initPath: function (layer) {
9522		var path = layer._path = L.SVG.create('path');
9523
9524		// @namespace Path
9525		// @option className: String = null
9526		// Custom class name set on an element. Only for SVG renderer.
9527		if (layer.options.className) {
9528			L.DomUtil.addClass(path, layer.options.className);
9529		}
9530
9531		if (layer.options.interactive) {
9532			L.DomUtil.addClass(path, 'leaflet-interactive');
9533		}
9534
9535		this._updateStyle(layer);
9536		this._layers[L.stamp(layer)] = layer;
9537	},
9538
9539	_addPath: function (layer) {
9540		this._rootGroup.appendChild(layer._path);
9541		layer.addInteractiveTarget(layer._path);
9542	},
9543
9544	_removePath: function (layer) {
9545		L.DomUtil.remove(layer._path);
9546		layer.removeInteractiveTarget(layer._path);
9547		delete this._layers[L.stamp(layer)];
9548	},
9549
9550	_updatePath: function (layer) {
9551		layer._project();
9552		layer._update();
9553	},
9554
9555	_updateStyle: function (layer) {
9556		var path = layer._path,
9557		    options = layer.options;
9558
9559		if (!path) { return; }
9560
9561		if (options.stroke) {
9562			path.setAttribute('stroke', options.color);
9563			path.setAttribute('stroke-opacity', options.opacity);
9564			path.setAttribute('stroke-width', options.weight);
9565			path.setAttribute('stroke-linecap', options.lineCap);
9566			path.setAttribute('stroke-linejoin', options.lineJoin);
9567
9568			if (options.dashArray) {
9569				path.setAttribute('stroke-dasharray', options.dashArray);
9570			} else {
9571				path.removeAttribute('stroke-dasharray');
9572			}
9573
9574			if (options.dashOffset) {
9575				path.setAttribute('stroke-dashoffset', options.dashOffset);
9576			} else {
9577				path.removeAttribute('stroke-dashoffset');
9578			}
9579		} else {
9580			path.setAttribute('stroke', 'none');
9581		}
9582
9583		if (options.fill) {
9584			path.setAttribute('fill', options.fillColor || options.color);
9585			path.setAttribute('fill-opacity', options.fillOpacity);
9586			path.setAttribute('fill-rule', options.fillRule || 'evenodd');
9587		} else {
9588			path.setAttribute('fill', 'none');
9589		}
9590	},
9591
9592	_updatePoly: function (layer, closed) {
9593		this._setPath(layer, L.SVG.pointsToPath(layer._parts, closed));
9594	},
9595
9596	_updateCircle: function (layer) {
9597		var p = layer._point,
9598		    r = layer._radius,
9599		    r2 = layer._radiusY || r,
9600		    arc = 'a' + r + ',' + r2 + ' 0 1,0 ';
9601
9602		// drawing a circle with two half-arcs
9603		var d = layer._empty() ? 'M0 0' :
9604				'M' + (p.x - r) + ',' + p.y +
9605				arc + (r * 2) + ',0 ' +
9606				arc + (-r * 2) + ',0 ';
9607
9608		this._setPath(layer, d);
9609	},
9610
9611	_setPath: function (layer, path) {
9612		layer._path.setAttribute('d', path);
9613	},
9614
9615	// SVG does not have the concept of zIndex so we resort to changing the DOM order of elements
9616	_bringToFront: function (layer) {
9617		L.DomUtil.toFront(layer._path);
9618	},
9619
9620	_bringToBack: function (layer) {
9621		L.DomUtil.toBack(layer._path);
9622	}
9623});
9624
9625
9626// @namespace SVG; @section
9627// There are several static functions which can be called without instantiating L.SVG:
9628L.extend(L.SVG, {
9629	// @function create(name: String): SVGElement
9630	// Returns a instance of [SVGElement](https://developer.mozilla.org/docs/Web/API/SVGElement),
9631	// corresponding to the class name passed. For example, using 'line' will return
9632	// an instance of [SVGLineElement](https://developer.mozilla.org/docs/Web/API/SVGLineElement).
9633	create: function (name) {
9634		return document.createElementNS('http://www.w3.org/2000/svg', name);
9635	},
9636
9637	// @function pointsToPath(rings: Point[], closed: Boolean): String
9638	// Generates a SVG path string for multiple rings, with each ring turning
9639	// into "M..L..L.." instructions
9640	pointsToPath: function (rings, closed) {
9641		var str = '',
9642		    i, j, len, len2, points, p;
9643
9644		for (i = 0, len = rings.length; i < len; i++) {
9645			points = rings[i];
9646
9647			for (j = 0, len2 = points.length; j < len2; j++) {
9648				p = points[j];
9649				str += (j ? 'L' : 'M') + p.x + ' ' + p.y;
9650			}
9651
9652			// closes the ring for polygons; "x" is VML syntax
9653			str += closed ? (L.Browser.svg ? 'z' : 'x') : '';
9654		}
9655
9656		// SVG complains about empty path strings
9657		return str || 'M0 0';
9658	}
9659});
9660
9661// @namespace Browser; @property svg: Boolean
9662// `true` when the browser supports [SVG](https://developer.mozilla.org/docs/Web/SVG).
9663L.Browser.svg = !!(document.createElementNS && L.SVG.create('svg').createSVGRect);
9664
9665
9666// @namespace SVG
9667// @factory L.svg(options?: Renderer options)
9668// Creates a SVG renderer with the given options.
9669L.svg = function (options) {
9670	return L.Browser.svg || L.Browser.vml ? new L.SVG(options) : null;
9671};
9672
9673
9674
9675/*
9676 * Thanks to Dmitry Baranovsky and his Raphael library for inspiration!
9677 */
9678
9679/*
9680 * @class SVG
9681 *
9682 * 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.
9683 *
9684 * VML was deprecated in 2012, which means VML functionality exists only for backwards compatibility
9685 * with old versions of Internet Explorer.
9686 */
9687
9688// @namespace Browser; @property vml: Boolean
9689// `true` if the browser supports [VML](https://en.wikipedia.org/wiki/Vector_Markup_Language).
9690L.Browser.vml = !L.Browser.svg && (function () {
9691	try {
9692		var div = document.createElement('div');
9693		div.innerHTML = '<v:shape adj="1"/>';
9694
9695		var shape = div.firstChild;
9696		shape.style.behavior = 'url(#default#VML)';
9697
9698		return shape && (typeof shape.adj === 'object');
9699
9700	} catch (e) {
9701		return false;
9702	}
9703}());
9704
9705// redefine some SVG methods to handle VML syntax which is similar but with some differences
9706L.SVG.include(!L.Browser.vml ? {} : {
9707
9708	_initContainer: function () {
9709		this._container = L.DomUtil.create('div', 'leaflet-vml-container');
9710	},
9711
9712	_update: function () {
9713		if (this._map._animatingZoom) { return; }
9714		L.Renderer.prototype._update.call(this);
9715		this.fire('update');
9716	},
9717
9718	_initPath: function (layer) {
9719		var container = layer._container = L.SVG.create('shape');
9720
9721		L.DomUtil.addClass(container, 'leaflet-vml-shape ' + (this.options.className || ''));
9722
9723		container.coordsize = '1 1';
9724
9725		layer._path = L.SVG.create('path');
9726		container.appendChild(layer._path);
9727
9728		this._updateStyle(layer);
9729		this._layers[L.stamp(layer)] = layer;
9730	},
9731
9732	_addPath: function (layer) {
9733		var container = layer._container;
9734		this._container.appendChild(container);
9735
9736		if (layer.options.interactive) {
9737			layer.addInteractiveTarget(container);
9738		}
9739	},
9740
9741	_removePath: function (layer) {
9742		var container = layer._container;
9743		L.DomUtil.remove(container);
9744		layer.removeInteractiveTarget(container);
9745		delete this._layers[L.stamp(layer)];
9746	},
9747
9748	_updateStyle: function (layer) {
9749		var stroke = layer._stroke,
9750		    fill = layer._fill,
9751		    options = layer.options,
9752		    container = layer._container;
9753
9754		container.stroked = !!options.stroke;
9755		container.filled = !!options.fill;
9756
9757		if (options.stroke) {
9758			if (!stroke) {
9759				stroke = layer._stroke = L.SVG.create('stroke');
9760			}
9761			container.appendChild(stroke);
9762			stroke.weight = options.weight + 'px';
9763			stroke.color = options.color;
9764			stroke.opacity = options.opacity;
9765
9766			if (options.dashArray) {
9767				stroke.dashStyle = L.Util.isArray(options.dashArray) ?
9768				    options.dashArray.join(' ') :
9769				    options.dashArray.replace(/( *, *)/g, ' ');
9770			} else {
9771				stroke.dashStyle = '';
9772			}
9773			stroke.endcap = options.lineCap.replace('butt', 'flat');
9774			stroke.joinstyle = options.lineJoin;
9775
9776		} else if (stroke) {
9777			container.removeChild(stroke);
9778			layer._stroke = null;
9779		}
9780
9781		if (options.fill) {
9782			if (!fill) {
9783				fill = layer._fill = L.SVG.create('fill');
9784			}
9785			container.appendChild(fill);
9786			fill.color = options.fillColor || options.color;
9787			fill.opacity = options.fillOpacity;
9788
9789		} else if (fill) {
9790			container.removeChild(fill);
9791			layer._fill = null;
9792		}
9793	},
9794
9795	_updateCircle: function (layer) {
9796		var p = layer._point.round(),
9797		    r = Math.round(layer._radius),
9798		    r2 = Math.round(layer._radiusY || r);
9799
9800		this._setPath(layer, layer._empty() ? 'M0 0' :
9801				'AL ' + p.x + ',' + p.y + ' ' + r + ',' + r2 + ' 0,' + (65535 * 360));
9802	},
9803
9804	_setPath: function (layer, path) {
9805		layer._path.v = path;
9806	},
9807
9808	_bringToFront: function (layer) {
9809		L.DomUtil.toFront(layer._container);
9810	},
9811
9812	_bringToBack: function (layer) {
9813		L.DomUtil.toBack(layer._container);
9814	}
9815});
9816
9817if (L.Browser.vml) {
9818	L.SVG.create = (function () {
9819		try {
9820			document.namespaces.add('lvml', 'urn:schemas-microsoft-com:vml');
9821			return function (name) {
9822				return document.createElement('<lvml:' + name + ' class="lvml">');
9823			};
9824		} catch (e) {
9825			return function (name) {
9826				return document.createElement('<' + name + ' xmlns="urn:schemas-microsoft.com:vml" class="lvml">');
9827			};
9828		}
9829	})();
9830}
9831
9832
9833
9834/*
9835 * @class Canvas
9836 * @inherits Renderer
9837 * @aka L.Canvas
9838 *
9839 * Allows vector layers to be displayed with [`<canvas>`](https://developer.mozilla.org/docs/Web/API/Canvas_API).
9840 * Inherits `Renderer`.
9841 *
9842 * Due to [technical limitations](http://caniuse.com/#search=canvas), Canvas is not
9843 * available in all web browsers, notably IE8, and overlapping geometries might
9844 * not display properly in some edge cases.
9845 *
9846 * @example
9847 *
9848 * Use Canvas by default for all paths in the map:
9849 *
9850 * ```js
9851 * var map = L.map('map', {
9852 * 	renderer: L.canvas()
9853 * });
9854 * ```
9855 *
9856 * Use a Canvas renderer with extra padding for specific vector geometries:
9857 *
9858 * ```js
9859 * var map = L.map('map');
9860 * var myRenderer = L.canvas({ padding: 0.5 });
9861 * var line = L.polyline( coordinates, { renderer: myRenderer } );
9862 * var circle = L.circle( center, { renderer: myRenderer } );
9863 * ```
9864 */
9865
9866L.Canvas = L.Renderer.extend({
9867	getEvents: function () {
9868		var events = L.Renderer.prototype.getEvents.call(this);
9869		events.viewprereset = this._onViewPreReset;
9870		return events;
9871	},
9872
9873	_onViewPreReset: function () {
9874		// Set a flag so that a viewprereset+moveend+viewreset only updates&redraws once
9875		this._postponeUpdatePaths = true;
9876	},
9877
9878	onAdd: function () {
9879		L.Renderer.prototype.onAdd.call(this);
9880
9881		// Redraw vectors since canvas is cleared upon removal,
9882		// in case of removing the renderer itself from the map.
9883		this._draw();
9884	},
9885
9886	_initContainer: function () {
9887		var container = this._container = document.createElement('canvas');
9888
9889		L.DomEvent
9890			.on(container, 'mousemove', L.Util.throttle(this._onMouseMove, 32, this), this)
9891			.on(container, 'click dblclick mousedown mouseup contextmenu', this._onClick, this)
9892			.on(container, 'mouseout', this._handleMouseOut, this);
9893
9894		this._ctx = container.getContext('2d');
9895	},
9896
9897	_updatePaths: function () {
9898		if (this._postponeUpdatePaths) { return; }
9899
9900		var layer;
9901		this._redrawBounds = null;
9902		for (var id in this._layers) {
9903			layer = this._layers[id];
9904			layer._update();
9905		}
9906		this._redraw();
9907	},
9908
9909	_update: function () {
9910		if (this._map._animatingZoom && this._bounds) { return; }
9911
9912		this._drawnLayers = {};
9913
9914		L.Renderer.prototype._update.call(this);
9915
9916		var b = this._bounds,
9917		    container = this._container,
9918		    size = b.getSize(),
9919		    m = L.Browser.retina ? 2 : 1;
9920
9921		L.DomUtil.setPosition(container, b.min);
9922
9923		// set canvas size (also clearing it); use double size on retina
9924		container.width = m * size.x;
9925		container.height = m * size.y;
9926		container.style.width = size.x + 'px';
9927		container.style.height = size.y + 'px';
9928
9929		if (L.Browser.retina) {
9930			this._ctx.scale(2, 2);
9931		}
9932
9933		// translate so we use the same path coordinates after canvas element moves
9934		this._ctx.translate(-b.min.x, -b.min.y);
9935
9936		// Tell paths to redraw themselves
9937		this.fire('update');
9938	},
9939
9940	_reset: function () {
9941		L.Renderer.prototype._reset.call(this);
9942
9943		if (this._postponeUpdatePaths) {
9944			this._postponeUpdatePaths = false;
vendor: 4,215 bytes, lines 9945-10132
9945			this._updatePaths();
9946		}
9947	},
9948
9949	_initPath: function (layer) {
9950		this._updateDashArray(layer);
9951		this._layers[L.stamp(layer)] = layer;
9952
9953		var order = layer._order = {
9954			layer: layer,
9955			prev: this._drawLast,
9956			next: null
9957		};
9958		if (this._drawLast) { this._drawLast.next = order; }
9959		this._drawLast = order;
9960		this._drawFirst = this._drawFirst || this._drawLast;
9961	},
9962
9963	_addPath: function (layer) {
9964		this._requestRedraw(layer);
9965	},
9966
9967	_removePath: function (layer) {
9968		var order = layer._order;
9969		var next = order.next;
9970		var prev = order.prev;
9971
9972		if (next) {
9973			next.prev = prev;
9974		} else {
9975			this._drawLast = prev;
9976		}
9977		if (prev) {
9978			prev.next = next;
9979		} else {
9980			this._drawFirst = next;
9981		}
9982
9983		delete layer._order;
9984
9985		delete this._layers[L.stamp(layer)];
9986
9987		this._requestRedraw(layer);
9988	},
9989
9990	_updatePath: function (layer) {
9991		// Redraw the union of the layer's old pixel
9992		// bounds and the new pixel bounds.
9993		this._extendRedrawBounds(layer);
9994		layer._project();
9995		layer._update();
9996		// The redraw will extend the redraw bounds
9997		// with the new pixel bounds.
9998		this._requestRedraw(layer);
9999	},
10000
10001	_updateStyle: function (layer) {
10002		this._updateDashArray(layer);
10003		this._requestRedraw(layer);
10004	},
10005
10006	_updateDashArray: function (layer) {
10007		if (layer.options.dashArray) {
10008			var parts = layer.options.dashArray.split(','),
10009			    dashArray = [],
10010			    i;
10011			for (i = 0; i < parts.length; i++) {
10012				dashArray.push(Number(parts[i]));
10013			}
10014			layer.options._dashArray = dashArray;
10015		}
10016	},
10017
10018	_requestRedraw: function (layer) {
10019		if (!this._map) { return; }
10020
10021		this._extendRedrawBounds(layer);
10022		this._redrawRequest = this._redrawRequest || L.Util.requestAnimFrame(this._redraw, this);
10023	},
10024
10025	_extendRedrawBounds: function (layer) {
10026		var padding = (layer.options.weight || 0) + 1;
10027		this._redrawBounds = this._redrawBounds || new L.Bounds();
10028		this._redrawBounds.extend(layer._pxBounds.min.subtract([padding, padding]));
10029		this._redrawBounds.extend(layer._pxBounds.max.add([padding, padding]));
10030	},
10031
10032	_redraw: function () {
10033		this._redrawRequest = null;
10034
10035		if (this._redrawBounds) {
10036			this._redrawBounds.min._floor();
10037			this._redrawBounds.max._ceil();
10038		}
10039
10040		this._clear(); // clear layers in redraw bounds
10041		this._draw(); // draw layers
10042
10043		this._redrawBounds = null;
10044	},
10045
10046	_clear: function () {
10047		var bounds = this._redrawBounds;
10048		if (bounds) {
10049			var size = bounds.getSize();
10050			this._ctx.clearRect(bounds.min.x, bounds.min.y, size.x, size.y);
10051		} else {
10052			this._ctx.clearRect(0, 0, this._container.width, this._container.height);
10053		}
10054	},
10055
10056	_draw: function () {
10057		var layer, bounds = this._redrawBounds;
10058		this._ctx.save();
10059		if (bounds) {
10060			var size = bounds.getSize();
10061			this._ctx.beginPath();
10062			this._ctx.rect(bounds.min.x, bounds.min.y, size.x, size.y);
10063			this._ctx.clip();
10064		}
10065
10066		this._drawing = true;
10067
10068		for (var order = this._drawFirst; order; order = order.next) {
10069			layer = order.layer;
10070			if (!bounds || (layer._pxBounds && layer._pxBounds.intersects(bounds))) {
10071				layer._updatePath();
10072			}
10073		}
10074
10075		this._drawing = false;
10076
10077		this._ctx.restore();  // Restore state before clipping.
10078	},
10079
10080	_updatePoly: function (layer, closed) {
10081		if (!this._drawing) { return; }
10082
10083		var i, j, len2, p,
10084		    parts = layer._parts,
10085		    len = parts.length,
10086		    ctx = this._ctx;
10087
10088		if (!len) { return; }
10089
10090		this._drawnLayers[layer._leaflet_id] = layer;
10091
10092		ctx.beginPath();
10093
10094		if (ctx.setLineDash) {
10095			ctx.setLineDash(layer.options && layer.options._dashArray || []);
10096		}
10097
10098		for (i = 0; i < len; i++) {
10099			for (j = 0, len2 = parts[i].length; j < len2; j++) {
10100				p = parts[i][j];
10101				ctx[j ? 'lineTo' : 'moveTo'](p.x, p.y);
10102			}
10103			if (closed) {
10104				ctx.closePath();
10105			}
10106		}
10107
10108		this._fillStroke(ctx, layer);
10109
10110		// TODO optimization: 1 fill/stroke for all features with equal style instead of 1 for each feature
10111	},
10112
10113	_updateCircle: function (layer) {
10114
10115		if (!this._drawing || layer._empty()) { return; }
10116
10117		var p = layer._point,
10118		    ctx = this._ctx,
10119		    r = layer._radius,
10120		    s = (layer._radiusY || r) / r;
10121
10122		this._drawnLayers[layer._leaflet_id] = layer;
10123
10124		if (s !== 1) {
10125			ctx.save();
10126			ctx.scale(1, s);
10127		}
10128
10129		ctx.beginPath();
10130		ctx.arc(p.x, p.y / s, r, 0, Math.PI * 2, false);
10131
10132		if (s !== 1) {
10133			ctx.restore();
10134		}
10135
10136		this._fillStroke(ctx, layer);
10137	},
10138
10139	_fillStroke: function (ctx, layer) {
10140		var options = layer.options;
10141
10142		if (options.fill) {
10143			ctx.globalAlpha = options.fillOpacity;
10144			ctx.fillStyle = options.fillColor || options.color;
10145			ctx.fill(options.fillRule || 'evenodd');
10146		}
10147
10148		if (options.stroke && options.weight !== 0) {
10149			ctx.globalAlpha = options.opacity;
10150			ctx.lineWidth = options.weight;
10151			ctx.strokeStyle = options.color;
10152			ctx.lineCap = options.lineCap;
10153			ctx.lineJoin = options.lineJoin;
10154			ctx.stroke();
10155		}
10156	},
10157
10158	// Canvas obviously doesn't have mouse events for individual drawn objects,
10159	// so we emulate that by calculating what's under the mouse on mousemove/click manually
10160
10161	_onClick: function (e) {
10162		var point = this._map.mouseEventToLayerPoint(e), layer, clickedLayer;
10163
10164		for (var order = this._drawFirst; order; order = order.next) {
10165			layer = order.layer;
10166			if (layer.options.interactive && layer._containsPoint(point) && !this._map._draggableMoved(layer)) {
10167				clickedLayer = layer;
10168			}
10169		}
10170		if (clickedLayer)  {
10171			L.DomEvent._fakeStop(e);
10172			this._fireEvent([clickedLayer], e);
10173		}
10174	},
10175
10176	_onMouseMove: function (e) {
10177		if (!this._map || this._map.dragging.moving() || this._map._animatingZoom) { return; }
10178
10179		var point = this._map.mouseEventToLayerPoint(e);
10180		this._handleMouseHover(e, point);
10181	},
10182
10183
10184	_handleMouseOut: function (e) {
10185		var layer = this._hoveredLayer;
10186		if (layer) {
10187			// if we're leaving the layer, fire mouseout
10188			L.DomUtil.removeClass(this._container, 'leaflet-interactive');
10189			this._fireEvent([layer], e, 'mouseout');
10190			this._hoveredLayer = null;
10191		}
10192	},
10193
10194	_handleMouseHover: function (e, point) {
10195		var layer, candidateHoveredLayer;
10196
10197		for (var order = this._drawFirst; order; order = order.next) {
10198			layer = order.layer;
10199			if (layer.options.interactive && layer._containsPoint(point)) {
10200				candidateHoveredLayer = layer;
10201			}
10202		}
10203
10204		if (candidateHoveredLayer !== this._hoveredLayer) {
10205			this._handleMouseOut(e);
10206
10207			if (candidateHoveredLayer) {
10208				L.DomUtil.addClass(this._container, 'leaflet-interactive'); // change cursor
10209				this._fireEvent([candidateHoveredLayer], e, 'mouseover');
10210				this._hoveredLayer = candidateHoveredLayer;
10211			}
10212		}
10213
10214		if (this._hoveredLayer) {
10215			this._fireEvent([this._hoveredLayer], e);
10216		}
10217	},
10218
10219	_fireEvent: function (layers, e, type) {
10220		this._map._fireDOMEvent(e, type || e.type, layers);
10221	},
10222
10223	_bringToFront: function (layer) {
10224		var order = layer._order;
10225		var next = order.next;
10226		var prev = order.prev;
10227
10228		if (next) {
10229			next.prev = prev;
10230		} else {
10231			// Already last
10232			return;
10233		}
10234		if (prev) {
10235			prev.next = next;
10236		} else if (next) {
10237			// Update first entry unless this is the
10238			// signle entry
10239			this._drawFirst = next;
10240		}
10241
10242		order.prev = this._drawLast;
10243		this._drawLast.next = order;
10244
10245		order.next = null;
10246		this._drawLast = order;
10247
10248		this._requestRedraw(layer);
10249	},
10250
10251	_bringToBack: function (layer) {
10252		var order = layer._order;
10253		var next = order.next;
10254		var prev = order.prev;
10255
10256		if (prev) {
10257			prev.next = next;
10258		} else {
10259			// Already first
10260			return;
10261		}
10262		if (next) {
10263			next.prev = prev;
10264		} else if (prev) {
10265			// Update last entry unless this is the
10266			// signle entry
10267			this._drawLast = prev;
10268		}
10269
10270		order.prev = null;
10271
10272		order.next = this._drawFirst;
10273		this._drawFirst.prev = order;
10274		this._drawFirst = order;
10275
10276		this._requestRedraw(layer);
10277	}
10278});
10279
10280// @namespace Browser; @property canvas: Boolean
10281// `true` when the browser supports [`<canvas>`](https://developer.mozilla.org/docs/Web/API/Canvas_API).
10282L.Browser.canvas = (function () {
10283	return !!document.createElement('canvas').getContext;
10284}());
10285
10286// @namespace Canvas
10287// @factory L.canvas(options?: Renderer options)
10288// Creates a Canvas renderer with the given options.
10289L.canvas = function (options) {
10290	return L.Browser.canvas ? new L.Canvas(options) : null;
10291};
10292
10293L.Polyline.prototype._containsPoint = function (p, closed) {
10294	var i, j, k, len, len2, part,
10295	    w = this._clickTolerance();
10296
10297	if (!this._pxBounds.contains(p)) { return false; }
10298
10299	// hit detection for polylines
10300	for (i = 0, len = this._parts.length; i < len; i++) {
10301		part = this._parts[i];
10302
10303		for (j = 0, len2 = part.length, k = len2 - 1; j < len2; k = j++) {
10304			if (!closed && (j === 0)) { continue; }
10305
10306			if (L.LineUtil.pointToSegmentDistance(p, part[k], part[j]) <= w) {
10307				return true;
10308			}
10309		}
10310	}
10311	return false;
vendor: 4,146 bytes, lines 10312-10461
10312};
10313
10314L.Polygon.prototype._containsPoint = function (p) {
10315	var inside = false,
10316	    part, p1, p2, i, j, k, len, len2;
10317
10318	if (!this._pxBounds.contains(p)) { return false; }
10319
10320	// ray casting algorithm for detecting if point is in polygon
10321	for (i = 0, len = this._parts.length; i < len; i++) {
10322		part = this._parts[i];
10323
10324		for (j = 0, len2 = part.length, k = len2 - 1; j < len2; k = j++) {
10325			p1 = part[j];
10326			p2 = part[k];
10327
10328			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)) {
10329				inside = !inside;
10330			}
10331		}
10332	}
10333
10334	// also check if it's on polygon stroke
10335	return inside || L.Polyline.prototype._containsPoint.call(this, p, true);
10336};
10337
10338L.CircleMarker.prototype._containsPoint = function (p) {
10339	return p.distanceTo(this._point) <= this._radius + this._clickTolerance();
10340};
10341
10342
10343
10344/*
10345 * @class GeoJSON
10346 * @aka L.GeoJSON
10347 * @inherits FeatureGroup
10348 *
10349 * Represents a GeoJSON object or an array of GeoJSON objects. Allows you to parse
10350 * GeoJSON data and display it on the map. Extends `FeatureGroup`.
10351 *
10352 * @example
10353 *
10354 * ```js
10355 * L.geoJSON(data, {
10356 * 	style: function (feature) {
10357 * 		return {color: feature.properties.color};
10358 * 	}
10359 * }).bindPopup(function (layer) {
10360 * 	return layer.feature.properties.description;
10361 * }).addTo(map);
10362 * ```
10363 */
10364
10365L.GeoJSON = L.FeatureGroup.extend({
10366
10367	/* @section
10368	 * @aka GeoJSON options
10369	 *
10370	 * @option pointToLayer: Function = *
10371	 * A `Function` defining how GeoJSON points spawn Leaflet layers. It is internally
10372	 * called when data is added, passing the GeoJSON point feature and its `LatLng`.
10373	 * The default is to spawn a default `Marker`:
10374	 * ```js
10375	 * function(geoJsonPoint, latlng) {
10376	 * 	return L.marker(latlng);
10377	 * }
10378	 * ```
10379	 *
10380	 * @option style: Function = *
10381	 * A `Function` defining the `Path options` for styling GeoJSON lines and polygons,
10382	 * called internally when data is added.
10383	 * The default value is to not override any defaults:
10384	 * ```js
10385	 * function (geoJsonFeature) {
10386	 * 	return {}
10387	 * }
10388	 * ```
10389	 *
10390	 * @option onEachFeature: Function = *
10391	 * A `Function` that will be called once for each created `Feature`, after it has
10392	 * been created and styled. Useful for attaching events and popups to features.
10393	 * The default is to do nothing with the newly created layers:
10394	 * ```js
10395	 * function (feature, layer) {}
10396	 * ```
10397	 *
10398	 * @option filter: Function = *
10399	 * A `Function` that will be used to decide whether to include a feature or not.
10400	 * The default is to include all features:
10401	 * ```js
10402	 * function (geoJsonFeature) {
10403	 * 	return true;
10404	 * }
10405	 * ```
10406	 * Note: dynamically changing the `filter` option will have effect only on newly
10407	 * added data. It will _not_ re-evaluate already included features.
10408	 *
10409	 * @option coordsToLatLng: Function = *
10410	 * A `Function` that will be used for converting GeoJSON coordinates to `LatLng`s.
10411	 * The default is the `coordsToLatLng` static method.
10412	 */
10413
10414	initialize: function (geojson, options) {
10415		L.setOptions(this, options);
10416
10417		this._layers = {};
10418
10419		if (geojson) {
10420			this.addData(geojson);
10421		}
10422	},
10423
10424	// @method addData( <GeoJSON> data ): this
10425	// Adds a GeoJSON object to the layer.
10426	addData: function (geojson) {
10427		var features = L.Util.isArray(geojson) ? geojson : geojson.features,
10428		    i, len, feature;
10429
10430		if (features) {
10431			for (i = 0, len = features.length; i < len; i++) {
10432				// only add this if geometry or geometries are set and not null
10433				feature = features[i];
10434				if (feature.geometries || feature.geometry || feature.features || feature.coordinates) {
10435					this.addData(feature);
10436				}
10437			}
10438			return this;
10439		}
10440
10441		var options = this.options;
10442
10443		if (options.filter && !options.filter(geojson)) { return this; }
10444
10445		var layer = L.GeoJSON.geometryToLayer(geojson, options);
10446		if (!layer) {
10447			return this;
10448		}
10449		layer.feature = L.GeoJSON.asFeature(geojson);
10450
10451		layer.defaultOptions = layer.options;
10452		this.resetStyle(layer);
10453
10454		if (options.onEachFeature) {
10455			options.onEachFeature(geojson, layer);
10456		}
10457
10458		return this.addLayer(layer);
10459	},
10460
10461	// @method resetStyle( <Path>
10461 layer ): this
10462	// Resets the given vector layer's style to the original GeoJSON style, useful for resetting style after hover events.
10463	resetStyle: function (layer) {
10464		// reset any custom styles
10465		layer.options = L.Util.extend({}, layer.defaultOptions);
10466		this._setLayerStyle(layer, this.options.style);
10467		return this;
10468	},
10469
10470	// @method setStyle( <Function> style ): this
10471	// Changes styles of GeoJSON vector layers with the given style function.
10472	setStyle: function (style) {
10473		return this.eachLayer(function (layer) {
10474			this._setLayerStyle(layer, style);
10475		}, this);
10476	},
10477
10478	_setLayerStyle: function (layer, style) {
10479		if (typeof style === 'function') {
10480			style = style(layer.feature);
10481		}
10482		if (layer.setStyle) {
10483			layer.setStyle(style);
10484		}
10485	}
10486});
10487
10488// @section
10489// There are several static functions which can be called without instantiating L.GeoJSON:
10490L.extend(L.GeoJSON, {
10491	// @function geometryToLayer(featureData: Object, options?: GeoJSON options): Layer
10492	// Creates a `Layer` from a given GeoJSON feature. Can use a custom
10493	// [`pointToLayer`](#geojson-pointtolayer) and/or [`coordsToLatLng`](#geojson-coordstolatlng)
10494	// functions if provided as options.
10495	geometryToLayer: function (geojson, options) {
10496
10497		var geometry = geojson.type === 'Feature' ? geojson.geometry : geojson,
10498		    coords = geometry ? geometry.coordinates : null,
10499		    layers = [],
10500		    pointToLayer = options && options.pointToLayer,
10501		    coordsToLatLng = options && options.coordsToLatLng || this.coordsToLatLng,
10502		    latlng, latlngs, i, len;
10503
10504		if (!coords && !geometry) {
10505			return null;
10506		}
10507
10508		switch (geometry.type) {
10509		case 'Point':
10510			latlng = coordsToLatLng(coords);
10511			return pointToLayer ? pointToLayer(geojson, latlng) : new L.Marker(latlng);
10512
10513		case 'MultiPoint':
10514			for (i = 0, len = coords.length; i < len; i++) {
10515				latlng = coordsToLatLng(coords[i]);
10516				layers.push(pointToLayer ? pointToLayer(geojson, latlng) : new L.Marker(latlng));
10517			}
10518			return new L.FeatureGroup(layers);
10519
10520		case 'LineString':
10521		case 'MultiLineString':
10522			latlngs = this.coordsToLatLngs(coords, geometry.type === 'LineString' ? 0 : 1, coordsToLatLng);
10523			return new L.Polyline(latlngs, options);
10524
10525		case 'Polygon':
10526		case 'MultiPolygon':
10527			latlngs = this.coordsToLatLngs(coords, geometry.type === 'Polygon' ? 1 : 2, coordsToLatLng);
10528			return new L.Polygon(latlngs, options);
10529
10530		case 'GeometryCollection':
10531			for (i = 0, len = geometry.geometries.length; i < len; i++) {
10532				var layer = this.geometryToLayer({
10533					geometry: geometry.geometries[i],
10534					type: 'Feature',
10535					properties: geojson.properties
10536				}, options);
10537
10538				if (layer) {
10539					layers.push(layer);
10540				}
10541			}
10542			return new L.FeatureGroup(layers);
10543
10544		default:
10545			throw new Error('Invalid GeoJSON object.');
10546		}
10547	},
10548
10549	// @function coordsToLatLng(coords: Array): LatLng
10550	// Creates a `LatLng` object from an array of 2 numbers (longitude, latitude)
10551	// or 3 numbers (longitude, latitude, altitude) used in GeoJSON for points.
10552	coordsToLatLng: function (coords) {
10553		return new L.LatLng(coords[1], coords[0], coords[2]);
10554	},
10555
10556	// @function coordsToLatLngs(coords: Array, levelsDeep?: Number, coordsToLatLng?: Function): Array
10557	// Creates a multidimensional array of `LatLng`s from a GeoJSON coordinates array.
10558	// `levelsDeep` specifies the nesting level (0 is for an array of points, 1 for an array of arrays of points, etc., 0 by default).
10559	// Can use a custom [`coordsToLatLng`](#geojson-coordstolatlng) function.
10560	coordsToLatLngs: function (coords, levelsDeep, coordsToLatLng) {
10561		var latlngs = [];
10562
10563		for (var i = 0, len = coords.length, latlng; i < len; i++) {
10564			latlng = levelsDeep ?
10565			        this.coordsToLatLngs(coords[i], levelsDeep - 1, coordsToLatLng) :
10566			        (coordsToLatLng || this.coordsToLatLng)(coords[i]);
10567
10568			latlngs.push(latlng);
10569		}
10570
10571		return latlngs;
10572	},
10573
10574	// @function latLngToCoords(latlng: LatLng): Array
10575	// Reverse of [`coordsToLatLng`](#geojson-coordstolatlng)
10576	latLngToCoords: function (latlng) {
10577		return latlng.alt !== undefined ?
10578				[latlng.lng, latlng.lat, latlng.alt] :
10579				[latlng.lng, latlng.lat];
10580	},
10581
10582	// @function latLngsToCoords(latlngs: Array, levelsDeep?: Number, 
10582closed?: Boolean): Array
10583	// Reverse of [`coordsToLatLngs`](#geojson-coordstolatlngs)
10584	// `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.
10585	latLngsToCoords: function (latlngs, levelsDeep, closed) {
10586		var coords = [];
10587
10588		for (var i = 0, len = latlngs.length; i < len; i++) {
10589			coords.push(levelsDeep ?
10590				L.GeoJSON.latLngsToCoords(latlngs[i], levelsDeep - 1, closed) :
10591				L.GeoJSON.latLngToCoords(latlngs[i]));
10592		}
10593
10594		if (!levelsDeep && closed) {
10595			coords.push(coords[0]);
10596		}
10597
10598		return coords;
10599	},
10600
10601	getFeature: function (layer, newGeometry) {
10602		return layer.feature ?
10603				L.extend({}, layer.feature, {geometry: newGeometry}) :
10604				L.GeoJSON.asFeature(newGeometry);
10605	},
10606
10607	// @function asFeature(geojson: Object): Object
10608	// Normalize GeoJSON geometries/features into GeoJSON features.
10609	asFeature: function (geojson) {
10610		if (geojson.type === 'Feature' || geojson.type === 'FeatureCollection') {
10611			return geojson;
10612		}
10613
10614		return {
10615			type: 'Feature',
10616			properties: {},
10617			geometry: geojson
10618		};
10619	}
10620});
10621
10622var PointToGeoJSON = {
10623	toGeoJSON: function () {
10624		return L.GeoJSON.getFeature(this, {
10625			type: 'Point',
10626			coordinates: L.GeoJSON.latLngToCoords(this.getLatLng())
10627		});
10628	}
10629};
10630
10631// @namespace Marker
10632// @method toGeoJSON(): Object
10633// Returns a [`GeoJSON`](http://en.wikipedia.org/wiki/GeoJSON) representation of the marker (as a GeoJSON `Point` Feature).
10634L.Marker.include(PointToGeoJSON);
10635
10636// @namespace CircleMarker
10637// @method toGeoJSON(): Object
10638// Returns a [`GeoJSON`](http://en.wikipedia.org/wiki/GeoJSON) representation of the circle marker (as a GeoJSON `Point` Feature).
10639L.Circle.include(PointToGeoJSON);
10640L.CircleMarker.include(PointToGeoJSON);
10641
10642
10643// @namespace Polyline
10644// @method toGeoJSON(): Object
10645// Returns a [`GeoJSON`](http://en.wikipedia.org/wiki/GeoJSON) representation of the polyline (as a GeoJSON `LineString` or `MultiLineString` Feature).
10646L.Polyline.prototype.toGeoJSON = function () {
10647	var multi = !L.Polyline._flat(this._latlngs);
10648
10649	var coords = L.GeoJSON.latLngsToCoords(this._latlngs, multi ? 1 : 0);
10650
10651	return L.GeoJSON.getFeature(this, {
10652		type: (multi ? 'Multi' : '') + 'LineString',
10653		coordinates: coords
10654	});
10655};
10656
10657// @namespace Polygon
10658// @method toGeoJSON(): Object
10659// Returns a [`GeoJSON`](http://en.wikipedia.org/wiki/GeoJSON) representation of the polygon (as a GeoJSON `Polygon` or `MultiPolygon` Feature).
10660L.Polygon.prototype.toGeoJSON = function () {
10661	var holes = !L.Polyline._flat(this._latlngs),
10662	    multi = holes && !L.Polyline._flat(this._latlngs[0]);
10663
10664	var coords = L.GeoJSON.latLngsToCoords(this._latlngs, multi ? 2 : holes ? 1 : 0, true);
10665
10666	if (!holes) {
10667		coords = [coords];
10668	}
10669
10670	return L.GeoJSON.getFeature(this, {
10671		type: (multi ? 'Multi' : '') + 'Polygon',
10672		coordinates: coords
10673	});
10674};
10675
10676
10677// @namespace LayerGroup
10678L.LayerGroup.include({
10679	toMultiPoint: function () {
10680		var coords = [];
10681
10682		this.eachLayer(function (layer) {
10683			coords.push(layer.toGeoJSON().geometry.coordinates);
10684		});
10685
10686		return L.GeoJSON.getFeature(this, {
10687			type: 'MultiPoint',
10688			coordinates: coords
10689		});
10690	},
10691
10692	// @method toGeoJSON(): Object
10693	// Returns a [`GeoJSON`](http://en.wikipedia.org/wiki/GeoJSON) representation of the layer group (as a GeoJSON `GeometryCollection`).
10694	toGeoJSON: function () {
10695
10696		var type = this.feature && this.feature.geometry && this.feature.geometry.type;
10697
10698		if (type === 'MultiPoint') {
10699			return this.toMultiPoint();
10700		}
10701
10702		var isGeometryCollection = type === 'GeometryCollection',
10703		    jsons = [];
10704
10705		this.eachLayer(function (layer) {
10706			if (layer.toGeoJSON) {
10707				var json = layer.toGeoJSON();
10708				jsons.push(isGeometryCollection ? json.geometry : L.GeoJSON.asFeature(json));
10709			}
10710		});
10711
10712		if (isGeometryCollection) {
10713			return L.GeoJSON.getFeature(this, {
10714				geometries: jsons,
10715				type: 'GeometryCollection'
10716			});
10717		}
10718
10719		return {
10720			type: 'FeatureCollection',
10721			features: jsons
10722		};
10723	}
10724});
10725
10726// @namespace GeoJSON
10727// @factory L.geoJSON(geojson?: Object, options?: GeoJSON options)
10728// Creates a GeoJSON layer. Optionally accepts an object in
10729// [GeoJSON format](http://geojson.org/geojson-spec.html) to display on the map
10730// (you can alternatively add it later with `addData` method) and an `options` object.
10731L.geoJSON = function (geojson, options) {
10732	return new L.GeoJSON(geojson, options);
10733};
10734// Backward compatibility.
10735L.geoJson = L.geoJSON;
10736
10737
10738
10739/*
10740 * @class Draggable
10741 * @aka L.Draggable
10742 * @inherits Evented
10743 *
10744 * A class for making DOM elements draggable (including touch support).
10745 * Used internally for map and marker dragging. Only works for elements
10746 * that were positioned with [`L.DomUtil.setPosition`](#domutil-setposition).
10747 *
10748 * @example
10749 * ```js
10750 * var draggable = new L.Draggable(elementToDrag);
10751 * draggable.enable();
10752 * ```
10753 */
10754
10755L.Draggable = L.Evented.extend({
10756
10757	options: {
10758		// @option clickTolerance: Number = 3
10759		// The max number of pixels a user can shift the mouse pointer during a click
10760		// for it to be considered a valid click (as opposed to a mouse drag).
10761		clickTolerance: 3
10762	},
10763
10764	statics: {
10765		START: L.Browser.touch ? ['touchstart', 'mousedown'] : ['mousedown'],
10766		END: {
10767			mousedown: 'mouseup',
10768			touchstart: 'touchend',
10769			pointerdown: 'touchend',
10770			MSPointerDown: 'touchend'
10771		},
10772		MOVE: {
10773			mousedown: 'mousemove',
10774			touchstart: 'touchmove',
10775			pointerdown: 'touchmove',
10776			MSPointerDown: 'touchmove'
10777		}
10778	},
10779
10780	// @constructor L.Draggable(el: HTMLElement, dragHandle?: HTMLElement, preventOutline: Boolean)
10781	// Creates a `Draggable` object for moving `el` when you start dragging the `dragHandle` element (equals `el` itself by default).
10782	initialize: function (element, dragStartTarget, preventOutline) {
10783		this._element = element;
10784		this._dragStartTarget = dragStartTarget || element;
10785		this._preventOutline = preventOutline;
10786	},
10787
10788	// @method enable()
10789	// Enables the dragging ability
10790	enable: function () {
10791		if (this._enabled) { return; }
10792
10793		L.DomEvent.on(this._dragStartTarget, L.Draggable.START.join(' '), this._onDown, this);
10794
10795		this._enabled = true;
10796	},
10797
10798	// @method disable()
10799	// Disables the dragging ability
10800	disable: function () {
10801		if (!this._enabled) { return; }
10802
10803		// If we're currently dragging this draggable,
10804		// disabling it counts as first ending the drag.
10805		if (L.Draggable._dragging === this) {
10806			this.finishDrag();
10807		}
10808
10809		L.DomEvent.off(this._dragStartTarget, L.Draggable.START.join(' '), this._onDown, this);
10810
10811		this._enabled = false;
10812		this._moved = false;
10813	},
10814
10815	_onDown: function (e) {
10816		// Ignore simulated events, since we handle both touch and
10817		// mouse explicitly; otherwise we risk getting duplicates of
10818		// touch events, see #4315.
10819		// Also ignore the event if disabled; this happens in IE11
10820		// under some circumstances, see #3666.
10821		if (e._simulated || !this._enabled) { return; }
10822
10823		this._moved = false;
10824
10825		if (L.DomUtil.hasClass(this._element, 'leaflet-zoom-anim')) { return; }
10826
10827		if (L.Draggable._dragging || e.shiftKey || ((e.which !== 1) && (e.button !== 1) && !e.touches)) { return; }
10828		L.Draggable._dragging = this;  // Prevent dragging multiple objects at once.
10829
10830		if (this._preventOutline) {
10831			L.DomUtil.preventOutline(this._element);
10832		}
10833
10834		L.DomUtil.disableImageDrag();
10835		L.DomUtil.disableTextSelection();
10836
10837		if (this._moving) { return; }
10838
10839		// @event down: Event
10840		// Fired when a drag is about to start.
10841		this.fire('down');
10842
10843		var first = e.touches ? e.touches[0] : e;
10844
10845		this._startPoint = new L.Point(first.clientX, first.clientY);
10846
10847		L.DomEvent
10848			.on(document, L.Draggable.MOVE[e.type], this._onMove, this)
10849			.on(document, L.Draggable.END[e.type], this._onUp, this);
10850	},
10851
10852	_onMove: function (e) {
10853		// Ignore simulated events, since we handle both touch and
10854		// mouse explicitly; otherwise we risk getting duplicates of
10855		// touch events, see #4315.
10856		// Also ignore the event if disabled; this happens in IE11
10857		// under some circumstances, see #3666.
10858		if (e._simulated || !this._enabled) { return; }
10859
10860		if (e.touches && e.touches.length > 1) {
10861			this._moved = true;
10862			return;
10863		}
10864
10865		var first = (e.touches && e.touches.length === 1 ? e.touches[0] : e),
10866		    newPoint = new L.Point(first.clientX, first.clientY),
10867		    offset = newPoint.subtract(this._startPoint);
10868
10869		if (!offset.x && !offset.y) { return; }
10870		if (Math.abs(offset.x) + Math.abs(offset.y) < this.options.clickTolerance) { return; }
10871
10872		L.DomEvent.preventDefault(e);
10873
10874		if (!this._moved) {
10875			// @event dragstart: Event
10876			// Fired when a drag starts
10877			this.fire('dragstart');
10878
10879			this._moved = true;
10880			this._startPos = L.DomUtil.getPosition(this._element).subtract(offset);
10881
10882			L.DomUtil.addClass(document.body, 'leaflet-dragging');
10883
10884			this._lastTarget = e.target || e.srcElement;
10885			// IE and Edge do not give the <use> element, so fetch it
10886			// if necessary
10887			if ((window.SVGElementInstance) && (this._lastTarget instanceof SVGElementInstance)) {
10888				this._lastTarget = this._lastTarget.correspondingUseElement;
10889			}
10890			L.DomUtil.addClass(this._lastTarget, 'leaflet-drag-target');
10891		}
10892
10893		this._newPos = this._startPos.add(offset);
10894		this._moving = true;
10895
10896		L.Util.cancelAnimFrame(this._animRequest);
10897		this._lastEvent = e;
10898		this._animRequest = L.Util.requestAnimFrame(this._updatePosition, this, true);
10899	},
10900
10901	_updatePosition: function () {
10902		var e = {originalEvent: this._lastEvent};
10903
10904		// @event predrag: Event
10905		// Fired continuously during dragging *before* each corresponding
10906		// update of the element's position.
10907		this.fire('predrag', e);
10908		L.DomUtil.setPosition(this._element, this._newPos);
10909
10910		// @event drag: Event
10911		// Fired continuously during dragging.
10912		this.fire('drag', e);
10913	},
10914
10915	_onUp: function (e) {
10916		// Ignore simulated events, since we handle both touch and
10917		// mouse explicitly; otherwise we risk getting duplicates of
10918		// touch events, see #4315.
10919		// Also ignore the event if disabled; this happens in IE11
10920		// under some circumstances, see #3666.
10921		if (e._simulated || !this._enabled) { return; }
10922		this.finishDrag();
10923	},
10924
10925	finishDrag: function () {
10926		L.DomUtil.removeClass(document.body, 'leaflet-dragging');
10927
10928		if (this._lastTarget) {
10929			L.DomUtil.removeClass(this._lastTarget, 'leaflet-drag-target');
10930			this._lastTarget = null;
10931		}
10932
10933		for (var i in L.Draggable.MOVE) {
10934			L.DomEvent
10935				.off(document, L.Draggable.MOVE[i], this._onMove, this)
10936				.off(document, L.Draggable.END[i], this._onUp, this);
10937		}
10938
10939		L.DomUtil.enableImageDrag();
10940		L.DomUtil.enableTextSelection();
10941
10942		if (this._moved && this._moving) {
10943			// ensure drag is not fired after dragend
10944			L.Util.cancelAnimFrame(this._animRequest);
10945
10946			// @event dragend: DragEndEvent
10947			// Fired when the drag ends.
10948			this.fire('dragend', {
10949				distance: this._newPos.distanceTo(this._startPos)
10950			});
10951		}
10952
10953		this._moving = false;
10954		L.Draggable._dragging = false;
10955	}
10956
10957});
10958
10959
10960
10961/*
10962	L.Handler is a base class for handler classes that are used internally to inject
10963	interaction features like dragging to classes like Map and Marker.
10964*/
10965
10966// @class Handler
10967// @aka L.Handler
10968// Abstract class for map interaction handlers
10969
10970L.Handler = L.Class.extend({
10971	initialize: function (map) {
10972		this._map = map;
10973	},
10974
10975	// @method enable(): this
10976	// Enables the handler
10977	enable: function () {
10978		if (this._enabled) { return this; }
10979
10980		this._enabled = true;
10981		this.addHooks();
10982		return this;
10983	},
10984
10985	// @method disable(): this
10986	// Disables the handler
10987	disable: function () {
10988		if (!this._enabled) { return this; }
10989
10990		this._enabled = false;
10991		this.removeHooks();
10992		return this;
10993	},
10994
10995	// @method enabled(): Boolean
10996	// Returns `true` if the handler is enabled
10997	enabled: function () {
10998		return !!this._enabled;
10999	}
11000
11001	// @section Extension methods
11002	// Classes inheriting from `Handler` must implement the two following methods:
11003	// @method addHooks()
11004	// Called when the handler is enabled, should add event hooks.
11005	// @method removeHooks()
11006	// Called when the handler is disabled, should remove the event hooks added previously.
11007});
11008
11009
11010
11011/*
11012 * L.Handler.MapDrag is used to make the map draggable (with panning inertia), enabled by default.
11013 */
11014
11015// @namespace Map
11016// @section Interaction Options
11017L.Map.mergeOptions({
11018	// @option dragging: Boolean = true
11019	// Whether the map be draggable with mouse/touch or not.
11020	dragging: true,
11021
11022	// @section Panning Inertia Options
11023	// @option inertia: Boolean = *
11024	// If enabled, panning of the map will have an inertia effect where
11025	// the map builds momentum while dragging and continues moving in
11026	// the same direction for some time. Feels especially nice on touch
11027	// devices. Enabled by default unless running on old Android devices.
11028	inertia: !L.Browser.android23,
11029
11030	// @option inertiaDeceleration: Number = 3000
11031	// The rate with which the inertial movement slows down, in pixels/second².
11032	inertiaDeceleration: 3400, // px/s^2
11033
11034	// @option inertiaMaxSpeed: Number = Infinity
11035	// Max speed of the inertial movement, in pixels/second.
11036	inertiaMaxSpeed: Infinity, // px/s
11037
11038	// @option easeLinearity: Number = 0.2
11039	easeLinearity: 0.2,
11040
11041	// TODO refactor, move to CRS
11042	// @option worldCopyJump: Boolean = false
11043	// With this option enabled, the map tracks when you pan to another "copy"
11044	// of the world and seamlessly jumps to the original one so that all overlays
11045	// like markers and vector layers are still visible.
vendor: 5,208 bytes, lines 11046-11219
11046	worldCopyJump: false,
11047
11048	// @option maxBoundsViscosity: Number = 0.0
11049	// If `maxBounds` is set, this option will control how solid the bounds
11050	// are when dragging the map around. The default value of `0.0` allows the
11051	// user to drag outside the bounds at normal speed, higher values will
11052	// slow down map dragging outside bounds, and `1.0` makes the bounds fully
11053	// solid, preventing the user from dragging outside the bounds.
11054	maxBoundsViscosity: 0.0
11055});
11056
11057L.Map.Drag = L.Handler.extend({
11058	addHooks: function () {
11059		if (!this._draggable) {
11060			var map = this._map;
11061
11062			this._draggable = new L.Draggable(map._mapPane, map._container);
11063
11064			this._draggable.on({
11065				down: this._onDown,
11066				dragstart: this._onDragStart,
11067				drag: this._onDrag,
11068				dragend: this._onDragEnd
11069			}, this);
11070
11071			this._draggable.on('predrag', this._onPreDragLimit, this);
11072			if (map.options.worldCopyJump) {
11073				this._draggable.on('predrag', this._onPreDragWrap, this);
11074				map.on('zoomend', this._onZoomEnd, this);
11075
11076				map.whenReady(this._onZoomEnd, this);
11077			}
11078		}
11079		L.DomUtil.addClass(this._map._container, 'leaflet-grab leaflet-touch-drag');
11080		this._draggable.enable();
11081		this._positions = [];
11082		this._times = [];
11083	},
11084
11085	removeHooks: function () {
11086		L.DomUtil.removeClass(this._map._container, 'leaflet-grab');
11087		L.DomUtil.removeClass(this._map._container, 'leaflet-touch-drag');
11088		this._draggable.disable();
11089	},
11090
11091	moved: function () {
11092		return this._draggable && this._draggable._moved;
11093	},
11094
11095	moving: function () {
11096		return this._draggable && this._draggable._moving;
11097	},
11098
11099	_onDown: function () {
11100		this._map._stop();
11101	},
11102
11103	_onDragStart: function () {
11104		var map = this._map;
11105
11106		if (this._map.options.maxBounds && this._map.options.maxBoundsViscosity) {
11107			var bounds = L.latLngBounds(this._map.options.maxBounds);
11108
11109			this._offsetLimit = L.bounds(
11110				this._map.latLngToContainerPoint(bounds.getNorthWest()).multiplyBy(-1),
11111				this._map.latLngToContainerPoint(bounds.getSouthEast()).multiplyBy(-1)
11112					.add(this._map.getSize()));
11113
11114			this._viscosity = Math.min(1.0, Math.max(0.0, this._map.options.maxBoundsViscosity));
11115		} else {
11116			this._offsetLimit = null;
11117		}
11118
11119		map
11120		    .fire('movestart')
11121		    .fire('dragstart');
11122
11123		if (map.options.inertia) {
11124			this._positions = [];
11125			this._times = [];
11126		}
11127	},
11128
11129	_onDrag: function (e) {
11130		if (this._map.options.inertia) {
11131			var time = this._lastTime = +new Date(),
11132			    pos = this._lastPos = this._draggable._absPos || this._draggable._newPos;
11133
11134			this._positions.push(pos);
11135			this._times.push(time);
11136
11137			if (time - this._times[0] > 50) {
11138				this._positions.shift();
11139				this._times.shift();
11140			}
11141		}
11142
11143		this._map
11144		    .fire('move', e)
11145		    .fire('drag', e);
11146	},
11147
11148	_onZoomEnd: function () {
11149		var pxCenter = this._map.getSize().divideBy(2),
11150		    pxWorldCenter = this._map.latLngToLayerPoint([0, 0]);
11151
11152		this._initialWorldOffset = pxWorldCenter.subtract(pxCenter).x;
11153		this._worldWidth = this._map.getPixelWorldBounds().getSize().x;
11154	},
11155
11156	_viscousLimit: function (value, threshold) {
11157		return value - (value - threshold) * this._viscosity;
11158	},
11159
11160	_onPreDragLimit: function () {
11161		if (!this._viscosity || !this._offsetLimit) { return; }
11162
11163		var offset = this._draggable._newPos.subtract(this._draggable._startPos);
11164
11165		var limit = this._offsetLimit;
11166		if (offset.x < limit.min.x) { offset.x = this._viscousLimit(offset.x, limit.min.x); }
11167		if (offset.y < limit.min.y) { offset.y = this._viscousLimit(offset.y, limit.min.y); }
11168		if (offset.x > limit.max.x) { offset.x = this._viscousLimit(offset.x, limit.max.x); }
11169		if (offset.y > limit.max.y) { offset.y = this._viscousLimit(offset.y, limit.max.y); }
11170
11171		this._draggable._newPos = this._draggable._startPos.add(offset);
11172	},
11173
11174	_onPreDragWrap: function () {
11175		// TODO refactor to be able to adjust map pane position after zoom
11176		var worldWidth = this._worldWidth,
11177		    halfWidth = Math.round(worldWidth / 2),
11178		    dx = this._initialWorldOffset,
11179		    x = this._draggable._newPos.x,
11180		    newX1 = (x - halfWidth + dx) % worldWidth + halfWidth - dx,
11181		    newX2 = (x + halfWidth + dx) % worldWidth - halfWidth - dx,
11182		    newX = Math.abs(newX1 + dx) < Math.abs(newX2 + dx) ? newX1 : newX2;
11183
11184		this._draggable._absPos = this._draggable._newPos.clone();
11185		this._draggable._newPos.x = newX;
11186	},
11187
11188	_onDragEnd: function (e) {
11189		var map = this._map,
11190		    options = map.options,
11191
11192		    noInertia = !options.inertia || this._times.length < 2;
11193
11194		map.fire('dragend', e);
11195
11196		if (noInertia) {
11197			map.fire('moveend');
11198
11199		} else {
11200
11201			var direction = this._lastPos.subtract(this._positions[0]),
11202			    duration = (this._lastTime - this._times[0]) / 1000,
11203			    ease = options.easeLinearity,
11204
11205			    speedVector = direction.multiplyBy(ease / duration),
11206			    speed = speedVector.distanceTo([0, 0]),
11207
11208			    limitedSpeed = Math.min(options.inertiaMaxSpeed, speed),
11209			    limitedSpeedVector = speedVector.multiplyBy(limitedSpeed / speed),
11210
11211			    decelerationDuration = limitedSpeed / (options.inertiaDeceleration * ease),
11212			    offset = limitedSpeedVector.multiplyBy(-decelerationDuration / 2).round();
11213
11214			if (!offset.x && !offset.y) {
11215				map.fire('moveend');
11216
11217			} else {
11218				offset = map._limitOffset(offset, map.options.maxBounds);
11219
11220				L.Util.requestAnimFrame(function () {
11221					map.panBy(offset, {
11222						duration: decelerationDuration,
11223						easeLinearity: ease,
11224						noMoveStart: true,
11225						animate: true
11226					});
11227				});
11228			}
11229		}
11230	}
11231});
11232
11233// @section Handlers
11234// @property dragging: Handler
11235// Map dragging handler (by both mouse and touch).
11236L.Map.addInitHook('addHandler', 'dragging', L.Map.Drag);
11237
11238
11239
11240/*
11241 * L.Handler.DoubleClickZoom is used to handle double-click zoom on the map, enabled by default.
11242 */
11243
11244// @namespace Map
11245// @section Interaction Options
11246
11247L.Map.mergeOptions({
11248	// @option doubleClickZoom: Boolean|String = true
11249	// Whether the map can be zoomed in by double clicking on it and
11250	// zoomed out by double clicking while holding shift. If passed
11251	// `'center'`, double-click zoom will zoom to the center of the
11252	//  view regardless of where the mouse was.
11253	doubleClickZoom: true
11254});
11255
11256L.Map.DoubleClickZoom = L.Handler.extend({
11257	addHooks: function () {
11258		this._map.on('dblclick', this._onDoubleClick, this);
11259	},
11260
11261	removeHooks: function () {
11262		this._map.off('dblclick', this._onDoubleClick, this);
11263	},
11264
11265	_onDoubleClick: function (e) {
11266		var map = this._map,
11267		    oldZoom = map.getZoom(),
11268		    delta = map.options.zoomDelta,
11269		    zoom = e.originalEvent.shiftKey ? oldZoom - delta : oldZoom + delta;
11270
11271		if (map.options.doubleClickZoom === 'center') {
11272			map.setZoom(zoom);
11273		} else {
11274			map.setZoomAround(e.containerPoint, zoom);
11275		}
11276	}
11277});
11278
11279// @section Handlers
11280//
11281// Map properties include interaction handlers that allow you to control
11282// interaction behavior in runtime, enabling or disabling certain features such
11283// as dragging or touch zoom (see `Handler` methods). For example:
11284//
11285// ```js
11286// map.doubleClickZoom.disable();
11287// ```
11288//
11289// @property doubleClickZoom: Handler
11290// Double click zoom handler.
11291L.Map.addInitHook('addHandler', 'doubleClickZoom', L.Map.DoubleClickZoom);
11292
11293
11294
11295/*
11296 * L.Handler.ScrollWheelZoom is used by L.Map to enable mouse scroll wheel zoom on the map.
11297 */
11298
11299// @namespace Map
11300// @section Interaction Options
11301L.Map.mergeOptions({
11302	// @section Mousewheel options
11303	// @option scrollWheelZoom: Boolean|String = true
11304	// Whether the map can be zoomed by using the mouse wheel. If passed `'center'`,
11305	// it will zoom to the center of the view regardless of where the mouse was.
11306	scrollWheelZoom: true,
11307
11308	// @option wheelDebounceTime: Number = 40
11309	// Limits the rate at which a wheel can fire (in milliseconds). By default
11310	// user can't zoom via wheel more often than once per 40 ms.
11311	wheelDebounceTime: 40,
11312
11313	// @option wheelPxPerZoomLevel: Number = 60
11314	// How many scroll pixels (as reported by [L.DomEvent.getWheelDelta](#domevent-getwheeldelta))
11315	// mean a change of one full zoom level. Smaller values will make wheel-zooming
11316	// faster (and vice versa).
11317	wheelPxPerZoomLevel: 60
11318});
11319
11320L.Map.ScrollWheelZoom = L.Handler.extend({
11321	addHooks: function () {
11322		L.DomEvent.on(this._map._container, 'mousewheel', this._onWheelScroll, this);
11323
11324		this._delta = 0;
11325	},
11326
11327	removeHooks: function () {
11328		L.DomEvent.off(this._map._container, 'mousewheel', this._onWheelScroll, this);
11329	},
11330
11331	_onWheelScroll: function (e) {
11332		var delta = L.DomEvent.getWheelDelta(e);
11333
11334		var debounce = this._map.options.wheelDebounceTime;
11335
11336		this._delta += delta;
11337		this._lastMousePos = this._map.mouseEventToContainerPoint(e);
11338
11339		if (!this._startTime) {
11340			this._startTime = +new Date();
11341		}
11342
11343		var left = Math.max(debounce - (+new Date() - this._startTime), 0);
11344
11345		clearTimeout(this._timer);
11346		this._timer = setTimeout(L.bind(this._performZoom, this), left);
11347
11348		L.DomEvent.stop(e);
11349	},
11350
11351	_performZoom: function () {
11352		var map = this._map,
11353		    zoom = map.getZoom(),
11354		    snap = this._map.options.zoomSnap || 0;
11355
11356		map._stop(); // stop panning and fly animations if any
11357
11358		// map the delta with a sigmoid function to -4..4 range leaning on -1..1
11359		var d2 = this._delta / (this._map.options.wheelPxPerZoomLevel * 4),
11360		    d3 = 4 * Math.log(2 / (1 + Math.exp(-Math.abs(d2)))) / Math.LN2,
11361		    d4 = snap ? Math.ceil(d3 / snap) * snap : d3,
11362		    delta = map._limitZoom(zoom + (this._delta > 0 ? d4 : -d4)) - zoom;
11363
11364		this._delta = 0;
11365		this._startTime = null;
11366
11367		if (!delta) { return; }
11368
11369		if (map.options.scrollWheelZoom === 'center') {
11370			map.setZoom(zoom + delta);
11371		} else {
11372			map.setZoomAround(this._lastMousePos, zoom + delta);
11373		}
11374	}
11375});
11376
11377// @section Handlers
11378// @property scrollWheelZoom: Handler
11379// Scroll wheel zoom handler.
11380L.Map.addInitHook('addHandler', 'scrollWheelZoom', L.Map.ScrollWheelZoom);
11381
11382
11383
11384/*
11385 * Extends the event handling code with double tap support for mobile browsers.
11386 */
11387
11388L.extend(L.DomEvent, {
11389
11390	_touchstart: L.Browser.msPointer ? 'MSPointerDown' : L.Browser.pointer ? 'pointerdown' : 'touchstart',
11391	_touchend: L.Browser.msPointer ? 'MSPointerUp' : L.Browser.pointer ? 'pointerup' : 'touchend',
11392
11393	// inspired by Zepto touch code by Thomas Fuchs
11394	addDoubleTapListener: function (obj, handler, id) {
11395		var last, touch,
11396		    doubleTap = false,
11397		    delay = 250;
11398
11399		function onTouchStart(e) {
11400			var count;
11401
11402			if (L.Browser.pointer) {
11403				if ((!L.Browser.edge) || e.pointerType === 'mouse') { return; }
11404				count = L.DomEvent._pointersCount;
11405			} else {
11406				count = e.touches.length;
11407			}
11408
11409			if (count > 1) { return; }
11410
11411			var now = Date.now(),
11412			    delta = now - (last || now);
11413
11414			touch = e.touches ? e.touches[0] : e;
11415			doubleTap = (delta > 0 && delta <= delay);
11416			last = now;
11417		}
11418
11419		function onTouchEnd(e) {
11420			if (doubleTap && !touch.cancelBubble) {
11421				if (L.Browser.pointer) {
11422					if ((!L.Browser.edge) || e.pointerType === 'mouse') { return; }
11423
11424					// work around .type being readonly with MSPointer* events
11425					var newTouch = {},
11426					    prop, i;
11427
11428					for (i in touch) {
11429						prop = touch[i];
11430						newTouch[i] = prop && prop.bind ? prop.bind(touch) : prop;
11431					}
11432					touch = newTouch;
11433				}
11434				touch.type = 'dblclick';
11435				handler(touch);
11436				last = null;
11437			}
11438		}
11439
11440		var pre = '_leaflet_',
11441		    touchstart = this._touchstart,
11442		    touchend = this._touchend;
11443
11444		obj[pre + touchstart + id] = onTouchStart;
11445		obj[pre + touchend + id] = onTouchEnd;
11446		obj[pre + 'dblclick' + id] = handler;
11447
11448		obj.addEventListener(touchstart, onTouchStart, false);
11449		obj.addEventListener(touchend, onTouchEnd, false);
11450
11451		// On some platforms (notably, chrome<55 on win10 + touchscreen + mouse),
11452		// the browser doesn't fire touchend/pointerup events but does fire
11453		// native dblclicks. See #4127.
11454		// Edge 14 also fires native dblclicks, but only for pointerType mouse, see #5180.
11455		obj.addEventListener('dblclick', handler, false);
11456
11457		return this;
11458	},
11459
11460	removeDoubleTapListener: function (obj, id) {
11461		var pre = '_leaflet_',
11462		    touchstart = obj[pre + this._touchstart + id],
11463		    touchend = obj[pre + this._touchend + id],
11464		    dblclick = obj[pre + 'dblclick' + id];
11465
11466		obj.removeEventListener(this._touchstart, touchstart, false);
11467		obj.removeEventListener(this._touchend, touchend, false);
11468		if (!L.Browser.edge) {
11469			obj.removeEventListener('dblclick', dblclick, false);
11470		}
11471
11472		return this;
11473	}
11474});
11475
11476
11477
11478/*
11479 * Extends L.DomEvent to provide touch support for Internet Explorer and Windows-based devices.
11480 */
11481
11482L.extend(L.DomEvent, {
11483
11484	POINTER_DOWN:   L.Browser.msPointer ? 'MSPointerDown'   : 'pointerdown',
11485	POINTER_MOVE:   L.Browser.msPointer ? 'MSPointerMove'   : 'pointermove',
11486	POINTER_UP:     L.Browser.msPointer ? 'MSPointerUp'     : 'pointerup',
11487	POINTER_CANCEL: L.Browser.msPointer ? 'MSPointerCancel' : 'pointercancel',
11488	TAG_WHITE_LIST: ['INPUT', 'SELECT', 'OPTION'],
11489
11490	_pointers: {},
11491	_pointersCount: 0,
11492
11493	// Provides a touch events wrapper for (ms)pointer events.
11494	// ref http://www.w3.org/TR/pointerevents/ https://www.w3.org/Bugs/Public/show_bug.cgi?id=22890
11495
11496	addPointerListener: function (obj, type, handler, id) {
11497
11498		if (type === 'touchstart') {
11499			this._addPointerStart(obj, handler, id);
11500
11501		} else if (type === 'touchmove') {
11502			this._addPointerMove(obj, handler, id);
11503
11504		} else if (type === 'touchend') {
11505			this._addPointerEnd(obj, handler, id);
11506		}
11507
11508		return this;
11509	},
11510
11511	removePointerListener: function (obj, type, id) {
11512		var handler = obj['_leaflet_' + type + id];
11513
11514		if (type === 'touchstart') {
11515			obj.removeEventListener(this.POINTER_DOWN, handler, false);
11516
11517		} else if (type === 'touchmove') {
11518			obj.removeEventListener(this.POINTER_MOVE, handler, false);
11519
11520		} else if (type === 'touchend') {
11521			obj.removeEventListener(this.POINTER_UP, handler, false);
11522			obj.removeEventListener(this.POINTER_CANCEL, handler, false);
11523		}
11524
11525		return this;
11526	},
11527
11528	_addPointerStart: function (obj, handler, id) {
11529		var onDown = L.bind(function (e) {
11530			if (e.pointerType !== 'mouse' && e.MSPOINTER_TYPE_MOUSE && e.pointerType !== e.MSPOINTER_TYPE_MOUSE) {
11531				// In IE11, some touch events needs to fire for form controls, or
11532				// the controls will stop working. We keep a whitelist of tag names that
11533				// need these events. For other target tags, we prevent default on the event.
11534				if (this.TAG_WHITE_LIST.indexOf(e.target.tagName) < 0) {
11535					L.DomEvent.preventDefault(e);
11536				} else {
11537					return;
11538				}
11539			}
11540
11541			this._handlePointer(e, handler);
11542		}, this);
11543
11544		obj['_leaflet_touchstart' + id] = onDown;
11545		obj.addEventListener(this.POINTER_DOWN, onDown, false);
11546
11547		// need to keep track of what pointers and how many are active to provide e.touches emulation
11548		if (!this._pointerDocListener) {
11549			var pointerUp = L.bind(this._globalPointerUp, this);
11550
11551			// we listen documentElement as any drags that end by moving the touch off the screen get fired there
11552			document.documentElement.addEventListener(this.POINTER_DOWN, L.bind(this._globalPointerDown, this), true);
11553			document.documentElement.addEventListener(this.POINTER_MOVE, L.bind(this._globalPointerMove, this), true);
11554			document.documentElement.addEventListener(this.POINTER_UP, pointerUp, true);
11555			document.documentElement.addEventListener(this.POINTER_CANCEL, pointerUp, true);
11556
11557			this._pointerDocListener = true;
11558		}
11559	},
11560
11561	_globalPointerDown: function (e) {
11562		this._pointers[e.pointerId] = e;
11563		this._pointersCount++;
11564	},
11565
11566	_globalPointerMove: function (e) {
11567		if (this._pointers[e.pointerId]) {
11568			this._pointers[e.pointerId] = e;
11569		}
11570	},
11571
11572	_globalPointerUp: function (e) {
11573		delete this._pointers[e.pointerId];
11574		this._pointersCount--;
11575	},
11576
11577	_handlePointer: function (e, handler) {
11578		e.touches = [];
11579		for (var i in this._pointers) {
11580			e.touches.push(this._pointers[i]);
11581		}
11582		e.changedTouches = [e];
11583
11584		handler(e);
11585	},
11586
11587	_addPointerMove: function (obj, handler, id) {
11588		var onMove = L.bind(function (e) {
11589			// don't fire touch moves when mouse isn't down
11590			if ((e.pointerType === e.MSPOINTER_TYPE_MOUSE || e.pointerType === 'mouse') && e.buttons === 0) { return; }
11591
11592			this._handlePointer(e, handler);
11593		}, this);
11594
11595		obj['_leaflet_touchmove' + id] = onMove;
11596		obj.addEventListener(this.POINTER_MOVE, onMove, false);
11597	},
11598
11599	_addPointerEnd: function (obj, handler, id) {
11600		var onUp = L.bind(function (e) {
11601			this._handlePointer(e, handler);
11602		}, this);
11603
11604		obj['_leaflet_touchend' + id] = onUp;
11605		obj.addEventListener(this.POINTER_UP, onUp, false);
11606		obj.addEventListener(this.POINTER_CANCEL, onUp, false);
11607	}
11608});
11609
11610
11611
11612/*
11613 * L.Handler.TouchZoom is used by L.Map to add pinch zoom on supported mobile browsers.
11614 */
11615
11616// @namespace Map
11617// @section Interaction Options
11618L.Map.mergeOptions({
11619	// @section Touch interaction options
11620	// @option touchZoom: Boolean|String = *
11621	// Whether the map can be zoomed by touch-dragging with two fingers. If
11622	// passed `'center'`, it will zoom to the center of the view regardless of
11623	// where the touch events (fingers) were. Enabled for touch-capable web
11624	// browsers except for old Androids.
11625	touchZoom: L.Browser.touch && !L.Browser.android23,
11626
11627	// @option bounceAtZoomLimits: Boolean = true
11628	// Set it to false if you don't want the map to zoom beyond min/max zoom
11629	// and then bounce back when pinch-zooming.
11630	bounceAtZoomLimits: true
11631});
11632
11633L.Map.TouchZoom = L.Handler.extend({
11634	addHooks: function () {
11635		L.DomUtil.addClass(this._map._container, 'leaflet-touch-zoom');
11636		L.DomEvent.on(this._map._container, 'touchstart', this._onTouchStart, this);
11637	},
11638
11639	removeHooks: function () {
11640		L.DomUtil.removeClass(this._map._container, 'leaflet-touch-zoom');
11641		L.DomEvent.off(this._map._container, 'touchstart', this._onTouchStart, this);
11642	},
11643
11644	_onTouchStart: function (e) {
11645		var map = this._map;
11646		if (!e.touches || e.touches.length !== 2 || map._animatingZoom || this._zooming) { return; }
11647
11648		var p1 = map.mouseEventToContainerPoint(e.touches[0]),
11649		    p2 = map.mouseEventToContainerPoint(e.touches[1]);
11650
11651		this._centerPoint = map.getSize()._divideBy(2);
11652		this._startLatLng = map.containerPointToLatLng(this._centerPoint);
11653		if (map.options.touchZoom !== 'center') {
11654			this._pinchStartLatLng = map.containerPointToLatLng(p1.add(p2)._divideBy(2));
11655		}
11656
11657		this._startDist = p1.distanceTo(p2);
11658		this._startZoom = map.getZoom();
11659
11660		this._moved = false;
11661		this._zooming = true;
11662
11663		map._stop();
11664
11665		L.DomEvent
11666		    .on(document, 'touchmove', this._onTouchMove, this)
11667		    .on(document, 'touchend', this._onTouchEnd, this);
11668
11669		L.DomEvent.preventDefault(e);
11670	},
11671
11672	_onTouchMove: function (e) {
11673		if (!e.touches || e.touches.length !== 2 || !this._zooming) { return; }
11674
11675		var map = this._map,
11676		    p1 = map.mouseEventToContainerPoint(e.touches[0]),
11677		    p2 = map.mouseEventToContainerPoint(e.touches[1]),
11678		    scale = p1.distanceTo(p2) / this._startDist;
11679
11680
11681		this._zoom = map.getScaleZoom(scale, this._startZoom);
11682
11683		if (!map.options.bounceAtZoomLimits && (
11684			(this._zoom < map.getMinZoom() && scale < 1) ||
11685			(this._zoom > map.getMaxZoom() && scale > 1))) {
11686			this._zoom = map._limitZoom(this._zoom);
11687		}
11688
11689		if (map.options.touchZoom === 'center') {
11690			this._center = this._startLatLng;
11691			if (scale === 1) { return; }
11692		} else {
11693			// Get delta from pinch to center, so centerLatLng is delta applied to initial pinchLatLng
11694			var delta = p1._add(p2)._divideBy(2)._subtract(this._centerPoint);
11695			if (scale === 1 && delta.x === 0 && delta.y === 0) { return; }
11696			this._center = map.unproject(map.project(this._pinchStartLatLng, this._zoom).subtract(delta), this._zoom);
11697		}
11698
11699		if (!this._moved) {
11700			map._moveStart(true);
11701			this._moved = true;
11702		}
11703
11704		L.Util.cancelAnimFrame(this._animRequest);
11705
11706		var moveFn = L.bind(map._move, map, this._center, this._zoom, {pinch: true, round: false});
11707		this._animRequest = L.Util.requestAnimFrame(moveFn, this, true);
11708
11709		L.DomEvent.preventDefault(e);
11710	},
11711
11712	_onTouchEnd: function () {
11713		if (!this._moved || !this._zooming) {
11714			this._zooming = false;
11715			return;
11716		}
11717
11718		this._zooming = false;
11719		L.Util.cancelAnimFrame(this._animRequest);
11720
11721		L.DomEvent
11722		    .off(document, 'touchmove', this._onTouchMove)
11723		    .off(document, 'touchend', this._onTouchEnd);
11724
11725		// Pinch updates GridLayers' levels only when zoomSnap is off, so zoomSnap becomes noUpdate.
11726		if (this._map.options.zoomAnimation) {
11727			this._map._animateZoom(this._center, this._map._limitZoom(this._zoom), true, this._map.options.zoomSnap);
11728		} else {
11729			this._map._resetView(this._center, this._map._limitZoom(this._zoom));
11730		}
11731	}
11732});
11733
11734// @section Handlers
11735// @property touchZoom: Handler
11736// Touch zoom handler.
11737L.Map.addInitHook('addHandler', 'touchZoom', L.Map.TouchZoom);
11738
11739
11740
11741/*
11742 * L.Map.Tap is used to enable mobile hacks like quick taps and long hold.
11743 */
11744
11745// @namespace Map
11746// @section Interaction Options
11747L.Map.mergeOptions({
11748	// @section Touch interaction options
11749	// @option tap: Boolean = true
11750	// Enables mobile hacks for supporting instant taps (fixing 200ms click
11751	// delay on iOS/Android) and touch holds (fired as `contextmenu` events).
11752	tap: true,
11753
11754	// @option tapTolerance: Number = 15
11755	// The max number of pixels a user can shift his finger during touch
11756	// for it to be considered a valid tap.
11757	tapTolerance: 15
11758});
11759
11760L.Map.Tap = L.Handler.extend({
11761	addHooks: function () {
11762		L.DomEvent.on(this._map._container, 'touchstart', this._onDown, this);
11763	},
11764
11765	removeHooks: function () {
11766		L.DomEvent.off(this._map._container, 'touchstart', this._onDown, this);
11767	},
11768
11769	_onDown: function (e) {
11770		if (!e.touches) { return; }
11771
11772		L.DomEvent.preventDefault(e);
11773
11774		this._fireClick = true;
11775
11776		// don't simulate click or track longpress if more than 1 touch
11777		if (e.touches.length > 1) {
11778			this._fireClick = false;
11779			clearTimeout(this._holdTimeout);
11780			return;
11781		}
11782
11783		var first = e.touches[0],
11784		    el = first.target;
11785
11786		this._startPos = this._newPos = new L.Point(first.clientX, first.clientY);
11787
11788		// if touching a link, highlight it
11789		if (el.tagName && el.tagName.toLowerCase() === 'a') {
11790			L.DomUtil.addClass(el, 'leaflet-active');
11791		}
11792
11793		// simulate long hold but setting a timeout
11794		this._holdTimeout = setTimeout(L.bind(function () {
11795			if (this._isTapValid()) {
11796				this._fireClick = false;
11797				this._onUp();
11798				this._simulateEvent('contextmenu', first);
11799			}
11800		}, this), 1000);
11801
11802		this._simulateEvent('mousedown', first);
11803
11804		L.DomEvent.on(document, {
11805			touchmove: this._onMove,
11806			touchend: this._onUp
11807		}, this);
11808	},
11809
11810	_onUp: function (e) {
11811		clearTimeout(this._holdTimeout);
11812
11813		L.DomEvent.off(document, {
11814			touchmove: this._onMove,
11815			touchend: this._onUp
11816		}, this);
11817
11818		if (this._fireClick && e && e.changedTouches) {
11819
11820			var first = e.changedTouches[0],
11821			    el = first.target;
11822
11823			if (el && el.tagName && el.tagName.toLowerCase() === 'a') {
11824				L.DomUtil.removeClass(el, 'leaflet-active');
11825			}
11826
11827			this._simulateEvent('mouseup', first);
11828
11829			// simulate click if the touch didn't move too much
11830			if (this._isTapValid()) {
11831				this._simulateEvent('click', first);
11832			}
11833		}
11834	},
11835
11836	_isTapValid: function () {
11837		return this._newPos.distanceTo(this._startPos) <= this._map.options.tapTolerance;
11838	},
11839
11840	_onMove: function (e) {
11841		var first = e.touches[0];
11842		this._newPos = new L.Point(first.clientX, first.clientY);
11843		this._simulateEvent('mousemove', first);
11844	},
11845
11846	_simulateEvent: function (type, e) {
11847		var simulatedEvent = document.createEvent('MouseEvents');
11848
11849		simulatedEvent._simulated = true;
11850		e.target._simulatedClick = true;
11851
11852		simulatedEvent.initMouseEvent(
11853		        type, true, true, window, 1,
11854		        e.screenX, e.screenY,
11855		        e.clientX, e.clientY,
11856		        false, false, false, false, 0, null);
11857
11858		e.target.dispatchEvent(simulatedEvent);
11859	}
11860});
11861
11862// @section Handlers
11863// @property tap: Handler
11864// Mobile touch hacks (quick tap and touch hold) handler.
11865if (L.Browser.touch && !L.Browser.pointer) {
11866	L.Map.addInitHook('addHandler', 'tap', L.Map.Tap);
11867}
11868
11869
11870
11871/*
11872 * L.Handler.BoxZoom is used to add shift-drag zoom interaction to the map
11873 * (zoom to a selected bounding box), enabled by default.
11874 */
11875
11876// @namespace Map
11877// @section Interaction Options
11878L.Map.mergeOptions({
11879	// @option boxZoom: Boolean = true
11880	// Whether the map can be zoomed to a rectangular area specified by
11881	// dragging the mouse while pressing the shift key.
11882	boxZoom: true
11883});
11884
11885L.Map.BoxZoom = L.Handler.extend({
11886	initialize: function (map) {
11887		this._map = map;
11888		this._container = map._container;
11889		this._pane = map._panes.overlayPane;
11890	},
11891
11892	addHooks: function () {
11893		L.DomEvent.on(this._container, 'mousedown', this._onMouseDown, this);
11894	},
11895
11896	removeHooks: function () {
11897		L.DomEvent.off(this._container, 'mousedown', this._onMouseDown, this);
11898	},
11899
11900	moved: function () {
11901		return this._moved;
11902	},
11903
11904	_resetState: function () {
11905		this._moved = false;
11906	},
11907
11908	_onMouseDown: function (e) {
11909		if (!e.shiftKey || ((e.which !== 1) && (e.button !== 1))) { return false; }
11910
11911		this._resetState();
11912
11913		L.DomUtil.disableTextSelection();
11914		L.DomUtil.disableImageDrag();
11915
11916		this._startPoint = this._map.mouseEventToContainerPoint(e);
11917
11918		L.DomEvent.on(document, {
11919			contextmenu: L.DomEvent.stop,
11920			mousemove: this._onMouseMove,
11921			mouseup: this._onMouseUp,
11922			keydown: this._onKeyDown
11923		}, this);
11924	},
11925
11926	_onMouseMove: function (e) {
11927		if (!this._moved) {
11928			this._moved = true;
11929
11930			this._box = L.DomUtil.create('div', 'leaflet-zoom-box', this._container);
11931			L.DomUtil.addClass(this._container, 'leaflet-crosshair');
11932
11933			this._map.fire('boxzoomstart');
11934		}
11935
11936		this._point = this._map.mouseEventToContainerPoint(e);
11937
11938		var bounds = new L.Bounds(this._point, this._startPoint),
11939		    size = bounds.getSize();
11940
11941		L.DomUtil.setPosition(this._box, bounds.min);
11942
11943		this._box.style.width  = size.x + 'px';
11944		this._box.style.height = size.y + 'px';
11945	},
11946
11947	_finish: function () {
11948		if (this._moved) {
11949			L.DomUtil.remove(this._box);
11950			L.DomUtil.removeClass(this._container, 'leaflet-crosshair');
11951		}
11952
11953		L.DomUtil.enableTextSelection();
11954		L.DomUtil.enableImageDrag();
11955
11956		L.DomEvent.off(document, {
11957			contextmenu: L.DomEvent.stop,
11958			mousemove: this._onMouseMove,
11959			mouseup: this._onMouseUp,
11960			keydown: this._onKeyDown
11961		}, this);
11962	},
11963
11964	_onMouseUp: function (e) {
11965		if ((e.which !== 1) && (e.button !== 1)) { return; }
11966
11967		this._finish();
11968
11969		if (!this._moved) { return; }
11970		// Postpone to next JS tick so internal click event handling
11971		// still see it as "moved".
11972		setTimeout(L.bind(this._resetState, this), 0);
11973
11974		var bounds = new L.LatLngBounds(
11975		        this._map.containerPointToLatLng(this._startPoint),
11976		        this._map.containerPointToLatLng(this._point));
11977
11978		this._map
11979			.fitBounds(bounds)
11980			.fire('boxzoomend', {boxZoomBounds: bounds});
11981	},
11982
11983	_onKeyDown: function (e) {
11984		if (e.keyCode === 27) {
11985			this._finish();
11986		}
11987	}
11988});
11989
11990// @section Handlers
11991// @property boxZoom: Handler
11992// Box (shift-drag with mouse) zoom handler.
11993L.Map.addInitHook('addHandler', 'boxZoom', L.Map.BoxZoom);
11994
11995
11996
11997/*
11998 * L.Map.Keyboard is handling keyboard interaction with the map, enabled by default.
11999 */
12000
12001// @namespace Map
12002// @section Keyboard Navigation Options
12003L.Map.mergeOptions({
12004	// @option keyboard: Boolean = true
12005	// Makes the map focusable and allows users to navigate the map with keyboard
12006	// arrows and `+`/`-` keys.
12007	keyboard: true,
12008
12009	// @option keyboardPanDelta: Number = 80
12010	// Amount of pixels to pan when pressing an arrow key.
12011	keyboardPanDelta: 80
12012});
12013
12014L.Map.Keyboard = L.Handler.extend({
12015
12016	keyCodes: {
12017		left:    [37],
12018		right:   [39],
12019		down:    [40],
12020		up:      [38],
12021		zoomIn:  [187, 107, 61, 171],
12022		zoomOut: [189, 109, 54, 173]
12023	},
12024
12025	initialize: function (map) {
12026		this._map = map;
12027
12028		this._setPanDelta(map.options.keyboardPanDelta);
12029		this._setZoomDelta(map.options.zoomDelta);
12030	},
12031
12032	addHooks: function () {
12033		var container = this._map._container;
12034
12035		// make the container focusable by tabbing
12036		if (container.tabIndex <= 0) {
12037			container.tabIndex = '0';
12038		}
12039
12040		L.DomEvent.on(container, {
12041			focus: this._onFocus,
12042			blur: this._onBlur,
12043			mousedown: this._onMouseDown
12044		}, this);
12045
12046		this._map.on({
12047			focus: this._addHooks,
12048			blur: this._removeHooks
12049		}, this);
12050	},
12051
12052	removeHooks: function () {
12053		this._removeHooks();
12054
12055		L.DomEvent.off(this._map._container, {
12056			focus: this._onFocus,
12057			blur: this._onBlur,
12058			mousedown: this._onMouseDown
12059		}, this);
12060
12061		this._map.off({
12062			focus: this._addHooks,
12063			blur: this._removeHooks
12064		}, this);
12065	},
12066
12067	_onMouseDown: function () {
12068		if (this._focused) { return; }
12069
12070		var body = document.body,
12071		    docEl = document.documentElement,
12072		    top = body.scrollTop || docEl.scrollTop,
12073		    left = body.scrollLeft || docEl.scrollLeft;
12074
12075		this._map._container.focus();
12076
12077		window.scrollTo(left, top);
12078	},
12079
12080	_onFocus: function () {
12081		this._focused = true;
12082		this._map.fire('focus');
12083	},
12084
12085	_onBlur: function () {
12086		this._focused = false;
12087		this._map.fire('blur');
12088	},
12089
12090	_setPanDelta: function (panDelta) {
12091		var keys = this._panKeys = {},
12092		    codes = this.keyCodes,
12093		    i, len;
12094
12095		for (i = 0, len = codes.left.length; i < len; i++) {
12096			keys[codes.left[i]] = [-1 * panDelta, 0];
12097		}
12098		for (i = 0, len = codes.right.length; i < len; i++) {
12099			keys[codes.right[i]] = [panDelta, 0];
12100		}
12101		for (i = 0, len = codes.down.length; i < len; i++) {
12102			keys[codes.down[i]] = [0, panDelta];
12103		}
12104		for (i = 0, len = codes.up.length; i < len; i++) {
12105			keys[codes.up[i]] = [0, -1 * panDelta];
12106		}
12107	},
12108
12109	_setZoomDelta: function (zoomDelta) {
12110		var keys = this._zoomKeys = {},
12111		    codes = this.keyCodes,
12112		    i, len;
12113
12114		for (i = 0, len = codes.zoomIn.length; i < len; i++) {
12115			keys[codes.zoomIn[i]] = zoomDelta;
12116		}
12117		for (i = 0, len = codes.zoomOut.length; i < len; i++) {
12118			keys[codes.zoomOut[i]] = -zoomDelta;
12119		}
12120	},
12121
12122	_addHooks: function () {
12123		L.DomEvent.on(document, 'keydown', this._onKeyDown, this);
12124	},
12125
12126	_removeHooks: function () {
12127		L.DomEvent.off(document, 'keydown', this._onKeyDown, this);
12128	},
12129
12130	_onKeyDown: function (e) {
12131		if (e.altKey || e.ctrlKey || e.metaKey) { return; }
12132
12133		var key = e.keyCode,
12134		    map = this._map,
12135		    offset;
12136
12137		if (key in this._panKeys) {
12138
12139			if (map._panAnim && map._panAnim._inProgress) { return; }
12140
12141			offset = this._panKeys[key];
12142			if (e.shiftKey) {
12143				offset = L.point(offset).multiplyBy(3);
12144			}
12145
12146			map.panBy(offset);
12147
12148			if (map.options.maxBounds) {
12149				map.panInsideBounds(map.options.maxBounds);
12150			}
12151
12152		} else if (key in this._zoomKeys) {
12153			map.setZoom(map.getZoom() + (e.shiftKey ? 3 : 1) * this._zoomKeys[key]);
12154
12155		} else if (key === 27) {
12156			map.closePopup();
12157
12158		} else {
12159			return;
12160		}
12161
12162		L.DomEvent.stop(e);
12163	}
12164});
12165
12166// @section Handlers
12167// @section Handlers
12168// @property keyboard: Handler
12169// Keyboard navigation handler.
12170L.Map.addInitHook('addHandler', 'keyboard', L.Map.Keyboard);
12171
12172
12173
12174/*
12175 * L.Handler.MarkerDrag is used internally by L.Marker to make the markers draggable.
12176 */
12177
12178
12179/* @namespace Marker
12180 * @section Interaction handlers
12181 *
12182 * 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:
12183 *
12184 * ```js
12185 * marker.dragging.disable();
12186 * ```
12187 *
12188 * @property dragging: Handler
12189 * Marker dragging handler (by both mouse and touch).
12190 */
12191
12192L.Handler.MarkerDrag = L.Handler.extend({
12193	initialize: function (marker) {
12194		this._marker = marker;
12195	},
12196
12197	addHooks: function () {
12198		var icon = this._marker._icon;
12199
12200		if (!this._draggable) {
12201			this._draggable = new L.Draggable(icon, icon, true);
12202		}
12203
12204		this._draggable.on({
12205			dragstart: this._onDragStart,
12206			drag: this._onDrag,
12207			dragend: this._onDragEnd
12208		}, this).enable();
12209
12210		L.DomUtil.addClass(icon, 'leaflet-marker-draggable');
12211	},
12212
12213	removeHooks: function () {
12214		this._draggable.off({
12215			dragstart: this._onDragStart,
12216			drag: this._onDrag,
12217			dragend: this._onDragEnd
12218		}, this).disable();
12219
12220		if (this._marker._icon) {
12221			L.DomUtil.removeClass(this._marker._icon, 'leaflet-marker-draggable');
12222		}
12223	},
12224
12225	moved: function () {
12226		return this._draggable && this._draggable._moved;
12227	},
12228
12229	_onDragStart: function () {
12230		// @section Dragging events
12231		// @event dragstart: Event
12232		// Fired when the user starts dragging the marker.
12233
12234		// @event movestart: Event
12235		// Fired when the marker starts moving (because of dragging).
12236
12237		this._oldLatLng = this._marker.getLatLng();
12238		this._marker
12239		    .closePopup()
12240		    .fire('movestart')
12241		    .fire('dragstart');
12242	},
12243
12244	_onDrag: function (e) {
12245		var marker = this._marker,
12246		    shadow = marker._shadow,
12247		    iconPos = L.DomUtil.getPosition(marker._icon),
12248		    latlng = marker._map.layerPointToLatLng(iconPos);
12249
12250		// update shadow position
12251		if (shadow) {
12252			L.DomUtil.setPosition(shadow, iconPos);
12253		}
12254
12255		marker._latlng = latlng;
12256		e.latlng = latlng;
12257		e.oldLatLng = this._oldLatLng;
12258
12259		// @event drag: Event
12260		// Fired repeatedly while the user drags the marker.
12261		marker
12262		    .fire('move', e)
12263		    .fire('drag', e);
12264	},
12265
12266	_onDragEnd: function (e) {
12267		// @event dragend: DragEndEvent
12268		// Fired when the user stops dragging the marker.
12269
12270		// @event moveend: Event
12271		// Fired when the marker stops moving (because of dragging).
12272		delete this._oldLatLng;
12273		this._marker
12274		    .fire('moveend')
12275		    .fire('dragend', e);
12276	}
12277});
12278
12279
12280
12281/*
12282 * @class Control
12283 * @aka L.Control
12284 * @inherits Class
12285 *
12286 * L.Control is a base class for implementing map controls. Handles positioning.
12287 * All other controls extend from this class.
12288 */
12289
vendor: 4,188 bytes, lines 12290-12457
12290L.Control = L.Class.extend({
12291	// @section
12292	// @aka Control options
12293	options: {
12294		// @option position: String = 'topright'
12295		// The position of the control (one of the map corners). Possible values are `'topleft'`,
12296		// `'topright'`, `'bottomleft'` or `'bottomright'`
12297		position: 'topright'
12298	},
12299
12300	initialize: function (options) {
12301		L.setOptions(this, options);
12302	},
12303
12304	/* @section
12305	 * Classes extending L.Control will inherit the following methods:
12306	 *
12307	 * @method getPosition: string
12308	 * Returns the position of the control.
12309	 */
12310	getPosition: function () {
12311		return this.options.position;
12312	},
12313
12314	// @method setPosition(position: string): this
12315	// Sets the position of the control.
12316	setPosition: function (position) {
12317		var map = this._map;
12318
12319		if (map) {
12320			map.removeControl(this);
12321		}
12322
12323		this.options.position = position;
12324
12325		if (map) {
12326			map.addControl(this);
12327		}
12328
12329		return this;
12330	},
12331
12332	// @method getContainer: HTMLElement
12333	// Returns the HTMLElement that contains the control.
12334	getContainer: function () {
12335		return this._container;
12336	},
12337
12338	// @method addTo(map: Map): this
12339	// Adds the control to the given map.
12340	addTo: function (map) {
12341		this.remove();
12342		this._map = map;
12343
12344		var container = this._container = this.onAdd(map),
12345		    pos = this.getPosition(),
12346		    corner = map._controlCorners[pos];
12347
12348		L.DomUtil.addClass(container, 'leaflet-control');
12349
12350		if (pos.indexOf('bottom') !== -1) {
12351			corner.insertBefore(container, corner.firstChild);
12352		} else {
12353			corner.appendChild(container);
12354		}
12355
12356		return this;
12357	},
12358
12359	// @method remove: this
12360	// Removes the control from the map it is currently active on.
12361	remove: function () {
12362		if (!this._map) {
12363			return this;
12364		}
12365
12366		L.DomUtil.remove(this._container);
12367
12368		if (this.onRemove) {
12369			this.onRemove(this._map);
12370		}
12371
12372		this._map = null;
12373
12374		return this;
12375	},
12376
12377	_refocusOnMap: function (e) {
12378		// if map exists and event is not a keyboard event
12379		if (this._map && e && e.screenX > 0 && e.screenY > 0) {
12380			this._map.getContainer().focus();
12381		}
12382	}
12383});
12384
12385L.control = function (options) {
12386	return new L.Control(options);
12387};
12388
12389/* @section Extension methods
12390 * @uninheritable
12391 *
12392 * Every control should extend from `L.Control` and (re-)implement the following methods.
12393 *
12394 * @method onAdd(map: Map): HTMLElement
12395 * Should return the container DOM element for the control and add listeners on relevant map events. Called on [`control.addTo(map)`](#control-addTo).
12396 *
12397 * @method onRemove(map: Map)
12398 * Optional method. Should contain all clean up code that removes the listeners previously added in [`onAdd`](#control-onadd). Called on [`control.remove()`](#control-remove).
12399 */
12400
12401/* @namespace Map
12402 * @section Methods for Layers and Controls
12403 */
12404L.Map.include({
12405	// @method addControl(control: Control): this
12406	// Adds the given control to the map
12407	addControl: function (control) {
12408		control.addTo(this);
12409		return this;
12410	},
12411
12412	// @method removeControl(control: Control): this
12413	// Removes the given control from the map
12414	removeControl: function (control) {
12415		control.remove();
12416		return this;
12417	},
12418
12419	_initControlPos: function () {
12420		var corners = this._controlCorners = {},
12421		    l = 'leaflet-',
12422		    container = this._controlContainer =
12423		            L.DomUtil.create('div', l + 'control-container', this._container);
12424
12425		function createCorner(vSide, hSide) {
12426			var className = l + vSide + ' ' + l + hSide;
12427
12428			corners[vSide + hSide] = L.DomUtil.create('div', className, container);
12429		}
12430
12431		createCorner('top', 'left');
12432		createCorner('top', 'right');
12433		createCorner('bottom', 'left');
12434		createCorner('bottom', 'right');
12435	},
12436
12437	_clearControlPos: function () {
12438		L.DomUtil.remove(this._controlContainer);
12439	}
12440});
12441
12442
12443
12444/*
12445 * @class Control.Zoom
12446 * @aka L.Control.Zoom
12447 * @inherits Control
12448 *
12449 * 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`.
12450 */
12451
12452L.Control.Zoom = L.Control.extend({
12453	// @section
12454	// @aka Control.Zoom options
12455	options: {
12456		position: 'topleft',
12457
12458		// @option zoomInText: String = '+'
12459		// The text set on the 'zoom in' button.
12460		zoomInText: '+',
12461
12462		// @option zoomInTitle: String = 'Zoom in'
12463		// The title set on the 'zoom in' button.
12464		zoomInTitle: 'Zoom in',
12465
12466		// @option zoomOutText: String = '-'
12467		// The text set on the 'zoom out' button.
12468		zoomOutText: '-',
12469
12470		// @option zoomOutTitle: String = 'Zoom out'
12471		// The title set on the 'zoom out' button.
12472		zoomOutTitle: 'Zoom out'
12473	},
12474
12475	onAdd: function (map) {
12476		var zoomName = 'leaflet-control-zoom',
12477		    container = L.DomUtil.create('div', zoomName + ' leaflet-bar'),
12478		    options = this.options;
12479
12480		this._zoomInButton  = this._createButton(options.zoomInText, options.zoomInTitle,
12481		        zoomName + '-in',  container, this._zoomIn);
12482		this._zoomOutButton = this._createButton(options.zoomOutText, options.zoomOutTitle,
12483		        zoomName + '-out', container, this._zoomOut);
12484
12485		this._updateDisabled();
12486		map.on('zoomend zoomlevelschange', this._updateDisabled, this);
12487
12488		return container;
12489	},
12490
12491	onRemove: function (map) {
12492		map.off('zoomend zoomlevelschange', this._updateDisabled, this);
12493	},
12494
12495	disable: function () {
12496		this._disabled = true;
12497		this._updateDisabled();
12498		return this;
12499	},
12500
12501	enable: function () {
12502		this._disabled = false;
12503		this._updateDisabled();
12504		return this;
12505	},
12506
12507	_zoomIn: function (e) {
12508		if (!this._disabled && this._map._zoom < this._map.getMaxZoom()) {
12509			this._map.zoomIn(this._map.options.zoomDelta * (e.shiftKey ? 3 : 1));
12510		}
12511	},
12512
12513	_zoomOut: function (e) {
12514		if (!this._disabled && this._map._zoom > this._map.getMinZoom()) {
12515			this._map.zoomOut(this._map.options.zoomDelta * (e.shiftKey ? 3 : 1));
12516		}
12517	},
12518
12519	_createButton: function (html, title, className, container, fn) {
12520		var link = L.DomUtil.create('a', className, container);
12521		link.innerHTML = html;
12522		link.href = '#';
12523		link.title = title;
12524
12525		/*
12526		 * Will force screen readers like VoiceOver to read this as "Zoom in - button"
12527		 */
12528		link.setAttribute('role', 'button');
12529		link.setAttribute('aria-label', title);
12530
12531		L.DomEvent
12532		    .on(link, 'mousedown dblclick', L.DomEvent.stopPropagation)
12533		    .on(link, 'click', L.DomEvent.stop)
12534		    .on(link, 'click', fn, this)
12535		    .on(link, 'click', this._refocusOnMap, this);
12536
12537		return link;
12538	},
12539
12540	_updateDisabled: function () {
12541		var map = this._map,
12542		    className = 'leaflet-disabled';
12543
12544		L.DomUtil.removeClass(this._zoomInButton, className);
12545		L.DomUtil.removeClass(this._zoomOutButton, className);
12546
12547		if (this._disabled || map._zoom === map.getMinZoom()) {
12548			L.DomUtil.addClass(this._zoomOutButton, className);
12549		}
12550		if (this._disabled || map._zoom === map.getMaxZoom()) {
12551			L.DomUtil.addClass(this._zoomInButton, className);
12552		}
12553	}
12554});
12555
12556// @namespace Map
12557// @section Control options
12558// @option zoomControl: Boolean = true
12559// Whether a [zoom control](#control-zoom) is added to the map by default.
12560L.Map.mergeOptions({
12561	zoomControl: true
12562});
12563
12564L.Map.addInitHook(function () {
12565	if (this.options.zoomControl) {
12566		this.zoomControl = new L.Control.Zoom();
12567		this.addControl(this.zoomControl);
12568	}
12569});
12570
12571// @namespace Control.Zoom
12572// @factory L.control.zoom(options: Control.Zoom options)
12573// Creates a zoom control
12574L.control.zoom = function (options) {
12575	return new L.Control.Zoom(options);
12576};
12577
12578
12579
12580/*
12581 * @class Control.Attribution
12582 * @aka L.Control.Attribution
12583 * @inherits Control
12584 *
12585 * 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.
12586 */
12587
12588L.Control.Attribution = L.Control.extend({
12589	// @section
12590	// @aka Control.Attribution options
12591	options: {
12592		position: 'bottomright',
12593
12594		// @option prefix: String = 'Leaflet'
12595		// The HTML text shown before the attributions. Pass `false` to disable.
12596		prefix: '<a href="http://leafletjs.com" title="A JS library for interactive maps">Leaflet</a>'
12597	},
12598
12599	initialize: function (options) {
12600		L.setOptions(this, options);
12601
12602		this._attributions = {};
12603	},
12604
12605	onAdd: function (map) {
12606		map.attributionControl = this;
12607		this._container = L.DomUtil.create('div', 'leaflet-control-attribution');
12608		if (L.DomEvent) {
12609			L.DomEvent.disableClickPropagation(this._container);
12610		}
12611
12612		// TODO ugly, refactor
12613		for (var i in map._layers) {
12614			if (map._layers[i].getAttribution) {
12615				this.addAttribution(map._layers[i].getAttribution());
12616			}
12617		}
12618
12619		this._update();
12620
12621		return this._container;
12622	},
12623
12624	// @method setPrefix(prefix: String): this
12625	// Sets the text before the attributions.
12626	setPrefix: function (prefix) {
12627		this.options.prefix = prefix;
12628		this._update();
12629		return this;
12630	},
12631
12632	// @method addAttribution(text: String): this
12633	// Adds an attribution text (e.g. `'Vector data &copy; Mapbox'`).
12634	addAttribution: function (text) {
12635		if (!text) { return this; }
12636
12637		if (!this._attributions[text]) {
12638			this._attributions[text] = 0;
12639		}
12640		this._attributions[text]++;
12641
12642		this._update();
12643
12644		return this;
12645	},
12646
12647	// @method removeAttribution(text: String): this
12648	// Removes an attribution text.
12649	removeAttribution: function (text) {
12650		if (!text) { return this; }
12651
12652		if (this._attributions[text]) {
12653			this._attributions[text]--;
12654			this._update();
12655		}
12656
12657		return this;
12658	},
12659
12660	_update: function () {
12661		if (!this._map) { return; }
12662
12663		var attribs = [];
12664
12665		for (var i in this._attributions) {
12666			if (this._attributions[i]) {
12667				attribs.push(i);
12668			}
12669		}
12670
12671		var prefixAndAttribs = [];
12672
12673		if (this.options.prefix) {
12674			prefixAndAttribs.push(this.options.prefix);
12675		}
12676		if (attribs.length) {
12677			prefixAndAttribs.push(attribs.join(', '));
12678		}
12679
12680		this._container.innerHTML = prefixAndAttribs.join(' | ');
12681	}
12682});
12683
12684// @namespace Map
12685// @section Control options
12686// @option attributionControl: Boolean = true
12687// Whether a [attribution control](#control-attribution) is added to the map by default.
12688L.Map.mergeOptions({
12689	attributionControl: true
12690});
12691
12692L.Map.addInitHook(function () {
12693	if (this.options.attributionControl) {
12694		new L.Control.Attribution().addTo(this);
12695	}
12696});
12697
12698// @namespace Control.Attribution
12699// @factory L.control.attribution(options: Control.Attribution options)
12700// Creates an attribution control.
12701L.control.attribution = function (options) {
12702	return new L.Control.Attribution(options);
12703};
12704
12705
12706
12707/*
12708 * @class Control.Scale
12709 * @aka L.Control.Scale
12710 * @inherits Control
12711 *
12712 * 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`.
12713 *
12714 * @example
12715 *
12716 * ```js
12717 * L.control.scale().addTo(map);
12718 * ```
12719 */
12720
12721L.Control.Scale = L.Control.extend({
12722	// @section
12723	// @aka Control.Scale options
12724	options: {
12725		position: 'bottomleft',
12726
12727		// @option maxWidth: Number = 100
12728		// Maximum width of the control in pixels. The width is set dynamically to show round values (e.g. 100, 200, 500).
12729		maxWidth: 100,
12730
12731		// @option metric: Boolean = True
12732		// Whether to show the metric scale line (m/km).
12733		metric: true,
12734
12735		// @option imperial: Boolean = True
12736		// Whether to show the imperial scale line (mi/ft).
vendor: 7,728 bytes, lines 12737-12999
12737		imperial: true
12738
12739		// @option updateWhenIdle: Boolean = false
12740		// If `true`, the control is updated on [`moveend`](#map-moveend), otherwise it's always up-to-date (updated on [`move`](#map-move)).
12741	},
12742
12743	onAdd: function (map) {
12744		var className = 'leaflet-control-scale',
12745		    container = L.DomUtil.create('div', className),
12746		    options = this.options;
12747
12748		this._addScales(options, className + '-line', container);
12749
12750		map.on(options.updateWhenIdle ? 'moveend' : 'move', this._update, this);
12751		map.whenReady(this._update, this);
12752
12753		return container;
12754	},
12755
12756	onRemove: function (map) {
12757		map.off(this.options.updateWhenIdle ? 'moveend' : 'move', this._update, this);
12758	},
12759
12760	_addScales: function (options, className, container) {
12761		if (options.metric) {
12762			this._mScale = L.DomUtil.create('div', className, container);
12763		}
12764		if (options.imperial) {
12765			this._iScale = L.DomUtil.create('div', className, container);
12766		}
12767	},
12768
12769	_update: function () {
12770		var map = this._map,
12771		    y = map.getSize().y / 2;
12772
12773		var maxMeters = map.distance(
12774				map.containerPointToLatLng([0, y]),
12775				map.containerPointToLatLng([this.options.maxWidth, y]));
12776
12777		this._updateScales(maxMeters);
12778	},
12779
12780	_updateScales: function (maxMeters) {
12781		if (this.options.metric && maxMeters) {
12782			this._updateMetric(maxMeters);
12783		}
12784		if (this.options.imperial && maxMeters) {
12785			this._updateImperial(maxMeters);
12786		}
12787	},
12788
12789	_updateMetric: function (maxMeters) {
12790		var meters = this._getRoundNum(maxMeters),
12791		    label = meters < 1000 ? meters + ' m' : (meters / 1000) + ' km';
12792
12793		this._updateScale(this._mScale, label, meters / maxMeters);
12794	},
12795
12796	_updateImperial: function (maxMeters) {
12797		var maxFeet = maxMeters * 3.2808399,
12798		    maxMiles, miles, feet;
12799
12800		if (maxFeet > 5280) {
12801			maxMiles = maxFeet / 5280;
12802			miles = this._getRoundNum(maxMiles);
12803			this._updateScale(this._iScale, miles + ' mi', miles / maxMiles);
12804
12805		} else {
12806			feet = this._getRoundNum(maxFeet);
12807			this._updateScale(this._iScale, feet + ' ft', feet / maxFeet);
12808		}
12809	},
12810
12811	_updateScale: function (scale, text, ratio) {
12812		scale.style.width = Math.round(this.options.maxWidth * ratio) + 'px';
12813		scale.innerHTML = text;
12814	},
12815
12816	_getRoundNum: function (num) {
12817		var pow10 = Math.pow(10, (Math.floor(num) + '').length - 1),
12818		    d = num / pow10;
12819
12820		d = d >= 10 ? 10 :
12821		    d >= 5 ? 5 :
12822		    d >= 3 ? 3 :
12823		    d >= 2 ? 2 : 1;
12824
12825		return pow10 * d;
12826	}
12827});
12828
12829
12830// @factory L.control.scale(options?: Control.Scale options)
12831// Creates an scale control with the given options.
12832L.control.scale = function (options) {
12833	return new L.Control.Scale(options);
12834};
12835
12836
12837
12838/*
12839 * @class Control.Layers
12840 * @aka L.Control.Layers
12841 * @inherits Control
12842 *
12843 * 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.html)). Extends `Control`.
12844 *
12845 * @example
12846 *
12847 * ```js
12848 * var baseLayers = {
12849 * 	"Mapbox": mapbox,
12850 * 	"OpenStreetMap": osm
12851 * };
12852 *
12853 * var overlays = {
12854 * 	"Marker": marker,
12855 * 	"Roads": roadsLayer
12856 * };
12857 *
12858 * L.control.layers(baseLayers, overlays).addTo(map);
12859 * ```
12860 *
12861 * The `baseLayers` and `overlays` parameters are object literals with layer names as keys and `Layer` objects as values:
12862 *
12863 * ```js
12864 * {
12865 *     "<someName1>": layer1,
12866 *     "<someName2>": layer2
12867 * }
12868 * ```
12869 *
12870 * The layer names can contain HTML, which allows you to add additional styling to the items:
12871 *
12872 * ```js
12873 * {"<img src='my-layer-icon' /> <span class='my-layer-item'>My Layer</span>": myLayer}
12874 * ```
12875 */
12876
12877
12878L.Control.Layers = L.Control.extend({
12879	// @section
12880	// @aka Control.Layers options
12881	options: {
12882		// @option collapsed: Boolean = true
12883		// If `true`, the control will be collapsed into an icon and expanded on mouse hover or touch.
12884		collapsed: true,
12885		position: 'topright',
12886
12887		// @option autoZIndex: Boolean = true
12888		// 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.
12889		autoZIndex: true,
12890
12891		// @option hideSingleBase: Boolean = false
12892		// If `true`, the base layers in the control will be hidden when there is only one.
12893		hideSingleBase: false,
12894
12895		// @option sortLayers: Boolean = false
12896		// Whether to sort the layers. When `false`, layers will keep the order
12897		// in which they were added to the control.
12898		sortLayers: false,
12899
12900		// @option sortFunction: Function = *
12901		// A [compare function](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Array/sort)
12902		// that will be used for sorting the layers, when `sortLayers` is `true`.
12903		// The function receives both the `L.Layer` instances and their names, as in
12904		// `sortFunction(layerA, layerB, nameA, nameB)`.
12905		// By default, it sorts layers alphabetically by their name.
12906		sortFunction: function (layerA, layerB, nameA, nameB) {
12907			return nameA < nameB ? -1 : (nameB < nameA ? 1 : 0);
12908		}
12909	},
12910
12911	initialize: function (baseLayers, overlays, options) {
12912		L.setOptions(this, options);
12913
12914		this._layers = [];
12915		this._lastZIndex = 0;
12916		this._handlingClick = false;
12917
12918		for (var i in baseLayers) {
12919			this._addLayer(baseLayers[i], i);
12920		}
12921
12922		for (i in overlays) {
12923			this._addLayer(overlays[i], i, true);
12924		}
12925	},
12926
12927	onAdd: function (map) {
12928		this._initLayout();
12929		this._update();
12930
12931		this._map = map;
12932		map.on('zoomend', this._checkDisabledLayers, this);
12933
12934		return this._container;
12935	},
12936
12937	onRemove: function () {
12938		this._map.off('zoomend', this._checkDisabledLayers, this);
12939
12940		for (var i = 0; i < this._layers.length; i++) {
12941			this._layers[i].layer.off('add remove', this._onLayerChange, this);
12942		}
12943	},
12944
12945	// @method addBaseLayer(layer: Layer, name: String): this
12946	// Adds a base layer (radio button entry) with the given name to the control.
12947	addBaseLayer: function (layer, name) {
12948		this._addLayer(layer, name);
12949		return (this._map) ? this._update() : this;
12950	},
12951
12952	// @method addOverlay(layer: Layer, name: String): this
12953	// Adds an overlay (checkbox entry) with the given name to the control.
12954	addOverlay: function (layer, name) {
12955		this._addLayer(layer, name, true);
12956		return (this._map) ? this._update() : this;
12957	},
12958
12959	// @method removeLayer(layer: Layer): this
12960	// Remove the given layer from the control.
12961	removeLayer: function (layer) {
12962		layer.off('add remove', this._onLayerChange, this);
12963
12964		var obj = this._getLayer(L.stamp(layer));
12965		if (obj) {
12966			this._layers.splice(this._layers.indexOf(obj), 1);
12967		}
12968		return (this._map) ? this._update() : this;
12969	},
12970
12971	// @method expand(): this
12972	// Expand the control container if collapsed.
12973	expand: function () {
12974		L.DomUtil.addClass(this._container, 'leaflet-control-layers-expanded');
12975		this._form.style.height = null;
12976		var acceptableHeight = this._map.getSize().y - (this._container.offsetTop + 50);
12977		if (acceptableHeight < this._form.clientHeight) {
12978			L.DomUtil.addClass(this._form, 'leaflet-control-layers-scrollbar');
12979			this._form.style.height = acceptableHeight + 'px';
12980		} else {
12981			L.DomUtil.removeClass(this._form, 'leaflet-control-layers-scrollbar');
12982		}
12983		this._checkDisabledLayers();
12984		return this;
12985	},
12986
12987	// @method collapse(): this
12988	// Collapse the control container if expanded.
12989	collapse: function () {
12990		L.DomUtil.removeClass(this._container, 'leaflet-control-layers-expanded');
12991		return this;
12992	},
12993
12994	_initLayout: function () {
12995		var className = 'leaflet-control-layers',
12996		    container = this._container = L.DomUtil.create('div', className),
12997		    collapsed = this.options.collapsed;
12998
12999		// makes this work on IE touch devices by stopping it from firing a mouseout event when the touch is released
13000		container.setAttribute('aria-haspopup', true);
13001
13002		L.DomEvent.disableClickPropagation(container);
13003		if (!L.Browser.touch) {
13004			L.DomEvent.disableScrollPropagation(container);
13005		}
13006
13007		var form = this._form = L.DomUtil.create('form', className + '-list');
13008
13009		if (collapsed) {
13010			this._map.on('click', this.collapse, this);
13011
13012			if (!L.Browser.android) {
13013				L.DomEvent.on(container, {
13014					mouseenter: this.expand,
13015					mouseleave: this.collapse
13016				}, this);
13017			}
13018		}
13019
13020		var link = this._layersLink = L.DomUtil.create('a', className + '-toggle', container);
13021		link.href = '#';
13022		link.title = 'Layers';
13023
13024		if (L.Browser.touch) {
13025			L.DomEvent
13026			    .on(link, 'click', L.DomEvent.stop)
13027			    .on(link, 'click', this.expand, this);
13028		} else {
13029			L.DomEvent.on(link, 'focus', this.expand, this);
13030		}
13031
13032		// work around for Firefox Android issue https://github.com/Leaflet/Leaflet/issues/2033
13033		L.DomEvent.on(form, 'click', function () {
13034			setTimeout(L.bind(this._onInputClick, this), 0);
13035		}, this);
13036
13037		// TODO keyboard accessibility
13038
13039		if (!collapsed) {
13040			this.expand();
13041		}
13042
13043		this._baseLayersList = L.DomUtil.create('div', className + '-base', form);
13044		this._separator = L.DomUtil.create('div', className + '-separator', form);
13045		this._overlaysList = L.DomUtil.create('div', className + '-overlays', form);
13046
13047		container.appendChild(form);
13048	},
13049
13050	_getLayer: function (id) {
13051		for (var i = 0; i < this._layers.length; i++) {
13052
13053			if (this._layers[i] && L.stamp(this._layers[i].layer) === id) {
13054				return this._layers[i];
13055			}
13056		}
13057	},
13058
13059	_addLayer: function (layer, name, overlay) {
13060		layer.on('add remove', this._onLayerChange, this);
13061
13062		this._layers.push({
13063			layer: layer,
13064			name: name,
13065			overlay: overlay
13066		});
13067
13068		if (this.options.sortLayers) {
13069			this._layers.sort(L.bind(function (a, b) {
13070				return this.options.sortFunction(a.layer, b.layer, a.name, b.name);
13071			}, this));
13072		}
13073
13074		if (this.options.autoZIndex && layer.setZIndex) {
13075			this._lastZIndex++;
13076			layer.setZIndex(this._lastZIndex);
13077		}
13078	},
13079
13080	_update: function () {
13081		if (!this._container) { return this; }
13082
13083		L.DomUtil.empty(this._baseLayersList);
13084		L.DomUtil.empty(this._overlaysList);
13085
13086		var baseLayersPresent, overlaysPresent, i, obj, baseLayersCount = 0;
13087
13088		for (i = 0; i < this._layers.length; i++) {
13089			obj = this._layers[i];
13090			this._addItem(obj);
13091			overlaysPresent = overlaysPresent || obj.overlay;
13092			baseLayersPresent = baseLayersPresent || !obj.overlay;
13093			baseLayersCount += !obj.overlay ? 1 : 0;
13094		}
13095
13096		// Hide base layers section if there's only one layer.
13097		if (this.options.hideSingleBase) {
13098			baseLayersPresent = baseLayersPresent && baseLayersCount > 1;
13099			this._baseLayersList.style.display = baseLayersPresent ? '' : 'none';
13100		}
13101
13102		this._separator.style.display = overlaysPresent && baseLayersPresent ? '' : 'none';
13103
13104		return this;
13105	},
13106
13107	_onLayerChange: function (e) {
13108		if (!this._handlingClick) {
13109			this._update();
13110		}
13111
13112		var obj = this._getLayer(L.stamp(e.target));
13113
13114		// @namespace Map
13115		// @section Layer events
13116		// @event baselayerchange: LayersControlEvent
13117		// Fired when the base layer is changed through the [layer control](#control-layers).
13118		// @event overlayadd: LayersControlEvent
13119		// Fired when an overlay is selected through the [layer control](#control-layers).
13120		// @event overlayremove: LayersControlEvent
13121		// Fired when an overlay is deselected through the [layer control](#control-layers).
13122		// @namespace Control.Layers
13123		var type = obj.overlay ?
13124			(e.type === 'add' ? 'overlayadd' : 'overlayremove') :
13125			(e.type === 'add' ? 'baselayerchange' : null);
13126
13127		if (type) {
13128			this._map.fire(type, obj);
13129		}
13130	},
13131
13132	// IE7 bugs out if you create a radio dynamically, so you have to do it this hacky way (see http://bit.ly/PqYLBe)
13133	_createRadioElement: function (name, checked) {
13134
13135		var radioHtml = '<input type="radio" class="leaflet-control-layers-selector" name="' +
13136				name + '"' + (checked ? ' checked="checked"' : '') + '/>';
13137
13138		var radioFragment = document.createElement('div');
13139		radioFragment.innerHTML = radioHtml;
13140
13141		return radioFragment.firstChild;
13142	},
13143
13144	_addItem: function (obj) {
13145		var label = document.createElement('label'),
13146		    checked = this._map.hasLayer(obj.layer),
13147		    input;
13148
13149		if (obj.overlay) {
13150			input = document.createElement('input');
13151			input.type = 'checkbox';
13152			input.className = 'leaflet-control-layers-selector';
13153			input.defaultChecked = checked;
13154		} else {
13155			input = this._createRadioElement('leaflet-base-layers', checked);
13156		}
13157
13158		input.layerId = L.stamp(obj.layer);
13159
13160		L.DomEvent.on(input, 'click', this._onInputClick, this);
13161
13162		var name = document.createElement('span');
13163		name.innerHTML = ' ' + obj.name;
13164
13165		// Helps from preventing layer control flicker when checkboxes are disabled
13166		// https://github.com/Leaflet/Leaflet/issues/2771
13167		var holder = document.createElement('div');
13168
13169		label.appendChild(holder);
13170		holder.appendChild(input);
13171		holder.appendChild(name);
13172
13173		var container = obj.overlay ? this._overlaysList : this._baseLayersList;
13174		container.appendChild(label);
13175
13176		this._checkDisabledLayers();
13177		return label;
13178	},
13179
13180	_onInputClick: function () {
13181		var inputs = this._form.getElementsByTagName('input'),
13182		    input, layer, hasLayer;
13183		var addedLayers = [],
13184		    removedLayers = [];
13185
13186		this._handlingClick = true;
13187
13188		for (var i = inputs.length - 1; i >= 0; i--) {
13189			input = inputs[i];
13190			layer = this._getLayer(input.layerId).layer;
13191			hasLayer = this._map.hasLayer(layer);
13192
13193			if (input.checked && !hasLayer) {
13194				addedLayers.push(layer);
13195
13196			} else if (!input.checked && hasLayer) {
13197				removedLayers.push(layer);
13198			}
13199		}
13200
13201		// Bugfix issue 2318: Should remove all old layers before readding new ones
13202		for (i = 0; i < removedLayers.length; i++) {
13203			this._map.removeLayer(removedLayers[i]);
13204		}
13205		for (i = 0; i < addedLayers.length; i++) {
13206			this._map.addLayer(addedLayers[i]);
13207		}
13208
13209		this._handlingClick = false;
13210
13211		this._refocusOnMap();
13212	},
13213
13214	_checkDisabledLayers: function () {
13215		var inputs = this._form.getElementsByTagName('input'),
13216		    input,
13217		    layer,
13218		    zoom = this._map.getZoom();
13219
13220		for (var i = inputs.length - 1; i >= 0; i--) {
13221			input = inputs[i];
13222			layer = this._getLayer(input.layerId).layer;
13223			input.disabled = (layer.options.minZoom !== undefined && zoom < layer.options.minZoom) ||
13224			                 (layer.options.maxZoom !== undefined && zoom > layer.options.maxZoom);
13225
13226		}
13227	},
13228
13229	_expand: function () {
13230		// Backward compatibility, remove me in 1.1.
13231		return this.expand();
13232	},
13233
13234	_collapse: function () {
13235		// Backward compatibility, remove me in 1.1.
13236		return this.collapse();
13237	}
13238
13239});
13240
13241
13242// @factory L.control.layers(baselayers?: Object, overlays?: Object, options?: Control.Layers options)
13243// 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.
13244L.control.layers = function (baseLayers, overlays, options) {
13245	return new L.Control.Layers(baseLayers, overlays, options);
13246};
13247
13248
13249
13250}(window, document));
13251//# sourceMappingURL=leaflet-src.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.