PageSourceSearch

https://www.conlins.com/js/libs/leaflet/leaflet-src.js

js conlins.com collected 2026-09-26 10:50:51 UTC 408,631 bytes, 13,932 lines download raw bytes

1/* @preserve
2 * Leaflet 1.4.0+Detached: 3337f36d2a2d2b33946779057619b31f674ff5dc.3337f36, a JS library for interactive maps. http://leafletjs.com
3 * (c) 2010-2018 Vladimir Agafonkin, (c) 2010-2011 CloudMade
4 */
5
6window.dispatchEvent(new Event('resize'));
7
8(function (global, factory) {
9	typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) :
10	typeof define === 'function' && define.amd ? define(['exports'], factory) :
11	(factory((global.L = {})));
12}(this, (function (exports) { 'use strict';
13
14var version = "1.4.0+HEAD.3337f36";
15
16/*
17 * @namespace Util
18 *
19 * Various utility functions, used by Leaflet internally.
20 */
21
22var freeze = Object.freeze;
23Object.freeze = function (obj) { return obj; };
24
25// @function extend(dest: Object, src?: Object): Object
26// Merges the properties of the `src` object (or multiple objects) into `dest` object and returns the latter. Has an `L.extend` shortcut.
27function extend(dest) {
28	var i, j, len, src;
29
30	for (j = 1, len = arguments.length; j < len; j++) {
31		src = arguments[j];
32		for (i in src) {
33			dest[i] = src[i];
vendor: 7,240 bytes, lines 34-235
34		}
35	}
36	return dest;
37}
38
39// @function create(proto: Object, properties?: Object): Object
40// Compatibility polyfill for [Object.create](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Object/create)
41var create = Object.create || (function () {
42	function F() {}
43	return function (proto) {
44		F.prototype = proto;
45		return new F();
46	};
47})();
48
49// @function bind(fn: Function, …): Function
50// 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).
51// Has a `L.bind()` shortcut.
52function bind(fn, obj) {
53	var slice = Array.prototype.slice;
54
55	if (fn.bind) {
56		return fn.bind.apply(fn, slice.call(arguments, 1));
57	}
58
59	var args = slice.call(arguments, 2);
60
61	return function () {
62		return fn.apply(obj, args.length ? args.concat(slice.call(arguments)) : arguments);
63	};
64}
65
66// @property lastId: Number
67// Last unique ID used by [`stamp()`](#util-stamp)
68var lastId = 0;
69
70// @function stamp(obj: Object): Number
71// Returns the unique ID of an object, assigning it one if it doesn't have it.
72function stamp(obj) {
73	/*eslint-disable */
74	obj._leaflet_id = obj._leaflet_id || ++lastId;
75	return obj._leaflet_id;
76	/* eslint-enable */
77}
78
79// @function throttle(fn: Function, time: Number, context: Object): Function
80// Returns a function which executes function `fn` with the given scope `context`
81// (so that the `this` keyword refers to `context` inside `fn`'s code). The function
82// `fn` will be called no more than one time per given amount of `time`. The arguments
83// received by the bound function will be any arguments passed when binding the
84// function, followed by any arguments passed when invoking the bound function.
85// Has an `L.throttle` shortcut.
86function throttle(fn, time, context) {
87	var lock, args, wrapperFn, later;
88
89	later = function () {
90		// reset lock and call if queued
91		lock = false;
92		if (args) {
93			wrapperFn.apply(context, args);
94			args = false;
95		}
96	};
97
98	wrapperFn = function () {
99		if (lock) {
100			// called too soon, queue to call later
101			args = arguments;
102
103		} else {
104			// call and lock until later
105			fn.apply(context, arguments);
106			setTimeout(later, time);
107			lock = true;
108		}
109	};
110
111	return wrapperFn;
112}
113
114// @function wrapNum(num: Number, range: Number[], includeMax?: Boolean): Number
115// Returns the number `num` modulo `range` in such a way so it lies within
116// `range[0]` and `range[1]`. The returned value will be always smaller than
117// `range[1]` unless `includeMax` is set to `true`.
118function wrapNum(x, range, includeMax) {
119	var max = range[1],
120	    min = range[0],
121	    d = max - min;
122	return x === max && includeMax ? x : ((x - min) % d + d) % d + min;
123}
124
125// @function falseFn(): Function
126// Returns a function which always returns `false`.
127function falseFn() { return false; }
128
129// @function formatNum(num: Number, digits?: Number): Number
130// Returns the number `num` rounded to `digits` decimals, or to 6 decimals by default.
131function formatNum(num, digits) {
132	var pow = Math.pow(10, (digits === undefined ? 6 : digits));
133	return Math.round(num * pow) / pow;
134}
135
136// @function trim(str: String): String
137// Compatibility polyfill for [String.prototype.trim](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String/Trim)
138function trim(str) {
139	return str.trim ? str.trim() : str.replace(/^\s+|\s+$/g, '');
140}
141
142// @function splitWords(str: String): String[]
143// Trims and splits the string on whitespace and returns the array of parts.
144function splitWords(str) {
145	return trim(str).split(/\s+/);
146}
147
148// @function setOptions(obj: Object, options: Object): Object
149// Merges the given properties to the `options` of the `obj` object, returning the resulting options. See `Class options`. Has an `L.setOptions` shortcut.
150function setOptions(obj, options) {
151	if (!obj.hasOwnProperty('options')) {
152		obj.options = obj.options ? create(obj.options) : {};
153	}
154	for (var i in options) {
155		obj.options[i] = options[i];
156	}
157	return obj.options;
158}
159
160// @function getParamString(obj: Object, existingUrl?: String, uppercase?: Boolean): String
161// Converts an object into a parameter URL string, e.g. `{a: "foo", b: "bar"}`
162// translates to `'?a=foo&b=bar'`. If `existingUrl` is set, the parameters will
163// be appended at the end. If `uppercase` is `true`, the parameter names will
164// be uppercased (e.g. `'?A=foo&B=bar'`)
165function getParamString(obj, existingUrl, uppercase) {
166	var params = [];
167	for (var i in obj) {
168		params.push(encodeURIComponent(uppercase ? i.toUpperCase() : i) + '=' + encodeURIComponent(obj[i]));
169	}
170	return ((!existingUrl || existingUrl.indexOf('?') === -1) ? '?' : '&') + params.join('&');
171}
172
173var templateRe = /\{ *([\w_-]+) *\}/g;
174
175// @function template(str: String, data: Object): String
176// Simple templating facility, accepts a template string of the form `'Hello {a}, {b}'`
177// and a data object like `{a: 'foo', b: 'bar'}`, returns evaluated string
178// `('Hello foo, bar')`. You can also specify functions instead of strings for
179// data values — they will be evaluated passing `data` as an argument.
180function template(str, data) {
181	return str.replace(templateRe, function (str, key) {
182		var value = data[key];
183
184		if (value === undefined) {
185			throw new Error('No value provided for variable ' + str);
186
187		} else if (typeof value === 'function') {
188			value = value(data);
189		}
190		return value;
191	});
192}
193
194// @function isArray(obj): Boolean
195// Compatibility polyfill for [Array.isArray](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Array/isArray)
196var isArray = Array.isArray || function (obj) {
197	return (Object.prototype.toString.call(obj) === '[object Array]');
198};
199
200// @function indexOf(array: Array, el: Object): Number
201// Compatibility polyfill for [Array.prototype.indexOf](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Array/indexOf)
202function indexOf(array, el) {
203	for (var i = 0; i < array.length; i++) {
204		if (array[i] === el) { return i; }
205	}
206	return -1;
207}
208
209// @property emptyImageUrl: String
210// Data URI string containing a base64-encoded empty GIF image.
211// Used as a hack to free memory from unused images on WebKit-powered
212// mobile devices (by setting image `src` to this string).
213var emptyImageUrl = 'data:image/gif;base64,R0lGODlhAQABAAD/ACwAAAAAAQABAAACADs=';
214
215// inspired by http://paulirish.com/2011/requestanimationframe-for-smart-animating/
216
217function getPrefixed(name) {
218	return window['webkit' + name] || window['moz' + name] || window['ms' + name];
219}
220
221var lastTime = 0;
222
223// fallback for IE 7-8
224function timeoutDefer(fn) {
225	var time = +new Date(),
226	    timeToCall = Math.max(0, 16 - (time - lastTime));
227
228	lastTime = time + timeToCall;
229	return window.setTimeout(fn, timeToCall);
230}
231
232var requestFn = window.requestAnimationFrame || getPrefixed('RequestAnimationFrame') || timeoutDefer;
233var cancelFn = window.cancelAnimationFrame || getPrefixed('CancelAnimationFrame') ||
234		getPrefixed('CancelRequestAnimationFrame') || function (id) { window.clearTimeout(id); };
235
236// @function requestAnimFrame(fn: Function, context?: Object, immediate?: Boolean): Number
237// Schedules `fn` to be executed when the browser repaints. `fn` is bound to
238// `context` if given. When `immediate` is set, `fn` is called immediately if
239// the browser doesn't have native support for
240// [`window.requestAnimationFrame`](https://developer.mozilla.org/docs/Web/API/window/requestAnimationFrame),
241// otherwise it's delayed. Returns a request ID that can be used to cancel the request.
242function requestAnimFrame(fn, context, immediate) {
243	if (immediate && requestFn === timeoutDefer) {
244		fn.call(context);
245	} else {
246		return requestFn.call(window, bind(fn, context));
247	}
248}
249
250// @function cancelAnimFrame(id: Number): undefined
251// Cancels a previous `requestAnimFrame`. See also [window.cancelAnimationFrame](https://developer.mozilla.org/docs/Web/API/window/cancelAnimationFrame).
252function cancelAnimFrame(id) {
253	if (id) {
254		cancelFn.call(window, id);
255	}
256}
257
258
259var Util = (Object.freeze || Object)({
260	freeze: freeze,
261	extend: extend,
262	create: create,
263	bind: bind,
264	lastId: lastId,
265	stamp: stamp,
266	throttle: throttle,
267	wrapNum: wrapNum,
268	falseFn: falseFn,
269	formatNum: formatNum,
270	trim: trim,
271	splitWords: splitWords,
272	setOptions: setOptions,
273	getParamString: getParamString,
274	template: template,
275	isArray: isArray,
276	indexOf: indexOf,
277	emptyImageUrl: emptyImageUrl,
278	requestFn: requestFn,
279	cancelFn: cancelFn,
280	requestAnimFrame: requestAnimFrame,
281	cancelAnimFrame: cancelAnimFrame
282});
283
284// @class Class
285// @aka L.Class
286
287// @section
288// @uninheritable
289
290// Thanks to John Resig and Dean Edwards for inspiration!
291
292function Class() {}
293
294Class.extend = function (props) {
295
296	// @function extend(props: Object): Function
297	// [Extends the current class](#class-inheritance) given the properties to be included.
298	// Returns a Javascript function that is a class constructor (to be called with `new`).
299	var NewClass = function () {
300
301		// call the constructor
302		if (this.initialize) {
303			this.initialize.apply(this, arguments);
304		}
305
306		// call all constructor hooks
307		this.callInitHooks();
308	};
309
310	var parentProto = NewClass.__super__ = this.prototype;
311
312	var proto = create(parentProto);
313	proto.constructor = NewClass;
314
315	NewClass.prototype = proto;
316
317	// inherit parent's statics
318	for (var i in this) {
319		if (this.hasOwnProperty(i) && i !== 'prototype' && i !== '__super__') {
320			NewClass[i] = this[i];
321		}
322	}
323
324	// mix static properties into the class
325	if (props.statics) {
326		extend(NewClass, props.statics);
327		delete props.statics;
328	}
329
330	// mix includes into the prototype
331	if (props.includes) {
332		checkDeprecatedMixinEvents(props.includes);
333		extend.apply(null, [proto].concat(props.includes));
334		delete props.includes;
335	}
336
337	// merge options
338	if (proto.options) {
339		props.options = extend(create(proto.options), props.options);
340	}
341
342	// mix given properties into the prototype
343	extend(proto, props);
344
345	proto._initHooks = [];
346
347	// add method for calling all hooks
348	proto.callInitHooks = function () {
349
350		if (this._initHooksCalled) { return; }
351
352		if (parentProto.callInitHooks) {
353			parentProto.callInitHooks.call(this);
354		}
355
356		this._initHooksCalled = true;
357
358		for (var i = 0, len = proto._initHooks.length; i < len; i++) {
359			proto._initHooks[i].call(this);
360		}
361	};
362
363	return NewClass;
364};
365
366
367// @function include(properties: Object): this
368// [Includes a mixin](#class-includes) into the current class.
369Class.include = function (props) {
370	extend(this.prototype, props);
371	return this;
372};
373
374// @function mergeOptions(options: Object): this
375// [Merges `options`](#class-options) into the defaults of the class.
376Class.mergeOptions = function (options) {
377	extend(this.prototype.options, options);
378	return this;
379};
380
381// @function addInitHook(fn: Function): this
382// Adds a [constructor hook](#class-constructor-hooks) to the class.
383Class.addInitHook = function (fn) { // (Function) || (String, args...)
384	var args = Array.prototype.slice.call(arguments, 1);
385
386	var init = typeof fn === 'function' ? fn : function () {
387		this[fn].apply(this, args);
388	};
389
390	this.prototype._initHooks = this.prototype._initHooks || [];
391	this.prototype._initHooks.push(init);
392	return this;
393};
394
395function checkDeprecatedMixinEvents(includes) {
396	if (typeof L === 'undefined' || !L || !L.Mixin) { return; }
397
398	includes = isArray(includes) ? includes : [includes];
399
400	for (var i = 0; i < includes.length; i++) {
401		if (includes[i] === L.Mixin.Events) {
402			console.warn('Deprecated include of L.Mixin.Events: ' +
403				'this property will be removed in future releases, ' +
404				'please inherit from L.Evented instead.', new Error().stack);
405		}
406	}
407}
408
409/*
410 * @class Evented
411 * @aka L.Evented
412 * @inherits Class
413 *
414 * A set of methods shared between event-powered classes (like `Map` and `Marker`). Generally, events allow you to execute some function when something happens with an object (e.g. the user clicks on the map, causing the map to fire `'click'` event).
415 *
416 * @example
417 *
418 * ```js
419 * map.on('click', function(e) {
420 * 	alert(e.latlng);
421 * } );
422 * ```
423 *
424 * Leaflet deals with event listeners by reference, so if you want to add a listener and then remove it, define it as a function:
425 *
426 * ```js
427 * function onClick(e) { ... }
428 *
429 * map.on('click', onClick);
430 * map.off('click', onClick);
431 * ```
432 */
433
434var Events = {
435	/* @method on(type: String, fn: Function, context?: Object): this
436	 * Adds a listener function (`fn`) to a particular event type of the object. You can optionally specify the context of the listener (object the this keyword will point to). You can also pass several space-separated types (e.g. `'click dblclick'`).
437	 *
438	 * @alternative
439	 * @method on(eventMap: Object): this
440	 * Adds a set of type/listener pairs, e.g. `{click: onClick, mousemove: onMouseMove}`
441	 */
442	on: function (types, fn, context) {
443
444		// types can be a map of types/handlers
445		if (typeof types === 'object') {
446			for (var type in types) {
447				// we don't process space-separated events here for performance;
448				// it's a hot path since Layer uses the on(obj) syntax
449				this._on(type, types[type], fn);
450			}
451
452		} else {
453			// types can be a string of space-separated words
454			types = splitWords(types);
455
456			for (var i = 0, len = types.length; i < len; i++) {
457				this._on(types[i], fn, context);
458			}
459		}
460
461		return this;
462	},
463
464	/* @method off(type: String, fn?: Function, context?: Object): this
465	 * Removes a previously added listener function. If no function is specified, it will remove all the listeners of that particular event from the object. Note that if you passed a custom context to `on`, you must pass the same context to `off` in order to remove the listener.
466	 *
467	 * @alternative
468	 * @method off(eventMap: Object): this
469	 * Removes a set of type/listener pairs.
470	 *
471	 * @alternative
472	 * @method off: this
473	 * Removes all listeners to all events on the object.
474	 */
475	off: function (types, fn, context) {
476
477		if (!types) {
478			// clear all listeners if called without arguments
479			delete this._events;
480
481		} else if (typeof types === 'object') {
482			for (var type in types) {
483				this._off(type, types[type], fn);
484			}
485
486		} else {
487			types = splitWords(types);
488
489			for (var i = 0, len = types.length; i < len; i++) {
490				this._off(types[i], fn, context);
491			}
492		}
493
494		return this;
495	},
496
497	// attach listener (without syntactic sugar now)
498	_on: function (type, fn, context) {
499		this._events = this._events || {};
500
501		/* get/init listeners for type */
502		var typeListeners = this._events[type];
503		if (!typeListeners) {
504			typeListeners = [];
505			this._events[type] = typeListeners;
506		}
507
508		if (context === this) {
509			// Less memory footprint.
510			context = undefined;
511		}
512		var newListener = {fn: fn, ctx: context},
513		    listeners = typeListeners;
514
515		// check if fn already there
516		for (var i = 0, len = listeners.length; i < len; i++) {
517			if (listeners[i].fn === fn && listeners[i].ctx === context) {
518				return;
519			}
520		}
521
522		listeners.push(newListener);
523	},
524
525	_off: function (type, fn, context) {
526		var listeners,
527		    i,
528		    len;
529
530		if (!this._events) { return; }
531
532		listeners = this._events[type];
533
534		if (!listeners) {
535			return;
536		}
537
538		if (!fn) {
539			// Set all removed listeners to noop so they are not called if remove happens in fire
540			for (i = 0, len = listeners.length; i < len; i++) {
541				listeners[i].fn = falseFn;
542			}
543			// clear all listeners for a type if function isn't specified
544			delete this._events[type];
545			return;
546		}
547
548		if (context === this) {
549			context = undefined;
550		}
551
552		if (listeners) {
553
554			// find fn and remove it
555			for (i = 0, len = listeners.length; i < len; i++) {
556				var l = listeners[i];
557				if (l.ctx !== context) { continue; }
558				if (l.fn === fn) {
559
560					// set the removed listener to noop so that's not called if remove happens in fire
561					l.fn = falseFn;
562
563					if (this._firingCount) {
564						/* copy array in case events are being fired */
565						this._events[type] = listeners = listeners.slice();
566					}
567					listeners.splice(i, 1);
568
569					return;
570				}
571			}
572		}
573	},
574
575	// @method fire(type: String, data?: Object, propagate?: Boolean): this
576	// Fires an event of the specified type. You can optionally provide an data
577	// object — the first argument of the listener function will contain its
578	// properties. The event can optionally be propagated to event parents.
579	fire: function (type, data, propagate) {
580		if (!this.listens(type, propagate)) { return this; }
581
582		var event = extend({}, data, {
583			type: type,
584			target: this,
585			sourceTarget: data && data.sourceTarget || this
586		});
587
588		if (this._events) {
589			var listeners = this._events[type];
590
591			if (listeners) {
592				this._firingCount = (this._firingCount + 1) || 1;
593				for (var i = 0, len = listeners.length; i < len; i++) {
594					var l = listeners[i];
595					l.fn.call(l.ctx || this, event);
596				}
597
598				this._firingCount--;
599			}
600		}
601
602		if (propagate) {
603			// propagate the event to parents (set with addEventParent)
604			this._propagateEvent(event);
605		}
606
607		return this;
608	},
609
610	// @method listens(type: String): Boolean
611	// Returns `true` if a particular event type has any listeners attached to it.
612	listens: function (type, propagate) {
613		var listeners = this._events && this._events[type];
614		if (listeners && listeners.length) { return true; }
615
616		if (propagate) {
617			// also check parents for listeners if event propagates
618			for (var id in this._eventParents) {
619				if (this._eventParents[id].listens(type, propagate)) { return true; }
620			}
621		}
622		return false;
623	},
624
625	// @method once(…): this
626	// Behaves as [`on(…)`](#evented-on), except the listener will only get fired once and then removed.
627	once: function (types, fn, context) {
628
629		if (typeof types === 'object') {
630			for (var type in types) {
631				this.once(type, types[type], fn);
632			}
633			return this;
634		}
635
636		var handler = bind(function () {
637			this
638			    .off(types, fn, context)
639			    .off(types, handler, context);
640		}, this);
641
642		// add a listener that's executed once and removed after that
643		return this
644		    .on(types, fn, context)
645		    .on(types, handler, context);
646	},
647
648	// @method addEventParent(obj: Evented): this
649	// Adds an event parent - an `Evented` that will receive propagated events
650	addEventParent: function (obj) {
651		this._eventParents = this._eventParents || {};
652		this._eventParents[stamp(obj)] = obj;
653		return this;
654	},
655
656	// @method removeEventParent(obj: Evented): this
657	// Removes an event parent, so it will stop receiving propagated events
658	removeEventParent: function (obj) {
659		if (this._eventParents) {
660			delete this._eventParents[stamp(obj)];
661		}
662		return this;
663	},
664
vendor: 12,682 bytes, lines 665-1120
665	_propagateEvent: function (e) {
666		for (var id in this._eventParents) {
667			this._eventParents[id].fire(e.type, extend({
668				layer: e.target,
669				propagatedFrom: e.target
670			}, e), true);
671		}
672	}
673};
674
675// aliases; we should ditch those eventually
676
677// @method addEventListener(…): this
678// Alias to [`on(…)`](#evented-on)
679Events.addEventListener = Events.on;
680
681// @method removeEventListener(…): this
682// Alias to [`off(…)`](#evented-off)
683
684// @method clearAllEventListeners(…): this
685// Alias to [`off()`](#evented-off)
686Events.removeEventListener = Events.clearAllEventListeners = Events.off;
687
688// @method addOneTimeEventListener(…): this
689// Alias to [`once(…)`](#evented-once)
690Events.addOneTimeEventListener = Events.once;
691
692// @method fireEvent(…): this
693// Alias to [`fire(…)`](#evented-fire)
694Events.fireEvent = Events.fire;
695
696// @method hasEventListeners(…): Boolean
697// Alias to [`listens(…)`](#evented-listens)
698Events.hasEventListeners = Events.listens;
699
700var Evented = Class.extend(Events);
701
702/*
703 * @class Point
704 * @aka L.Point
705 *
706 * Represents a point with `x` and `y` coordinates in pixels.
707 *
708 * @example
709 *
710 * ```js
711 * var point = L.point(200, 300);
712 * ```
713 *
714 * 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:
715 *
716 * ```js
717 * map.panBy([200, 300]);
718 * map.panBy(L.point(200, 300));
719 * ```
720 *
721 * Note that `Point` does not inherit from Leafet's `Class` object,
722 * which means new classes can't inherit from it, and new methods
723 * can't be added to it with the `include` function.
724 */
725
726function Point(x, y, round) {
727	// @property x: Number; The `x` coordinate of the point
728	this.x = (round ? Math.round(x) : x);
729	// @property y: Number; The `y` coordinate of the point
730	this.y = (round ? Math.round(y) : y);
731}
732
733var trunc = Math.trunc || function (v) {
734	return v > 0 ? Math.floor(v) : Math.ceil(v);
735};
736
737Point.prototype = {
738
739	// @method clone(): Point
740	// Returns a copy of the current point.
741	clone: function () {
742		return new Point(this.x, this.y);
743	},
744
745	// @method add(otherPoint: Point): Point
746	// Returns the result of addition of the current and the given points.
747	add: function (point) {
748		// non-destructive, returns a new point
749		return this.clone()._add(toPoint(point));
750	},
751
752	_add: function (point) {
753		// destructive, used directly for performance in situations where it's safe to modify existing point
754		this.x += point.x;
755		this.y += point.y;
756		return this;
757	},
758
759	// @method subtract(otherPoint: Point): Point
760	// Returns the result of subtraction of the given point from the current.
761	subtract: function (point) {
762		return this.clone()._subtract(toPoint(point));
763	},
764
765	_subtract: function (point) {
766		this.x -= point.x;
767		this.y -= point.y;
768		return this;
769	},
770
771	// @method divideBy(num: Number): Point
772	// Returns the result of division of the current point by the given number.
773	divideBy: function (num) {
774		return this.clone()._divideBy(num);
775	},
776
777	_divideBy: function (num) {
778		this.x /= num;
779		this.y /= num;
780		return this;
781	},
782
783	// @method multiplyBy(num: Number): Point
784	// Returns the result of multiplication of the current point by the given number.
785	multiplyBy: function (num) {
786		return this.clone()._multiplyBy(num);
787	},
788
789	_multiplyBy: function (num) {
790		this.x *= num;
791		this.y *= num;
792		return this;
793	},
794
795	// @method scaleBy(scale: Point): Point
796	// Multiply each coordinate of the current point by each coordinate of
797	// `scale`. In linear algebra terms, multiply the point by the
798	// [scaling matrix](https://en.wikipedia.org/wiki/Scaling_%28geometry%29#Matrix_representation)
799	// defined by `scale`.
800	scaleBy: function (point) {
801		return new Point(this.x * point.x, this.y * point.y);
802	},
803
804	// @method unscaleBy(scale: Point): Point
805	// Inverse of `scaleBy`. Divide each coordinate of the current point by
806	// each coordinate of `scale`.
807	unscaleBy: function (point) {
808		return new Point(this.x / point.x, this.y / point.y);
809	},
810
811	// @method round(): Point
812	// Returns a copy of the current point with rounded coordinates.
813	round: function () {
814		return this.clone()._round();
815	},
816
817	_round: function () {
818		this.x = Math.round(this.x);
819		this.y = Math.round(this.y);
820		return this;
821	},
822
823	// @method floor(): Point
824	// Returns a copy of the current point with floored coordinates (rounded down).
825	floor: function () {
826		return this.clone()._floor();
827	},
828
829	_floor: function () {
830		this.x = Math.floor(this.x);
831		this.y = Math.floor(this.y);
832		return this;
833	},
834
835	// @method ceil(): Point
836	// Returns a copy of the current point with ceiled coordinates (rounded up).
837	ceil: function () {
838		return this.clone()._ceil();
839	},
840
841	_ceil: function () {
842		this.x = Math.ceil(this.x);
843		this.y = Math.ceil(this.y);
844		return this;
845	},
846
847	// @method trunc(): Point
848	// Returns a copy of the current point with truncated coordinates (rounded towards zero).
849	trunc: function () {
850		return this.clone()._trunc();
851	},
852
853	_trunc: function () {
854		this.x = trunc(this.x);
855		this.y = trunc(this.y);
856		return this;
857	},
858
859	// @method distanceTo(otherPoint: Point): Number
860	// Returns the cartesian distance between the current and the given points.
861	distanceTo: function (point) {
862		point = toPoint(point);
863
864		var x = point.x - this.x,
865		    y = point.y - this.y;
866
867		return Math.sqrt(x * x + y * y);
868	},
869
870	// @method equals(otherPoint: Point): Boolean
871	// Returns `true` if the given point has the same coordinates.
872	equals: function (point) {
873		point = toPoint(point);
874
875		return point.x === this.x &&
876		       point.y === this.y;
877	},
878
879	// @method contains(otherPoint: Point): Boolean
880	// Returns `true` if both coordinates of the given point are less than the corresponding current point coordinates (in absolute values).
881	contains: function (point) {
882		point = toPoint(point);
883
884		return Math.abs(point.x) <= Math.abs(this.x) &&
885		       Math.abs(point.y) <= Math.abs(this.y);
886	},
887
888	// @method toString(): String
889	// Returns a string representation of the point for debugging purposes.
890	toString: function () {
891		return 'Point(' +
892		        formatNum(this.x) + ', ' +
893		        formatNum(this.y) + ')';
894	}
895};
896
897// @factory L.point(x: Number, y: Number, round?: Boolean)
898// Creates a Point object with the given `x` and `y` coordinates. If optional `round` is set to true, rounds the `x` and `y` values.
899
900// @alternative
901// @factory L.point(coords: Number[])
902// Expects an array of the form `[x, y]` instead.
903
904// @alternative
905// @factory L.point(coords: Object)
906// Expects a plain object of the form `{x: Number, y: Number}` instead.
907function toPoint(x, y, round) {
908	if (x instanceof Point) {
909		return x;
910	}
911	if (isArray(x)) {
912		return new Point(x[0], x[1]);
913	}
914	if (x === undefined || x === null) {
915		return x;
916	}
917	if (typeof x === 'object' && 'x' in x && 'y' in x) {
918		return new Point(x.x, x.y);
919	}
920	return new Point(x, y, round);
921}
922
923/*
924 * @class Bounds
925 * @aka L.Bounds
926 *
927 * Represents a rectangular area in pixel coordinates.
928 *
929 * @example
930 *
931 * ```js
932 * var p1 = L.point(10, 10),
933 * p2 = L.point(40, 60),
934 * bounds = L.bounds(p1, p2);
935 * ```
936 *
937 * 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:
938 *
939 * ```js
940 * otherBounds.intersects([[10, 10], [40, 60]]);
941 * ```
942 *
943 * Note that `Bounds` does not inherit from Leafet's `Class` object,
944 * which means new classes can't inherit from it, and new methods
945 * can't be added to it with the `include` function.
946 */
947
948function Bounds(a, b) {
949	if (!a) { return; }
950
951	var points = b ? [a, b] : a;
952
953	for (var i = 0, len = points.length; i < len; i++) {
954		this.extend(points[i]);
955	}
956}
957
958Bounds.prototype = {
959	// @method extend(point: Point): this
960	// Extends the bounds to contain the given point.
961	extend: function (point) { // (Point)
962		point = toPoint(point);
963
964		// @property min: Point
965		// The top left corner of the rectangle.
966		// @property max: Point
967		// The bottom right corner of the rectangle.
968		if (!this.min && !this.max) {
969			this.min = point.clone();
970			this.max = point.clone();
971		} else {
972			this.min.x = Math.min(point.x, this.min.x);
973			this.max.x = Math.max(point.x, this.max.x);
974			this.min.y = Math.min(point.y, this.min.y);
975			this.max.y = Math.max(point.y, this.max.y);
976		}
977		return this;
978	},
979
980	// @method getCenter(round?: Boolean): Point
981	// Returns the center point of the bounds.
982	getCenter: function (round) {
983		return new Point(
984		        (this.min.x + this.max.x) / 2,
985		        (this.min.y + this.max.y) / 2, round);
986	},
987
988	// @method getBottomLeft(): Point
989	// Returns the bottom-left point of the bounds.
990	getBottomLeft: function () {
991		return new Point(this.min.x, this.max.y);
992	},
993
994	// @method getTopRight(): Point
995	// Returns the top-right point of the bounds.
996	getTopRight: function () { // -> Point
997		return new Point(this.max.x, this.min.y);
998	},
999
1000	// @method getTopLeft(): Point
1001	// Returns the top-left point of the bounds (i.e. [`this.min`](#bounds-min)).
1002	getTopLeft: function () {
1003		return this.min; // left, top
1004	},
1005
1006	// @method getBottomRight(): Point
1007	// Returns the bottom-right point of the bounds (i.e. [`this.max`](#bounds-max)).
1008	getBottomRight: function () {
1009		return this.max; // right, bottom
1010	},
1011
1012	// @method getSize(): Point
1013	// Returns the size of the given bounds
1014	getSize: function () {
1015		return this.max.subtract(this.min);
1016	},
1017
1018	// @method contains(otherBounds: Bounds): Boolean
1019	// Returns `true` if the rectangle contains the given one.
1020	// @alternative
1021	// @method contains(point: Point): Boolean
1022	// Returns `true` if the rectangle contains the given point.
1023	contains: function (obj) {
1024		var min, max;
1025
1026		if (typeof obj[0] === 'number' || obj instanceof Point) {
1027			obj = toPoint(obj);
1028		} else {
1029			obj = toBounds(obj);
1030		}
1031
1032		if (obj instanceof Bounds) {
1033			min = obj.min;
1034			max = obj.max;
1035		} else {
1036			min = max = obj;
1037		}
1038
1039		return (min.x >= this.min.x) &&
1040		       (max.x <= this.max.x) &&
1041		       (min.y >= this.min.y) &&
1042		       (max.y <= this.max.y);
1043	},
1044
1045	// @method intersects(otherBounds: Bounds): Boolean
1046	// Returns `true` if the rectangle intersects the given bounds. Two bounds
1047	// intersect if they have at least one point in common.
1048	intersects: function (bounds) { // (Bounds) -> Boolean
1049		bounds = toBounds(bounds);
1050
1051		var min = this.min,
1052		    max = this.max,
1053		    min2 = bounds.min,
1054		    max2 = bounds.max,
1055		    xIntersects = (max2.x >= min.x) && (min2.x <= max.x),
1056		    yIntersects = (max2.y >= min.y) && (min2.y <= max.y);
1057
1058		return xIntersects && yIntersects;
1059	},
1060
1061	// @method overlaps(otherBounds: Bounds): Boolean
1062	// Returns `true` if the rectangle overlaps the given bounds. Two bounds
1063	// overlap if their intersection is an area.
1064	overlaps: function (bounds) { // (Bounds) -> Boolean
1065		bounds = toBounds(bounds);
1066
1067		var min = this.min,
1068		    max = this.max,
1069		    min2 = bounds.min,
1070		    max2 = bounds.max,
1071		    xOverlaps = (max2.x > min.x) && (min2.x < max.x),
1072		    yOverlaps = (max2.y > min.y) && (min2.y < max.y);
1073
1074		return xOverlaps && yOverlaps;
1075	},
1076
1077	isValid: function () {
1078		return !!(this.min && this.max);
1079	}
1080};
1081
1082
1083// @factory L.bounds(corner1: Point, corner2: Point)
1084// Creates a Bounds object from two corners coordinate pairs.
1085// @alternative
1086// @factory L.bounds(points: Point[])
1087// Creates a Bounds object from the given array of points.
1088function toBounds(a, b) {
1089	if (!a || a instanceof Bounds) {
1090		return a;
1091	}
1092	return new Bounds(a, b);
1093}
1094
1095/*
1096 * @class LatLngBounds
1097 * @aka L.LatLngBounds
1098 *
1099 * Represents a rectangular geographical area on a map.
1100 *
1101 * @example
1102 *
1103 * ```js
1104 * var corner1 = L.latLng(40.712, -74.227),
1105 * corner2 = L.latLng(40.774, -74.125),
1106 * bounds = L.latLngBounds(corner1, corner2);
1107 * ```
1108 *
1109 * 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:
1110 *
1111 * ```js
1112 * map.fitBounds([
1113 * 	[40.712, -74.227],
1114 * 	[40.774, -74.125]
1115 * ]);
1116 * ```
1117 *
1118 * 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.
1119 *
1120 * Note that `LatLngBounds` does not inherit from Leaf
vendor: 4,947 bytes, lines 1120-1290
1120et's `Class` object,
1121 * which means new classes can't inherit from it, and new methods
1122 * can't be added to it with the `include` function.
1123 */
1124
1125function LatLngBounds(corner1, corner2) { // (LatLng, LatLng) or (LatLng[])
1126	if (!corner1) { return; }
1127
1128	var latlngs = corner2 ? [corner1, corner2] : corner1;
1129
1130	for (var i = 0, len = latlngs.length; i < len; i++) {
1131		this.extend(latlngs[i]);
1132	}
1133}
1134
1135LatLngBounds.prototype = {
1136
1137	// @method extend(latlng: LatLng): this
1138	// Extend the bounds to contain the given point
1139
1140	// @alternative
1141	// @method extend(otherBounds: LatLngBounds): this
1142	// Extend the bounds to contain the given bounds
1143	extend: function (obj) {
1144		var sw = this._southWest,
1145		    ne = this._northEast,
1146		    sw2, ne2;
1147
1148		if (obj instanceof LatLng) {
1149			sw2 = obj;
1150			ne2 = obj;
1151
1152		} else if (obj instanceof LatLngBounds) {
1153			sw2 = obj._southWest;
1154			ne2 = obj._northEast;
1155
1156			if (!sw2 || !ne2) { return this; }
1157
1158		} else {
1159			return obj ? this.extend(toLatLng(obj) || toLatLngBounds(obj)) : this;
1160		}
1161
1162		if (!sw && !ne) {
1163			this._southWest = new LatLng(sw2.lat, sw2.lng);
1164			this._northEast = new LatLng(ne2.lat, ne2.lng);
1165		} else {
1166			sw.lat = Math.min(sw2.lat, sw.lat);
1167			sw.lng = Math.min(sw2.lng, sw.lng);
1168			ne.lat = Math.max(ne2.lat, ne.lat);
1169			ne.lng = Math.max(ne2.lng, ne.lng);
1170		}
1171
1172		return this;
1173	},
1174
1175	// @method pad(bufferRatio: Number): LatLngBounds
1176	// Returns bounds created by extending or retracting the current bounds by a given ratio in each direction.
1177	// For example, a ratio of 0.5 extends the bounds by 50% in each direction.
1178	// Negative values will retract the bounds.
1179	pad: function (bufferRatio) {
1180		var sw = this._southWest,
1181		    ne = this._northEast,
1182		    heightBuffer = Math.abs(sw.lat - ne.lat) * bufferRatio,
1183		    widthBuffer = Math.abs(sw.lng - ne.lng) * bufferRatio;
1184
1185		return new LatLngBounds(
1186		        new LatLng(sw.lat - heightBuffer, sw.lng - widthBuffer),
1187		        new LatLng(ne.lat + heightBuffer, ne.lng + widthBuffer));
1188	},
1189
1190	// @method getCenter(): LatLng
1191	// Returns the center point of the bounds.
1192	getCenter: function () {
1193		return new LatLng(
1194		        (this._southWest.lat + this._northEast.lat) / 2,
1195		        (this._southWest.lng + this._northEast.lng) / 2);
1196	},
1197
1198	// @method getSouthWest(): LatLng
1199	// Returns the south-west point of the bounds.
1200	getSouthWest: function () {
1201		return this._southWest;
1202	},
1203
1204	// @method getNorthEast(): LatLng
1205	// Returns the north-east point of the bounds.
1206	getNorthEast: function () {
1207		return this._northEast;
1208	},
1209
1210	// @method getNorthWest(): LatLng
1211	// Returns the north-west point of the bounds.
1212	getNorthWest: function () {
1213		return new LatLng(this.getNorth(), this.getWest());
1214	},
1215
1216	// @method getSouthEast(): LatLng
1217	// Returns the south-east point of the bounds.
1218	getSouthEast: function () {
1219		return new LatLng(this.getSouth(), this.getEast());
1220	},
1221
1222	// @method getWest(): Number
1223	// Returns the west longitude of the bounds
1224	getWest: function () {
1225		return this._southWest.lng;
1226	},
1227
1228	// @method getSouth(): Number
1229	// Returns the south latitude of the bounds
1230	getSouth: function () {
1231		return this._southWest.lat;
1232	},
1233
1234	// @method getEast(): Number
1235	// Returns the east longitude of the bounds
1236	getEast: function () {
1237		return this._northEast.lng;
1238	},
1239
1240	// @method getNorth(): Number
1241	// Returns the north latitude of the bounds
1242	getNorth: function () {
1243		return this._northEast.lat;
1244	},
1245
1246	// @method contains(otherBounds: LatLngBounds): Boolean
1247	// Returns `true` if the rectangle contains the given one.
1248
1249	// @alternative
1250	// @method contains (latlng: LatLng): Boolean
1251	// Returns `true` if the rectangle contains the given point.
1252	contains: function (obj) { // (LatLngBounds) or (LatLng) -> Boolean
1253		if (typeof obj[0] === 'number' || obj instanceof LatLng || 'lat' in obj) {
1254			obj = toLatLng(obj);
1255		} else {
1256			obj = toLatLngBounds(obj);
1257		}
1258
1259		var sw = this._southWest,
1260		    ne = this._northEast,
1261		    sw2, ne2;
1262
1263		if (obj instanceof LatLngBounds) {
1264			sw2 = obj.getSouthWest();
1265			ne2 = obj.getNorthEast();
1266		} else {
1267			sw2 = ne2 = obj;
1268		}
1269
1270		return (sw2.lat >= sw.lat) && (ne2.lat <= ne.lat) &&
1271		       (sw2.lng >= sw.lng) && (ne2.lng <= ne.lng);
1272	},
1273
1274	// @method intersects(otherBounds: LatLngBounds): Boolean
1275	// Returns `true` if the rectangle intersects the given bounds. Two bounds intersect if they have at least one point in common.
1276	intersects: function (bounds) {
1277		bounds = toLatLngBounds(bounds);
1278
1279		var sw = this._southWest,
1280		    ne = this._northEast,
1281		    sw2 = bounds.getSouthWest(),
1282		    ne2 = bounds.getNorthEast(),
1283
1284		    latIntersects = (ne2.lat >= sw.lat) && (sw2.lat <= ne.lat),
1285		    lngIntersects = (ne2.lng >= sw.lng) && (sw2.lng <= ne.lng);
1286
1287		return latIntersects && lngIntersects;
1288	},
1289
1290	// @method overlaps(otherBounds: 
1290Bounds): Boolean
1291	// Returns `true` if the rectangle overlaps the given bounds. Two bounds overlap if their intersection is an area.
1292	overlaps: function (bounds) {
1293		bounds = toLatLngBounds(bounds);
1294
1295		var sw = this._southWest,
1296		    ne = this._northEast,
vendor: 6,186 bytes, lines 1297-1483
1297		    sw2 = bounds.getSouthWest(),
1298		    ne2 = bounds.getNorthEast(),
1299
1300		    latOverlaps = (ne2.lat > sw.lat) && (sw2.lat < ne.lat),
1301		    lngOverlaps = (ne2.lng > sw.lng) && (sw2.lng < ne.lng);
1302
1303		return latOverlaps && lngOverlaps;
1304	},
1305
1306	// @method toBBoxString(): String
1307	// 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.
1308	toBBoxString: function () {
1309		return [this.getWest(), this.getSouth(), this.getEast(), this.getNorth()].join(',');
1310	},
1311
1312	// @method equals(otherBounds: LatLngBounds, maxMargin?: Number): Boolean
1313	// Returns `true` if the rectangle is equivalent (within a small margin of error) to the given bounds. The margin of error can be overridden by setting `maxMargin` to a small number.
1314	equals: function (bounds, maxMargin) {
1315		if (!bounds) { return false; }
1316
1317		bounds = toLatLngBounds(bounds);
1318
1319		return this._southWest.equals(bounds.getSouthWest(), maxMargin) &&
1320		       this._northEast.equals(bounds.getNorthEast(), maxMargin);
1321	},
1322
1323	// @method isValid(): Boolean
1324	// Returns `true` if the bounds are properly initialized.
1325	isValid: function () {
1326		return !!(this._southWest && this._northEast);
1327	}
1328};
1329
1330// TODO International date line?
1331
1332// @factory L.latLngBounds(corner1: LatLng, corner2: LatLng)
1333// Creates a `LatLngBounds` object by defining two diagonally opposite corners of the rectangle.
1334
1335// @alternative
1336// @factory L.latLngBounds(latlngs: LatLng[])
1337// 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).
1338function toLatLngBounds(a, b) {
1339	if (a instanceof LatLngBounds) {
1340		return a;
1341	}
1342	return new LatLngBounds(a, b);
1343}
1344
1345/* @class LatLng
1346 * @aka L.LatLng
1347 *
1348 * Represents a geographical point with a certain latitude and longitude.
1349 *
1350 * @example
1351 *
1352 * ```
1353 * var latlng = L.latLng(50.5, 30.5);
1354 * ```
1355 *
1356 * 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:
1357 *
1358 * ```
1359 * map.panTo([50, 30]);
1360 * map.panTo({lon: 30, lat: 50});
1361 * map.panTo({lat: 50, lng: 30});
1362 * map.panTo(L.latLng(50, 30));
1363 * ```
1364 *
1365 * Note that `LatLng` does not inherit from Leaflet's `Class` object,
1366 * which means new classes can't inherit from it, and new methods
1367 * can't be added to it with the `include` function.
1368 */
1369
1370function LatLng(lat, lng, alt) {
1371	if (isNaN(lat) || isNaN(lng)) {
1372		throw new Error('Invalid LatLng object: (' + lat + ', ' + lng + ')');
1373	}
1374
1375	// @property lat: Number
1376	// Latitude in degrees
1377	this.lat = +lat;
1378
1379	// @property lng: Number
1380	// Longitude in degrees
1381	this.lng = +lng;
1382
1383	// @property alt: Number
1384	// Altitude in meters (optional)
1385	if (alt !== undefined) {
1386		this.alt = +alt;
1387	}
1388}
1389
1390LatLng.prototype = {
1391	// @method equals(otherLatLng: LatLng, maxMargin?: Number): Boolean
1392	// Returns `true` if the given `LatLng` point is at the same position (within a small margin of error). The margin of error can be overridden by setting `maxMargin` to a small number.
1393	equals: function (obj, maxMargin) {
1394		if (!obj) { return false; }
1395
1396		obj = toLatLng(obj);
1397
1398		var margin = Math.max(
1399		        Math.abs(this.lat - obj.lat),
1400		        Math.abs(this.lng - obj.lng));
1401
1402		return margin <= (maxMargin === undefined ? 1.0E-9 : maxMargin);
1403	},
1404
1405	// @method toString(): String
1406	// Returns a string representation of the point (for debugging purposes).
1407	toString: function (precision) {
1408		return 'LatLng(' +
1409		        formatNum(this.lat, precision) + ', ' +
1410		        formatNum(this.lng, precision) + ')';
1411	},
1412
1413	// @method distanceTo(otherLatLng: LatLng): Number
1414	// Returns the distance (in meters) to the given `LatLng` calculated using the [Spherical Law of Cosines](https://en.wikipedia.org/wiki/Spherical_law_of_cosines).
1415	distanceTo: function (other) {
1416		return Earth.distance(this, toLatLng(other));
1417	},
1418
1419	// @method wrap(): LatLng
1420	// Returns a new `LatLng` object with the longitude wrapped so it's always between -180 and +180 degrees.
1421	wrap: function () {
1422		return Earth.wrapLatLng(this);
1423	},
1424
1425	// @method toBounds(sizeInMeters: Number): LatLngBounds
1426	// Returns a new `LatLngBounds` object in which each boundary is `sizeInMeters/2` meters apart from the `LatLng`.
1427	toBounds: function (sizeInMeters) {
1428		var latAccuracy = 180 * sizeInMeters / 40075017,
1429		    lngAccuracy = latAccuracy / Math.cos((Math.PI / 180) * this.lat);
1430
1431		return toLatLngBounds(
1432		        [this.lat - latAccuracy, this.lng - lngAccuracy],
1433		        [this.lat + latAccuracy, this.lng + lngAccuracy]);
1434	},
1435
1436	clone: function () {
1437		return new LatLng(this.lat, this.lng, this.alt);
1438	}
1439};
1440
1441
1442
1443// @factory L.latLng(latitude: Number, longitude: Number, altitude?: Number): LatLng
1444// Creates an object representing a geographical point with the given latitude and longitude (and optionally altitude).
1445
1446// @alternative
1447// @factory L.latLng(coords: Array): LatLng
1448// Expects an array of the form `[Number, Number]` or `[Number, Number, Number]` instead.
1449
1450// @alternative
1451// @factory L.latLng(coords: Object): LatLng
1452// Expects an plain object of the form `{lat: Number, lng: Number}` or `{lat: Number, lng: Number, alt: Number}` instead.
1453
1454function toLatLng(a, b, c) {
1455	if (a instanceof LatLng) {
1456		return a;
1457	}
1458	if (isArray(a) && typeof a[0] !== 'object') {
1459		if (a.length === 3) {
1460			return new LatLng(a[0], a[1], a[2]);
1461		}
1462		if (a.length === 2) {
1463			return new LatLng(a[0], a[1]);
1464		}
1465		return null;
1466	}
1467	if (a === undefined || a === null) {
1468		return a;
1469	}
1470	if (typeof a === 'object' && 'lat' in a) {
1471		return new LatLng(a.lat, 'lng' in a ? a.lng : a.lon, a.alt);
1472	}
1473	if (b === undefined) {
1474		return null;
1475	}
1476	return new LatLng(a, b, c);
1477}
1478
1479/*
1480 * @namespace CRS
1481 * @crs L.CRS.Base
1482 * Object that defines coordinate reference systems for projecting
1483 * geographical points into pixel (screen) coordinates and back (and to
1484 * coordinates in other units for [WMS](https://en.wikipedia.org/wiki/Web_Map_Service) services). See
1485 * [spatial reference system](http://en.wikipedia.org/wiki/Coordinate_reference_system).
1486 *
1487 * Leaflet defines the most usual CRSs by default. If you want to use a
1488 * CRS not defined by default, take a look at the
1489 * [Proj4Leaflet](https://github.com/kartena/Proj4Leaflet) plugin.
1490 *
1491 * Note that the CRS instances do not inherit from Leafet's `Class` object,
1492 * and can't be instantiated. Also, new classes can't inherit from them,
1493 * and methods can't be added to them with the `include` function.
1494 */
1495
1496var CRS = {
1497	// @method latLngToPoint(latlng: LatLng, zoom: Number): Point
1498	// Projects geographical coordinates into pixel coordinates for a given zoom.
1499	latLngToPoint: function (latlng, zoom) {
1500		var projectedPoint = this.projection.project(latlng),
1501		    scale = this.scale(zoom);
1502
1503		return this.transformation._transform(projectedPoint, scale);
1504	},
1505
1506	// @method pointToLatLng(point: Point, zoom: Number): LatLng
1507	// The inverse of `latLngToPoint`. Projects pixel coordinates on a given
1508	// zoom into geographical coordinates.
1509	pointToLatLng: function (point, zoom) {
1510		var scale = this.scale(zoom),
1511		    untransformedPoint = this.transformation.untransform(point, scale);
1512
1513		return this.projection.unproject(untransformedPoint);
1514	},
1515
1516	// @method project(latlng: LatLng): Point
1517	// Projects geographical coordinates into coordinates in units accepted for
1518	// this CRS (e.g. meters for EPSG:3857, for passing it to WMS services).
1519	project: function (latlng) {
1520		return this.projection.project(latlng);
1521	},
1522
1523	// @method unproject(point: Point): LatLng
1524	// Given a projected coordinate returns the corresponding LatLng.
1525	// The inverse of `project`.
1526	unproject: function (point) {
1527		return this.projection.unproject(point);
1528	},
1529
1530	// @method scale(zoom: Number): Number
1531	// Returns the scale used when transforming projected coordinates into
1532	// pixel coordinates for a particular zoom. For example, it returns
1533	// `256 * 2^zoom` for Mercator-based CRS.
1534	scale: function (zoom) {
1535		return 256 * Math.pow(2, zoom);
1536	},
1537
1538	// @method zoom(scale: Number): Number
1539	// Inverse of `scale()`, returns the zoom level corresponding to a scale
1540	// factor of `scale`.
1541	zoom: function (scale) {
1542		return Math.log(scale / 256) / Math.LN2;
1543	},
1544
1545	// @method getProjectedBounds(zoom: Number): Bounds
1546	// Returns the projection's bounds scaled and transformed for the provided `zoom`.
1547	getProjectedBounds: function (zoom) {
1548		if (this.infinite) { return null; }
1549
1550		var b = this.projection.bounds,
1551		    s = this.scale(zoom),
1552		    min = this.transformation.transform(b.min, s),
1553		    max = this.transformation.transform(b.max, s);
1554
1555		return new Bounds(min, max);
1556	},
1557
1558	// @method distance(latlng1: LatLng, latlng2: LatLng): Number
1559	// Returns the distance between two geographical coordinates.
1560
1561	// @property code: String
1562	// Standard code name of the CRS passed into WMS services (e.g. `'EPSG:3857'`)
1563	//
1564	// @property wrapLng: Number[]
1565	// An array of two numbers defining whether the longitude (horizontal) coordinate
1566	// axis wraps around a given range and how. Defaults to `[-180, 180]` in most
1567	// geographical CRSs. If `undefined`, the longitude axis does not wrap around.
1568	//
1569	// @property wrapLat: Number[]
1570	// Like `wrapLng`, but for the latitude (vertical) axis.
1571
1572	// wrapLng: [min, max],
1573	// wrapLat: [min, max],
1574
1575	// @property infinite: Boolean
1576	// If true, the coordinate space will be unbounded (infinite in both axes)
1577	infinite: false,
1578
1579	// @method wrapLatLng(latlng: LatLng): LatLng
1580	// Returns a `LatLng` where lat and lng has been wrapped according to the
1581	// CRS's `wrapLat` and `wrapLng` properties, if they are outside the CRS's bounds.
1582	wrapLatLng: function (latlng) {
1583		var lng = this.wrapLng ? wrapNum(latlng.lng, this.wrapLng, true) : latlng.lng,
1584		    lat = this.wrapLat ? wrapNum(latlng.lat, this.wrapLat, true) : latlng.lat,
1585		    alt = latlng.alt;
1586
1587		return new LatLng(lat, lng, alt);
1588	},
1589
1590	// @method wrapLatLngBounds(bounds: LatLngBounds): LatLngBounds
1591	// Returns a `LatLngBounds` with the same size as the given one, ensuring
1592	// that its center is within the CRS's bounds.
1593	// Only accepts actual `L.LatLngBounds` instances, not arrays.
1594	wrapLatLngBounds: function (bounds) {
1595		var center = bounds.getCenter(),
1596		    newCenter = this.wrapLatLng(center),
1597		    latShift = center.lat - newCenter.lat,
1598		    lngShift = center.lng - newCenter.lng;
1599
1600		if (latShift === 0 && lngShift === 0) {
1601			return bounds;
1602		}
1603
vendor: 8,834 bytes, lines 1604-1880
1604		var sw = bounds.getSouthWest(),
1605		    ne = bounds.getNorthEast(),
1606		    newSw = new LatLng(sw.lat - latShift, sw.lng - lngShift),
1607		    newNe = new LatLng(ne.lat - latShift, ne.lng - lngShift);
1608
1609		return new LatLngBounds(newSw, newNe);
1610	}
1611};
1612
1613/*
1614 * @namespace CRS
1615 * @crs L.CRS.Earth
1616 *
1617 * Serves as the base for CRS that are global such that they cover the earth.
1618 * Can only be used as the base for other CRS and cannot be used directly,
1619 * since it does not have a `code`, `projection` or `transformation`. `distance()` returns
1620 * meters.
1621 */
1622
1623var Earth = extend({}, CRS, {
1624	wrapLng: [-180, 180],
1625
1626	// Mean Earth Radius, as recommended for use by
1627	// the International Union of Geodesy and Geophysics,
1628	// see http://rosettacode.org/wiki/Haversine_formula
1629	R: 6371000,
1630
1631	// distance between two geographical points using spherical law of cosines approximation
1632	distance: function (latlng1, latlng2) {
1633		var rad = Math.PI / 180,
1634		    lat1 = latlng1.lat * rad,
1635		    lat2 = latlng2.lat * rad,
1636		    sinDLat = Math.sin((latlng2.lat - latlng1.lat) * rad / 2),
1637		    sinDLon = Math.sin((latlng2.lng - latlng1.lng) * rad / 2),
1638		    a = sinDLat * sinDLat + Math.cos(lat1) * Math.cos(lat2) * sinDLon * sinDLon,
1639		    c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
1640		return this.R * c;
1641	}
1642});
1643
1644/*
1645 * @namespace Projection
1646 * @projection L.Projection.SphericalMercator
1647 *
1648 * Spherical Mercator projection — the most common projection for online maps,
1649 * used by almost all free and commercial tile providers. Assumes that Earth is
1650 * a sphere. Used by the `EPSG:3857` CRS.
1651 */
1652
1653var SphericalMercator = {
1654
1655	R: 6378137,
1656	MAX_LATITUDE: 85.0511287798,
1657
1658	project: function (latlng) {
1659		var d = Math.PI / 180,
1660		    max = this.MAX_LATITUDE,
1661		    lat = Math.max(Math.min(max, latlng.lat), -max),
1662		    sin = Math.sin(lat * d);
1663
1664		return new Point(
1665			this.R * latlng.lng * d,
1666			this.R * Math.log((1 + sin) / (1 - sin)) / 2);
1667	},
1668
1669	unproject: function (point) {
1670		var d = 180 / Math.PI;
1671
1672		return new LatLng(
1673			(2 * Math.atan(Math.exp(point.y / this.R)) - (Math.PI / 2)) * d,
1674			point.x * d / this.R);
1675	},
1676
1677	bounds: (function () {
1678		var d = 6378137 * Math.PI;
1679		return new Bounds([-d, -d], [d, d]);
1680	})()
1681};
1682
1683/*
1684 * @class Transformation
1685 * @aka L.Transformation
1686 *
1687 * Represents an affine transformation: a set of coefficients `a`, `b`, `c`, `d`
1688 * for transforming a point of a form `(x, y)` into `(a*x + b, c*y + d)` and doing
1689 * the reverse. Used by Leaflet in its projections code.
1690 *
1691 * @example
1692 *
1693 * ```js
1694 * var transformation = L.transformation(2, 5, -1, 10),
1695 * 	p = L.point(1, 2),
1696 * 	p2 = transformation.transform(p), //  L.point(7, 8)
1697 * 	p3 = transformation.untransform(p2); //  L.point(1, 2)
1698 * ```
1699 */
1700
1701
1702// factory new L.Transformation(a: Number, b: Number, c: Number, d: Number)
1703// Creates a `Transformation` object with the given coefficients.
1704function Transformation(a, b, c, d) {
1705	if (isArray(a)) {
1706		// use array properties
1707		this._a = a[0];
1708		this._b = a[1];
1709		this._c = a[2];
1710		this._d = a[3];
1711		return;
1712	}
1713	this._a = a;
1714	this._b = b;
1715	this._c = c;
1716	this._d = d;
1717}
1718
1719Transformation.prototype = {
1720	// @method transform(point: Point, scale?: Number): Point
1721	// Returns a transformed point, optionally multiplied by the given scale.
1722	// Only accepts actual `L.Point` instances, not arrays.
1723	transform: function (point, scale) { // (Point, Number) -> Point
1724		return this._transform(point.clone(), scale);
1725	},
1726
1727	// destructive transform (faster)
1728	_transform: function (point, scale) {
1729		scale = scale || 1;
1730		point.x = scale * (this._a * point.x + this._b);
1731		point.y = scale * (this._c * point.y + this._d);
1732		return point;
1733	},
1734
1735	// @method untransform(point: Point, scale?: Number): Point
1736	// Returns the reverse transformation of the given point, optionally divided
1737	// by the given scale. Only accepts actual `L.Point` instances, not arrays.
1738	untransform: function (point, scale) {
1739		scale = scale || 1;
1740		return new Point(
1741		        (point.x / scale - this._b) / this._a,
1742		        (point.y / scale - this._d) / this._c);
1743	}
1744};
1745
1746// factory L.transformation(a: Number, b: Number, c: Number, d: Number)
1747
1748// @factory L.transformation(a: Number, b: Number, c: Number, d: Number)
1749// Instantiates a Transformation object with the given coefficients.
1750
1751// @alternative
1752// @factory L.transformation(coefficients: Array): Transformation
1753// Expects an coefficients array of the form
1754// `[a: Number, b: Number, c: Number, d: Number]`.
1755
1756function toTransformation(a, b, c, d) {
1757	return new Transformation(a, b, c, d);
1758}
1759
1760/*
1761 * @namespace CRS
1762 * @crs L.CRS.EPSG3857
1763 *
1764 * The most common CRS for online maps, used by almost all free and commercial
1765 * tile providers. Uses Spherical Mercator projection. Set in by default in
1766 * Map's `crs` option.
1767 */
1768
1769var EPSG3857 = extend({}, Earth, {
1770	code: 'EPSG:3857',
1771	projection: SphericalMercator,
1772
1773	transformation: (function () {
1774		var scale = 0.5 / (Math.PI * SphericalMercator.R);
1775		return toTransformation(scale, 0.5, -scale, 0.5);
1776	}())
1777});
1778
1779var EPSG900913 = extend({}, EPSG3857, {
1780	code: 'EPSG:900913'
1781});
1782
1783// @namespace SVG; @section
1784// There are several static functions which can be called without instantiating L.SVG:
1785
1786// @function create(name: String): SVGElement
1787// Returns a instance of [SVGElement](https://developer.mozilla.org/docs/Web/API/SVGElement),
1788// corresponding to the class name passed. For example, using 'line' will return
1789// an instance of [SVGLineElement](https://developer.mozilla.org/docs/Web/API/SVGLineElement).
1790function svgCreate(name) {
1791	return document.createElementNS('http://www.w3.org/2000/svg', name);
1792}
1793
1794// @function pointsToPath(rings: Point[], closed: Boolean): String
1795// Generates a SVG path string for multiple rings, with each ring turning
1796// into "M..L..L.." instructions
1797function pointsToPath(rings, closed) {
1798	var str = '',
1799	i, j, len, len2, points, p;
1800
1801	for (i = 0, len = rings.length; i < len; i++) {
1802		points = rings[i];
1803
1804		for (j = 0, len2 = points.length; j < len2; j++) {
1805			p = points[j];
1806			str += (j ? 'L' : 'M') + p.x + ' ' + p.y;
1807		}
1808
1809		// closes the ring for polygons; "x" is VML syntax
1810		str += closed ? (svg ? 'z' : 'x') : '';
1811	}
1812
1813	// SVG complains about empty path strings
1814	return str || 'M0 0';
1815}
1816
1817/*
1818 * @namespace Browser
1819 * @aka L.Browser
1820 *
1821 * A namespace with static properties for browser/feature detection used by Leaflet internally.
1822 *
1823 * @example
1824 *
1825 * ```js
1826 * if (L.Browser.ielt9) {
1827 *   alert('Upgrade your browser, dude!');
1828 * }
1829 * ```
1830 */
1831
1832var style$1 = document.documentElement.style;
1833
1834// @property ie: Boolean; `true` for all Internet Explorer versions (not Edge).
1835var ie = 'ActiveXObject' in window;
1836
1837// @property ielt9: Boolean; `true` for Internet Explorer versions less than 9.
1838var ielt9 = ie && !document.addEventListener;
1839
1840// @property edge: Boolean; `true` for the Edge web browser.
1841var edge = 'msLaunchUri' in navigator && !('documentMode' in document);
1842
1843// @property webkit: Boolean;
1844// `true` for webkit-based browsers like Chrome and Safari (including mobile versions).
1845var webkit = userAgentContains('webkit');
1846
1847// @property android: Boolean
1848// `true` for any browser running on an Android platform.
1849var android = userAgentContains('android');
1850
1851// @property android23: Boolean; `true` for browsers running on Android 2 or Android 3.
1852var android23 = userAgentContains('android 2') || userAgentContains('android 3');
1853
1854/* See https://stackoverflow.com/a/17961266 for details on detecting stock Android */
1855var webkitVer = parseInt(/WebKit\/([0-9]+)|$/.exec(navigator.userAgent)[1], 10); // also matches AppleWebKit
1856// @property androidStock: Boolean; `true` for the Android stock browser (i.e. not Chrome)
1857var androidStock = android && userAgentContains('Google') && webkitVer < 537 && !('AudioNode' in window);
1858
1859// @property opera: Boolean; `true` for the Opera browser
1860var opera = !!window.opera;
1861
1862// @property chrome: Boolean; `true` for the Chrome browser.
1863var chrome = userAgentContains('chrome');
1864
1865// @property gecko: Boolean; `true` for gecko-based browsers like Firefox.
1866var gecko = userAgentContains('gecko') && !webkit && !opera && !ie;
1867
1868// @property safari: Boolean; `true` for the Safari browser.
1869var safari = !chrome && userAgentContains('safari');
1870
1871var phantom = userAgentContains('phantom');
1872
1873// @property opera12: Boolean
1874// `true` for the Opera browser supporting CSS transforms (version 12 or later).
1875var opera12 = 'OTransition' in style$1;
1876
1877// @property win: Boolean; `true` when the browser is running in a Windows platform
1878var win = navigator.platform.indexOf('Win') === 0;
1879
1880// @property ie3d: Boolean;
1880 `true` for all Internet Explorer versions supporting CSS transforms.
1881var ie3d = ie && ('transition' in style$1);
1882
1883// @property webkit3d: Boolean; `true` for webkit-based browsers supporting CSS transforms.
1884var webkit3d = ('WebKitCSSMatrix' in window) && ('m11' in new window.WebKitCSSMatrix()) && !android23;
1885
1886// @property gecko3d: Boolean; `true` for gecko-based browsers supporting CSS transforms.
1887var gecko3d = 'MozPerspective' in style$1;
1888
1889// @property any3d: Boolean
1890// `true` for all browsers supporting CSS transforms.
1891var any3d = !window.L_DISABLE_3D && (ie3d || webkit3d || gecko3d) && !opera12 && !phantom;
1892
1893// @property mobile: Boolean; `true` for all browsers running in a mobile device.
1894var mobile = typeof orientation !== 'undefined' || userAgentContains('mobile');
1895
1896// @property mobileWebkit: Boolean; `true` for all webkit-based browsers in a mobile device.
1897var mobileWebkit = mobile && webkit;
1898
1899// @property mobileWebkit3d: Boolean
1900// `true` for all webkit-based browsers in a mobile device supporting CSS transforms.
1901var mobileWebkit3d = mobile && webkit3d;
1902
1903// @property msPointer: Boolean
1904// `true` for browsers implementing the Microsoft touch events model (notably IE10).
1905var msPointer = !window.PointerEvent && window.MSPointerEvent;
1906
1907// @property pointer: Boolean
1908// `true` for all browsers supporting [pointer events](https://msdn.microsoft.com/en-us/library/dn433244%28v=vs.85%29.aspx).
1909var pointer = !!(window.PointerEvent || msPointer);
1910
1911// @property touch: Boolean
1912// `true` for all browsers supporting [touch events](https://developer.mozilla.org/docs/Web/API/Touch_events).
1913// This does not necessarily mean that the browser is running in a computer with
1914// a touchscreen, it only means that the browser is capable of understanding
1915// touch events.
1916var touch = !window.L_NO_TOUCH && (pointer || 'ontouchstart' in window ||
1917		(window.DocumentTouch && document instanceof window.DocumentTouch));
1918
1919// @property mobileOpera: Boolean; `true` for the Opera browser in a mobile device.
1920var mobileOpera = mobile && opera;
1921
1922// @property mobileGecko: Boolean
1923// `true` for gecko-based browsers running in a mobile device.
1924var mobileGecko = mobile && gecko;
1925
1926// @property retina: Boolean
1927// `true` for browsers on a high-resolution "retina" screen or on any screen when browser's display zoom is more than 100%.
1928var retina = (window.devicePixelRatio || (window.screen.deviceXDPI / window.screen.logicalXDPI)) > 1;
1929
1930
1931// @property canvas: Boolean
1932// `true` when the browser supports [`<canvas>`](https://developer.mozilla.org/docs/Web/API/Canvas_API).
1933var canvas = (function () {
1934	return !!document.createElement('canvas').getContext;
1935}());
1936
1937// @property svg: Boolean
1938// `true` when the browser supports [SVG](https://developer.mozilla.org/docs/Web/SVG).
1939var svg = !!(document.createElementNS && svgCreate('svg').createSVGRect);
1940
1941// @property vml: Boolean
1942// `true` if the browser supports [VML](https://en.wikipedia.org/wiki/Vector_Markup_Language).
1943var vml = !svg && (function () {
1944	try {
1945		var div = document.createElement('div');
1946		div.innerHTML = '<v:shape adj="1"/>';
1947
1948		var shape = div.firstChild;
1949		shape.style.behavior = 'url(#default#VML)';
1950
1951		return shape && (typeof shape.adj === 'object');
1952
1953	} catch (e) {
1954		return false;
1955	}
1956}());
1957
1958
1959function userAgentContains(str) {
1960	return navigator.userAgent.toLowerCase().indexOf(str) >= 0;
1961}
1962
1963
1964var Browser = (Object.freeze || Object)({
1965	ie: ie,
1966	ielt9: ielt9,
1967	edge: edge,
1968	webkit: webkit,
1969	android: android,
1970	android23: android23,
1971	androidStock: androidStock,
1972	opera: opera,
1973	chrome: chrome,
1974	gecko: gecko,
1975	safari: safari,
1976	phantom: phantom,
1977	opera12: opera12,
1978	win: win,
1979	ie3d: ie3d,
1980	webkit3d: webkit3d,
1981	gecko3d: gecko3d,
1982	any3d: any3d,
1983	mobile: mobile,
1984	mobileWebkit: mobileWebkit,
1985	mobileWebkit3d: mobileWebkit3d,
1986	msPointer: msPointer,
1987	pointer: pointer,
1988	touch: touch,
1989	mobileOpera: mobileOpera,
1990	mobileGecko: mobileGecko,
1991	retina: retina,
1992	canvas: canvas,
1993	svg: svg,
1994	vml: vml
1995});
1996
1997/*
1998 * Extends L.DomEvent to provide touch support for Internet Explorer and Windows-based devices.
1999 */
2000
2001
2002var POINTER_DOWN =   msPointer ? 'MSPointerDown'   : 'pointerdown';
2003var POINTER_MOVE =   msPointer ? 'MSPointerMove'   : 'pointermove';
2004var POINTER_UP =     msPointer ? 'MSPointerUp'     : 'pointerup';
2005var POINTER_CANCEL = msPointer ? 'MSPointerCancel' : 'pointercancel';
2006var TAG_WHITE_LIST = ['INPUT', 'SELECT', 'OPTION'];
2007
2008var _pointers = {};
2009var _pointerDocListener = false;
2010
2011// DomEvent.DoubleTap needs to know about this
2012var _pointersCount = 0;
2013
2014// Provides a touch events wrapper for (ms)pointer events.
2015// ref http://www.w3.org/TR/pointerevents/ https://www.w3.org/Bugs/Public/show_bug.cgi?id=22890
2016
2017function addPointerListener(obj, type, handler, id) {
2018	if (type === 'touchstart') {
2019		_addPointerStart(obj, handler, id);
2020
2021	} else if (type === 'touchmove') {
2022		_addPointerMove(obj, handler, id);
2023
2024	} else if (type === 'touchend') {
2025		_addPointerEnd(obj, handler, id);
2026	}
2027
2028	return this;
2029}
2030
2031function removePointerListener(obj, type, id) {
2032	var handler = obj['_leaflet_' + type + id];
2033
2034	if (type === 'touchstart') {
2035		obj.removeEventListener(POINTER_DOWN, handler, false);
2036
2037	} else if (type === 'touchmove') {
2038		obj.removeEventListener(POINTER_MOVE, handler, false);
2039
2040	} else if (type === 'touchend') {
2041		obj.removeEventListener(POINTER_UP, handler, false);
2042		obj.removeEventListener(POINTER_CANCEL, handler, false);
2043	}
2044
2045	return this;
2046}
2047
2048function _addPointerStart(obj, handler, id) {
2049	var onDown = bind(function (e) {
2050		if (e.pointerType !== 'mouse' && e.MSPOINTER_TYPE_MOUSE && e.pointerType !== e.MSPOINTER_TYPE_MOUSE) {
2051			// In IE11, some touch events needs to fire for form controls, or
2052			// the controls will stop working. We keep a whitelist of tag names that
2053			// need these events. For other target tags, we prevent default on the event.
2054			if (TAG_WHITE_LIST.indexOf(e.target.tagName) < 0) {
2055				preventDefault(e);
2056			} else {
2057				return;
2058			}
2059		}
2060
2061		_handlePointer(e, handler);
2062	});
2063
2064	obj['_leaflet_touchstart' + id] = onDown;
2065	obj.addEventListener(POINTER_DOWN, onDown, false);
2066
2067	// need to keep track of what pointers and how many are active to provide e.touches emulation
2068	if (!_pointerDocListener) {
2069		// we listen documentElement as any drags that end by moving the touch off the screen get fired there
2070		document.documentElement.addEventListener(POINTER_DOWN, _globalPointerDown, true);
2071		document.documentElement.addEventListener(POINTER_MOVE, _globalPointerMove, true);
2072		document.documentElement.addEventListener(POINTER_UP, _globalPointerUp, true);
2073		document.documentElement.addEventListener(POINTER_CANCEL, _globalPointerUp, true);
2074
2075		_pointerDocListener = true;
2076	}
2077}
2078
2079function _globalPointerDown(e) {
2080	_pointers[e.pointerId] = e;
2081	_pointersCount++;
2082}
2083
2084function _globalPointerMove(e) {
2085	if (_pointers[e.pointerId]) {
2086		_pointers[e.pointerId] = e;
2087	}
2088}
2089
2090function _globalPointerUp(e) {
2091	delete _pointers[e.pointerId];
2092	_pointersCount--;
2093}
2094
2095function _handlePointer(e, handler) {
2096	e.touches = [];
2097	for (var i in _pointers) {
2098		e.touches.push(_pointers[i]);
2099	}
2100	e.changedTouches = [e];
2101
2102	handler(e);
2103}
2104
2105function _addPointerMove(obj, handler, id) {
2106	var onMove = function (e) {
2107		// don't fire touch moves when mouse isn't down
2108		if ((e.pointerType === e.MSPOINTER_TYPE_MOUSE || e.pointerType === 'mouse') && e.buttons === 0) { return; }
2109
2110		_handlePointer(e, handler);
2111	};
2112
2113	obj['_leaflet_touchmove' + id] = onMove;
2114	obj.addEventListener(POINTER_MOVE, onMove, false);
2115}
2116
2117function _addPointerEnd(obj, handler, id) {
2118	var onUp = function (e) {
2119		_handlePointer(e, handler);
2120	};
2121
2122	obj['_leaflet_touchend' + id] = onUp;
2123	obj.addEventListener(POINTER_UP, onUp, false);
2124	obj.addEventListener(POINTER_CANCEL, onUp, false);
2125}
2126
2127/*
2128 * Extends the event handling code with double tap support for mobile browsers.
2129 */
2130
2131var _touchstart = msPointer ? 'MSPointerDown' : pointer ? 'pointerdown' : 'touchstart';
2132var _touchend = msPointer ? 'MSPointerUp' : pointer ? 'pointerup' : 'touchend';
2133var _pre = '_leaflet_';
2134
2135// inspired by Zepto touch code by Thomas Fuchs
2136function addDoubleTapListener(obj, handler, id) {
2137	var last, touch$$1,
2138	    doubleTap = false,
2139	    delay = 250;
2140
2141	function onTouchStart(e) {
2142		var count;
2143
2144		if (pointer) {
2145			if ((!edge) || e.pointerType === 'mouse') { return; }
2146			count = _pointersCount;
2147		} else {
2148			count = e.touches.length;
2149		}
2150
2151		if (count > 1) { return; }
2152
2153		var now = Date.now(),
2154		    delta = now - (last || now);
2155
2156		touch$$1 = e.touches ? e.touches[0] : e;
2157		doubleTap = (delta > 0 && delta <= delay);
2158		last = now;
2159	}
2160
2161	function onTouchEnd(e) {
2162		if (doubleTap && !touch$$1.cancelBubble) {
2163			if (pointer) {
2164				if ((!edge) || e.pointerType === 'mouse') { return; }
2165				// work around .type being readonly with MSPointer* events
2166				var newTouch = {},
2167				    prop, i;
2168
2169				for (i in touch$$1) {
2170					prop = touch$$1[i];
2171					newTouch[i] = prop && prop.bind ? prop.bind(touch$$1) : prop;
2172				}
2173				touch$$1 = newTouch;
2174			}
2175			touch$$1.type = 'dblclick';
2176			handler(touch$$1);
2177			last = null;
2178		}
2179	}
2180
2181	obj[_pre + _touchstart + id] = onTouchStart;
2182	obj[_pre + _touchend + id] = onTouchEnd;
2183	obj[_pre + 'dblclick' + id] = handler;
2184
2185	obj.addEventListener(_touchstart, onTouchStart, false);
2186	obj.addEventListener(_touchend, onTouchEnd, false);
2187
2188	// On some platforms (notably, chrome<55 on win10 + touchscreen + mouse),
2189	// the browser doesn't fire touchend/pointerup events but does fire
2190	// native dblclicks. See #4127.
2191	// Edge 14 also fires native dblclicks, but only for pointerType mouse, see #5180.
2192	obj.addEventListener('dblclick', handler, false);
2193
2194	return this;
2195}
2196
2197function removeDoubleTapListener(obj, id) {
2198	var touchstart = obj[_pre + _touchstart + id],
2199	    touchend = obj[_pre + _touchend + id],
2200	    dblclick = obj[_pre + 'dblclick' + id];
2201
2202	obj.removeEventListener(_touchstart, touchstart, false);
2203	obj.removeEventListener(_touchend, touchend, false);
2204	if (!edge) {
2205		obj.removeEventListener('dblclick', dblclick, false);
2206	}
2207
2208	return this;
2209}
2210
2211/*
2212 * @namespace DomUtil
2213 *
2214 * Utility functions to work with the [DOM](https://developer.mozilla.org/docs/Web/API/Document_Object_Model)
2215 * tree, used by Leaflet internally.
2216 *
2217 * Most functions expecting or returning a `HTMLElement` also work for
2218 * SVG elements. The only difference is that classes refer to CSS classes
2219 * in HTML and SVG classes in SVG.
2220 */
2221
2222
2223// @property TRANSFORM: String
2224// Vendor-prefixed transform style name (e.g. `'webkitTransform'` for WebKit).
2225var TRANSFORM = testProp(
2226	['transform', 'webkitTransform', 'OTransform', 'MozTransform', 'msTransform']);
2227
2228// webkitTransition comes first because some browser versions that drop vendor prefix don't do
2229// the same for the transitionend event, in particular the Android 4.1 stock browser
2230
2231// @property TRANSITION: String
2232// Vendor-prefixed transition style name.
2233var TRANSITION = testProp(
2234	['webkitTransition', 'transition', 'OTransition', 'MozTransition', 'msTransition']);
2235
2236// @property TRANSITION_END: String
2237// Vendor-prefixed transitionend event name.
2238var TRANSITION_END =
2239	TRANSITION === 'webkitTransition' || TRANSITION === 'OTransition' ? TRANSITION + 'End' : 'transitionend';
2240
2241
2242// @function get(id: String|HTMLElement): HTMLElement
2243// Returns an element given its DOM id, or returns the element itself
2244// if it was passed directly.
2245function get(id) {
2246	return typeof id === 'string' ? document.getElementById(id) : id;
2247}
2248
2249// @function getStyle(el: HTMLElement, styleAttrib: String): String
2250// Returns the value for a certain style attribute on an element,
2251// including computed values or values set through CSS.
2252function getStyle(el, style) {
2253	var value = el.style[style] || (el.currentStyle && el.currentStyle[style]);
2254
2255	if ((!value || value === 'auto') && document.defaultView) {
2256		var css = document.defaultView.getComputedStyle(el, null);
2257		value = css ? css[style] : null;
2258	}
2259	return value === 'auto' ? null : value;
2260}
2261
2262// @function create(tagName: String, className?: String, container?: HTMLElement): HTMLElement
2263// Creates an HTML element with `tagName`, sets its class to `className`, and optionally appends it to `container` element.
2264function create$1(tagName, className, container) {
2265	var el = document.createElement(tagName);
2266	el.className = className || '';
2267
2268	if (container) {
2269		container.appendChild(el);
2270	}
2271	return el;
2272}
2273
2274// @function remove(el: HTMLElement)
2275// Removes `el` from its parent element
2276function remove(el) {
2277	var parent = el.parentNode;
2278	if (parent) {
2279		parent.removeChild(el);
2280	}
2281}
2282
2283// @function empty(el: HTMLElement)
2284// Removes all of `el`'s children elements from `el`
2285function empty(el) {
2286	while (el.firstChild) {
2287		el.removeChild(el.firstChild);
2288	}
2289}
2290
2291// @function toFront(el: HTMLElement)
2292// Makes `el` the last child of its parent, so it renders in front of the other children.
2293function toFront(el) {
2294	var parent = el.parentNode;
2295	if (parent && parent.lastChild !== el) {
2296		parent.appendChild(el);
2297	}
2298}
2299
2300// @function toBack(el: HTMLElement)
2301// Makes `el` the first child of its parent, so it renders behind the other children.
2302function toBack(el) {
2303	var parent = el.parentNode;
2304	if (parent && parent.firstChild !== el) {
2305		parent.insertBefore(el, parent.firstChild);
2306	}
2307}
2308
2309// @function hasClass(el: HTMLElement, name: String): Boolean
2310// Returns `true` if the element's class attribute contains `name`.
2311function hasClass(el, name) {
2312	if (el.classList !== undefined) {
2313		return el.classList.contains(name);
2314	}
2315	var className = getClass(el);
2316	return className.length > 0 && new RegExp('(^|\\s)' + name + '(\\s|$)').test(className);
2317}
2318
2319// @function addClass(el: HTMLElement, name: String)
2320// Adds `name` to the element's class attribute.
2321function addClass(el, name) {
2322	if (el.classList !== undefined) {
2323		var classes = splitWords(name);
2324		for (var i = 0, len = classes.length; i < len; i++) {
2325			el.classList.add(classes[i]);
2326		}
2327	} else if (!hasClass(el, name)) {
2328		var className = getClass(el);
2329		setClass(el, (className ? className + ' ' : '') + name);
2330	}
2331}
2332
2333// @function removeClass(el: HTMLElement, name: String)
2334// Removes `name` from the element's class attribute.
2335function removeClass(el, name) {
2336	if (el.classList !== undefined) {
2337		el.classList.remove(name);
2338	} else {
2339		setClass(el, trim((' ' + getClass(el) + ' ').replace(' ' + name + ' ', ' ')));
2340	}
2341}
2342
2343// @function setClass(el: HTMLElement, name: String)
2344// Sets the element's class.
2345function setClass(el, name) {
2346	if (el.className.baseVal === undefined) {
2347		el.className = name;
2348	} else {
2349		// in case of SVG element
2350		el.className.baseVal = name;
2351	}
2352}
2353
2354// @function getClass(el: HTMLElement): String
2355// Returns the element's class.
2356function getClass(el) {
2357	// Check if the element is an SVGElementInstance and use the correspondingElement instead
2358	// (Required for linked SVG elements in IE11.)
2359	if (el.correspondingElement) {
2360		el = el.correspondingElement;
2361	}
2362	return el.className.baseVal === undefined ? el.className : el.className.baseVal;
2363}
2364
2365// @function setOpacity(el: HTMLElement, opacity: Number)
2366// Set the opacity of an element (including old IE support).
2367// `opacity` must be a number from `0` to `1`.
2368function setOpacity(el, value) {
2369	if ('opacity' in el.style) {
2370		el.style.opacity = value;
2371	} else if ('filter' in el.style) {
2372		_setOpacityIE(el, value);
2373	}
2374}
2375
2376function _setOpacityIE(el, value) {
2377	var filter = false,
2378	    filterName = 'DXImageTransform.Microsoft.Alpha';
2379
2380	// filters collection throws an error if we try to retrieve a filter that doesn't exist
2381	try {
2382		filter = el.filters.item(filterName);
2383	} catch (e) {
2384		// don't set opacity to 1 if we haven't already set an opacity,
2385		// it isn't needed and breaks transparent pngs.
2386		if (value === 1) { return; }
2387	}
2388
2389	value = Math.round(value * 100);
2390
2391	if (filter) {
2392		filter.Enabled = (value !== 100);
2393		filter.Opacity = value;
2394	} else {
2395		el.style.filter += ' progid:' + filterName + '(opacity=' + value + ')';
2396	}
2397}
2398
2399// @function testProp(props: String[]): String|false
2400// Goes through the array of style names and returns the first name
2401// that is a valid style name for an element. If no such name is found,
2402// it returns false. Useful for vendor-prefixed styles like `transform`.
2403function testProp(props) {
2404	var style = document.documentElement.style;
2405
2406	for (var i = 0; i < props.length; i++) {
2407		if (props[i] in style) {
2408			return props[i];
2409		}
2410	}
2411	return false;
2412}
2413
2414// @function setTransform(el: HTMLElement, offset: Point, scale?: Number)
2415// Resets the 3D CSS transform of `el` so it is translated by `offset` pixels
2416// and optionally scaled by `scale`. Does not have an effect if the
2417// browser doesn't support 3D CSS transforms.
2418function setTransform(el, offset, scale) {
2419	var pos = offset || new Point(0, 0);
2420
2421	el.style[TRANSFORM] =
2422		(ie3d ?
2423			'translate(' + pos.x + 'px,' + pos.y + 'px)' :
2424			'translate3d(' + pos.x + 'px,' + pos.y + 'px,0)') +
2425		(scale ? ' scale(' + scale + ')' : '');
2426}
2427
2428// @function setPosition(el: HTMLElement, position: Point)
2429// Sets the position of `el` to coordinates specified by `position`,
vendor: 7,486 bytes, lines 2430-2659
2430// using CSS translate or top/left positioning depending on the browser
2431// (used by Leaflet internally to position its layers).
2432function setPosition(el, point) {
2433
2434	/*eslint-disable */
2435	el._leaflet_pos = point;
2436	/* eslint-enable */
2437
2438	if (any3d) {
2439		setTransform(el, point);
2440	} else {
2441		el.style.left = point.x + 'px';
2442		el.style.top = point.y + 'px';
2443	}
2444}
2445
2446// @function getPosition(el: HTMLElement): Point
2447// Returns the coordinates of an element previously positioned with setPosition.
2448function getPosition(el) {
2449	// this method is only used for elements previously positioned using setPosition,
2450	// so it's safe to cache the position for performance
2451
2452	return el._leaflet_pos || new Point(0, 0);
2453}
2454
2455// @function disableTextSelection()
2456// Prevents the user from generating `selectstart` DOM events, usually generated
2457// when the user drags the mouse through a page with text. Used internally
2458// by Leaflet to override the behaviour of any click-and-drag interaction on
2459// the map. Affects drag interactions on the whole document.
2460
2461// @function enableTextSelection()
2462// Cancels the effects of a previous [`L.DomUtil.disableTextSelection`](#domutil-disabletextselection).
2463var disableTextSelection;
2464var enableTextSelection;
2465var _userSelect;
2466if ('onselectstart' in document) {
2467	disableTextSelection = function () {
2468		on(window, 'selectstart', preventDefault);
2469	};
2470	enableTextSelection = function () {
2471		off(window, 'selectstart', preventDefault);
2472	};
2473} else {
2474	var userSelectProperty = testProp(
2475		['userSelect', 'WebkitUserSelect', 'OUserSelect', 'MozUserSelect', 'msUserSelect']);
2476
2477	disableTextSelection = function () {
2478		if (userSelectProperty) {
2479			var style = document.documentElement.style;
2480			_userSelect = style[userSelectProperty];
2481			style[userSelectProperty] = 'none';
2482		}
2483	};
2484	enableTextSelection = function () {
2485		if (userSelectProperty) {
2486			document.documentElement.style[userSelectProperty] = _userSelect;
2487			_userSelect = undefined;
2488		}
2489	};
2490}
2491
2492// @function disableImageDrag()
2493// As [`L.DomUtil.disableTextSelection`](#domutil-disabletextselection), but
2494// for `dragstart` DOM events, usually generated when the user drags an image.
2495function disableImageDrag() {
2496	on(window, 'dragstart', preventDefault);
2497}
2498
2499// @function enableImageDrag()
2500// Cancels the effects of a previous [`L.DomUtil.disableImageDrag`](#domutil-disabletextselection).
2501function enableImageDrag() {
2502	off(window, 'dragstart', preventDefault);
2503}
2504
2505var _outlineElement;
2506var _outlineStyle;
2507// @function preventOutline(el: HTMLElement)
2508// Makes the [outline](https://developer.mozilla.org/docs/Web/CSS/outline)
2509// of the element `el` invisible. Used internally by Leaflet to prevent
2510// focusable elements from displaying an outline when the user performs a
2511// drag interaction on them.
2512function preventOutline(element) {
2513	while (element.tabIndex === -1) {
2514		element = element.parentNode;
2515	}
2516	if (!element.style) { return; }
2517	restoreOutline();
2518	_outlineElement = element;
2519	_outlineStyle = element.style.outline;
2520	element.style.outline = 'none';
2521	on(window, 'keydown', restoreOutline);
2522}
2523
2524// @function restoreOutline()
2525// Cancels the effects of a previous [`L.DomUtil.preventOutline`]().
2526function restoreOutline() {
2527	if (!_outlineElement) { return; }
2528	_outlineElement.style.outline = _outlineStyle;
2529	_outlineElement = undefined;
2530	_outlineStyle = undefined;
2531	off(window, 'keydown', restoreOutline);
2532}
2533
2534// @function getSizedParentNode(el: HTMLElement): HTMLElement
2535// Finds the closest parent node which size (width and height) is not null.
2536function getSizedParentNode(element) {
2537	do {
2538		element = element.parentNode;
2539	} while ((!element.offsetWidth || !element.offsetHeight) && element !== document.body);
2540	return element;
2541}
2542
2543// @function getScale(el: HTMLElement): Object
2544// Computes the CSS scale currently applied on the element.
2545// Returns an object with `x` and `y` members as horizontal and vertical scales respectively,
2546// and `boundingClientRect` as the result of [`getBoundingClientRect()`](https://developer.mozilla.org/en-US/docs/Web/API/Element/getBoundingClientRect).
2547function getScale(element) {
2548	var rect = element.getBoundingClientRect(); // Read-only in old browsers.
2549
2550	return {
2551		x: rect.width / element.offsetWidth || 1,
2552		y: rect.height / element.offsetHeight || 1,
2553		boundingClientRect: rect
2554	};
2555}
2556
2557
2558var DomUtil = (Object.freeze || Object)({
2559	TRANSFORM: TRANSFORM,
2560	TRANSITION: TRANSITION,
2561	TRANSITION_END: TRANSITION_END,
2562	get: get,
2563	getStyle: getStyle,
2564	create: create$1,
2565	remove: remove,
2566	empty: empty,
2567	toFront: toFront,
2568	toBack: toBack,
2569	hasClass: hasClass,
2570	addClass: addClass,
2571	removeClass: removeClass,
2572	setClass: setClass,
2573	getClass: getClass,
2574	setOpacity: setOpacity,
2575	testProp: testProp,
2576	setTransform: setTransform,
2577	setPosition: setPosition,
2578	getPosition: getPosition,
2579	disableTextSelection: disableTextSelection,
2580	enableTextSelection: enableTextSelection,
2581	disableImageDrag: disableImageDrag,
2582	enableImageDrag: enableImageDrag,
2583	preventOutline: preventOutline,
2584	restoreOutline: restoreOutline,
2585	getSizedParentNode: getSizedParentNode,
2586	getScale: getScale
2587});
2588
2589/*
2590 * @namespace DomEvent
2591 * Utility functions to work with the [DOM events](https://developer.mozilla.org/docs/Web/API/Event), used by Leaflet internally.
2592 */
2593
2594// Inspired by John Resig, Dean Edwards and YUI addEvent implementations.
2595
2596// @function on(el: HTMLElement, types: String, fn: Function, context?: Object): this
2597// Adds a listener function (`fn`) to a particular DOM event type of the
2598// element `el`. You can optionally specify the context of the listener
2599// (object the `this` keyword will point to). You can also pass several
2600// space-separated types (e.g. `'click dblclick'`).
2601
2602// @alternative
2603// @function on(el: HTMLElement, eventMap: Object, context?: Object): this
2604// Adds a set of type/listener pairs, e.g. `{click: onClick, mousemove: onMouseMove}`
2605function on(obj, types, fn, context) {
2606
2607	if (typeof types === 'object') {
2608		for (var type in types) {
2609			addOne(obj, type, types[type], fn);
2610		}
2611	} else {
2612		types = splitWords(types);
2613
2614		for (var i = 0, len = types.length; i < len; i++) {
2615			addOne(obj, types[i], fn, context);
2616		}
2617	}
2618
2619	return this;
2620}
2621
2622var eventsKey = '_leaflet_events';
2623
2624// @function off(el: HTMLElement, types: String, fn: Function, context?: Object): this
2625// Removes a previously added listener function.
2626// Note that if you passed a custom context to on, you must pass the same
2627// context to `off` in order to remove the listener.
2628
2629// @alternative
2630// @function off(el: HTMLElement, eventMap: Object, context?: Object): this
2631// Removes a set of type/listener pairs, e.g. `{click: onClick, mousemove: onMouseMove}`
2632function off(obj, types, fn, context) {
2633
2634	if (typeof types === 'object') {
2635		for (var type in types) {
2636			removeOne(obj, type, types[type], fn);
2637		}
2638	} else if (types) {
2639		types = splitWords(types);
2640
2641		for (var i = 0, len = types.length; i < len; i++) {
2642			removeOne(obj, types[i], fn, context);
2643		}
2644	} else {
2645		for (var j in obj[eventsKey]) {
2646			removeOne(obj, j, obj[eventsKey][j]);
2647		}
2648		delete obj[eventsKey];
2649	}
2650
2651	return this;
2652}
2653
2654function addOne(obj, type, fn, context) {
2655	var id = type + stamp(fn) + (context ? '_' + stamp(context) : '');
2656
2657	if (obj[eventsKey] && obj[eventsKey][id]) { return this; }
2658
2659	var handler = function (e) {
2660		return fn.call(context || obj, e || window.event);
2661	};
2662
2663	var originalHandler = handler;
2664
2665	if (pointer && type.indexOf('touch') === 0) {
2666		// Needs DomEvent.Pointer.js
2667		addPointerListener(obj, type, handler, id);
2668
2669	} else if (touch && (type === 'dblclick') && addDoubleTapListener &&
2670	           !(pointer && chrome)) {
2671		// Chrome >55 does not need the synthetic dblclicks from addDoubleTapListener
2672		// See #5180
2673		addDoubleTapListener(obj, handler, id);
2674
2675	} else if ('addEventListener' in obj) {
2676
2677		if (type === 'mousewheel') {
2678			obj.addEventListener('onwheel' in obj ? 'wheel' : 'mousewheel', handler, false);
2679
2680		} else if ((type === 'mouseenter') || (type === 'mouseleave')) {
2681			handler = function (e) {
2682				e = e || window.event;
2683				if (isExternalTarget(obj, e)) {
2684					originalHandler(e);
2685				}
2686			};
2687			obj.addEventListener(type === 'mouseenter' ? 'mouseover' : 'mouseout', handler, false);
2688
2689		} else {
2690			if (type === 'click' && android) {
2691				handler = function (e) {
2692					filterClick(e, originalHandler);
2693				};
2694			}
2695			obj.addEventListener(type, handler, false);
2696		}
2697
2698	} else if ('attachEvent' in obj) {
2699		obj.attachEvent('on' + type, handler);
2700	}
2701
2702	obj[eventsKey] = obj[eventsKey] || {};
2703	obj[eventsKey][id] = handler;
2704}
2705
2706function removeOne(obj, type, fn, context) {
2707
2708	var id = type + stamp(fn) + (context ? '_' + stamp(context) : ''),
2709	    handler = obj[eventsKey] && obj[eventsKey][id];
2710
2711	if (!handler) { return this; }
2712
2713	if (pointer && type.indexOf('touch') === 0) {
2714		removePointerListener(obj, type, id);
2715
2716	} else if (touch && (type === 'dblclick') && removeDoubleTapListener &&
2717	           !(pointer && chrome)) {
2718		removeDoubleTapListener(obj, id);
2719
2720	} else if ('removeEventListener' in obj) {
2721
2722		if (type === 'mousewheel') {
2723			obj.removeEventListener('onwheel' in obj ? 'wheel' : 'mousewheel', handler, false);
2724
2725		} else {
2726			obj.removeEventListener(
2727				type === 'mouseenter' ? 'mouseover' :
2728				type === 'mouseleave' ? 'mouseout' : type, handler, false);
2729		}
2730
2731	} else if ('detachEvent' in obj) {
2732		obj.detachEvent('on' + type, handler);
2733	}
2734
2735	obj[eventsKey][id] = null;
2736}
2737
2738// @function stopPropagation(ev: DOMEvent): this
2739// Stop the given event from propagation to parent elements. Used inside the listener functions:
2740// ```js
2741// L.DomEvent.on(div, 'click', function (ev) {
2742// 	L.DomEvent.stopPropagation(ev);
2743// });
2744// ```
2745function stopPropagation(e) {
2746
2747	if (e.stopPropagation) {
2748		e.stopPropagation();
2749	} else if (e.originalEvent) {  // In case of Leaflet event.
2750		e.originalEvent._stopped = true;
2751	} else {
2752		e.cancelBubble = true;
2753	}
2754	skipped(e);
2755
2756	return this;
2757}
2758
2759// @function disableScrollPropagation(el: HTMLElement): this
2760// Adds `stopPropagation` to the element's `'mousewheel'` events (plus browser variants).
2761function disableScrollPropagation(el) {
2762	addOne(el, 'mousewheel', stopPropagation);
2763	return this;
2764}
2765
2766// @function disableClickPropagation(el: HTMLElement): this
2767// Adds `stopPropagation` to the element's `'click'`, `'doubleclick'`,
2768// `'mousedown'` and `'touchstart'` events (plus browser variants).
2769function disableClickPropagation(el) {
2770	on(el, 'mousedown touchstart dblclick', stopPropagation);
2771	addOne(el, 'click', fakeStop);
2772	return this;
2773}
2774
2775// @function preventDefault(ev: DOMEvent): this
2776// Prevents the default action of the DOM Event `ev` from happening (such as
2777// following a link in the href of the a element, or doing a POST request
2778// with page reload when a `<form>` is submitted).
2779// Use it inside listener functions.
2780function preventDefault(e) {
2781	if (e.preventDefault) {
2782		e.preventDefault();
2783	} else {
2784		e.returnValue = false;
2785	}
2786	return this;
2787}
2788
2789// @function stop(ev: DOMEvent): this
2790// Does `stopPropagation` and `preventDefault` at the same time.
2791function stop(e) {
2792	preventDefault(e);
2793	stopPropagation(e);
2794	return this;
2795}
2796
2797// @function getMousePosition(ev: DOMEvent, container?: HTMLElement): Point
2798// Gets normalized mouse position from a DOM event relative to the
2799// `container` (border excluded) or to the whole page if not specified.
2800function getMousePosition(e, container) {
2801	if (!container) {
2802		return new Point(e.clientX, e.clientY);
2803	}
2804
2805	var scale = getScale(container),
2806	    offset = scale.boundingClientRect; // left and top  values are in page scale (like the event clientX/Y)
2807
2808	return new Point(
2809		// offset.left/top values are in page scale (like clientX/Y),
2810		// whereas clientLeft/Top (border width) values are the original values (before CSS scale applies).
2811		(e.clientX - offset.left) / scale.x - container.clientLeft,
2812		(e.clientY - offset.top) / scale.y - container.clientTop
2813	);
2814}
2815
2816// Chrome on Win scrolls double the pixels as in other platforms (see #4538),
2817// and Firefox scrolls device pixels, not CSS pixels
2818var wheelPxFactor =
2819	(win && chrome) ? 2 * window.devicePixelRatio :
2820	gecko ? window.devicePixelRatio : 1;
2821
2822// @function getWheelDelta(ev: DOMEvent): Number
2823// Gets normalized wheel delta from a mousewheel DOM event, in vertical
2824// pixels scrolled (negative if scrolling down).
2825// Events from pointing devices without precise scrolling are mapped to
2826// a best guess of 60 pixels.
2827function getWheelDelta(e) {
2828	return (edge) ? e.wheelDeltaY / 2 : // Don't trust window-geometry-based delta
2829	       (e.deltaY && e.deltaMode === 0) ? -e.deltaY / wheelPxFactor : // Pixels
2830	       (e.deltaY && e.deltaMode === 1) ? -e.deltaY * 20 : // Lines
2831	       (e.deltaY && e.deltaMode === 2) ? -e.deltaY * 60 : // Pages
2832	       (e.deltaX || e.deltaZ) ? 0 :	// Skip horizontal/depth wheel events
2833	       e.wheelDelta ? (e.wheelDeltaY || e.wheelDelta) / 2 : // Legacy IE pixels
2834	       (e.detail && Math.abs(e.detail) < 32765) ? -e.detail * 20 : // Legacy Moz lines
2835	       e.detail ? e.detail / -32765 * 60 : // Legacy Moz pages
2836	       0;
2837}
2838
2839var skipEvents = {};
2840
2841function fakeStop(e) {
2842	// fakes stopPropagation by setting a special event flag, checked/reset with skipped(e)
2843	skipEvents[e.type] = true;
2844}
2845
2846function skipped(e) {
2847	var events = skipEvents[e.type];
2848	// reset when checking, as it's only used in map container and propagates outside of the map
2849	skipEvents[e.type] = false;
2850	return events;
2851}
2852
2853// check if element really left/entered the event target (for mouseenter/mouseleave)
2854function isExternalTarget(el, e) {
2855
2856	var related = e.relatedTarget;
2857
2858	if (!related) { return true; }
2859
2860	try {
2861		while (related && (related !== el)) {
2862			related = related.parentNode;
2863		}
2864	} catch (err) {
2865		return false;
2866	}
2867	return (related !== el);
2868}
2869
2870var lastClick;
2871
2872// this is a horrible workaround for a bug in Android where a single touch triggers two click events
2873function filterClick(e, handler) {
2874	var timeStamp = (e.timeStamp || (e.originalEvent && e.originalEvent.timeStamp)),
2875	    elapsed = lastClick && (timeStamp - lastClick);
2876
2877	// are they closer together than 500ms yet more than 100ms?
2878	// Android typically triggers them ~300ms apart while multiple listeners
2879	// on the same event should be triggered far faster;
2880	// or check if click is simulated on the element, and if it is, reject any non-simulated events
2881
2882	if ((elapsed && elapsed > 100 && elapsed < 500) || (e.target._simulatedClick && !e._simulated)) {
2883		stop(e);
2884		return;
2885	}
2886	lastClick = timeStamp;
2887
2888	handler(e);
2889}
2890
2891
2892
2893
2894var DomEvent = (Object.freeze || Object)({
2895	on: on,
2896	off: off,
2897	stopPropagation: stopPropagation,
2898	disableScrollPropagation: disableScrollPropagation,
2899	disableClickPropagation: disableClickPropagation,
2900	preventDefault: preventDefault,
2901	stop: stop,
2902	getMousePosition: getMousePosition,
2903	getWheelDelta: getWheelDelta,
2904	fakeStop: fakeStop,
2905	skipped: skipped,
2906	isExternalTarget: isExternalTarget,
2907	addListener: on,
2908	removeListener: off
2909});
2910
2911/*
2912 * @class PosAnimation
2913 * @aka L.PosAnimation
2914 * @inherits Evented
2915 * Used internally for panning animations, utilizing CSS3 Transitions for modern browsers and a timer fallback for IE6-9.
2916 *
2917 * @example
2918 * ```js
2919 * var fx = new L.PosAnimation();
2920 * fx.run(el, [300, 500], 0.5);
2921 * ```
2922 *
2923 * @constructor L.PosAnimation()
2924 * Creates a `PosAnimation` object.
2925 *
2926 */
2927
2928var PosAnimation = Evented.extend({
2929
2930	// @method run(el: HTMLElement, newPos: Point, duration?: Number, easeLinearity?: 
vendor: 1,697 bytes, lines 2930-2996
2930Number)
2931	// Run an animation of a given element to a new position, optionally setting
2932	// duration in seconds (`0.25` by default) and easing linearity factor (3rd
2933	// argument of the [cubic bezier curve](http://cubic-bezier.com/#0,0,.5,1),
2934	// `0.5` by default).
2935	run: function (el, newPos, duration, easeLinearity) {
2936		this.stop();
2937
2938		this._el = el;
2939		this._inProgress = true;
2940		this._duration = duration || 0.25;
2941		this._easeOutPower = 1 / Math.max(easeLinearity || 0.5, 0.2);
2942
2943		this._startPos = getPosition(el);
2944		this._offset = newPos.subtract(this._startPos);
2945		this._startTime = +new Date();
2946
2947		// @event start: Event
2948		// Fired when the animation starts
2949		this.fire('start');
2950
2951		this._animate();
2952	},
2953
2954	// @method stop()
2955	// Stops the animation (if currently running).
2956	stop: function () {
2957		if (!this._inProgress) { return; }
2958
2959		this._step(true);
2960		this._complete();
2961	},
2962
2963	_animate: function () {
2964		// animation loop
2965		this._animId = requestAnimFrame(this._animate, this);
2966		this._step();
2967	},
2968
2969	_step: function (round) {
2970		var elapsed = (+new Date()) - this._startTime,
2971		    duration = this._duration * 1000;
2972
2973		if (elapsed < duration) {
2974			this._runFrame(this._easeOut(elapsed / duration), round);
2975		} else {
2976			this._runFrame(1);
2977			this._complete();
2978		}
2979	},
2980
2981	_runFrame: function (progress, round) {
2982		var pos = this._startPos.add(this._offset.multiplyBy(progress));
2983		if (round) {
2984			pos._round();
2985		}
2986		setPosition(this._el, pos);
2987
2988		// @event step: Event
2989		// Fired continuously during the animation.
2990		this.fire('step');
2991	},
2992
2993	_complete: function () {
2994		cancelAnimFrame(this._animId);
2995
2996		this._inProgress = false;
2997		// @event end: Event
2998		// Fired when the animation ends.
2999		this.fire('end');
3000	},
3001
3002	_easeOut: function (t) {
3003		return 1 - Math.pow(1 - t, this._easeOutPower);
3004	}
3005});
3006
3007/*
3008 * @class Map
3009 * @aka L.Map
3010 * @inherits Evented
3011 *
3012 * The central class of the API — it is used to create a map on a page and manipulate it.
3013 *
3014 * @example
3015 *
3016 * ```js
3017 * // initialize the map on the "map" div with a given center and zoom
3018 * var map = L.map('map', {
3019 * 	center: [51.505, -0.09],
3020 * 	zoom: 13
3021 * });
3022 * ```
3023 *
3024 */
3025
3026var Map = Evented.extend({
3027
3028	options: {
3029		// @section Map State Options
3030		// @option crs: CRS = L.CRS.EPSG3857
3031		// The [Coordinate Reference System](#crs) to use. Don't change this if you're not
3032		// sure what it means.
3033		crs: EPSG3857,
3034
3035		// @option center: LatLng = undefined
3036		// Initial geographic center of the map
3037		center: undefined,
3038
3039		// @option zoom: Number = undefined
3040		// Initial map zoom level
3041		zoom: undefined,
3042
3043		// @option minZoom: Number = *
3044		// Minimum zoom level of the map.
3045		// If not specified and at least one `GridLayer` or `TileLayer` is in the map,
3046		// the lowest of their `minZoom` options will be used instead.
3047		minZoom: undefined,
3048
3049		// @option maxZoom: Number = *
3050		// Maximum zoom level of the map.
3051		// If not specified and at least one `GridLayer` or `TileLayer` is in the map,
3052		// the highest of their `maxZoom` options will be used instead.
3053		maxZoom: undefined,
3054
3055		// @option layers: Layer[] = []
3056		// Array of layers that will be added to the map initially
3057		layers: [],
3058
3059		// @option maxBounds: LatLngBounds = null
3060		// When this option is set, the map restricts the view to the given
3061		// geographical bounds, bouncing the user back if the user tries to pan
3062		// outside the view. To set the restriction dynamically, use
3063		// [`setMaxBounds`](#map-setmaxbounds) method.
3064		maxBounds: undefined,
3065
3066		// @option renderer: Renderer = *
3067		// The default method for drawing vector layers on the map. `L.SVG`
3068		// or `L.Canvas` by default depending on browser support.
3069		renderer: undefined,
3070
3071
3072		// @section Animation Options
3073		// @option zoomAnimation: Boolean = true
3074		// Whether the map zoom animation is enabled. By default it's enabled
3075		// in all browsers that support CSS3 Transitions except Android.
3076		zoomAnimation: true,
3077
3078		// @option zoomAnimationThreshold: Number = 4
3079		// Won't animate zoom if the zoom difference exceeds this value.
3080		zoomAnimationThreshold: 4,
3081
3082		// @option fadeAnimation: Boolean = true
3083		// Whether the tile fade animation is enabled. By default it's enabled
3084		// in all browsers that support CSS3 Transitions except Android.
3085		fadeAnimation: true,
3086
3087		// @option markerZoomAnimation: Boolean = true
3088		// Whether markers animate their zoom with the zoom animation, if disabled
3089		// they will disappear for the length of the animation. By default it's
3090		// enabled in all browsers that support CSS3 Transitions except Android.
3091		markerZoomAnimation: true,
3092
3093		// @option transform3DLimit: Number = 2^23
3094		// Defines the maximum size of a CSS translation transform. The default
3095		// value should not be changed unless a web browser positions layers in
3096		// the wrong place after doing a large `panBy`.
3097		transform3DLimit: 8388608, // Precision limit of a 32-bit float
3098
3099		// @section Interaction Options
3100		// @option zoomSnap: Number = 1
3101		// Forces the map's zoom level to always be a multiple of this, particularly
3102		// right after a [`fitBounds()`](#map-fitbounds) or a pinch-zoom.
3103		// By default, the zoom level snaps to the nearest integer; lower values
3104		// (e.g. `0.5` or `0.1`) allow for greater granularity. A value of `0`
3105		// means the zoom level will not be snapped after `fitBounds` or a pinch-zoom.
3106		zoomSnap: 1,
3107
3108		// @option zoomDelta: Number = 1
3109		// Controls how much the map's zoom level will change after a
3110		// [`zoomIn()`](#map-zoomin), [`zoomOut()`](#map-zoomout), pressing `+`
3111		// or `-` on the keyboard, or using the [zoom controls](#control-zoom).
3112		// Values smaller than `1` (e.g. `0.5`) allow for greater granularity.
3113		zoomDelta: 1,
3114
vendor: 3,639 bytes, lines 3115-3225
3115		// @option trackResize: Boolean = true
3116		// Whether the map automatically handles browser window resize to update itself.
3117		trackResize: true
3118	},
3119
3120	initialize: function (id, options) { // (HTMLElement or String, Object)
3121		options = setOptions(this, options);
3122
3123		// Make sure to assign internal flags at the beginning,
3124		// to avoid inconsistent state in some edge cases.
3125		this._handlers = [];
3126		this._layers = {};
3127		this._zoomBoundLayers = {};
3128		this._sizeChanged = true;
3129
3130		this._initContainer(id);
3131		this._initLayout();
3132
3133		// hack for https://github.com/Leaflet/Leaflet/issues/1980
3134		this._onResize = bind(this._onResize, this);
3135
3136		this._initEvents();
3137
3138		if (options.maxBounds) {
3139			this.setMaxBounds(options.maxBounds);
3140		}
3141
3142		if (options.zoom !== undefined) {
3143			this._zoom = this._limitZoom(options.zoom);
3144		}
3145
3146		if (options.center && options.zoom !== undefined) {
3147			this.setView(toLatLng(options.center), options.zoom, {reset: true});
3148		}
3149
3150		this.callInitHooks();
3151
3152		// don't animate on browsers without hardware-accelerated transitions or old Android/Opera
3153		this._zoomAnimated = TRANSITION && any3d && !mobileOpera &&
3154				this.options.zoomAnimation;
3155
3156		// zoom transitions run with the same duration for all layers, so if one of transitionend events
3157		// happens after starting zoom animation (propagating to the map pane), we know that it ended globally
3158		if (this._zoomAnimated) {
3159			this._createAnimProxy();
3160			on(this._proxy, TRANSITION_END, this._catchTransitionEnd, this);
3161		}
3162
3163		this._addLayers(this.options.layers);
3164	},
3165
3166
3167	// @section Methods for modifying map state
3168
3169	// @method setView(center: LatLng, zoom: Number, options?: Zoom/pan options): this
3170	// Sets the view of the map (geographical center and zoom) with the given
3171	// animation options.
3172	setView: function (center, zoom, options) {
3173
3174		zoom = zoom === undefined ? this._zoom : this._limitZoom(zoom);
3175		center = this._limitCenter(toLatLng(center), zoom, this.options.maxBounds);
3176		options = options || {};
3177
3178		this._stop();
3179
3180		if (this._loaded && !options.reset && options !== true) {
3181
3182			if (options.animate !== undefined) {
3183				options.zoom = extend({animate: options.animate}, options.zoom);
3184				options.pan = extend({animate: options.animate, duration: options.duration}, options.pan);
3185			}
3186
3187			// try animating pan or zoom
3188			var moved = (this._zoom !== zoom) ?
3189				this._tryAnimatedZoom && this._tryAnimatedZoom(center, zoom, options.zoom) :
3190				this._tryAnimatedPan(center, options.pan);
3191
3192			if (moved) {
3193				// prevent resize handler call, the view will refresh after animation anyway
3194				clearTimeout(this._sizeTimer);
3195				return this;
3196			}
3197		}
3198
3199		// animation didn't start, just reset the map view
3200		this._resetView(center, zoom);
3201
3202		return this;
3203	},
3204
3205	// @method setZoom(zoom: Number, options?: Zoom/pan options): this
3206	// Sets the zoom of the map.
3207	setZoom: function (zoom, options) {
3208		if (!this._loaded) {
3209			this._zoom = zoom;
3210			return this;
3211		}
3212		return this.setView(this.getCenter(), zoom, {zoom: options});
3213	},
3214
3215	// @method zoomIn(delta?: Number, options?: Zoom options): this
3216	// Increases the zoom of the map by `delta` ([`zoomDelta`](#map-zoomdelta) by default).
3217	zoomIn: function (delta, options) {
3218		delta = delta || (any3d ? this.options.zoomDelta : 1);
3219		return this.setZoom(this._zoom + delta, options);
3220	},
3221
3222	// @method zoomOut(delta?: Number, options?: Zoom options): this
3223	// Decreases the zoom of the map by `delta` ([`zoomDelta`](#map-zoomdelta) by default).
3224	zoomOut: function (delta, options) {
3225		delta = delta || (
vendor: 4,557 bytes, lines 3225-3355
3225any3d ? this.options.zoomDelta : 1);
3226		return this.setZoom(this._zoom - delta, options);
3227	},
3228
3229	// @method setZoomAround(latlng: LatLng, zoom: Number, options: Zoom options): this
3230	// Zooms the map while keeping a specified geographical point on the map
3231	// stationary (e.g. used internally for scroll zoom and double-click zoom).
3232	// @alternative
3233	// @method setZoomAround(offset: Point, zoom: Number, options: Zoom options): this
3234	// Zooms the map while keeping a specified pixel on the map (relative to the top-left corner) stationary.
3235	setZoomAround: function (latlng, zoom, options) {
3236		var scale = this.getZoomScale(zoom),
3237		    viewHalf = this.getSize().divideBy(2),
3238		    containerPoint = latlng instanceof Point ? latlng : this.latLngToContainerPoint(latlng),
3239
3240		    centerOffset = containerPoint.subtract(viewHalf).multiplyBy(1 - 1 / scale),
3241		    newCenter = this.containerPointToLatLng(viewHalf.add(centerOffset));
3242
3243		return this.setView(newCenter, zoom, {zoom: options});
3244	},
3245
3246	_getBoundsCenterZoom: function (bounds, options) {
3247
3248		options = options || {};
3249		bounds = bounds.getBounds ? bounds.getBounds() : toLatLngBounds(bounds);
3250
3251		var paddingTL = toPoint(options.paddingTopLeft || options.padding || [0, 0]),
3252		    paddingBR = toPoint(options.paddingBottomRight || options.padding || [0, 0]),
3253
3254		    zoom = this.getBoundsZoom(bounds, false, paddingTL.add(paddingBR));
3255
3256		zoom = (typeof options.maxZoom === 'number') ? Math.min(options.maxZoom, zoom) : zoom;
3257
3258		if (zoom === Infinity) {
3259			return {
3260				center: bounds.getCenter(),
3261				zoom: zoom
3262			};
3263		}
3264
3265		var paddingOffset = paddingBR.subtract(paddingTL).divideBy(2),
3266
3267		    swPoint = this.project(bounds.getSouthWest(), zoom),
3268		    nePoint = this.project(bounds.getNorthEast(), zoom),
3269		    center = this.unproject(swPoint.add(nePoint).divideBy(2).add(paddingOffset), zoom);
3270
3271		return {
3272			center: center,
3273			zoom: zoom
3274		};
3275	},
3276
3277	// @method fitBounds(bounds: LatLngBounds, options?: fitBounds options): this
3278	// Sets a map view that contains the given geographical bounds with the
3279	// maximum zoom level possible.
3280	fitBounds: function (bounds, options) {
3281
3282		bounds = toLatLngBounds(bounds);
3283
3284		if (!bounds.isValid()) {
3285			throw new Error('Bounds are not valid.');
3286		}
3287
3288		var target = this._getBoundsCenterZoom(bounds, options);
3289		return this.setView(target.center, target.zoom, options);
3290	},
3291
3292	// @method fitWorld(options?: fitBounds options): this
3293	// Sets a map view that mostly contains the whole world with the maximum
3294	// zoom level possible.
3295	fitWorld: function (options) {
3296		return this.fitBounds([[-90, -180], [90, 180]], options);
3297	},
3298
3299	// @method panTo(latlng: LatLng, options?: Pan options): this
3300	// Pans the map to a given center.
3301	panTo: function (center, options) { // (LatLng)
3302		return this.setView(center, this._zoom, {pan: options});
3303	},
3304
3305	// @method panBy(offset: Point, options?: Pan options): this
3306	// Pans the map by a given number of pixels (animated).
3307	panBy: function (offset, options) {
3308		offset = toPoint(offset).round();
3309		options = options || {};
3310
3311		if (!offset.x && !offset.y) {
3312			return this.fire('moveend');
3313		}
3314		// If we pan too far, Chrome gets issues with tiles
3315		// and makes them disappear or appear in the wrong place (slightly offset) #2602
3316		if (options.animate !== true && !this.getSize().contains(offset)) {
3317			this._resetView(this.unproject(this.project(this.getCenter()).add(offset)), this.getZoom());
3318			return this;
3319		}
3320
3321		if (!this._panAnim) {
3322			this._panAnim = new PosAnimation();
3323
3324			this._panAnim.on({
3325				'step': this._onPanTransitionStep,
3326				'end': this._onPanTransitionEnd
3327			}, this);
3328		}
3329
3330		// don't fire movestart if animating inertia
3331		if (!options.noMoveStart) {
3332			this.fire('movestart');
3333		}
3334
3335		// animate pan unless animate: false specified
3336		if (options.animate !== false) {
3337			addClass(this._mapPane, 'leaflet-pan-anim');
3338
3339			var newPos = this._getMapPanePos().subtract(offset).round();
3340			this._panAnim.run(this._mapPane, newPos, options.duration || 0.25, options.easeLinearity);
3341		} else {
3342			this._rawPanBy(offset);
3343			this.fire('move').fire('moveend');
3344		}
3345
3346		return this;
3347	},
3348
3349	// @method flyTo(latlng: LatLng, zoom?: Number, options?: Zoom/pan options): this
3350	// Sets the view of the map (geographical center and zoom) performing a smooth
3351	// pan-zoom animation.
3352	flyTo: function (targetCenter, targetZoom, options) {
3353
3354		options = options || {};
3355		if (options.animate === false || !
vendor: 1,654 bytes, lines 3355-3409
3355any3d) {
3356			return this.setView(targetCenter, targetZoom, options);
3357		}
3358
3359		this._stop();
3360
3361		var from = this.project(this.getCenter()),
3362		    to = this.project(targetCenter),
3363		    size = this.getSize(),
3364		    startZoom = this._zoom;
3365
3366		targetCenter = toLatLng(targetCenter);
3367		targetZoom = targetZoom === undefined ? startZoom : targetZoom;
3368
3369		var w0 = Math.max(size.x, size.y),
3370		    w1 = w0 * this.getZoomScale(startZoom, targetZoom),
3371		    u1 = (to.distanceTo(from)) || 1,
3372		    rho = 1.42,
3373		    rho2 = rho * rho;
3374
3375		function r(i) {
3376			var s1 = i ? -1 : 1,
3377			    s2 = i ? w1 : w0,
3378			    t1 = w1 * w1 - w0 * w0 + s1 * rho2 * rho2 * u1 * u1,
3379			    b1 = 2 * s2 * rho2 * u1,
3380			    b = t1 / b1,
3381			    sq = Math.sqrt(b * b + 1) - b;
3382
3383			    // workaround for floating point precision bug when sq = 0, log = -Infinite,
3384			    // thus triggering an infinite loop in flyTo
3385			    var log = sq < 0.000000001 ? -18 : Math.log(sq);
3386
3387			return log;
3388		}
3389
3390		function sinh(n) { return (Math.exp(n) - Math.exp(-n)) / 2; }
3391		function cosh(n) { return (Math.exp(n) + Math.exp(-n)) / 2; }
3392		function tanh(n) { return sinh(n) / cosh(n); }
3393
3394		var r0 = r(0);
3395
3396		function w(s) { return w0 * (cosh(r0) / cosh(r0 + rho * s)); }
3397		function u(s) { return w0 * (cosh(r0) * tanh(r0 + rho * s) - sinh(r0)) / rho2; }
3398
3399		function easeOut(t) { return 1 - Math.pow(1 - t, 1.5); }
3400
3401		var start = Date.now(),
3402		    S = (r(1) - r0) / rho,
3403		    duration = options.duration ? 1000 * options.duration : 1000 * S * 0.8;
3404
3405		function frame() {
3406			var t = (Date.now() - start) / duration,
3407			    s = easeOut(t) * S;
3408
3409			if (t <= 1) {
3410				this._flyToFrame = requestAnimFrame(frame, this);
3411
3412				this._move(
3413					this.unproject(from.add(to.subtract(from).multiplyBy(u(s) / u1)), startZoom),
3414					this.getScaleZoom(w0 / w(s), startZoom),
3415					{flyTo: true});
3416
3417			} else {
3418				this
3419					._move(targetCenter, targetZoom)
3420					._moveEnd(true);
3421			}
3422		}
3423
3424		this._moveStart(true, options.noMoveStart);
3425
3426		frame.call(this);
3427		return this;
3428	},
3429
3430	// @method flyToBounds(bounds: LatLngBounds, options?: fitBounds options): this
3431	// Sets the view of the map with a smooth animation like [`flyTo`](#map-flyto),
3432	// but takes a bounds parameter like [`fitBounds`](#map-fitbounds).
3433	flyToBounds: function (bounds, options) {
3434		var target = this._getBoundsCenterZoom(bounds, options);
3435		return this.flyTo(target.center, target.zoom, options);
3436	},
3437
3438	// @method setMaxBounds(bounds: Bounds): this
3439	// Restricts the map view to the given bounds (see the [maxBounds](#map-maxbounds) option).
3440	setMaxBounds: function (bounds) {
3441		bounds = toLatLngBounds(bounds);
3442
3443		if (!bounds.isValid()) {
3444			this.options.maxBounds = null;
3445			return this.off('moveend', this._panInsideMaxBounds);
3446		} else if (this.options.maxBounds) {
3447			this.off('moveend', this._panInsideMaxBounds);
3448		}
3449
3450		this.options.maxBounds = bounds;
3451
3452		if (this._loaded) {
3453			this._panInsideMaxBounds();
3454		}
3455
3456		return this.on('moveend', this._panInsideMaxBounds);
3457	},
3458
3459	// @method setMinZoom(zoom: Number): this
3460	// Sets the lower limit for the available zoom levels (see the [minZoom](#map-minzoom) option).
3461	setMinZoom: function (zoom) {
3462		var oldZoom = this.options.minZoom;
3463		this.options.minZoom = zoom;
3464
3465		if (this._loaded && oldZoom !== zoom) {
3466			this.fire('zoomlevelschange');
3467
3468			if (this.getZoom() < this.options.minZoom) {
3469				return this.setZoom(zoom);
3470			}
3471		}
3472
3473		return this;
3474	},
3475
3476	// @method setMaxZoom(zoom: Number): this
3477	// Sets the upper limit for the available zoom levels (see the [maxZoom](#map-maxzoom) option).
3478	setMaxZoom: function (zoom) {
3479		var oldZoom = this.options.maxZoom;
3480		this.options.maxZoom = zoom;
3481
3482		if (this._loaded && oldZoom !== zoom) {
3483			this.fire('zoomlevelschange');
3484
3485			if (this.getZoom() > this.options.maxZoom) {
3486				return this.setZoom(zoom);
3487			}
3488		}
3489
3490		return this;
3491	},
3492
3493	// @method panInsideBounds(bounds: LatLngBounds, options?: Pan options): this
3494	// 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.
3495	panInsideBounds: function (bounds, options) {
3496		this._enforcingBounds = true;
3497		var center = this.getCenter(),
3498		    newCenter = this._limitCenter(center, this._zoom, toLatLngBounds(bounds));
3499
3500		if (!center.equals(newCenter)) {
3501			this.panTo(newCenter, options);
3502		}
3503
3504		this._enforcingBounds = false;
3505		return this;
3506	},
3507
3508	// @method panInside(latlng: LatLng, options?: options): this
3509	// Pans the map the minimum amount to make the `latlng` visible. Use
3510	// `padding`, `paddingTopLeft` and `paddingTopRight` options to fit
3511	// the display to more restricted bounds, like [`fitBounds`](#map-fitbounds).
3512	// If `latlng` is already within the (optionally padded) display bounds,
3513	// the map will not be panned.
3514	panInside: function (latlng, options) {
3515		options = options || {};
3516
3517		var paddingTL = toPoint(options.paddingTopLeft || options.padding || [0, 0]),
3518		    paddingBR = toPoint(options.paddingBottomRight || options.padding || [0, 0]),
3519		    center = this.getCenter(),
3520		    pixelCenter = this.project(center),
3521		    pixelPoint = this.project(latlng),
3522		    pixelBounds = this.getPixelBounds(),
3523		    halfPixelBounds = pixelBounds.getSize().divideBy(2),
3524		    paddedBounds = toBounds([pixelBounds.min.add(paddingTL), pixelBounds.max.subtract(paddingBR)]);
3525
3526		if (!paddedBounds.contains(pixelPoint)) {
3527			this._enforcingBounds = true;
3528			var diff = pixelCenter.subtract(pixelPoint),
3529			    newCenter = toPoint(pixelPoint.x + diff.x, pixelPoint.y + diff.y);
3530
3531			if (pixelPoint.x < paddedBounds.min.x || pixelPoint.x > paddedBounds.max.x) {
3532				newCenter.x = pixelCenter.x - diff.x;
3533				if (diff.x > 0) {
3534					newCenter.x += halfPixelBounds.x - paddingTL.x;
3535				} else {
3536					newCenter.x -= halfPixelBounds.x - paddingBR.x;
3537				}
3538			}
3539			if (pixelPoint.y < paddedBounds.min.y || pixelPoint.y > paddedBounds.max.y) {
3540				newCenter.y = pixelCenter.y - diff.y;
3541				if (diff.y > 0) {
3542					newCenter.y += halfPixelBounds.y - paddingTL.y;
3543				} else {
3544					newCenter.y -= halfPixelBounds.y - paddingBR.y;
3545				}
3546			}
3547			this.panTo(this.unproject(new
vendor: 4,163 bytes, lines 3547-3678
3547Center), options);
3548			this._enforcingBounds = false;
3549		}
3550		return this;
3551	},
3552
3553	// @method invalidateSize(options: Zoom/pan options): this
3554	// Checks if the map container size changed and updates the map if so —
3555	// call it after you've changed the map size dynamically, also animating
3556	// pan by default. If `options.pan` is `false`, panning will not occur.
3557	// If `options.debounceMoveend` is `true`, it will delay `moveend` event so
3558	// that it doesn't happen often even if the method is called many
3559	// times in a row.
3560
3561	// @alternative
3562	// @method invalidateSize(animate: Boolean): this
3563	// Checks if the map container size changed and updates the map if so —
3564	// call it after you've changed the map size dynamically, also animating
3565	// pan by default.
3566	invalidateSize: function (options) {
3567		if (!this._loaded) { return this; }
3568
3569		options = extend({
3570			animate: false,
3571			pan: true
3572		}, options === true ? {animate: true} : options);
3573
3574		var oldSize = this.getSize();
3575		this._sizeChanged = true;
3576		this._lastCenter = null;
3577
3578		var newSize = this.getSize(),
3579		    oldCenter = oldSize.divideBy(2).round(),
3580		    newCenter = newSize.divideBy(2).round(),
3581		    offset = oldCenter.subtract(newCenter);
3582
3583		if (!offset.x && !offset.y) { return this; }
3584
3585		if (options.animate && options.pan) {
3586			this.panBy(offset);
3587
3588		} else {
3589			if (options.pan) {
3590				this._rawPanBy(offset);
3591			}
3592
3593			this.fire('move');
3594
3595			if (options.debounceMoveend) {
3596				clearTimeout(this._sizeTimer);
3597				this._sizeTimer = setTimeout(bind(this.fire, this, 'moveend'), 200);
3598			} else {
3599				this.fire('moveend');
3600			}
3601		}
3602
3603		// @section Map state change events
3604		// @event resize: ResizeEvent
3605		// Fired when the map is resized.
3606		return this.fire('resize', {
3607			oldSize: oldSize,
3608			newSize: newSize
3609		});
3610	},
3611
3612	// @section Methods for modifying map state
3613	// @method stop(): this
3614	// Stops the currently running `panTo` or `flyTo` animation, if any.
3615	stop: function () {
3616		this.setZoom(this._limitZoom(this._zoom));
3617		if (!this.options.zoomSnap) {
3618			this.fire('viewreset');
3619		}
3620		return this._stop();
3621	},
3622
3623	// @section Geolocation methods
3624	// @method locate(options?: Locate options): this
3625	// Tries to locate the user using the Geolocation API, firing a [`locationfound`](#map-locationfound)
3626	// event with location data on success or a [`locationerror`](#map-locationerror) event on failure,
3627	// and optionally sets the map view to the user's location with respect to
3628	// detection accuracy (or to the world view if geolocation failed).
3629	// Note that, if your page doesn't use HTTPS, this method will fail in
3630	// modern browsers ([Chrome 50 and newer](https://sites.google.com/a/chromium.org/dev/Home/chromium-security/deprecating-powerful-features-on-insecure-origins))
3631	// See `Locate options` for more details.
3632	locate: function (options) {
3633
3634		options = this._locateOptions = extend({
3635			timeout: 10000,
3636			watch: false
3637			// setView: false
3638			// maxZoom: <Number>
3639			// maximumAge: 0
3640			// enableHighAccuracy: false
3641		}, options);
3642
3643		if (!('geolocation' in navigator)) {
3644			this._handleGeolocationError({
3645				code: 0,
3646				message: 'Geolocation not supported.'
3647			});
3648			return this;
3649		}
3650
3651		var onResponse = bind(this._handleGeolocationResponse, this),
3652		    onError = bind(this._handleGeolocationError, this);
3653
3654		if (options.watch) {
3655			this._locationWatchId =
3656			        navigator.geolocation.watchPosition(onResponse, onError, options);
3657		} else {
3658			navigator.geolocation.getCurrentPosition(onResponse, onError, options);
3659		}
3660		return this;
3661	},
3662
3663	// @method stopLocate(): this
3664	// Stops watching location previously initiated by `map.locate({watch: true})`
3665	// and aborts resetting the map view if map.locate was called with
3666	// `{setView: true}`.
3667	stopLocate: function () {
3668		if (navigator.geolocation && navigator.geolocation.clearWatch) {
3669			navigator.geolocation.clearWatch(this._locationWatchId);
3670		}
3671		if (this._locateOptions) {
3672			this._locateOptions.setView = false;
3673		}
3674		return this;
3675	},
3676
3677	_handleGeolocationError: function (error) {
3678		
vendor: 5,993 bytes, lines 3678-3880
3678var c = error.code,
3679		    message = error.message ||
3680		            (c === 1 ? 'permission denied' :
3681		            (c === 2 ? 'position unavailable' : 'timeout'));
3682
3683		if (this._locateOptions.setView && !this._loaded) {
3684			this.fitWorld();
3685		}
3686
3687		// @section Location events
3688		// @event locationerror: ErrorEvent
3689		// Fired when geolocation (using the [`locate`](#map-locate) method) failed.
3690		this.fire('locationerror', {
3691			code: c,
3692			message: 'Geolocation error: ' + message + '.'
3693		});
3694	},
3695
3696	_handleGeolocationResponse: function (pos) {
3697		var lat = pos.coords.latitude,
3698		    lng = pos.coords.longitude,
3699		    latlng = new LatLng(lat, lng),
3700		    bounds = latlng.toBounds(pos.coords.accuracy * 2),
3701		    options = this._locateOptions;
3702
3703		if (options.setView) {
3704			var zoom = this.getBoundsZoom(bounds);
3705			this.setView(latlng, options.maxZoom ? Math.min(zoom, options.maxZoom) : zoom);
3706		}
3707
3708		var data = {
3709			latlng: latlng,
3710			bounds: bounds,
3711			timestamp: pos.timestamp
3712		};
3713
3714		for (var i in pos.coords) {
3715			if (typeof pos.coords[i] === 'number') {
3716				data[i] = pos.coords[i];
3717			}
3718		}
3719
3720		// @event locationfound: LocationEvent
3721		// Fired when geolocation (using the [`locate`](#map-locate) method)
3722		// went successfully.
3723		this.fire('locationfound', data);
3724	},
3725
3726	// TODO Appropriate docs section?
3727	// @section Other Methods
3728	// @method addHandler(name: String, HandlerClass: Function): this
3729	// Adds a new `Handler` to the map, given its name and constructor function.
3730	addHandler: function (name, HandlerClass) {
3731		if (!HandlerClass) { return this; }
3732
3733		var handler = this[name] = new HandlerClass(this);
3734
3735		this._handlers.push(handler);
3736
3737		if (this.options[name]) {
3738			handler.enable();
3739		}
3740
3741		return this;
3742	},
3743
3744	// @method remove(): this
3745	// Destroys the map and clears all related event listeners.
3746	remove: function () {
3747
3748		this._initEvents(true);
3749
3750		if (this._containerId !== this._container._leaflet_id) {
3751			throw new Error('Map container is being reused by another instance');
3752		}
3753
3754		try {
3755			// throws error in IE6-8
3756			delete this._container._leaflet_id;
3757			delete this._containerId;
3758		} catch (e) {
3759			/*eslint-disable */
3760			this._container._leaflet_id = undefined;
3761			/* eslint-enable */
3762			this._containerId = undefined;
3763		}
3764
3765		if (this._locationWatchId !== undefined) {
3766			this.stopLocate();
3767		}
3768
3769		this._stop();
3770
3771		remove(this._mapPane);
3772
3773		if (this._clearControlPos) {
3774			this._clearControlPos();
3775		}
3776		if (this._resizeRequest) {
3777			cancelAnimFrame(this._resizeRequest);
3778			this._resizeRequest = null;
3779		}
3780
3781		this._clearHandlers();
3782
3783		if (this._loaded) {
3784			// @section Map state change events
3785			// @event unload: Event
3786			// Fired when the map is destroyed with [remove](#map-remove) method.
3787			this.fire('unload');
3788		}
3789
3790		var i;
3791		for (i in this._layers) {
3792			this._layers[i].remove();
3793		}
3794		for (i in this._panes) {
3795			remove(this._panes[i]);
3796		}
3797
3798		this._layers = [];
3799		this._panes = [];
3800		delete this._mapPane;
3801		delete this._renderer;
3802
3803		return this;
3804	},
3805
3806	// @section Other Methods
3807	// @method createPane(name: String, container?: HTMLElement): HTMLElement
3808	// Creates a new [map pane](#map-pane) with the given name if it doesn't exist already,
3809	// then returns it. The pane is created as a child of `container`, or
3810	// as a child of the main map pane if not set.
3811	createPane: function (name, container) {
3812		var className = 'leaflet-pane' + (name ? ' leaflet-' + name.replace('Pane', '') + '-pane' : ''),
3813		    pane = create$1('div', className, container || this._mapPane);
3814
3815		if (name) {
3816			this._panes[name] = pane;
3817		}
3818		return pane;
3819	},
3820
3821	// @section Methods for Getting Map State
3822
3823	// @method getCenter(): LatLng
3824	// Returns the geographical center of the map view
3825	getCenter: function () {
3826		this._checkIfLoaded();
3827
3828		if (this._lastCenter && !this._moved()) {
3829			return this._lastCenter;
3830		}
3831		return this.layerPointToLatLng(this._getCenterLayerPoint());
3832	},
3833
3834	// @method getZoom(): Number
3835	// Returns the current zoom level of the map view
3836	getZoom: function () {
3837		return this._zoom;
3838	},
3839
3840	// @method getBounds(): LatLngBounds
3841	// Returns the geographical bounds visible in the current map view
3842	getBounds: function () {
3843		var bounds = this.getPixelBounds(),
3844		    sw = this.unproject(bounds.getBottomLeft()),
3845		    ne = this.unproject(bounds.getTopRight());
3846
3847		return new LatLngBounds(sw, ne);
3848	},
3849
3850	// @method getMinZoom(): Number
3851	// 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.
3852	getMinZoom: function () {
3853		return this.options.minZoom === undefined ? this._layersMinZoom || 0 : this.options.minZoom;
3854	},
3855
3856	// @method getMaxZoom(): Number
3857	// Returns the maximum zoom level of the map (if set in the `maxZoom` option of the map or of any layers).
3858	getMaxZoom: function () {
3859		return this.options.maxZoom === undefined ?
3860			(this._layersMaxZoom === undefined ? Infinity : this._layersMaxZoom) :
3861			this.options.maxZoom;
3862	},
3863
3864	// @method getBoundsZoom(bounds: LatLngBounds, inside?: Boolean, padding?: Point): Number
3865	// Returns the maximum zoom level on which the given bounds fit to the map
3866	// view in its entirety. If `inside` (optional) is set to `true`, the method
3867	// instead returns the minimum zoom level on which the map view fits into
3868	// the given bounds in its entirety.
3869	getBoundsZoom: function (bounds, inside, padding) { // (LatLngBounds[, Boolean, Point]) -> Number
3870		bounds = toLatLngBounds(bounds);
3871		padding = toPoint(padding || [0, 0]);
3872
3873		var zoom = this.getZoom() || 0,
3874		    min = this.getMinZoom(),
3875		    max = this.getMaxZoom(),
3876		    nw = bounds.getNorthWest(),
3877		    se = bounds.getSouthEast(),
3878		    size = this.getSize().subtract(padding),
3879		    boundsSize = toBounds(this.project(se, zoom), this.project(nw, zoom)).getSize(),
3880		    snap = 
vendor: 9,204 bytes, lines 3880-4108
3880any3d ? this.options.zoomSnap : 1,
3881		    scalex = size.x / boundsSize.x,
3882		    scaley = size.y / boundsSize.y,
3883		    scale = inside ? Math.max(scalex, scaley) : Math.min(scalex, scaley);
3884
3885		zoom = this.getScaleZoom(scale, zoom);
3886
3887		if (snap) {
3888			zoom = Math.round(zoom / (snap / 100)) * (snap / 100); // don't jump if within 1% of a snap level
3889			zoom = inside ? Math.ceil(zoom / snap) * snap : Math.floor(zoom / snap) * snap;
3890		}
3891
3892		return Math.max(min, Math.min(max, zoom));
3893	},
3894
3895	// @method getSize(): Point
3896	// Returns the current size of the map container (in pixels).
3897	getSize: function () {
3898		if (!this._size || this._sizeChanged) {
3899			this._size = new Point(
3900				this._container.clientWidth || 0,
3901				this._container.clientHeight || 0);
3902
3903			this._sizeChanged = false;
3904		}
3905		return this._size.clone();
3906	},
3907
3908	// @method getPixelBounds(): Bounds
3909	// Returns the bounds of the current map view in projected pixel
3910	// coordinates (sometimes useful in layer and overlay implementations).
3911	getPixelBounds: function (center, zoom) {
3912		var topLeftPoint = this._getTopLeftPoint(center, zoom);
3913		return new Bounds(topLeftPoint, topLeftPoint.add(this.getSize()));
3914	},
3915
3916	// TODO: Check semantics - isn't the pixel origin the 0,0 coord relative to
3917	// the map pane? "left point of the map layer" can be confusing, specially
3918	// since there can be negative offsets.
3919	// @method getPixelOrigin(): Point
3920	// Returns the projected pixel coordinates of the top left point of
3921	// the map layer (useful in custom layer and overlay implementations).
3922	getPixelOrigin: function () {
3923		this._checkIfLoaded();
3924		return this._pixelOrigin;
3925	},
3926
3927	// @method getPixelWorldBounds(zoom?: Number): Bounds
3928	// Returns the world's bounds in pixel coordinates for zoom level `zoom`.
3929	// If `zoom` is omitted, the map's current zoom level is used.
3930	getPixelWorldBounds: function (zoom) {
3931		return this.options.crs.getProjectedBounds(zoom === undefined ? this.getZoom() : zoom);
3932	},
3933
3934	// @section Other Methods
3935
3936	// @method getPane(pane: String|HTMLElement): HTMLElement
3937	// Returns a [map pane](#map-pane), given its name or its HTML element (its identity).
3938	getPane: function (pane) {
3939		return typeof pane === 'string' ? this._panes[pane] : pane;
3940	},
3941
3942	// @method getPanes(): Object
3943	// Returns a plain object containing the names of all [panes](#map-pane) as keys and
3944	// the panes as values.
3945	getPanes: function () {
3946		return this._panes;
3947	},
3948
3949	// @method getContainer: HTMLElement
3950	// Returns the HTML element that contains the map.
3951	getContainer: function () {
3952		return this._container;
3953	},
3954
3955
3956	// @section Conversion Methods
3957
3958	// @method getZoomScale(toZoom: Number, fromZoom: Number): Number
3959	// Returns the scale factor to be applied to a map transition from zoom level
3960	// `fromZoom` to `toZoom`. Used internally to help with zoom animations.
3961	getZoomScale: function (toZoom, fromZoom) {
3962		// TODO replace with universal implementation after refactoring projections
3963		var crs = this.options.crs;
3964		fromZoom = fromZoom === undefined ? this._zoom : fromZoom;
3965		return crs.scale(toZoom) / crs.scale(fromZoom);
3966	},
3967
3968	// @method getScaleZoom(scale: Number, fromZoom: Number): Number
3969	// Returns the zoom level that the map would end up at, if it is at `fromZoom`
3970	// level and everything is scaled by a factor of `scale`. Inverse of
3971	// [`getZoomScale`](#map-getZoomScale).
3972	getScaleZoom: function (scale, fromZoom) {
3973		var crs = this.options.crs;
3974		fromZoom = fromZoom === undefined ? this._zoom : fromZoom;
3975		var zoom = crs.zoom(scale * crs.scale(fromZoom));
3976		return isNaN(zoom) ? Infinity : zoom;
3977	},
3978
3979	// @method project(latlng: LatLng, zoom: Number): Point
3980	// Projects a geographical coordinate `LatLng` according to the projection
3981	// of the map's CRS, then scales it according to `zoom` and the CRS's
3982	// `Transformation`. The result is pixel coordinate relative to
3983	// the CRS origin.
3984	project: function (latlng, zoom) {
3985		zoom = zoom === undefined ? this._zoom : zoom;
3986		return this.options.crs.latLngToPoint(toLatLng(latlng), zoom);
3987	},
3988
3989	// @method unproject(point: Point, zoom: Number): LatLng
3990	// Inverse of [`project`](#map-project).
3991	unproject: function (point, zoom) {
3992		zoom = zoom === undefined ? this._zoom : zoom;
3993		return this.options.crs.pointToLatLng(toPoint(point), zoom);
3994	},
3995
3996	// @method layerPointToLatLng(point: Point): LatLng
3997	// Given a pixel coordinate relative to the [origin pixel](#map-getpixelorigin),
3998	// returns the corresponding geographical coordinate (for the current zoom level).
3999	layerPointToLatLng: function (point) {
4000		var projectedPoint = toPoint(point).add(this.getPixelOrigin());
4001		return this.unproject(projectedPoint);
4002	},
4003
4004	// @method latLngToLayerPoint(latlng: LatLng): Point
4005	// Given a geographical coordinate, returns the corresponding pixel coordinate
4006	// relative to the [origin pixel](#map-getpixelorigin).
4007	latLngToLayerPoint: function (latlng) {
4008		var projectedPoint = this.project(toLatLng(latlng))._round();
4009		return projectedPoint._subtract(this.getPixelOrigin());
4010	},
4011
4012	// @method wrapLatLng(latlng: LatLng): LatLng
4013	// Returns a `LatLng` where `lat` and `lng` has been wrapped according to the
4014	// map's CRS's `wrapLat` and `wrapLng` properties, if they are outside the
4015	// CRS's bounds.
4016	// By default this means longitude is wrapped around the dateline so its
4017	// value is between -180 and +180 degrees.
4018	wrapLatLng: function (latlng) {
4019		return this.options.crs.wrapLatLng(toLatLng(latlng));
4020	},
4021
4022	// @method wrapLatLngBounds(bounds: LatLngBounds): LatLngBounds
4023	// Returns a `LatLngBounds` with the same size as the given one, ensuring that
4024	// its center is within the CRS's bounds.
4025	// By default this means the center longitude is wrapped around the dateline so its
4026	// value is between -180 and +180 degrees, and the majority of the bounds
4027	// overlaps the CRS's bounds.
4028	wrapLatLngBounds: function (latlng) {
4029		return this.options.crs.wrapLatLngBounds(toLatLngBounds(latlng));
4030	},
4031
4032	// @method distance(latlng1: LatLng, latlng2: LatLng): Number
4033	// Returns the distance between two geographical coordinates according to
4034	// the map's CRS. By default this measures distance in meters.
4035	distance: function (latlng1, latlng2) {
4036		return this.options.crs.distance(toLatLng(latlng1), toLatLng(latlng2));
4037	},
4038
4039	// @method containerPointToLayerPoint(point: Point): Point
4040	// Given a pixel coordinate relative to the map container, returns the corresponding
4041	// pixel coordinate relative to the [origin pixel](#map-getpixelorigin).
4042	containerPointToLayerPoint: function (point) { // (Point)
4043		return toPoint(point).subtract(this._getMapPanePos());
4044	},
4045
4046	// @method layerPointToContainerPoint(point: Point): Point
4047	// Given a pixel coordinate relative to the [origin pixel](#map-getpixelorigin),
4048	// returns the corresponding pixel coordinate relative to the map container.
4049	layerPointToContainerPoint: function (point) { // (Point)
4050		return toPoint(point).add(this._getMapPanePos());
4051	},
4052
4053	// @method containerPointToLatLng(point: Point): LatLng
4054	// Given a pixel coordinate relative to the map container, returns
4055	// the corresponding geographical coordinate (for the current zoom level).
4056	containerPointToLatLng: function (point) {
4057		var layerPoint = this.containerPointToLayerPoint(toPoint(point));
4058		return this.layerPointToLatLng(layerPoint);
4059	},
4060
4061	// @method latLngToContainerPoint(latlng: LatLng): Point
4062	// Given a geographical coordinate, returns the corresponding pixel coordinate
4063	// relative to the map container.
4064	latLngToContainerPoint: function (latlng) {
4065		return this.layerPointToContainerPoint(this.latLngToLayerPoint(toLatLng(latlng)));
4066	},
4067
4068	// @method mouseEventToContainerPoint(ev: MouseEvent): Point
4069	// Given a MouseEvent object, returns the pixel coordinate relative to the
4070	// map container where the event took place.
4071	mouseEventToContainerPoint: function (e) {
4072		return getMousePosition(e, this._container);
4073	},
4074
4075	// @method mouseEventToLayerPoint(ev: MouseEvent): Point
4076	// Given a MouseEvent object, returns the pixel coordinate relative to
4077	// the [origin pixel](#map-getpixelorigin) where the event took place.
4078	mouseEventToLayerPoint: function (e) {
4079		return this.containerPointToLayerPoint(this.mouseEventToContainerPoint(e));
4080	},
4081
4082	// @method mouseEventToLatLng(ev: MouseEvent): LatLng
4083	// Given a MouseEvent object, returns geographical coordinate where the
4084	// event took place.
4085	mouseEventToLatLng: function (e) { // (MouseEvent)
4086		return this.layerPointToLatLng(this.mouseEventToLayerPoint(e));
4087	},
4088
4089
4090	// map initialization methods
4091
4092	_initContainer: function (id) {
4093		var container = this._container = get(id);
4094
4095		if (!container) {
4096			throw new Error('Map container not found.');
4097		} else if (container._leaflet_id) {
4098			throw new Error('Map container is already initialized.');
4099		}
4100
4101		on(container, 'scroll', this._onScroll, this);
4102		this._containerId = stamp(container);
4103	},
4104
4105	_initLayout: function () {
4106		var container = this._container;
4107
4108		this._fadeAnimated = this.options.fadeAnimation && 
vendor: 23,539 bytes, lines 4108-4895
4108any3d;
4109
4110		addClass(container, 'leaflet-container' +
4111			(touch ? ' leaflet-touch' : '') +
4112			(retina ? ' leaflet-retina' : '') +
4113			(ielt9 ? ' leaflet-oldie' : '') +
4114			(safari ? ' leaflet-safari' : '') +
4115			(this._fadeAnimated ? ' leaflet-fade-anim' : ''));
4116
4117		var position = getStyle(container, 'position');
4118
4119		if (position !== 'absolute' && position !== 'relative' && position !== 'fixed') {
4120			container.style.position = 'relative';
4121		}
4122
4123		this._initPanes();
4124
4125		if (this._initControlPos) {
4126			this._initControlPos();
4127		}
4128	},
4129
4130	_initPanes: function () {
4131		var panes = this._panes = {};
4132		this._paneRenderers = {};
4133
4134		// @section
4135		//
4136		// Panes are DOM elements used to control the ordering of layers on the map. You
4137		// can access panes with [`map.getPane`](#map-getpane) or
4138		// [`map.getPanes`](#map-getpanes) methods. New panes can be created with the
4139		// [`map.createPane`](#map-createpane) method.
4140		//
4141		// Every map has the following default panes that differ only in zIndex.
4142		//
4143		// @pane mapPane: HTMLElement = 'auto'
4144		// Pane that contains all other map panes
4145
4146		this._mapPane = this.createPane('mapPane', this._container);
4147		setPosition(this._mapPane, new Point(0, 0));
4148
4149		// @pane tilePane: HTMLElement = 200
4150		// Pane for `GridLayer`s and `TileLayer`s
4151		this.createPane('tilePane');
4152		// @pane overlayPane: HTMLElement = 400
4153		// Pane for vectors (`Path`s, like `Polyline`s and `Polygon`s), `ImageOverlay`s and `VideoOverlay`s
4154		this.createPane('shadowPane');
4155		// @pane shadowPane: HTMLElement = 500
4156		// Pane for overlay shadows (e.g. `Marker` shadows)
4157		this.createPane('overlayPane');
4158		// @pane markerPane: HTMLElement = 600
4159		// Pane for `Icon`s of `Marker`s
4160		this.createPane('markerPane');
4161		// @pane tooltipPane: HTMLElement = 650
4162		// Pane for `Tooltip`s.
4163		this.createPane('tooltipPane');
4164		// @pane popupPane: HTMLElement = 700
4165		// Pane for `Popup`s.
4166		this.createPane('popupPane');
4167
4168		if (!this.options.markerZoomAnimation) {
4169			addClass(panes.markerPane, 'leaflet-zoom-hide');
4170			addClass(panes.shadowPane, 'leaflet-zoom-hide');
4171		}
4172	},
4173
4174
4175	// private methods that modify map state
4176
4177	// @section Map state change events
4178	_resetView: function (center, zoom) {
4179		setPosition(this._mapPane, new Point(0, 0));
4180
4181		var loading = !this._loaded;
4182		this._loaded = true;
4183		zoom = this._limitZoom(zoom);
4184
4185		this.fire('viewprereset');
4186
4187		var zoomChanged = this._zoom !== zoom;
4188		this
4189			._moveStart(zoomChanged, false)
4190			._move(center, zoom)
4191			._moveEnd(zoomChanged);
4192
4193		// @event viewreset: Event
4194		// Fired when the map needs to redraw its content (this usually happens
4195		// on map zoom or load). Very useful for creating custom overlays.
4196		this.fire('viewreset');
4197
4198		// @event load: Event
4199		// Fired when the map is initialized (when its center and zoom are set
4200		// for the first time).
4201		if (loading) {
4202			this.fire('load');
4203		}
4204	},
4205
4206	_moveStart: function (zoomChanged, noMoveStart) {
4207		// @event zoomstart: Event
4208		// Fired when the map zoom is about to change (e.g. before zoom animation).
4209		// @event movestart: Event
4210		// Fired when the view of the map starts changing (e.g. user starts dragging the map).
4211		if (zoomChanged) {
4212			this.fire('zoomstart');
4213		}
4214		if (!noMoveStart) {
4215			this.fire('movestart');
4216		}
4217		return this;
4218	},
4219
4220	_move: function (center, zoom, data) {
4221		if (zoom === undefined) {
4222			zoom = this._zoom;
4223		}
4224		var zoomChanged = this._zoom !== zoom;
4225
4226		this._zoom = zoom;
4227		this._lastCenter = center;
4228		this._pixelOrigin = this._getNewPixelOrigin(center);
4229
4230		// @event zoom: Event
4231		// Fired repeatedly during any change in zoom level, including zoom
4232		// and fly animations.
4233		if (zoomChanged || (data && data.pinch)) {	// Always fire 'zoom' if pinching because #3530
4234			this.fire('zoom', data);
4235		}
4236
4237		// @event move: Event
4238		// Fired repeatedly during any movement of the map, including pan and
4239		// fly animations.
4240		return this.fire('move', data);
4241	},
4242
4243	_moveEnd: function (zoomChanged) {
4244		// @event zoomend: Event
4245		// Fired when the map has changed, after any animations.
4246		if (zoomChanged) {
4247			this.fire('zoomend');
4248		}
4249
4250		// @event moveend: Event
4251		// Fired when the center of the map stops changing (e.g. user stopped
4252		// dragging the map).
4253		return this.fire('moveend');
4254	},
4255
4256	_stop: function () {
4257		cancelAnimFrame(this._flyToFrame);
4258		if (this._panAnim) {
4259			this._panAnim.stop();
4260		}
4261		return this;
4262	},
4263
4264	_rawPanBy: function (offset) {
4265		setPosition(this._mapPane, this._getMapPanePos().subtract(offset));
4266	},
4267
4268	_getZoomSpan: function () {
4269		return this.getMaxZoom() - this.getMinZoom();
4270	},
4271
4272	_panInsideMaxBounds: function () {
4273		if (!this._enforcingBounds) {
4274			this.panInsideBounds(this.options.maxBounds);
4275		}
4276	},
4277
4278	_checkIfLoaded: function () {
4279		if (!this._loaded) {
4280			throw new Error('Set map center and zoom first.');
4281		}
4282	},
4283
4284	// DOM event handling
4285
4286	// @section Interaction events
4287	_initEvents: function (remove$$1) {
4288		this._targets = {};
4289		this._targets[stamp(this._container)] = this;
4290
4291		var onOff = remove$$1 ? off : on;
4292
4293		// @event click: MouseEvent
4294		// Fired when the user clicks (or taps) the map.
4295		// @event dblclick: MouseEvent
4296		// Fired when the user double-clicks (or double-taps) the map.
4297		// @event mousedown: MouseEvent
4298		// Fired when the user pushes the mouse button on the map.
4299		// @event mouseup: MouseEvent
4300		// Fired when the user releases the mouse button on the map.
4301		// @event mouseover: MouseEvent
4302		// Fired when the mouse enters the map.
4303		// @event mouseout: MouseEvent
4304		// Fired when the mouse leaves the map.
4305		// @event mousemove: MouseEvent
4306		// Fired while the mouse moves over the map.
4307		// @event contextmenu: MouseEvent
4308		// Fired when the user pushes the right mouse button on the map, prevents
4309		// default browser context menu from showing if there are listeners on
4310		// this event. Also fired on mobile when the user holds a single touch
4311		// for a second (also called long press).
4312		// @event keypress: KeyboardEvent
4313		// Fired when the user presses a key from the keyboard while the map is focused.
4314		onOff(this._container, 'click dblclick mousedown mouseup ' +
4315			'mouseover mouseout mousemove contextmenu keypress', this._handleDOMEvent, this);
4316
4317		if (this.options.trackResize) {
4318			onOff(window, 'resize', this._onResize, this);
4319		}
4320
4321		if (any3d && this.options.transform3DLimit) {
4322			(remove$$1 ? this.off : this.on).call(this, 'moveend', this._onMoveEnd);
4323		}
4324	},
4325
4326	_onResize: function () {
4327		cancelAnimFrame(this._resizeRequest);
4328		this._resizeRequest = requestAnimFrame(
4329		        function () { this.invalidateSize({debounceMoveend: true}); }, this);
4330	},
4331
4332	_onScroll: function () {
4333		this._container.scrollTop  = 0;
4334		this._container.scrollLeft = 0;
4335	},
4336
4337	_onMoveEnd: function () {
4338		var pos = this._getMapPanePos();
4339		if (Math.max(Math.abs(pos.x), Math.abs(pos.y)) >= this.options.transform3DLimit) {
4340			// https://bugzilla.mozilla.org/show_bug.cgi?id=1203873 but Webkit also have
4341			// a pixel offset on very high values, see: http://jsfiddle.net/dg6r5hhb/
4342			this._resetView(this.getCenter(), this.getZoom());
4343		}
4344	},
4345
4346	_findEventTargets: function (e, type) {
4347		var targets = [],
4348		    target,
4349		    isHover = type === 'mouseout' || type === 'mouseover',
4350		    src = e.target || e.srcElement,
4351		    dragging = false;
4352
4353		while (src) {
4354			target = this._targets[stamp(src)];
4355			if (target && (type === 'click' || type === 'preclick') && !e._simulated && this._draggableMoved(target)) {
4356				// Prevent firing click after you just dragged an object.
4357				dragging = true;
4358				break;
4359			}
4360			if (target && target.listens(type, true)) {
4361				if (isHover && !isExternalTarget(src, e)) { break; }
4362				targets.push(target);
4363				if (isHover) { break; }
4364			}
4365			if (src === this._container) { break; }
4366			src = src.parentNode;
4367		}
4368		if (!targets.length && !dragging && !isHover && isExternalTarget(src, e)) {
4369			targets = [this];
4370		}
4371		return targets;
4372	},
4373
4374	_handleDOMEvent: function (e) {
4375		if (!this._loaded || skipped(e)) { return; }
4376
4377		var type = e.type;
4378
4379		if (type === 'mousedown' || type === 'keypress') {
4380			// prevents outline when clicking on keyboard-focusable element
4381			preventOutline(e.target || e.srcElement);
4382		}
4383
4384		this._fireDOMEvent(e, type);
4385	},
4386
4387	_mouseEvents: ['click', 'dblclick', 'mouseover', 'mouseout', 'contextmenu'],
4388
4389	_fireDOMEvent: function (e, type, targets) {
4390
4391		if (e.type === 'click') {
4392			// Fire a synthetic 'preclick' event which propagates up (mainly for closing popups).
4393			// @event preclick: MouseEvent
4394			// Fired before mouse click on the map (sometimes useful when you
4395			// want something to happen on click before any existing click
4396			// handlers start running).
4397			var synth = extend({}, e);
4398			synth.type = 'preclick';
4399			this._fireDOMEvent(synth, synth.type, targets);
4400		}
4401
4402		if (e._stopped) { return; }
4403
4404		// Find the layer the event is propagating from and its parents.
4405		targets = (targets || []).concat(this._findEventTargets(e, type));
4406
4407		if (!targets.length) { return; }
4408
4409		var target = targets[0];
4410		if (type === 'contextmenu' && target.listens(type, true)) {
4411			preventDefault(e);
4412		}
4413
4414		var data = {
4415			originalEvent: e
4416		};
4417
4418		if (e.type !== 'keypress') {
4419			var isMarker = target.getLatLng && (!target._radius || target._radius <= 10);
4420			data.containerPoint = isMarker ?
4421				this.latLngToContainerPoint(target.getLatLng()) : this.mouseEventToContainerPoint(e);
4422			data.layerPoint = this.containerPointToLayerPoint(data.containerPoint);
4423			data.latlng = isMarker ? target.getLatLng() : this.layerPointToLatLng(data.layerPoint);
4424		}
4425
4426		for (var i = 0; i < targets.length; i++) {
4427			targets[i].fire(type, data, true);
4428			if (data.originalEvent._stopped ||
4429				(targets[i].options.bubblingMouseEvents === false && indexOf(this._mouseEvents, type) !== -1)) { return; }
4430		}
4431	},
4432
4433	_draggableMoved: function (obj) {
4434		obj = obj.dragging && obj.dragging.enabled() ? obj : this;
4435		return (obj.dragging && obj.dragging.moved()) || (this.boxZoom && this.boxZoom.moved());
4436	},
4437
4438	_clearHandlers: function () {
4439		for (var i = 0, len = this._handlers.length; i < len; i++) {
4440			this._handlers[i].disable();
4441		}
4442	},
4443
4444	// @section Other Methods
4445
4446	// @method whenReady(fn: Function, context?: Object): this
4447	// Runs the given function `fn` when the map gets initialized with
4448	// a view (center and zoom) and at least one layer, or immediately
4449	// if it's already initialized, optionally passing a function context.
4450	whenReady: function (callback, context) {
4451		if (this._loaded) {
4452			callback.call(context || this, {target: this});
4453		} else {
4454			this.on('load', callback, context);
4455		}
4456		return this;
4457	},
4458
4459
4460	// private methods for getting map state
4461
4462	_getMapPanePos: function () {
4463		return getPosition(this._mapPane) || new Point(0, 0);
4464	},
4465
4466	_moved: function () {
4467		var pos = this._getMapPanePos();
4468		return pos && !pos.equals([0, 0]);
4469	},
4470
4471	_getTopLeftPoint: function (center, zoom) {
4472		var pixelOrigin = center && zoom !== undefined ?
4473			this._getNewPixelOrigin(center, zoom) :
4474			this.getPixelOrigin();
4475		return pixelOrigin.subtract(this._getMapPanePos());
4476	},
4477
4478	_getNewPixelOrigin: function (center, zoom) {
4479		var viewHalf = this.getSize()._divideBy(2);
4480		return this.project(center, zoom)._subtract(viewHalf)._add(this._getMapPanePos())._round();
4481	},
4482
4483	_latLngToNewLayerPoint: function (latlng, zoom, center) {
4484		var topLeft = this._getNewPixelOrigin(center, zoom);
4485		return this.project(latlng, zoom)._subtract(topLeft);
4486	},
4487
4488	_latLngBoundsToNewLayerBounds: function (latLngBounds, zoom, center) {
4489		var topLeft = this._getNewPixelOrigin(center, zoom);
4490		return toBounds([
4491			this.project(latLngBounds.getSouthWest(), zoom)._subtract(topLeft),
4492			this.project(latLngBounds.getNorthWest(), zoom)._subtract(topLeft),
4493			this.project(latLngBounds.getSouthEast(), zoom)._subtract(topLeft),
4494			this.project(latLngBounds.getNorthEast(), zoom)._subtract(topLeft)
4495		]);
4496	},
4497
4498	// layer point of the current center
4499	_getCenterLayerPoint: function () {
4500		return this.containerPointToLayerPoint(this.getSize()._divideBy(2));
4501	},
4502
4503	// offset of the specified place to the current center in pixels
4504	_getCenterOffset: function (latlng) {
4505		return this.latLngToLayerPoint(latlng).subtract(this._getCenterLayerPoint());
4506	},
4507
4508	// adjust center for view to get inside bounds
4509	_limitCenter: function (center, zoom, bounds) {
4510
4511		if (!bounds) { return center; }
4512
4513		var centerPoint = this.project(center, zoom),
4514		    viewHalf = this.getSize().divideBy(2),
4515		    viewBounds = new Bounds(centerPoint.subtract(viewHalf), centerPoint.add(viewHalf)),
4516		    offset = this._getBoundsOffset(viewBounds, bounds, zoom);
4517
4518		// If offset is less than a pixel, ignore.
4519		// This prevents unstable projections from getting into
4520		// an infinite loop of tiny offsets.
4521		if (offset.round().equals([0, 0])) {
4522			return center;
4523		}
4524
4525		return this.unproject(centerPoint.add(offset), zoom);
4526	},
4527
4528	// adjust offset for view to get inside bounds
4529	_limitOffset: function (offset, bounds) {
4530		if (!bounds) { return offset; }
4531
4532		var viewBounds = this.getPixelBounds(),
4533		    newBounds = new Bounds(viewBounds.min.add(offset), viewBounds.max.add(offset));
4534
4535		return offset.add(this._getBoundsOffset(newBounds, bounds));
4536	},
4537
4538	// returns offset needed for pxBounds to get inside maxBounds at a specified zoom
4539	_getBoundsOffset: function (pxBounds, maxBounds, zoom) {
4540		var projectedMaxBounds = toBounds(
4541		        this.project(maxBounds.getNorthEast(), zoom),
4542		        this.project(maxBounds.getSouthWest(), zoom)
4543		    ),
4544		    minOffset = projectedMaxBounds.min.subtract(pxBounds.min),
4545		    maxOffset = projectedMaxBounds.max.subtract(pxBounds.max),
4546
4547		    dx = this._rebound(minOffset.x, -maxOffset.x),
4548		    dy = this._rebound(minOffset.y, -maxOffset.y);
4549
4550		return new Point(dx, dy);
4551	},
4552
4553	_rebound: function (left, right) {
4554		return left + right > 0 ?
4555			Math.round(left - right) / 2 :
4556			Math.max(0, Math.ceil(left)) - Math.max(0, Math.floor(right));
4557	},
4558
4559	_limitZoom: function (zoom) {
4560		var min = this.getMinZoom(),
4561		    max = this.getMaxZoom(),
4562		    snap = any3d ? this.options.zoomSnap : 1;
4563		if (snap) {
4564			zoom = Math.round(zoom / snap) * snap;
4565		}
4566		return Math.max(min, Math.min(max, zoom));
4567	},
4568
4569	_onPanTransitionStep: function () {
4570		this.fire('move');
4571	},
4572
4573	_onPanTransitionEnd: function () {
4574		removeClass(this._mapPane, 'leaflet-pan-anim');
4575		this.fire('moveend');
4576	},
4577
4578	_tryAnimatedPan: function (center, options) {
4579		// difference between the new and current centers in pixels
4580		var offset = this._getCenterOffset(center)._trunc();
4581
4582		// don't animate too far unless animate: true specified in options
4583		if ((options && options.animate) !== true && !this.getSize().contains(offset)) { return false; }
4584
4585		this.panBy(offset, options);
4586
4587		return true;
4588	},
4589
4590	_createAnimProxy: function () {
4591
4592		var proxy = this._proxy = create$1('div', 'leaflet-proxy leaflet-zoom-animated');
4593		this._panes.mapPane.appendChild(proxy);
4594
4595		this.on('zoomanim', function (e) {
4596			var prop = TRANSFORM,
4597			    transform = this._proxy.style[prop];
4598
4599			setTransform(this._proxy, this.project(e.center, e.zoom), this.getZoomScale(e.zoom, 1));
4600
4601			// workaround for case when transform is the same and so transitionend event is not fired
4602			if (transform === this._proxy.style[prop] && this._animatingZoom) {
4603				this._onZoomTransitionEnd();
4604			}
4605		}, this);
4606
4607		this.on('load moveend', function () {
4608			var c = this.getCenter(),
4609			    z = this.getZoom();
4610			setTransform(this._proxy, this.project(c, z), this.getZoomScale(z, 1));
4611		}, this);
4612
4613		this._on('unload', this._destroyAnimProxy, this);
4614	},
4615
4616	_destroyAnimProxy: function () {
4617		remove(this._proxy);
4618		delete this._proxy;
4619	},
4620
4621	_catchTransitionEnd: function (e) {
4622		if (this._animatingZoom && e.propertyName.indexOf('transform') >= 0) {
4623			this._onZoomTransitionEnd();
4624		}
4625	},
4626
4627	_nothingToAnimate: function () {
4628		return !this._container.getElementsByClassName('leaflet-zoom-animated').length;
4629	},
4630
4631	_tryAnimatedZoom: function (center, zoom, options) {
4632
4633		if (this._animatingZoom) { return true; }
4634
4635		options = options || {};
4636
4637		// don't animate if disabled, not supported or zoom difference is too large
4638		if (!this._zoomAnimated || options.animate === false || this._nothingToAnimate() ||
4639		        Math.abs(zoom - this._zoom) > this.options.zoomAnimationThreshold) { return false; }
4640
4641		// offset is the pixel coords of the zoom origin relative to the current center
4642		var scale = this.getZoomScale(zoom),
4643		    offset = this._getCenterOffset(center)._divideBy(1 - 1 / scale);
4644
4645		// don't animate if the zoom origin isn't within one screen from the current center, unless forced
4646		if (options.animate !== true && !this.getSize().contains(offset)) { return false; }
4647
4648		requestAnimFrame(function () {
4649			this
4650			    ._moveStart(true, false)
4651			    ._animateZoom(center, zoom, true);
4652		}, this);
4653
4654		return true;
4655	},
4656
4657	_animateZoom: function (center, zoom, startAnim, noUpdate) {
4658		if (!this._mapPane) { return; }
4659
4660		if (startAnim) {
4661			this._animatingZoom = true;
4662
4663			// remember what center/zoom to set after animation
4664			this._animateToCenter = center;
4665			this._animateToZoom = zoom;
4666
4667			addClass(this._mapPane, 'leaflet-zoom-anim');
4668		}
4669
4670		// @event zoomanim: ZoomAnimEvent
4671		// Fired at least once per zoom animation. For continous zoom, like pinch zooming, fired once per frame during zoom.
4672		this.fire('zoomanim', {
4673			center: center,
4674			zoom: zoom,
4675			noUpdate: noUpdate
4676		});
4677
4678		// Work around webkit not firing 'transitionend', see https://github.com/Leaflet/Leaflet/issues/3689, 2693
4679		setTimeout(bind(this._onZoomTransitionEnd, this), 250);
4680	},
4681
4682	_onZoomTransitionEnd: function () {
4683		if (!this._animatingZoom) { return; }
4684
4685		if (this._mapPane) {
4686			removeClass(this._mapPane, 'leaflet-zoom-anim');
4687		}
4688
4689		this._animatingZoom = false;
4690
4691		this._move(this._animateToCenter, this._animateToZoom);
4692
4693		// This anim frame should prevent an obscure iOS webkit tile loading race condition.
4694		requestAnimFrame(function () {
4695			this._moveEnd(true);
4696		}, this);
4697	}
4698});
4699
4700// @section
4701
4702// @factory L.map(id: String, options?: Map options)
4703// Instantiates a map object given the DOM ID of a `<div>` element
4704// and optionally an object literal with `Map options`.
4705//
4706// @alternative
4707// @factory L.map(el: HTMLElement, options?: Map options)
4708// Instantiates a map object given an instance of a `<div>` HTML element
4709// and optionally an object literal with `Map options`.
4710function createMap(id, options) {
4711	return new Map(id, options);
4712}
4713
4714/*
4715 * @class Control
4716 * @aka L.Control
4717 * @inherits Class
4718 *
4719 * L.Control is a base class for implementing map controls. Handles positioning.
4720 * All other controls extend from this class.
4721 */
4722
4723var Control = Class.extend({
4724	// @section
4725	// @aka Control options
4726	options: {
4727		// @option position: String = 'topright'
4728		// The position of the control (one of the map corners). Possible values are `'topleft'`,
4729		// `'topright'`, `'bottomleft'` or `'bottomright'`
4730		position: 'topright'
4731	},
4732
4733	initialize: function (options) {
4734		setOptions(this, options);
4735	},
4736
4737	/* @section
4738	 * Classes extending L.Control will inherit the following methods:
4739	 *
4740	 * @method getPosition: string
4741	 * Returns the position of the control.
4742	 */
4743	getPosition: function () {
4744		return this.options.position;
4745	},
4746
4747	// @method setPosition(position: string): this
4748	// Sets the position of the control.
4749	setPosition: function (position) {
4750		var map = this._map;
4751
4752		if (map) {
4753			map.removeControl(this);
4754		}
4755
4756		this.options.position = position;
4757
4758		if (map) {
4759			map.addControl(this);
4760		}
4761
4762		return this;
4763	},
4764
4765	// @method getContainer: HTMLElement
4766	// Returns the HTMLElement that contains the control.
4767	getContainer: function () {
4768		return this._container;
4769	},
4770
4771	// @method addTo(map: Map): this
4772	// Adds the control to the given map.
4773	addTo: function (map) {
4774		this.remove();
4775		this._map = map;
4776
4777		var container = this._container = this.onAdd(map),
4778		    pos = this.getPosition(),
4779		    corner = map._controlCorners[pos];
4780
4781		addClass(container, 'leaflet-control');
4782
4783		if (pos.indexOf('bottom') !== -1) {
4784			corner.insertBefore(container, corner.firstChild);
4785		} else {
4786			corner.appendChild(container);
4787		}
4788
4789		return this;
4790	},
4791
4792	// @method remove: this
4793	// Removes the control from the map it is currently active on.
4794	remove: function () {
4795		if (!this._map) {
4796			return this;
4797		}
4798
4799		remove(this._container);
4800
4801		if (this.onRemove) {
4802			this.onRemove(this._map);
4803		}
4804
4805		this._map = null;
4806
4807		return this;
4808	},
4809
4810	_refocusOnMap: function (e) {
4811		// if map exists and event is not a keyboard event
4812		if (this._map && e && e.screenX > 0 && e.screenY > 0) {
4813			this._map.getContainer().focus();
4814		}
4815	}
4816});
4817
4818var control = function (options) {
4819	return new Control(options);
4820};
4821
4822/* @section Extension methods
4823 * @uninheritable
4824 *
4825 * Every control should extend from `L.Control` and (re-)implement the following methods.
4826 *
4827 * @method onAdd(map: Map): HTMLElement
4828 * Should return the container DOM element for the control and add listeners on relevant map events. Called on [`control.addTo(map)`](#control-addTo).
4829 *
4830 * @method onRemove(map: Map)
4831 * Optional method. Should contain all clean up code that removes the listeners previously added in [`onAdd`](#control-onadd). Called on [`control.remove()`](#control-remove).
4832 */
4833
4834/* @namespace Map
4835 * @section Methods for Layers and Controls
4836 */
4837Map.include({
4838	// @method addControl(control: Control): this
4839	// Adds the given control to the map
4840	addControl: function (control) {
4841		control.addTo(this);
4842		return this;
4843	},
4844
4845	// @method removeControl(control: Control): this
4846	// Removes the given control from the map
4847	removeControl: function (control) {
4848		control.remove();
4849		return this;
4850	},
4851
4852	_initControlPos: function () {
4853		var corners = this._controlCorners = {},
4854		    l = 'leaflet-',
4855		    container = this._controlContainer =
4856		            create$1('div', l + 'control-container', this._container);
4857
4858		function createCorner(vSide, hSide) {
4859			var className = l + vSide + ' ' + l + hSide;
4860
4861			corners[vSide + hSide] = create$1('div', className, container);
4862		}
4863
4864		createCorner('top', 'left');
4865		createCorner('top', 'right');
4866		createCorner('bottom', 'left');
4867		createCorner('bottom', 'right');
4868	},
4869
4870	_clearControlPos: function () {
4871		for (var i in this._controlCorners) {
4872			remove(this._controlCorners[i]);
4873		}
4874		remove(this._controlContainer);
4875		delete this._controlCorners;
4876		delete this._controlContainer;
4877	}
4878});
4879
4880/*
4881 * @class Control.Layers
4882 * @aka L.Control.Layers
4883 * @inherits Control
4884 *
4885 * The layers control gives users the ability to switch between different base layers and switch overlays on/off (check out the [detailed example](http://leafletjs.com/examples/layers-control/)). Extends `Control`.
4886 *
4887 * @example
4888 *
4889 * ```js
4890 * var baseLayers = {
4891 * 	"Mapbox": mapbox,
4892 * 	"OpenStreetMap": osm
4893 * };
4894 *
4895 * var overlays = {
4896 * 	"Marker": marker,
4897 * 	"Roads": roadsLayer
4898 * };
4899 *
4900 * L.control.layers(baseLayers, overlays).addTo(map);
4901 * ```
4902 *
4903 * The `baseLayers` and `overlays` parameters are object literals with layer names as keys and `Layer` objects as values:
4904 *
4905 * ```js
4906 * {
4907 *     "<someName1>": layer1,
4908 *     "<someName2>": layer2
4909 * }
4910 * ```
4911 *
4912 * The layer names can contain HTML, which allows you to add additional styling to the items:
4913 *
4914 * ```js
4915 * {"<img src='my-layer-icon' /> <span class='my-layer-item'>My Layer</span>": myLayer}
4916 * ```
4917 */
4918
4919var Layers = Control.extend({
4920	// @section
4921	// @aka Control.Layers options
4922	options: {
4923		// @option collapsed: Boolean = true
4924		// If `true`, the control will be collapsed into an icon and expanded on mouse hover or touch.
4925		collapsed: true,
4926		position: 'topright',
4927
4928		// @option autoZIndex: Boolean = true
4929		// 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.
4930		autoZIndex: true,
4931
4932		// @option hideSingleBase: Boolean = false
4933		// If `true`, the base layers in the control will be hidden when there is only one.
4934		hideSingleBase: false,
4935
4936		// @option sortLayers: Boolean = false
4937		// Whether to sort the layers. When `false`, layers will keep the order
4938		// in which they were added to the control.
4939		sortLayers: false,
4940
4941		// @option sortFunction: Function = *
4942		// A [compare function](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Array/sort)
4943		// that will be used for sorting the layers, when `sortLayers` is `true`.
4944		// The function receives both the `L.Layer` instances and their names, as in
4945		// `sortFunction(layerA, layerB, nameA, nameB)`.
4946		// By default, it sorts layers alphabetically by their name.
4947		sortFunction: function (layerA, layerB, nameA, nameB) {
4948			return nameA < nameB ? -1 : (nameB < nameA ? 1 : 0);
4949		}
4950	},
4951
4952	initialize: function (baseLayers, overlays, options) {
4953		setOptions(this, options);
4954
4955		this._layerControlInputs = [];
4956		this._layers = [];
4957		this._lastZIndex = 0;
4958		this._handlingClick = false;
4959
4960		for (var i in baseLayers) {
4961			this._addLayer(baseLayers[i], i);
4962		}
4963
4964		for (i in overlays) {
4965			this._addLayer(overlays[i], i, true);
4966		}
4967	},
4968
4969	onAdd: function (map) {
4970		this._initLayout();
4971		this._update();
4972
4973		this._map = map;
4974		map.on('zoomend', this._checkDisabledLayers, this);
4975
4976		for (var i = 0; i < this._layers.length; i++) {
4977			this._layers[i].layer.on('add remove', this._onLayerChange, this);
4978		}
4979
4980		return this._container;
4981	},
4982
4983	addTo: function (map) {
4984		Control.prototype.addTo.call(this, map);
4985		// Trigger expand after Layers Control has been inserted into DOM so that is now has an actual height.
4986		return this._expandIfNotCollapsed();
4987	},
4988
4989	onRemove: function () {
4990		this._map.off('zoomend', this._checkDisabledLayers, this);
4991
4992		for (var i = 0; i < this._layers.length; i++) {
4993			this._layers[i].layer.off('add remove', this._onLayerChange, this);
4994		}
4995	},
4996
4997	// @method addBaseLayer(layer: Layer, name: String): this
4998	// Adds a base layer (radio button entry) with the given name to the control.
4999	addBaseLayer: function (layer, name) {
5000		this._addLayer(layer, name);
5001		return (this._map) ? this._update() : this;
5002	},
5003
5004	// @method addOverlay(layer: Layer, name: String): this
5005	// Adds an overlay (checkbox entry) with the given name to the control.
5006	addOverlay: function (layer, name) {
5007		this._addLayer(layer, name, true);
5008		return (this._map) ? this._update() : this;
5009	},
5010
5011	// @method removeLayer(layer: Layer): this
5012	// Remove the given layer from the control.
5013	removeLayer: function (layer) {
5014		layer.off('add remove', this._onLayerChange, this);
5015
5016		var obj = this._getLayer(stamp(layer));
5017		if (obj) {
5018			this._layers.splice(this._layers.indexOf(obj), 1);
5019		}
5020		return (this._map) ? this._update() : this;
5021	},
5022
5023	// @method expand(): this
5024	// Expand the control container if collapsed.
5025	expand: function () {
5026		addClass(this._container, 'leaflet-control-layers-expanded');
5027		this._section.style.height = null;
5028		var acceptableHeight = this._map.getSize().y - (this._container.offsetTop + 50);
5029		if (acceptableHeight < this._section.clientHeight) {
5030			addClass(this._section, 'leaflet-control-layers-scrollbar');
5031			this._section.style.height = acceptableHeight + 'px';
5032		} else {
5033			removeClass(this._section, 'leaflet-control-layers-scrollbar');
5034		}
5035		this._checkDisabledLayers();
5036		return this;
5037	},
5038
5039	// @method collapse(): this
5040	// Collapse the control container if expanded.
5041	collapse: function () {
5042		removeClass(this._container, 'leaflet-control-layers-expanded');
5043		return this;
5044	},
5045
5046	_initLayout: function () {
5047		var className = 'leaflet-control-layers',
5048		    container = this._container = create$1('div', className),
5049		    collapsed = this.options.collapsed;
5050
5051		// makes this work on IE touch devices by stopping it from firing a mouseout event when the touch is released
vendor: 19,116 bytes, lines 5052-5753
5052		container.setAttribute('aria-haspopup', true);
5053
5054		disableClickPropagation(container);
5055		disableScrollPropagation(container);
5056
5057		var section = this._section = create$1('section', className + '-list');
5058
5059		if (collapsed) {
5060			this._map.on('click', this.collapse, this);
5061
5062			if (!android) {
5063				on(container, {
5064					mouseenter: this.expand,
5065					mouseleave: this.collapse
5066				}, this);
5067			}
5068		}
5069
5070		var link = this._layersLink = create$1('a', className + '-toggle', container);
5071		link.href = '#';
5072		link.title = 'Layers';
5073
5074		if (touch) {
5075			on(link, 'click', stop);
5076			on(link, 'click', this.expand, this);
5077		} else {
5078			on(link, 'focus', this.expand, this);
5079		}
5080
5081		if (!collapsed) {
5082			this.expand();
5083		}
5084
5085		this._baseLayersList = create$1('div', className + '-base', section);
5086		this._separator = create$1('div', className + '-separator', section);
5087		this._overlaysList = create$1('div', className + '-overlays', section);
5088
5089		container.appendChild(section);
5090	},
5091
5092	_getLayer: function (id) {
5093		for (var i = 0; i < this._layers.length; i++) {
5094
5095			if (this._layers[i] && stamp(this._layers[i].layer) === id) {
5096				return this._layers[i];
5097			}
5098		}
5099	},
5100
5101	_addLayer: function (layer, name, overlay) {
5102		if (this._map) {
5103			layer.on('add remove', this._onLayerChange, this);
5104		}
5105
5106		this._layers.push({
5107			layer: layer,
5108			name: name,
5109			overlay: overlay
5110		});
5111
5112		if (this.options.sortLayers) {
5113			this._layers.sort(bind(function (a, b) {
5114				return this.options.sortFunction(a.layer, b.layer, a.name, b.name);
5115			}, this));
5116		}
5117
5118		if (this.options.autoZIndex && layer.setZIndex) {
5119			this._lastZIndex++;
5120			layer.setZIndex(this._lastZIndex);
5121		}
5122
5123		this._expandIfNotCollapsed();
5124	},
5125
5126	_update: function () {
5127		if (!this._container) { return this; }
5128
5129		empty(this._baseLayersList);
5130		empty(this._overlaysList);
5131
5132		this._layerControlInputs = [];
5133		var baseLayersPresent, overlaysPresent, i, obj, baseLayersCount = 0;
5134
5135		for (i = 0; i < this._layers.length; i++) {
5136			obj = this._layers[i];
5137			this._addItem(obj);
5138			overlaysPresent = overlaysPresent || obj.overlay;
5139			baseLayersPresent = baseLayersPresent || !obj.overlay;
5140			baseLayersCount += !obj.overlay ? 1 : 0;
5141		}
5142
5143		// Hide base layers section if there's only one layer.
5144		if (this.options.hideSingleBase) {
5145			baseLayersPresent = baseLayersPresent && baseLayersCount > 1;
5146			this._baseLayersList.style.display = baseLayersPresent ? '' : 'none';
5147		}
5148
5149		this._separator.style.display = overlaysPresent && baseLayersPresent ? '' : 'none';
5150
5151		return this;
5152	},
5153
5154	_onLayerChange: function (e) {
5155		if (!this._handlingClick) {
5156			this._update();
5157		}
5158
5159		var obj = this._getLayer(stamp(e.target));
5160
5161		// @namespace Map
5162		// @section Layer events
5163		// @event baselayerchange: LayersControlEvent
5164		// Fired when the base layer is changed through the [layer control](#control-layers).
5165		// @event overlayadd: LayersControlEvent
5166		// Fired when an overlay is selected through the [layer control](#control-layers).
5167		// @event overlayremove: LayersControlEvent
5168		// Fired when an overlay is deselected through the [layer control](#control-layers).
5169		// @namespace Control.Layers
5170		var type = obj.overlay ?
5171			(e.type === 'add' ? 'overlayadd' : 'overlayremove') :
5172			(e.type === 'add' ? 'baselayerchange' : null);
5173
5174		if (type) {
5175			this._map.fire(type, obj);
5176		}
5177	},
5178
5179	// IE7 bugs out if you create a radio dynamically, so you have to do it this hacky way (see http://bit.ly/PqYLBe)
5180	_createRadioElement: function (name, checked) {
5181
5182		var radioHtml = '<input type="radio" class="leaflet-control-layers-selector" name="' +
5183				name + '"' + (checked ? ' checked="checked"' : '') + '/>';
5184
5185		var radioFragment = document.createElement('div');
5186		radioFragment.innerHTML = radioHtml;
5187
5188		return radioFragment.firstChild;
5189	},
5190
5191	_addItem: function (obj) {
5192		var label = document.createElement('label'),
5193		    checked = this._map.hasLayer(obj.layer),
5194		    input;
5195
5196		if (obj.overlay) {
5197			input = document.createElement('input');
5198			input.type = 'checkbox';
5199			input.className = 'leaflet-control-layers-selector';
5200			input.defaultChecked = checked;
5201		} else {
5202			input = this._createRadioElement('leaflet-base-layers', checked);
5203		}
5204
5205		this._layerControlInputs.push(input);
5206		input.layerId = stamp(obj.layer);
5207
5208		on(input, 'click', this._onInputClick, this);
5209
5210		var name = document.createElement('span');
5211		name.innerHTML = ' ' + obj.name;
5212
5213		// Helps from preventing layer control flicker when checkboxes are disabled
5214		// https://github.com/Leaflet/Leaflet/issues/2771
5215		var holder = document.createElement('div');
5216
5217		label.appendChild(holder);
5218		holder.appendChild(input);
5219		holder.appendChild(name);
5220
5221		var container = obj.overlay ? this._overlaysList : this._baseLayersList;
5222		container.appendChild(label);
5223
5224		this._checkDisabledLayers();
5225		return label;
5226	},
5227
5228	_onInputClick: function () {
5229		var inputs = this._layerControlInputs,
5230		    input, layer;
5231		var addedLayers = [],
5232		    removedLayers = [];
5233
5234		this._handlingClick = true;
5235
5236		for (var i = inputs.length - 1; i >= 0; i--) {
5237			input = inputs[i];
5238			layer = this._getLayer(input.layerId).layer;
5239
5240			if (input.checked) {
5241				addedLayers.push(layer);
5242			} else if (!input.checked) {
5243				removedLayers.push(layer);
5244			}
5245		}
5246
5247		// Bugfix issue 2318: Should remove all old layers before readding new ones
5248		for (i = 0; i < removedLayers.length; i++) {
5249			if (this._map.hasLayer(removedLayers[i])) {
5250				this._map.removeLayer(removedLayers[i]);
5251			}
5252		}
5253		for (i = 0; i < addedLayers.length; i++) {
5254			if (!this._map.hasLayer(addedLayers[i])) {
5255				this._map.addLayer(addedLayers[i]);
5256			}
5257		}
5258
5259		this._handlingClick = false;
5260
5261		this._refocusOnMap();
5262	},
5263
5264	_checkDisabledLayers: function () {
5265		var inputs = this._layerControlInputs,
5266		    input,
5267		    layer,
5268		    zoom = this._map.getZoom();
5269
5270		for (var i = inputs.length - 1; i >= 0; i--) {
5271			input = inputs[i];
5272			layer = this._getLayer(input.layerId).layer;
5273			input.disabled = (layer.options.minZoom !== undefined && zoom < layer.options.minZoom) ||
5274			                 (layer.options.maxZoom !== undefined && zoom > layer.options.maxZoom);
5275
5276		}
5277	},
5278
5279	_expandIfNotCollapsed: function () {
5280		if (this._map && !this.options.collapsed) {
5281			this.expand();
5282		}
5283		return this;
5284	},
5285
5286	_expand: function () {
5287		// Backward compatibility, remove me in 1.1.
5288		return this.expand();
5289	},
5290
5291	_collapse: function () {
5292		// Backward compatibility, remove me in 1.1.
5293		return this.collapse();
5294	}
5295
5296});
5297
5298
5299// @factory L.control.layers(baselayers?: Object, overlays?: Object, options?: Control.Layers options)
5300// 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.
5301var layers = function (baseLayers, overlays, options) {
5302	return new Layers(baseLayers, overlays, options);
5303};
5304
5305/*
5306 * @class Control.Zoom
5307 * @aka L.Control.Zoom
5308 * @inherits Control
5309 *
5310 * 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`.
5311 */
5312
5313var Zoom = Control.extend({
5314	// @section
5315	// @aka Control.Zoom options
5316	options: {
5317		position: 'topleft',
5318
5319		// @option zoomInText: String = '+'
5320		// The text set on the 'zoom in' button.
5321		zoomInText: '+',
5322
5323		// @option zoomInTitle: String = 'Zoom in'
5324		// The title set on the 'zoom in' button.
5325		zoomInTitle: 'Zoom in',
5326
5327		// @option zoomOutText: String = '&#x2212;'
5328		// The text set on the 'zoom out' button.
5329		zoomOutText: '&#x2212;',
5330
5331		// @option zoomOutTitle: String = 'Zoom out'
5332		// The title set on the 'zoom out' button.
5333		zoomOutTitle: 'Zoom out'
5334	},
5335
5336	onAdd: function (map) {
5337		var zoomName = 'leaflet-control-zoom',
5338		    container = create$1('div', zoomName + ' leaflet-bar'),
5339		    options = this.options;
5340
5341		this._zoomInButton  = this._createButton(options.zoomInText, options.zoomInTitle,
5342		        zoomName + '-in',  container, this._zoomIn);
5343		this._zoomOutButton = this._createButton(options.zoomOutText, options.zoomOutTitle,
5344		        zoomName + '-out', container, this._zoomOut);
5345
5346		this._updateDisabled();
5347		map.on('zoomend zoomlevelschange', this._updateDisabled, this);
5348
5349		return container;
5350	},
5351
5352	onRemove: function (map) {
5353		map.off('zoomend zoomlevelschange', this._updateDisabled, this);
5354	},
5355
5356	disable: function () {
5357		this._disabled = true;
5358		this._updateDisabled();
5359		return this;
5360	},
5361
5362	enable: function () {
5363		this._disabled = false;
5364		this._updateDisabled();
5365		return this;
5366	},
5367
5368	_zoomIn: function (e) {
5369		if (!this._disabled && this._map._zoom < this._map.getMaxZoom()) {
5370			this._map.zoomIn(this._map.options.zoomDelta * (e.shiftKey ? 3 : 1));
5371		}
5372	},
5373
5374	_zoomOut: function (e) {
5375		if (!this._disabled && this._map._zoom > this._map.getMinZoom()) {
5376			this._map.zoomOut(this._map.options.zoomDelta * (e.shiftKey ? 3 : 1));
5377		}
5378	},
5379
5380	_createButton: function (html, title, className, container, fn) {
5381		var link = create$1('a', className, container);
5382		link.innerHTML = html;
5383		link.href = '#';
5384		link.title = title;
5385
5386		/*
5387		 * Will force screen readers like VoiceOver to read this as "Zoom in - button"
5388		 */
5389		link.setAttribute('role', 'button');
5390		link.setAttribute('aria-label', title);
5391
5392		disableClickPropagation(link);
5393		on(link, 'click', stop);
5394		on(link, 'click', fn, this);
5395		on(link, 'click', this._refocusOnMap, this);
5396
5397		return link;
5398	},
5399
5400	_updateDisabled: function () {
5401		var map = this._map,
5402		    className = 'leaflet-disabled';
5403
5404		removeClass(this._zoomInButton, className);
5405		removeClass(this._zoomOutButton, className);
5406
5407		if (this._disabled || map._zoom === map.getMinZoom()) {
5408			addClass(this._zoomOutButton, className);
5409		}
5410		if (this._disabled || map._zoom === map.getMaxZoom()) {
5411			addClass(this._zoomInButton, className);
5412		}
5413	}
5414});
5415
5416// @namespace Map
5417// @section Control options
5418// @option zoomControl: Boolean = true
5419// Whether a [zoom control](#control-zoom) is added to the map by default.
5420Map.mergeOptions({
5421	zoomControl: true
5422});
5423
5424Map.addInitHook(function () {
5425	if (this.options.zoomControl) {
5426		// @section Controls
5427		// @property zoomControl: Control.Zoom
5428		// The default zoom control (only available if the
5429		// [`zoomControl` option](#map-zoomcontrol) was `true` when creating the map).
5430		this.zoomControl = new Zoom();
5431		this.addControl(this.zoomControl);
5432	}
5433});
5434
5435// @namespace Control.Zoom
5436// @factory L.control.zoom(options: Control.Zoom options)
5437// Creates a zoom control
5438var zoom = function (options) {
5439	return new Zoom(options);
5440};
5441
5442/*
5443 * @class Control.Scale
5444 * @aka L.Control.Scale
5445 * @inherits Control
5446 *
5447 * 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`.
5448 *
5449 * @example
5450 *
5451 * ```js
5452 * L.control.scale().addTo(map);
5453 * ```
5454 */
5455
5456var Scale = Control.extend({
5457	// @section
5458	// @aka Control.Scale options
5459	options: {
5460		position: 'bottomleft',
5461
5462		// @option maxWidth: Number = 100
5463		// Maximum width of the control in pixels. The width is set dynamically to show round values (e.g. 100, 200, 500).
5464		maxWidth: 100,
5465
5466		// @option metric: Boolean = True
5467		// Whether to show the metric scale line (m/km).
5468		metric: true,
5469
5470		// @option imperial: Boolean = True
5471		// Whether to show the imperial scale line (mi/ft).
5472		imperial: true
5473
5474		// @option updateWhenIdle: Boolean = false
5475		// If `true`, the control is updated on [`moveend`](#map-moveend), otherwise it's always up-to-date (updated on [`move`](#map-move)).
5476	},
5477
5478	onAdd: function (map) {
5479		var className = 'leaflet-control-scale',
5480		    container = create$1('div', className),
5481		    options = this.options;
5482
5483		this._addScales(options, className + '-line', container);
5484
5485		map.on(options.updateWhenIdle ? 'moveend' : 'move', this._update, this);
5486		map.whenReady(this._update, this);
5487
5488		return container;
5489	},
5490
5491	onRemove: function (map) {
5492		map.off(this.options.updateWhenIdle ? 'moveend' : 'move', this._update, this);
5493	},
5494
5495	_addScales: function (options, className, container) {
5496		if (options.metric) {
5497			this._mScale = create$1('div', className, container);
5498		}
5499		if (options.imperial) {
5500			this._iScale = create$1('div', className, container);
5501		}
5502	},
5503
5504	_update: function () {
5505		var map = this._map,
5506		    y = map.getSize().y / 2;
5507
5508		var maxMeters = map.distance(
5509			map.containerPointToLatLng([0, y]),
5510			map.containerPointToLatLng([this.options.maxWidth, y]));
5511
5512		this._updateScales(maxMeters);
5513	},
5514
5515	_updateScales: function (maxMeters) {
5516		if (this.options.metric && maxMeters) {
5517			this._updateMetric(maxMeters);
5518		}
5519		if (this.options.imperial && maxMeters) {
5520			this._updateImperial(maxMeters);
5521		}
5522	},
5523
5524	_updateMetric: function (maxMeters) {
5525		var meters = this._getRoundNum(maxMeters),
5526		    label = meters < 1000 ? meters + ' m' : (meters / 1000) + ' km';
5527
5528		this._updateScale(this._mScale, label, meters / maxMeters);
5529	},
5530
5531	_updateImperial: function (maxMeters) {
5532		var maxFeet = maxMeters * 3.2808399,
5533		    maxMiles, miles, feet;
5534
5535		if (maxFeet > 5280) {
5536			maxMiles = maxFeet / 5280;
5537			miles = this._getRoundNum(maxMiles);
5538			this._updateScale(this._iScale, miles + ' mi', miles / maxMiles);
5539
5540		} else {
5541			feet = this._getRoundNum(maxFeet);
5542			this._updateScale(this._iScale, feet + ' ft', feet / maxFeet);
5543		}
5544	},
5545
5546	_updateScale: function (scale, text, ratio) {
5547		scale.style.width = Math.round(this.options.maxWidth * ratio) + 'px';
5548		scale.innerHTML = text;
5549	},
5550
5551	_getRoundNum: function (num) {
5552		var pow10 = Math.pow(10, (Math.floor(num) + '').length - 1),
5553		    d = num / pow10;
5554
5555		d = d >= 10 ? 10 :
5556		    d >= 5 ? 5 :
5557		    d >= 3 ? 3 :
5558		    d >= 2 ? 2 : 1;
5559
5560		return pow10 * d;
5561	}
5562});
5563
5564
5565// @factory L.control.scale(options?: Control.Scale options)
5566// Creates an scale control with the given options.
5567var scale = function (options) {
5568	return new Scale(options);
5569};
5570
5571/*
5572 * @class Control.Attribution
5573 * @aka L.Control.Attribution
5574 * @inherits Control
5575 *
5576 * 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.
5577 */
5578
5579var Attribution = Control.extend({
5580	// @section
5581	// @aka Control.Attribution options
5582	options: {
5583		position: 'bottomright',
5584
5585		// @option prefix: String = 'Leaflet'
5586		// The HTML text shown before the attributions. Pass `false` to disable.
5587		prefix: '<a href="http://leafletjs.com" title="A JS library for interactive maps">Leaflet</a>'
5588	},
5589
5590	initialize: function (options) {
5591		setOptions(this, options);
5592
5593		this._attributions = {};
5594	},
5595
5596	onAdd: function (map) {
5597		map.attributionControl = this;
5598		this._container = create$1('div', 'leaflet-control-attribution');
5599		disableClickPropagation(this._container);
5600
5601		// TODO ugly, refactor
5602		for (var i in map._layers) {
5603			if (map._layers[i].getAttribution) {
5604				this.addAttribution(map._layers[i].getAttribution());
5605			}
5606		}
5607
5608		this._update();
5609
5610		return this._container;
5611	},
5612
5613	// @method setPrefix(prefix: String): this
5614	// Sets the text before the attributions.
5615	setPrefix: function (prefix) {
5616		this.options.prefix = prefix;
5617		this._update();
5618		return this;
5619	},
5620
5621	// @method addAttribution(text: String): this
5622	// Adds an attribution text (e.g. `'Vector data &copy; Mapbox'`).
5623	addAttribution: function (text) {
5624		if (!text) { return this; }
5625
5626		if (!this._attributions[text]) {
5627			this._attributions[text] = 0;
5628		}
5629		this._attributions[text]++;
5630
5631		this._update();
5632
5633		return this;
5634	},
5635
5636	// @method removeAttribution(text: String): this
5637	// Removes an attribution text.
5638	removeAttribution: function (text) {
5639		if (!text) { return this; }
5640
5641		if (this._attributions[text]) {
5642			this._attributions[text]--;
5643			this._update();
5644		}
5645
5646		return this;
5647	},
5648
5649	_update: function () {
5650		if (!this._map) { return; }
5651
5652		var attribs = [];
5653
5654		for (var i in this._attributions) {
5655			if (this._attributions[i]) {
5656				attribs.push(i);
5657			}
5658		}
5659
5660		var prefixAndAttribs = [];
5661
5662		if (this.options.prefix) {
5663			prefixAndAttribs.push(this.options.prefix);
5664		}
5665		if (attribs.length) {
5666			prefixAndAttribs.push(attribs.join(', '));
5667		}
5668
5669		this._container.innerHTML = prefixAndAttribs.join(' | ');
5670	}
5671});
5672
5673// @namespace Map
5674// @section Control options
5675// @option attributionControl: Boolean = true
5676// Whether a [attribution control](#control-attribution) is added to the map by default.
5677Map.mergeOptions({
5678	attributionControl: true
5679});
5680
5681Map.addInitHook(function () {
5682	if (this.options.attributionControl) {
5683		new Attribution().addTo(this);
5684	}
5685});
5686
5687// @namespace Control.Attribution
5688// @factory L.control.attribution(options: Control.Attribution options)
5689// Creates an attribution control.
5690var attribution = function (options) {
5691	return new Attribution(options);
5692};
5693
5694Control.Layers = Layers;
5695Control.Zoom = Zoom;
5696Control.Scale = Scale;
5697Control.Attribution = Attribution;
5698
5699control.layers = layers;
5700control.zoom = zoom;
5701control.scale = scale;
5702control.attribution = attribution;
5703
5704/*
5705	L.Handler is a base class for handler classes that are used internally to inject
5706	interaction features like dragging to classes like Map and Marker.
5707*/
5708
5709// @class Handler
5710// @aka L.Handler
5711// Abstract class for map interaction handlers
5712
5713var Handler = Class.extend({
5714	initialize: function (map) {
5715		this._map = map;
5716	},
5717
5718	// @method enable(): this
5719	// Enables the handler
5720	enable: function () {
5721		if (this._enabled) { return this; }
5722
5723		this._enabled = true;
5724		this.addHooks();
5725		return this;
5726	},
5727
5728	// @method disable(): this
5729	// Disables the handler
5730	disable: function () {
5731		if (!this._enabled) { return this; }
5732
5733		this._enabled = false;
5734		this.removeHooks();
5735		return this;
5736	},
5737
5738	// @method enabled(): Boolean
5739	// Returns `true` if the handler is enabled
5740	enabled: function () {
5741		return !!this._enabled;
5742	}
5743
5744	// @section Extension methods
5745	// Classes inheriting from `Handler` must implement the two following methods:
5746	// @method addHooks()
5747	// Called when the handler is enabled, should add event hooks.
5748	// @method removeHooks()
5749	// Called when the handler is disabled, should remove the event hooks added previously.
5750});
5751
5752// @section There is static function which can be called without instantiating L.Handler:
5753// @function addTo(map: Map, name: String): this
5754// Adds a new Handler to the given map with the given name.
5755Handler.addTo = function (map, name) {
5756	map.addHandler(name, this);
5757	return this;
5758};
5759
5760var Mixin = {Events: Events};
5761
5762/*
5763 * @class Draggable
5764 * @aka L.Draggable
5765 * @inherits Evented
5766 *
5767 * A class for making DOM elements draggable (including touch support).
5768 * Used internally for map and marker dragging. Only works for elements
5769 * that were positioned with [`L.DomUtil.setPosition`](#domutil-setposition).
5770 *
5771 * @example
5772 * ```js
5773 * var draggable = new L.Draggable(elementToDrag);
5774 * draggable.enable();
5775 * ```
5776 */
5777
5778var START = touch ? 'touchstart mousedown' : 'mousedown';
5779var END = {
5780	mousedown: 'mouseup',
5781	touchstart: 'touchend',
5782	pointerdown: 'touchend',
5783	MSPointerDown: 'touchend'
5784};
5785var MOVE = {
5786	mousedown: 'mousemove',
5787	touchstart: 'touchmove',
5788	pointerdown: 'touchmove',
5789	MSPointerDown: 'touchmove'
5790};
5791
5792
5793var Draggable = Evented.extend({
5794
5795	options: {
5796		// @section
5797		// @aka Draggable options
5798		// @option clickTolerance: Number = 3
5799		// The max number of pixels a user can shift the mouse pointer during a click
5800		// for it to be considered a valid click (as opposed to a mouse drag).
5801		clickTolerance: 3
5802	},
5803
5804	// @constructor L.Draggable(el: HTMLElement, dragHandle?: HTMLElement, preventOutline?: Boolean, options?: Draggable options)
5805	// Creates a `Draggable` object for moving `el` when you start dragging the `dragHandle` element (equals `el` itself by default).
5806	initialize: function (element, dragStartTarget, preventOutline$$1, options) {
5807		setOptions(this, options);
5808
5809		this._element = element;
5810		this._dragStartTarget = dragStartTarget || element;
5811		this._preventOutline = preventOutline$$1;
5812	},
5813
5814	// @method enable()
5815	// Enables the dragging ability
5816	enable: function () {
5817		if (this._enabled) { return; }
5818
5819		on(this._dragStartTarget, START, this._onDown, this);
5820
5821		this._enabled = true;
5822	},
5823
5824	// @method disable()
5825	// Disables the dragging ability
5826	disable: function () {
5827		if (!this._enabled) { return; }
5828
5829		// If we're currently dragging this draggable,
5830		// disabling it counts as first ending the drag.
5831		if (Draggable._dragging === this) {
5832			this.finishDrag();
5833		}
5834
5835		off(this._dragStartTarget, START, this._onDown, this);
5836
5837		this._enabled = false;
5838		this._moved = false;
5839	},
5840
5841	_onDown: function (e) {
5842		// Ignore simulated events, since we handle both touch and
5843		// mouse explicitly; otherwise we risk getting duplicates of
5844		// touch events, see #4315.
5845		// Also ignore the event if disabled; this happens in IE11
5846		// under some circumstances, see #3666.
5847		if (e._simulated || !this._enabled) { return; }
5848
5849		this._moved = false;
5850
5851		if (hasClass(this._element, 'leaflet-zoom-anim')) { return; }
5852
5853		if (Draggable._dragging || e.shiftKey || ((e.which !== 1) && (e.button !== 1) && !e.touches)) { return; }
5854		Draggable._dragging = this;  // Prevent dragging multiple objects at once.
5855
5856		if (this._preventOutline) {
5857			preventOutline(this._element);
5858		}
5859
5860		disableImageDrag();
5861		disableTextSelection();
5862
5863		if (this._moving) { return; }
5864
5865		// @event down: Event
5866		// Fired when a drag is about to start.
5867		this.fire('down');
5868
5869		var first = e.touches ? e.touches[0] : e,
5870		    sizedParent = getSizedParentNode(this._element);
5871
5872		this._startPoint = new Point(first.clientX, first.clientY);
5873
5874		// Cache the scale, so that we can continuously compensate for it during drag (_onMove).
5875		this._parentScale = getScale(sizedParent);
5876
5877		on(document, MOVE[e.type], this._onMove, this);
5878		on(document, END[e.type], this._onUp, this);
5879	},
5880
5881	_onMove: function (e) {
5882		// Ignore simulated events, since we handle both touch and
5883		// mouse explicitly; otherwise we risk getting duplicates of
5884		// touch events, see #4315.
5885		// Also ignore the event if disabled; this happens in IE11
5886		// under some circumstances, see #3666.
5887		if (e._simulated || !this._enabled) { return; }
5888
5889		if (e.touches && e.touches.length > 1) {
5890			this._moved = true;
5891			return;
5892		}
5893
5894		var first = (e.touches && e.touches.length === 1 ? e.touches[0] : e),
5895		    offset = new Point(first.clientX, first.clientY)._subtract(this._startPoint);
5896
5897		if (!offset.x && !offset.y) { return; }
5898		if (Math.abs(offset.x) + Math.abs(offset.y) < this.options.clickTolerance) { return; }
5899
5900		// We assume that the parent container's position, border and scale do not change for the duration of the drag.
5901		// Therefore there is no need to account for the position and border (they are eliminated by the subtraction)
5902		// and we can use the cached value for the scale.
5903		offset.x /= this._parentScale.x;
5904		offset.y /= this._parentScale.y;
5905
5906		preventDefault(e);
5907
5908		if (!this._moved) {
5909			// @event dragstart: Event
5910			// Fired when a drag starts
5911			this.fire('dragstart');
5912
5913			this._moved = true;
5914			this._startPos = getPosition(this._element).subtract(offset);
5915
5916			addClass(document.body, 'leaflet-dragging');
5917
5918			this._lastTarget = e.target || e.srcElement;
5919			// IE and Edge do not give the <use> element, so fetch it
5920			// if necessary
5921			if ((window.SVGElementInstance) && (this._lastTarget instanceof SVGElementInstance)) {
5922				this._lastTarget = this._lastTarget.correspondingUseElement;
5923			}
5924			addClass(this._lastTarget, 'leaflet-drag-target');
5925		}
5926
5927		this._newPos = this._startPos.add(offset);
5928		this._moving = true;
5929
5930		cancelAnimFrame(this._animRequest);
5931		this._lastEvent = e;
5932		this._animRequest = requestAnimFrame(this._updatePosition, this, true);
5933	},
5934
5935	_updatePosition: function () {
5936		var e = {originalEvent: this._lastEvent};
5937
5938		// @event predrag: Event
5939		// Fired continuously during dragging *before* each corresponding
5940		// update of the element's position.
5941		this.fire('predrag', e);
5942		setPosition(this._element, this._newPos);
5943
5944		// @event drag: Event
5945		// Fired continuously during dragging.
5946		this.fire('drag', e);
5947	},
5948
5949	_onUp: function (e) {
5950		// Ignore simulated events, since we handle both touch and
5951		// mouse explicitly; otherwise we risk getting duplicates of
5952		// touch events, see #4315.
5953		// Also ignore the event if disabled; this happens in IE11
5954		// under some circumstances, see #3666.
5955		if (e._simulated || !this._enabled) { return; }
5956		this.finishDrag();
5957	},
5958
5959	finishDrag: function () {
5960		removeClass(document.body, 'leaflet-dragging');
5961
5962		if (this._lastTarget) {
5963			removeClass(this._lastTarget, 'leaflet-drag-target');
5964			this._lastTarget = null;
5965		}
5966
5967		for (var i in MOVE) {
5968			off(document, MOVE[i], this._onMove, this);
5969			off(document, END[i], this._onUp, this);
5970		}
5971
5972		enableImageDrag();
5973		enableTextSelection();
5974
5975		if (this._moved && this._moving) {
5976			// ensure drag is not fired after dragend
5977			cancelAnimFrame(this._animRequest);
5978
5979			// @event dragend: DragEndEvent
5980			// Fired when the drag ends.
5981			this.fire('dragend', {
5982				distance: this._newPos.distanceTo(this._startPos)
5983			});
5984		}
5985
5986		this._moving = false;
5987		Draggable._dragging = false;
5988	}
5989
5990});
5991
5992/*
5993 * @namespace LineUtil
5994 *
5995 * Various utility functions for polyline points processing, used by Leaflet internally to make polylines lightning-fast.
5996 */
5997
5998// Simplify polyline with vertex reduction and Douglas-Peucker simplification.
5999// Improves rendering performance dramatically by lessening the number of points to draw.
6000
6001// @function simplify(points: Point[], tolerance: Number): Point[]
6002// Dramatically reduces the number of points in a polyline while retaining
6003// its shape and returns a new array of simplified points, using the
6004// [Douglas-Peucker algorithm](http://en.wikipedia.org/wiki/Douglas-Peucker_algorithm).
6005// Used for a huge performance boost when processing/displaying Leaflet polylines for
6006// each zoom level and also reducing visual noise. tolerance affects the amount of
6007// simplification (lesser value means higher quality but slower and with more points).
6008// Also released as a separated micro-library [Simplify.js](http://mourner.github.com/simplify-js/).
6009function simplify(points, tolerance) {
6010	if (!tolerance || !points.length) {
6011		return points.slice();
6012	}
6013
6014	var sqTolerance = tolerance * tolerance;
6015
6016	    // stage 1: vertex reduction
6017	    points = _reducePoints(points, sqTolerance);
6018
6019	    // stage 2: Douglas-Peucker simplification
6020	    points = _simplifyDP(points, sqTolerance);
6021
6022	return points;
6023}
6024
6025// @function pointToSegmentDistance(p: Point, p1: Point, p2: Point): Number
6026// Returns the distance between point `p` and segment `p1` to `p2`.
6027function pointToSegmentDistance(p, p1, p2) {
6028	return Math.sqrt(_sqClosestPointOnSegment(p, p1, p2, true));
6029}
6030
6031// @function closestPointOnSegment(p: Point, p1: Point, p2: Point): Number
6032// Returns the closest point from a point `p` on a segment `p1` to `p2`.
6033function closestPointOnSegment(p, p1, p2) {
6034	return _sqClosestPointOnSegment(p, p1, p2);
6035}
6036
6037// Douglas-Peucker simplification, see http://en.wikipedia.org/wiki/
vendor: 4,588 bytes, lines 6037-6230
6037Douglas-Peucker_algorithm
6038function _simplifyDP(points, sqTolerance) {
6039
6040	var len = points.length,
6041	    ArrayConstructor = typeof Uint8Array !== undefined + '' ? Uint8Array : Array,
6042	    markers = new ArrayConstructor(len);
6043
6044	    markers[0] = markers[len - 1] = 1;
6045
6046	_simplifyDPStep(points, markers, sqTolerance, 0, len - 1);
6047
6048	var i,
6049	    newPoints = [];
6050
6051	for (i = 0; i < len; i++) {
6052		if (markers[i]) {
6053			newPoints.push(points[i]);
6054		}
6055	}
6056
6057	return newPoints;
6058}
6059
6060function _simplifyDPStep(points, markers, sqTolerance, first, last) {
6061
6062	var maxSqDist = 0,
6063	index, i, sqDist;
6064
6065	for (i = first + 1; i <= last - 1; i++) {
6066		sqDist = _sqClosestPointOnSegment(points[i], points[first], points[last], true);
6067
6068		if (sqDist > maxSqDist) {
6069			index = i;
6070			maxSqDist = sqDist;
6071		}
6072	}
6073
6074	if (maxSqDist > sqTolerance) {
6075		markers[index] = 1;
6076
6077		_simplifyDPStep(points, markers, sqTolerance, first, index);
6078		_simplifyDPStep(points, markers, sqTolerance, index, last);
6079	}
6080}
6081
6082// reduce points that are too close to each other to a single point
6083function _reducePoints(points, sqTolerance) {
6084	var reducedPoints = [points[0]];
6085
6086	for (var i = 1, prev = 0, len = points.length; i < len; i++) {
6087		if (_sqDist(points[i], points[prev]) > sqTolerance) {
6088			reducedPoints.push(points[i]);
6089			prev = i;
6090		}
6091	}
6092	if (prev < len - 1) {
6093		reducedPoints.push(points[len - 1]);
6094	}
6095	return reducedPoints;
6096}
6097
6098var _lastCode;
6099
6100// @function clipSegment(a: Point, b: Point, bounds: Bounds, useLastCode?: Boolean, round?: Boolean): Point[]|Boolean
6101// Clips the segment a to b by rectangular bounds with the
6102// [Cohen-Sutherland algorithm](https://en.wikipedia.org/wiki/Cohen%E2%80%93Sutherland_algorithm)
6103// (modifying the segment points directly!). Used by Leaflet to only show polyline
6104// points that are on the screen or near, increasing performance.
6105function clipSegment(a, b, bounds, useLastCode, round) {
6106	var codeA = useLastCode ? _lastCode : _getBitCode(a, bounds),
6107	    codeB = _getBitCode(b, bounds),
6108
6109	    codeOut, p, newCode;
6110
6111	    // save 2nd code to avoid calculating it on the next segment
6112	    _lastCode = codeB;
6113
6114	while (true) {
6115		// if a,b is inside the clip window (trivial accept)
6116		if (!(codeA | codeB)) {
6117			return [a, b];
6118		}
6119
6120		// if a,b is outside the clip window (trivial reject)
6121		if (codeA & codeB) {
6122			return false;
6123		}
6124
6125		// other cases
6126		codeOut = codeA || codeB;
6127		p = _getEdgeIntersection(a, b, codeOut, bounds, round);
6128		newCode = _getBitCode(p, bounds);
6129
6130		if (codeOut === codeA) {
6131			a = p;
6132			codeA = newCode;
6133		} else {
6134			b = p;
6135			codeB = newCode;
6136		}
6137	}
6138}
6139
6140function _getEdgeIntersection(a, b, code, bounds, round) {
6141	var dx = b.x - a.x,
6142	    dy = b.y - a.y,
6143	    min = bounds.min,
6144	    max = bounds.max,
6145	    x, y;
6146
6147	if (code & 8) { // top
6148		x = a.x + dx * (max.y - a.y) / dy;
6149		y = max.y;
6150
6151	} else if (code & 4) { // bottom
6152		x = a.x + dx * (min.y - a.y) / dy;
6153		y = min.y;
6154
6155	} else if (code & 2) { // right
6156		x = max.x;
6157		y = a.y + dy * (max.x - a.x) / dx;
6158
6159	} else if (code & 1) { // left
6160		x = min.x;
6161		y = a.y + dy * (min.x - a.x) / dx;
6162	}
6163
6164	return new Point(x, y, round);
6165}
6166
6167function _getBitCode(p, bounds) {
6168	var code = 0;
6169
6170	if (p.x < bounds.min.x) { // left
6171		code |= 1;
6172	} else if (p.x > bounds.max.x) { // right
6173		code |= 2;
6174	}
6175
6176	if (p.y < bounds.min.y) { // bottom
6177		code |= 4;
6178	} else if (p.y > bounds.max.y) { // top
6179		code |= 8;
6180	}
6181
6182	return code;
6183}
6184
6185// square distance (to avoid unnecessary Math.sqrt calls)
6186function _sqDist(p1, p2) {
6187	var dx = p2.x - p1.x,
6188	    dy = p2.y - p1.y;
6189	return dx * dx + dy * dy;
6190}
6191
6192// return closest point on segment or distance to that point
6193function _sqClosestPointOnSegment(p, p1, p2, sqDist) {
6194	var x = p1.x,
6195	    y = p1.y,
6196	    dx = p2.x - x,
6197	    dy = p2.y - y,
6198	    dot = dx * dx + dy * dy,
6199	    t;
6200
6201	if (dot > 0) {
6202		t = ((p.x - x) * dx + (p.y - y) * dy) / dot;
6203
6204		if (t > 1) {
6205			x = p2.x;
6206			y = p2.y;
6207		} else if (t > 0) {
6208			x += dx * t;
6209			y += dy * t;
6210		}
6211	}
6212
6213	dx = p.x - x;
6214	dy = p.y - y;
6215
6216	return sqDist ? dx * dx + dy * dy : new Point(x, y);
6217}
6218
6219
6220// @function isFlat(latlngs: LatLng[]): Boolean
6221// Returns true if `latlngs` is a flat array, false is nested.
6222function isFlat(latlngs) {
6223	return !isArray(latlngs[0]) || (typeof latlngs[0][0] !== 'object' && typeof latlngs[0][0] !== 'undefined');
6224}
6225
6226function _flat(latlngs) {
6227	console.warn('Deprecated use of _flat, please use L.LineUtil.isFlat instead.');
6228	return isFlat(latlngs);
6229}
6230
6231
6232var LineUtil = (Object.freeze || Object)({
6233	simplify: simplify,
6234	pointToSegmentDistance: pointToSegmentDistance,
6235	closestPointOnSegment: closestPointOnSegment,
6236	clipSegment: clipSegment,
6237	_getEdgeIntersection: _getEdgeIntersection,
6238	_getBitCode: _getBitCode,
6239	_sqClosestPointOnSegment: _sqClosestPointOnSegment,
6240	isFlat: isFlat,
6241	_flat: _flat
6242});
6243
6244/*
6245 * @namespace PolyUtil
6246 * Various utility functions for polygon geometries.
6247 */
6248
6249/* @function clipPolygon(points: Point[], bounds: Bounds, round?: Boolean): Point[]
6250 * Clips the polygon geometry defined by the given `points` by the given bounds (using the [Sutherland-Hodgman algorithm](https://en.wikipedia.org/wiki/Sutherland%E2%80%93Hodgman_algorithm)).
6251 * Used by Leaflet to only show polygon points that are on the screen or near, increasing
6252 * performance. Note that polygon points needs different algorithm for clipping
6253 * than polyline, so there's a separate method for it.
6254 */
6255function clipPolygon(points, bounds, round) {
6256	var clippedPoints,
6257	    edges = [1, 4, 2, 8],
6258	    i, j, k,
6259	    a, b,
6260	    len, edge, p;
6261
6262	for (i = 0, len = points.length; i < len; i++) {
6263		points[i]._code = _getBitCode(points[i], bounds);
6264	}
6265
6266	// for each edge (left, bottom, right, top)
6267	for (k = 0; k < 4; k++) {
6268		edge = edges[k];
6269		clippedPoints = [];
6270
6271		for (i = 0, len = points.length, j = len - 1; i < len; j = i++) {
6272			a = points[i];
6273			b = points[j];
6274
6275			// if a is inside the clip window
6276			if (!(a._code & edge)) {
6277				// if b is outside the clip window (a->b goes out of screen)
6278				if (b._code & edge) {
6279					p = _getEdgeIntersection(b, a, edge, bounds, round);
6280					p._code = _getBitCode(p, bounds);
6281					clippedPoints.push(p);
6282				}
6283				clippedPoints.push(a);
6284
6285			// else if b is inside the clip window (a->b enters the screen)
6286			} else if (!(b._code & edge)) {
6287				p = _getEdgeIntersection(b, a, edge, bounds, round);
6288				p._code = _getBitCode(p, bounds);
6289				clippedPoints.push(p);
6290			}
6291		}
6292		points = clippedPoints;
6293	}
6294
6295	return points;
6296}
6297
6298
6299var PolyUtil = (Object.freeze || Object)({
6300	clipPolygon: clipPolygon
6301});
6302
6303/*
6304 * @namespace Projection
6305 * @section
vendor: 21,269 bytes, lines 6306-7049
6306 * Leaflet comes with a set of already defined Projections out of the box:
6307 *
6308 * @projection L.Projection.LonLat
6309 *
6310 * Equirectangular, or Plate Carree projection — the most simple projection,
6311 * mostly used by GIS enthusiasts. Directly maps `x` as longitude, and `y` as
6312 * latitude. Also suitable for flat worlds, e.g. game maps. Used by the
6313 * `EPSG:4326` and `Simple` CRS.
6314 */
6315
6316var LonLat = {
6317	project: function (latlng) {
6318		return new Point(latlng.lng, latlng.lat);
6319	},
6320
6321	unproject: function (point) {
6322		return new LatLng(point.y, point.x);
6323	},
6324
6325	bounds: new Bounds([-180, -90], [180, 90])
6326};
6327
6328/*
6329 * @namespace Projection
6330 * @projection L.Projection.Mercator
6331 *
6332 * 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.
6333 */
6334
6335var Mercator = {
6336	R: 6378137,
6337	R_MINOR: 6356752.314245179,
6338
6339	bounds: new Bounds([-20037508.34279, -15496570.73972], [20037508.34279, 18764656.23138]),
6340
6341	project: function (latlng) {
6342		var d = Math.PI / 180,
6343		    r = this.R,
6344		    y = latlng.lat * d,
6345		    tmp = this.R_MINOR / r,
6346		    e = Math.sqrt(1 - tmp * tmp),
6347		    con = e * Math.sin(y);
6348
6349		var ts = Math.tan(Math.PI / 4 - y / 2) / Math.pow((1 - con) / (1 + con), e / 2);
6350		y = -r * Math.log(Math.max(ts, 1E-10));
6351
6352		return new Point(latlng.lng * d * r, y);
6353	},
6354
6355	unproject: function (point) {
6356		var d = 180 / Math.PI,
6357		    r = this.R,
6358		    tmp = this.R_MINOR / r,
6359		    e = Math.sqrt(1 - tmp * tmp),
6360		    ts = Math.exp(-point.y / r),
6361		    phi = Math.PI / 2 - 2 * Math.atan(ts);
6362
6363		for (var i = 0, dphi = 0.1, con; i < 15 && Math.abs(dphi) > 1e-7; i++) {
6364			con = e * Math.sin(phi);
6365			con = Math.pow((1 - con) / (1 + con), e / 2);
6366			dphi = Math.PI / 2 - 2 * Math.atan(ts * con) - phi;
6367			phi += dphi;
6368		}
6369
6370		return new LatLng(phi * d, point.x * d / r);
6371	}
6372};
6373
6374/*
6375 * @class Projection
6376
6377 * An object with methods for projecting geographical coordinates of the world onto
6378 * a flat surface (and back). See [Map projection](http://en.wikipedia.org/wiki/Map_projection).
6379
6380 * @property bounds: Bounds
6381 * The bounds (specified in CRS units) where the projection is valid
6382
6383 * @method project(latlng: LatLng): Point
6384 * Projects geographical coordinates into a 2D point.
6385 * Only accepts actual `L.LatLng` instances, not arrays.
6386
6387 * @method unproject(point: Point): LatLng
6388 * The inverse of `project`. Projects a 2D point into a geographical location.
6389 * Only accepts actual `L.Point` instances, not arrays.
6390
6391 * Note that the projection instances do not inherit from Leafet's `Class` object,
6392 * and can't be instantiated. Also, new classes can't inherit from them,
6393 * and methods can't be added to them with the `include` function.
6394
6395 */
6396
6397
6398
6399
6400var index = (Object.freeze || Object)({
6401	LonLat: LonLat,
6402	Mercator: Mercator,
6403	SphericalMercator: SphericalMercator
6404});
6405
6406/*
6407 * @namespace CRS
6408 * @crs L.CRS.EPSG3395
6409 *
6410 * Rarely used by some commercial tile providers. Uses Elliptical Mercator projection.
6411 */
6412var EPSG3395 = extend({}, Earth, {
6413	code: 'EPSG:3395',
6414	projection: Mercator,
6415
6416	transformation: (function () {
6417		var scale = 0.5 / (Math.PI * Mercator.R);
6418		return toTransformation(scale, 0.5, -scale, 0.5);
6419	}())
6420});
6421
6422/*
6423 * @namespace CRS
6424 * @crs L.CRS.EPSG4326
6425 *
6426 * A common CRS among GIS enthusiasts. Uses simple Equirectangular projection.
6427 *
6428 * Leaflet 1.0.x complies with the [TMS coordinate scheme for EPSG:4326](https://wiki.osgeo.org/wiki/Tile_Map_Service_Specification#global-geodetic),
6429 * which is a breaking change from 0.7.x behaviour.  If you are using a `TileLayer`
6430 * with this CRS, ensure that there are two 256x256 pixel tiles covering the
6431 * whole earth at zoom level zero, and that the tile coordinate origin is (-180,+90),
6432 * or (-180,-90) for `TileLayer`s with [the `tms` option](#tilelayer-tms) set.
6433 */
6434
6435var EPSG4326 = extend({}, Earth, {
6436	code: 'EPSG:4326',
6437	projection: LonLat,
6438	transformation: toTransformation(1 / 180, 1, -1 / 180, 0.5)
6439});
6440
6441/*
6442 * @namespace CRS
6443 * @crs L.CRS.Simple
6444 *
6445 * A simple CRS that maps longitude and latitude into `x` and `y` directly.
6446 * May be used for maps of flat surfaces (e.g. game maps). Note that the `y`
6447 * axis should still be inverted (going from bottom to top). `distance()` returns
6448 * simple euclidean distance.
6449 */
6450
6451var Simple = extend({}, CRS, {
6452	projection: LonLat,
6453	transformation: toTransformation(1, 0, -1, 0),
6454
6455	scale: function (zoom) {
6456		return Math.pow(2, zoom);
6457	},
6458
6459	zoom: function (scale) {
6460		return Math.log(scale) / Math.LN2;
6461	},
6462
6463	distance: function (latlng1, latlng2) {
6464		var dx = latlng2.lng - latlng1.lng,
6465		    dy = latlng2.lat - latlng1.lat;
6466
6467		return Math.sqrt(dx * dx + dy * dy);
6468	},
6469
6470	infinite: true
6471});
6472
6473CRS.Earth = Earth;
6474CRS.EPSG3395 = EPSG3395;
6475CRS.EPSG3857 = EPSG3857;
6476CRS.EPSG900913 = EPSG900913;
6477CRS.EPSG4326 = EPSG4326;
6478CRS.Simple = Simple;
6479
6480/*
6481 * @class Layer
6482 * @inherits Evented
6483 * @aka L.Layer
6484 * @aka ILayer
6485 *
6486 * A set of methods from the Layer base class that all Leaflet layers use.
6487 * Inherits all methods, options and events from `L.Evented`.
6488 *
6489 * @example
6490 *
6491 * ```js
6492 * var layer = L.Marker(latlng).addTo(map);
6493 * layer.addTo(map);
6494 * layer.remove();
6495 * ```
6496 *
6497 * @event add: Event
6498 * Fired after the layer is added to a map
6499 *
6500 * @event remove: Event
6501 * Fired after the layer is removed from a map
6502 */
6503
6504
6505var Layer = Evented.extend({
6506
6507	// Classes extending `L.Layer` will inherit the following options:
6508	options: {
6509		// @option pane: String = 'overlayPane'
6510		// 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.
6511		pane: 'overlayPane',
6512
6513		// @option attribution: String = null
6514		// String to be shown in the attribution control, e.g. "© OpenStreetMap contributors". It describes the layer data and is often a legal obligation towards copyright holders and tile providers.
6515		attribution: null,
6516
6517		bubblingMouseEvents: true
6518	},
6519
6520	/* @section
6521	 * Classes extending `L.Layer` will inherit the following methods:
6522	 *
6523	 * @method addTo(map: Map|LayerGroup): this
6524	 * Adds the layer to the given map or layer group.
6525	 */
6526	addTo: function (map) {
6527		map.addLayer(this);
6528		return this;
6529	},
6530
6531	// @method remove: this
6532	// Removes the layer from the map it is currently active on.
6533	remove: function () {
6534		return this.removeFrom(this._map || this._mapToAdd);
6535	},
6536
6537	// @method removeFrom(map: Map): this
6538	// Removes the layer from the given map
6539	removeFrom: function (obj) {
6540		if (obj) {
6541			obj.removeLayer(this);
6542		}
6543		return this;
6544	},
6545
6546	// @method getPane(name? : String): HTMLElement
6547	// Returns the `HTMLElement` representing the named pane on the map. If `name` is omitted, returns the pane for this layer.
6548	getPane: function (name) {
6549		return this._map.getPane(name ? (this.options[name] || name) : this.options.pane);
6550	},
6551
6552	addInteractiveTarget: function (targetEl) {
6553		this._map._targets[stamp(targetEl)] = this;
6554		return this;
6555	},
6556
6557	removeInteractiveTarget: function (targetEl) {
6558		delete this._map._targets[stamp(targetEl)];
6559		return this;
6560	},
6561
6562	// @method getAttribution: String
6563	// Used by the `attribution control`, returns the [attribution option](#gridlayer-attribution).
6564	getAttribution: function () {
6565		return this.options.attribution;
6566	},
6567
6568	_layerAdd: function (e) {
6569		var map = e.target;
6570
6571		// check in case layer gets added and then removed before the map is ready
6572		if (!map.hasLayer(this)) { return; }
6573
6574		this._map = map;
6575		this._zoomAnimated = map._zoomAnimated;
6576
6577		if (this.getEvents) {
6578			var events = this.getEvents();
6579			map.on(events, this);
6580			this.once('remove', function () {
6581				map.off(events, this);
6582			}, this);
6583		}
6584
6585		this.onAdd(map);
6586
6587		if (this.getAttribution && map.attributionControl) {
6588			map.attributionControl.addAttribution(this.getAttribution());
6589		}
6590
6591		this.fire('add');
6592		map.fire('layeradd', {layer: this});
6593	}
6594});
6595
6596/* @section Extension methods
6597 * @uninheritable
6598 *
6599 * Every layer should extend from `L.Layer` and (re-)implement the following methods.
6600 *
6601 * @method onAdd(map: Map): this
6602 * 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).
6603 *
6604 * @method onRemove(map: Map): this
6605 * 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).
6606 *
6607 * @method getEvents(): Object
6608 * 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.
6609 *
6610 * @method getAttribution(): String
6611 * This optional method should return a string containing HTML to be shown on the `Attribution control` whenever the layer is visible.
6612 *
6613 * @method beforeAdd(map: Map): this
6614 * 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.
6615 */
6616
6617
6618/* @namespace Map
6619 * @section Layer events
6620 *
6621 * @event layeradd: LayerEvent
6622 * Fired when a new layer is added to the map.
6623 *
6624 * @event layerremove: LayerEvent
6625 * Fired when some layer is removed from the map
6626 *
6627 * @section Methods for Layers and Controls
6628 */
6629Map.include({
6630	// @method addLayer(layer: Layer): this
6631	// Adds the given layer to the map
6632	addLayer: function (layer) {
6633		if (!layer._layerAdd) {
6634			throw new Error('The provided object is not a Layer.');
6635		}
6636
6637		var id = stamp(layer);
6638		if (this._layers[id]) { return this; }
6639		this._layers[id] = layer;
6640
6641		layer._mapToAdd = this;
6642
6643		if (layer.beforeAdd) {
6644			layer.beforeAdd(this);
6645		}
6646
6647		this.whenReady(layer._layerAdd, layer);
6648
6649		return this;
6650	},
6651
6652	// @method removeLayer(layer: Layer): this
6653	// Removes the given layer from the map.
6654	removeLayer: function (layer) {
6655		var id = stamp(layer);
6656
6657		if (!this._layers[id]) { return this; }
6658
6659		if (this._loaded) {
6660			layer.onRemove(this);
6661		}
6662
6663		if (layer.getAttribution && this.attributionControl) {
6664			this.attributionControl.removeAttribution(layer.getAttribution());
6665		}
6666
6667		delete this._layers[id];
6668
6669		if (this._loaded) {
6670			this.fire('layerremove', {layer: layer});
6671			layer.fire('remove');
6672		}
6673
6674		layer._map = layer._mapToAdd = null;
6675
6676		return this;
6677	},
6678
6679	// @method hasLayer(layer: Layer): Boolean
6680	// Returns `true` if the given layer is currently added to the map
6681	hasLayer: function (layer) {
6682		return !!layer && (stamp(layer) in this._layers);
6683	},
6684
6685	/* @method eachLayer(fn: Function, context?: Object): this
6686	 * Iterates over the layers of the map, optionally specifying context of the iterator function.
6687	 * ```
6688	 * map.eachLayer(function(layer){
6689	 *     layer.bindPopup('Hello');
6690	 * });
6691	 * ```
6692	 */
6693	eachLayer: function (method, context) {
6694		for (var i in this._layers) {
6695			method.call(context, this._layers[i]);
6696		}
6697		return this;
6698	},
6699
6700	_addLayers: function (layers) {
6701		layers = layers ? (isArray(layers) ? layers : [layers]) : [];
6702
6703		for (var i = 0, len = layers.length; i < len; i++) {
6704			this.addLayer(layers[i]);
6705		}
6706	},
6707
6708	_addZoomLimit: function (layer) {
6709		if (isNaN(layer.options.maxZoom) || !isNaN(layer.options.minZoom)) {
6710			this._zoomBoundLayers[stamp(layer)] = layer;
6711			this._updateZoomLevels();
6712		}
6713	},
6714
6715	_removeZoomLimit: function (layer) {
6716		var id = stamp(layer);
6717
6718		if (this._zoomBoundLayers[id]) {
6719			delete this._zoomBoundLayers[id];
6720			this._updateZoomLevels();
6721		}
6722	},
6723
6724	_updateZoomLevels: function () {
6725		var minZoom = Infinity,
6726		    maxZoom = -Infinity,
6727		    oldZoomSpan = this._getZoomSpan();
6728
6729		for (var i in this._zoomBoundLayers) {
6730			var options = this._zoomBoundLayers[i].options;
6731
6732			minZoom = options.minZoom === undefined ? minZoom : Math.min(minZoom, options.minZoom);
6733			maxZoom = options.maxZoom === undefined ? maxZoom : Math.max(maxZoom, options.maxZoom);
6734		}
6735
6736		this._layersMaxZoom = maxZoom === -Infinity ? undefined : maxZoom;
6737		this._layersMinZoom = minZoom === Infinity ? undefined : minZoom;
6738
6739		// @section Map state change events
6740		// @event zoomlevelschange: Event
6741		// Fired when the number of zoomlevels on the map is changed due
6742		// to adding or removing a layer.
6743		if (oldZoomSpan !== this._getZoomSpan()) {
6744			this.fire('zoomlevelschange');
6745		}
6746
6747		if (this.options.maxZoom === undefined && this._layersMaxZoom && this.getZoom() > this._layersMaxZoom) {
6748			this.setZoom(this._layersMaxZoom);
6749		}
6750		if (this.options.minZoom === undefined && this._layersMinZoom && this.getZoom() < this._layersMinZoom) {
6751			this.setZoom(this._layersMinZoom);
6752		}
6753	}
6754});
6755
6756/*
6757 * @class LayerGroup
6758 * @aka L.LayerGroup
6759 * @inherits Layer
6760 *
6761 * Used to group several layers and handle them as one. If you add it to the map,
6762 * any layers added or removed from the group will be added/removed on the map as
6763 * well. Extends `Layer`.
6764 *
6765 * @example
6766 *
6767 * ```js
6768 * L.layerGroup([marker1, marker2])
6769 * 	.addLayer(polyline)
6770 * 	.addTo(map);
6771 * ```
6772 */
6773
6774var LayerGroup = Layer.extend({
6775
6776	initialize: function (layers, options) {
6777		setOptions(this, options);
6778
6779		this._layers = {};
6780
6781		var i, len;
6782
6783		if (layers) {
6784			for (i = 0, len = layers.length; i < len; i++) {
6785				this.addLayer(layers[i]);
6786			}
6787		}
6788	},
6789
6790	// @method addLayer(layer: Layer): this
6791	// Adds the given layer to the group.
6792	addLayer: function (layer) {
6793		var id = this.getLayerId(layer);
6794
6795		this._layers[id] = layer;
6796
6797		if (this._map) {
6798			this._map.addLayer(layer);
6799		}
6800
6801		return this;
6802	},
6803
6804	// @method removeLayer(layer: Layer): this
6805	// Removes the given layer from the group.
6806	// @alternative
6807	// @method removeLayer(id: Number): this
6808	// Removes the layer with the given internal ID from the group.
6809	removeLayer: function (layer) {
6810		var id = layer in this._layers ? layer : this.getLayerId(layer);
6811
6812		if (this._map && this._layers[id]) {
6813			this._map.removeLayer(this._layers[id]);
6814		}
6815
6816		delete this._layers[id];
6817
6818		return this;
6819	},
6820
6821	// @method hasLayer(layer: Layer): Boolean
6822	// Returns `true` if the given layer is currently added to the group.
6823	// @alternative
6824	// @method hasLayer(id: Number): Boolean
6825	// Returns `true` if the given internal ID is currently added to the group.
6826	hasLayer: function (layer) {
6827		return !!layer && (layer in this._layers || this.getLayerId(layer) in this._layers);
6828	},
6829
6830	// @method clearLayers(): this
6831	// Removes all the layers from the group.
6832	clearLayers: function () {
6833		return this.eachLayer(this.removeLayer, this);
6834	},
6835
6836	// @method invoke(methodName: String, …): this
6837	// Calls `methodName` on every layer contained in this group, passing any
6838	// additional parameters. Has no effect if the layers contained do not
6839	// implement `methodName`.
6840	invoke: function (methodName) {
6841		var args = Array.prototype.slice.call(arguments, 1),
6842		    i, layer;
6843
6844		for (i in this._layers) {
6845			layer = this._layers[i];
6846
6847			if (layer[methodName]) {
6848				layer[methodName].apply(layer, args);
6849			}
6850		}
6851
6852		return this;
6853	},
6854
6855	onAdd: function (map) {
6856		this.eachLayer(map.addLayer, map);
6857	},
6858
6859	onRemove: function (map) {
6860		this.eachLayer(map.removeLayer, map);
6861	},
6862
6863	// @method eachLayer(fn: Function, context?: Object): this
6864	// Iterates over the layers of the group, optionally specifying context of the iterator function.
6865	// ```js
6866	// group.eachLayer(function (layer) {
6867	// 	layer.bindPopup('Hello');
6868	// });
6869	// ```
6870	eachLayer: function (method, context) {
6871		for (var i in this._layers) {
6872			method.call(context, this._layers[i]);
6873		}
6874		return this;
6875	},
6876
6877	// @method getLayer(id: Number): Layer
6878	// Returns the layer with the given internal ID.
6879	getLayer: function (id) {
6880		return this._layers[id];
6881	},
6882
6883	// @method getLayers(): Layer[]
6884	// Returns an array of all the layers added to the group.
6885	getLayers: function () {
6886		var layers = [];
6887		this.eachLayer(layers.push, layers);
6888		return layers;
6889	},
6890
6891	// @method setZIndex(zIndex: Number): this
6892	// Calls `setZIndex` on every layer contained in this group, passing the z-index.
6893	setZIndex: function (zIndex) {
6894		return this.invoke('setZIndex', zIndex);
6895	},
6896
6897	// @method getLayerId(layer: Layer): Number
6898	// Returns the internal ID for a layer
6899	getLayerId: function (layer) {
6900		return stamp(layer);
6901	}
6902});
6903
6904
6905// @factory L.layerGroup(layers?: Layer[], options?: Object)
6906// Create a layer group, optionally given an initial set of layers and an `options` object.
6907var layerGroup = function (layers, options) {
6908	return new LayerGroup(layers, options);
6909};
6910
6911/*
6912 * @class FeatureGroup
6913 * @aka L.FeatureGroup
6914 * @inherits LayerGroup
6915 *
6916 * Extended `LayerGroup` that makes it easier to do the same thing to all its member layers:
6917 *  * [`bindPopup`](#layer-bindpopup) binds a popup to all of the layers at once (likewise with [`bindTooltip`](#layer-bindtooltip))
6918 *  * Events are propagated to the `FeatureGroup`, so if the group has an event
6919 * handler, it will handle events from any of the layers. This includes mouse events
6920 * and custom events.
6921 *  * Has `layeradd` and `layerremove` events
6922 *
6923 * @example
6924 *
6925 * ```js
6926 * L.featureGroup([marker1, marker2, polyline])
6927 * 	.bindPopup('Hello world!')
6928 * 	.on('click', function() { alert('Clicked on a member of the group!'); })
6929 * 	.addTo(map);
6930 * ```
6931 */
6932
6933var FeatureGroup = LayerGroup.extend({
6934
6935	addLayer: function (layer) {
6936		if (this.hasLayer(layer)) {
6937			return this;
6938		}
6939
6940		layer.addEventParent(this);
6941
6942		LayerGroup.prototype.addLayer.call(this, layer);
6943
6944		// @event layeradd: LayerEvent
6945		// Fired when a layer is added to this `FeatureGroup`
6946		return this.fire('layeradd', {layer: layer});
6947	},
6948
6949	removeLayer: function (layer) {
6950		if (!this.hasLayer(layer)) {
6951			return this;
6952		}
6953		if (layer in this._layers) {
6954			layer = this._layers[layer];
6955		}
6956
6957		layer.removeEventParent(this);
6958
6959		LayerGroup.prototype.removeLayer.call(this, layer);
6960
6961		// @event layerremove: LayerEvent
6962		// Fired when a layer is removed from this `FeatureGroup`
6963		return this.fire('layerremove', {layer: layer});
6964	},
6965
6966	// @method setStyle(style: Path options): this
6967	// Sets the given path options to each layer of the group that has a `setStyle` method.
6968	setStyle: function (style) {
6969		return this.invoke('setStyle', style);
6970	},
6971
6972	// @method bringToFront(): this
6973	// Brings the layer group to the top of all other layers
6974	bringToFront: function () {
6975		return this.invoke('bringToFront');
6976	},
6977
6978	// @method bringToBack(): this
6979	// Brings the layer group to the back of all other layers
6980	bringToBack: function () {
6981		return this.invoke('bringToBack');
6982	},
6983
6984	// @method getBounds(): LatLngBounds
6985	// Returns the LatLngBounds of the Feature Group (created from bounds and coordinates of its children).
6986	getBounds: function () {
6987		var bounds = new LatLngBounds();
6988
6989		for (var id in this._layers) {
6990			var layer = this._layers[id];
6991			bounds.extend(layer.getBounds ? layer.getBounds() : layer.getLatLng());
6992		}
6993		return bounds;
6994	}
6995});
6996
6997// @factory L.featureGroup(layers: Layer[])
6998// Create a feature group, optionally given an initial set of layers.
6999var featureGroup = function (layers) {
7000	return new FeatureGroup(layers);
7001};
7002
7003/*
7004 * @class Icon
7005 * @aka L.Icon
7006 *
7007 * Represents an icon to provide when creating a marker.
7008 *
7009 * @example
7010 *
7011 * ```js
7012 * var myIcon = L.icon({
7013 *     iconUrl: 'my-icon.png',
7014 *     iconRetinaUrl: '[email protected]',
7015 *     iconSize: [38, 95],
7016 *     iconAnchor: [22, 94],
7017 *     popupAnchor: [-3, -76],
7018 *     shadowUrl: 'my-icon-shadow.png',
7019 *     shadowRetinaUrl: '[email protected]',
7020 *     shadowSize: [68, 95],
7021 *     shadowAnchor: [22, 94]
7022 * });
7023 *
7024 * L.marker([50.505, 30.57], {icon: myIcon}).addTo(map);
7025 * ```
7026 *
7027 * `L.Icon.Default` extends `L.Icon` and is the blue icon Leaflet uses for markers by default.
7028 *
7029 */
7030
7031var Icon = Class.extend({
7032
7033	/* @section
7034	 * @aka Icon options
7035	 *
7036	 * @option iconUrl: String = null
7037	 * **(required)** The URL to the icon image (absolute or relative to your script path).
7038	 *
7039	 * @option iconRetinaUrl: String = null
7040	 * The URL to a retina sized version of the icon image (absolute or relative to your
7041	 * script path). Used for Retina screen devices.
7042	 *
7043	 * @option iconSize: Point = null
7044	 * Size of the icon image in pixels.
7045	 *
7046	 * @option iconAnchor: Point = null
7047	 * The coordinates of the "tip" of the icon (relative to its top left corner). The icon
7048	 * will be aligned so that this point is at the marker's geographical location. Centered
7049	 * by default if size is specified, also can be set in CSS with 
7049negative margins.
7050	 *
7051	 * @option popupAnchor: Point = [0, 0]
7052	 * The coordinates of the point from which popups will "open", relative to the icon anchor.
7053	 *
7054	 * @option tooltipAnchor: Point = [0, 0]
7055	 * The coordinates of the point from which tooltips will "open", relative to the icon anchor.
7056	 *
7057	 * @option shadowUrl: String = null
7058	 * The URL to the icon shadow image. If not specified, no shadow image will be created.
7059	 *
7060	 * @option shadowRetinaUrl: String = null
7061	 *
7062	 * @option shadowSize: Point = null
7063	 * Size of the shadow image in pixels.
7064	 *
7065	 * @option shadowAnchor: Point = null
7066	 * The coordinates of the "tip" of the shadow (relative to its top left corner) (the same
7067	 * as iconAnchor if not specified).
7068	 *
7069	 * @option className: String = ''
7070	 * A custom class name to assign to both icon and shadow images. Empty by default.
7071	 */
7072
7073	options: {
7074		popupAnchor: [0, 0],
7075		tooltipAnchor: [0, 0]
7076	},
7077
7078	initialize: function (options) {
7079		setOptions(this, options);
7080	},
7081
7082	// @method createIcon(oldIcon?: HTMLElement): HTMLElement
7083	// Called internally when the icon has to be shown, returns a `<img>` HTML element
7084	// styled according to the options.
7085	createIcon: function (oldIcon) {
7086		return this._createIcon('icon', oldIcon);
7087	},
7088
7089	// @method createShadow(oldIcon?: HTMLElement): HTMLElement
7090	// As `createIcon`, but for the shadow beneath it.
7091	createShadow: function (oldIcon) {
7092		return this._createIcon('shadow', oldIcon);
7093	},
7094
7095	_createIcon: function (name, oldIcon) {
7096		var src = this._getIconUrl(name);
7097
7098		if (!src) {
7099			if (name === 'icon') {
7100				throw new Error('iconUrl not set in Icon options (see the docs).');
7101			}
7102			return null;
7103		}
7104
7105		var img = this._createImg(src, oldIcon && oldIcon.tagName === 'IMG' ? oldIcon : null);
7106		this._setIconStyles(img, name);
7107
7108		return img;
7109	},
7110
7111	_setIconStyles: function (img, name) {
7112		var options = this.options;
7113		var sizeOption = options[name + 'Size'];
7114
7115		if (typeof sizeOption === 'number') {
7116			sizeOption = [sizeOption, sizeOption];
7117		}
7118
7119		var size = toPoint(sizeOption),
7120		    anchor = toPoint(name === 'shadow' && options.shadowAnchor || options.iconAnchor ||
7121		            size && size.divideBy(2, true));
7122
7123		img.className = 'leaflet-marker-' + name + ' ' + (options.className || '');
7124
7125		if (anchor) {
7126			img.style.marginLeft = (-anchor.x) + 'px';
7127			img.style.marginTop  = (-anchor.y) + 'px';
7128		}
7129
7130		if (size) {
7131			img.style.width  = size.x + 'px';
7132			img.style.height = size.y + 'px';
7133		}
7134	},
7135
7136	_createImg: function (src, el) {
7137		el = el || document.createElement('img');
7138		el.src = src;
7139		return el;
7140	},
7141
7142	_getIconUrl: function (name) {
7143		return retina && this.options[name + 'RetinaUrl'] || this.options[name + 'Url'];
7144	}
7145});
7146
7147
7148// @factory L.icon(options: Icon options)
7149// Creates an icon instance with the given options.
7150function icon(options) {
7151	return new Icon(options);
7152}
7153
7154/*
7155 * @miniclass Icon.Default (Icon)
7156 * @aka L.Icon.Default
7157 * @section
7158 *
7159 * A trivial subclass of `Icon`, represents the icon to use in `Marker`s when
7160 * no icon is specified. Points to the blue marker image distributed with Leaflet
7161 * releases.
7162 *
7163 * In order to customize the default icon, just change the properties of `L.Icon.Default.prototype.options`
7164 * (which is a set of `Icon options`).
7165 *
7166 * If you want to _completely_ replace the default icon, override the
7167 * `L.Marker.prototype.options.icon` with your own icon instead.
7168 */
7169
7170var IconDefault = Icon.extend({
7171
7172	options: {
7173		iconUrl:       'marker-icon.png',
7174		iconRetinaUrl: 'marker-icon-2x.png',
7175		shadowUrl:     'marker-shadow.png',
7176		iconSize:    [25, 41],
7177		iconAnchor:  [12, 41],
7178		popupAnchor: [1, -34],
7179		tooltipAnchor: [16, -28],
7180		shadowSize:  [41, 41]
7181	},
7182
7183	_getIconUrl: function (name) {
7184		if (!IconDefault.imagePath) {	// Deprecated, backwards-compatibility only
7185			IconDefault.imagePath = this._detectIconPath();
7186		}
7187
7188		// @option imagePath: String
7189		// `Icon.Default` will try to auto-detect the location of the
7190		// blue icon images. If you are placing these images in a non-standard
7191		// way, set this option to point to the right path.
7192		return (this.options.imagePath || IconDefault.imagePath) + Icon.prototype._getIconUrl.call(this, name);
7193	},
7194
7195	_detectIconPath: function () {
7196		var el = create$1('div',  'leaflet-default-icon-path', document.body);
7197		var path = getStyle(el, 'background-image') ||
7198		           getStyle(el, 'backgroundImage');	// IE8
7199
7200		document.body.removeChild(el);
7201
7202		if (path === null || path.indexOf('url') !== 0) {
7203			path = '';
7204		} else {
7205			path = path.replace(/^url\(["']?/, '').replace(/marker-icon\.png["']?\)$/, '');
7206		}
7207
7208		return path;
7209	}
7210});
7211
7212/*
7213 * L.Handler.MarkerDrag is used internally by L.Marker to make the markers draggable.
7214 */
7215
7216
7217/* @namespace Marker
7218 * @section Interaction handlers
7219 *
7220 * 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:
7221 *
7222 * ```js
7223 * marker.dragging.disable();
7224 * ```
7225 *
7226 * @property dragging: Handler
7227 * Marker dragging handler (by both mouse and touch). Only valid when the marker is on the map (Otherwise set [`marker.options.draggable`](#marker-draggable)).
7228 */
7229
7230var MarkerDrag = Handler.extend({
7231	initialize: function (marker) {
7232		this._marker = marker;
7233	},
7234
7235	addHooks: function () {
7236		var icon = this._marker._icon;
7237
7238		if (!this._draggable) {
7239			this._draggable = new Draggable(icon, icon, true);
7240		}
7241
7242		this._draggable.on({
7243			dragstart: this._onDragStart,
7244			predrag: this._onPreDrag,
7245			drag: this._onDrag,
7246			dragend: this._onDragEnd
7247		}, this).enable();
7248
7249		addClass(icon, 'leaflet-marker-draggable');
7250	},
7251
7252	removeHooks: function () {
7253		this._draggable.off({
7254			dragstart: this._onDragStart,
7255			predrag: this._onPreDrag,
7256			drag: this._onDrag,
7257			dragend: this._onDragEnd
7258		}, this).disable();
7259
7260		if (this._marker._icon) {
7261			removeClass(this._marker._icon, 'leaflet-marker-draggable');
7262		}
7263	},
7264
7265	moved: function () {
7266		return this._draggable && this._draggable._moved;
7267	},
7268
7269	_adjustPan: function (e) {
7270		var marker = this._marker,
7271		    map = marker._map,
7272		    speed = this._marker.options.autoPanSpeed,
7273		    padding = this._marker.options.autoPanPadding,
7274		    iconPos = getPosition(marker._icon),
7275		    bounds = map.getPixelBounds(),
7276		    origin = map.getPixelOrigin();
7277
7278		var panBounds = toBounds(
7279			bounds.min._subtract(origin).add(padding),
7280			bounds.max._subtract(origin).subtract(padding)
7281		);
7282
7283		if (!panBounds.contains(iconPos)) {
7284			// Compute incremental movement
7285			var movement = toPoint(
7286				(Math.max(panBounds.max.x, iconPos.x) - panBounds.max.x) / (bounds.max.x - panBounds.max.x) -
7287				(Math.min(panBounds.min.x, iconPos.x) - panBounds.min.x) / (bounds.min.x - panBounds.min.x),
7288
7289				(Math.max(panBounds.max.y, iconPos.y) - panBounds.max.y) / (bounds.max.y - panBounds.max.y) -
7290				(Math.min(panBounds.min.y, iconPos.y) - panBounds.min.y) / (bounds.min.y - panBounds.min.y)
7291			).multiplyBy(speed);
7292
7293			map.panBy(movement, {animate: false});
7294
7295			this._draggable._newPos._add(movement);
7296			this._draggable._startPos._add(movement);
7297
7298			setPosition(marker._icon, this._draggable._newPos);
7299			this._onDrag(e);
7300
vendor: 20,622 bytes, lines 7301-8069
7301			this._panRequest = requestAnimFrame(this._adjustPan.bind(this, e));
7302		}
7303	},
7304
7305	_onDragStart: function () {
7306		// @section Dragging events
7307		// @event dragstart: Event
7308		// Fired when the user starts dragging the marker.
7309
7310		// @event movestart: Event
7311		// Fired when the marker starts moving (because of dragging).
7312
7313		this._oldLatLng = this._marker.getLatLng();
7314		this._marker
7315		    .closePopup()
7316		    .fire('movestart')
7317		    .fire('dragstart');
7318	},
7319
7320	_onPreDrag: function (e) {
7321		if (this._marker.options.autoPan) {
7322			cancelAnimFrame(this._panRequest);
7323			this._panRequest = requestAnimFrame(this._adjustPan.bind(this, e));
7324		}
7325	},
7326
7327	_onDrag: function (e) {
7328		var marker = this._marker,
7329		    shadow = marker._shadow,
7330		    iconPos = getPosition(marker._icon),
7331		    latlng = marker._map.layerPointToLatLng(iconPos);
7332
7333		// update shadow position
7334		if (shadow) {
7335			setPosition(shadow, iconPos);
7336		}
7337
7338		marker._latlng = latlng;
7339		e.latlng = latlng;
7340		e.oldLatLng = this._oldLatLng;
7341
7342		// @event drag: Event
7343		// Fired repeatedly while the user drags the marker.
7344		marker
7345		    .fire('move', e)
7346		    .fire('drag', e);
7347	},
7348
7349	_onDragEnd: function (e) {
7350		// @event dragend: DragEndEvent
7351		// Fired when the user stops dragging the marker.
7352
7353		 cancelAnimFrame(this._panRequest);
7354
7355		// @event moveend: Event
7356		// Fired when the marker stops moving (because of dragging).
7357		delete this._oldLatLng;
7358		this._marker
7359		    .fire('moveend')
7360		    .fire('dragend', e);
7361	}
7362});
7363
7364/*
7365 * @class Marker
7366 * @inherits Interactive layer
7367 * @aka L.Marker
7368 * L.Marker is used to display clickable/draggable icons on the map. Extends `Layer`.
7369 *
7370 * @example
7371 *
7372 * ```js
7373 * L.marker([50.5, 30.5]).addTo(map);
7374 * ```
7375 */
7376
7377var Marker = Layer.extend({
7378
7379	// @section
7380	// @aka Marker options
7381	options: {
7382		// @option icon: Icon = *
7383		// Icon instance to use for rendering the marker.
7384		// See [Icon documentation](#L.Icon) for details on how to customize the marker icon.
7385		// If not specified, a common instance of `L.Icon.Default` is used.
7386		icon: new IconDefault(),
7387
7388		// Option inherited from "Interactive layer" abstract class
7389		interactive: true,
7390
7391		// @option keyboard: Boolean = true
7392		// Whether the marker can be tabbed to with a keyboard and clicked by pressing enter.
7393		keyboard: true,
7394
7395		// @option title: String = ''
7396		// Text for the browser tooltip that appear on marker hover (no tooltip by default).
7397		title: '',
7398
7399		// @option alt: String = ''
7400		// Text for the `alt` attribute of the icon image (useful for accessibility).
7401		alt: '',
7402
7403		// @option zIndexOffset: Number = 0
7404		// 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).
7405		zIndexOffset: 0,
7406
7407		// @option opacity: Number = 1.0
7408		// The opacity of the marker.
7409		opacity: 1,
7410
7411		// @option riseOnHover: Boolean = false
7412		// If `true`, the marker will get on top of others when you hover the mouse over it.
7413		riseOnHover: false,
7414
7415		// @option riseOffset: Number = 250
7416		// The z-index offset used for the `riseOnHover` feature.
7417		riseOffset: 250,
7418
7419		// @option pane: String = 'markerPane'
7420		// `Map pane` where the markers icon will be added.
7421		pane: 'markerPane',
7422
7423		// @option bubblingMouseEvents: Boolean = false
7424		// When `true`, a mouse event on this marker will trigger the same event on the map
7425		// (unless [`L.DomEvent.stopPropagation`](#domevent-stoppropagation) is used).
7426		bubblingMouseEvents: false,
7427
7428		// @section Draggable marker options
7429		// @option draggable: Boolean = false
7430		// Whether the marker is draggable with mouse/touch or not.
7431		draggable: false,
7432
7433		// @option autoPan: Boolean = false
7434		// Whether to pan the map when dragging this marker near its edge or not.
7435		autoPan: false,
7436
7437		// @option autoPanPadding: Point = Point(50, 50)
7438		// Distance (in pixels to the left/right and to the top/bottom) of the
7439		// map edge to start panning the map.
7440		autoPanPadding: [50, 50],
7441
7442		// @option autoPanSpeed: Number = 10
7443		// Number of pixels the map should pan by.
7444		autoPanSpeed: 10
7445	},
7446
7447	/* @section
7448	 *
7449	 * In addition to [shared layer methods](#Layer) like `addTo()` and `remove()` and [popup methods](#Popup) like bindPopup() you can also use the following methods:
7450	 */
7451
7452	initialize: function (latlng, options) {
7453		setOptions(this, options);
7454		this._latlng = toLatLng(latlng);
7455	},
7456
7457	onAdd: function (map) {
7458		this._zoomAnimated = this._zoomAnimated && map.options.markerZoomAnimation;
7459
7460		if (this._zoomAnimated) {
7461			map.on('zoomanim', this._animateZoom, this);
7462		}
7463
7464		this._initIcon();
7465		this.update();
7466	},
7467
7468	onRemove: function (map) {
7469		if (this.dragging && this.dragging.enabled()) {
7470			this.options.draggable = true;
7471			this.dragging.removeHooks();
7472		}
7473		delete this.dragging;
7474
7475		if (this._zoomAnimated) {
7476			map.off('zoomanim', this._animateZoom, this);
7477		}
7478
7479		this._removeIcon();
7480		this._removeShadow();
7481	},
7482
7483	getEvents: function () {
7484		return {
7485			zoom: this.update,
7486			viewreset: this.update
7487		};
7488	},
7489
7490	// @method getLatLng: LatLng
7491	// Returns the current geographical position of the marker.
7492	getLatLng: function () {
7493		return this._latlng;
7494	},
7495
7496	// @method setLatLng(latlng: LatLng): this
7497	// Changes the marker position to the given point.
7498	setLatLng: function (latlng) {
7499		var oldLatLng = this._latlng;
7500		this._latlng = toLatLng(latlng);
7501		this.update();
7502
7503		// @event move: Event
7504		// 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`.
7505		return this.fire('move', {oldLatLng: oldLatLng, latlng: this._latlng});
7506	},
7507
7508	// @method setZIndexOffset(offset: Number): this
7509	// Changes the [zIndex offset](#marker-zindexoffset) of the marker.
7510	setZIndexOffset: function (offset) {
7511		this.options.zIndexOffset = offset;
7512		return this.update();
7513	},
7514
7515	// @method setIcon(icon: Icon): this
7516	// Changes the marker icon.
7517	setIcon: function (icon) {
7518
7519		this.options.icon = icon;
7520
7521		if (this._map) {
7522			this._initIcon();
7523			this.update();
7524		}
7525
7526		if (this._popup) {
7527			this.bindPopup(this._popup, this._popup.options);
7528		}
7529
7530		return this;
7531	},
7532
7533	getElement: function () {
7534		return this._icon;
7535	},
7536
7537	update: function () {
7538
7539		if (this._icon && this._map) {
7540			var pos = this._map.latLngToLayerPoint(this._latlng).round();
7541			this._setPos(pos);
7542		}
7543
7544		return this;
7545	},
7546
7547	_initIcon: function () {
7548		var options = this.options,
7549		    classToAdd = 'leaflet-zoom-' + (this._zoomAnimated ? 'animated' : 'hide');
7550
7551		var icon = options.icon.createIcon(this._icon),
7552		    addIcon = false;
7553
7554		// if we're not reusing the icon, remove the old one and init new one
7555		if (icon !== this._icon) {
7556			if (this._icon) {
7557				this._removeIcon();
7558			}
7559			addIcon = true;
7560
7561			if (options.title) {
7562				icon.title = options.title;
7563			}
7564
7565			if (icon.tagName === 'IMG') {
7566				icon.alt = options.alt || '';
7567			}
7568		}
7569
7570		addClass(icon, classToAdd);
7571
7572		if (options.keyboard) {
7573			icon.tabIndex = '0';
7574		}
7575
7576		this._icon = icon;
7577
7578		if (options.riseOnHover) {
7579			this.on({
7580				mouseover: this._bringToFront,
7581				mouseout: this._resetZIndex
7582			});
7583		}
7584
7585		var newShadow = options.icon.createShadow(this._shadow),
7586		    addShadow = false;
7587
7588		if (newShadow !== this._shadow) {
7589			this._removeShadow();
7590			addShadow = true;
7591		}
7592
7593		if (newShadow) {
7594			addClass(newShadow, classToAdd);
7595			newShadow.alt = '';
7596		}
7597		this._shadow = newShadow;
7598
7599
7600		if (options.opacity < 1) {
7601			this._updateOpacity();
7602		}
7603
7604
7605		if (addIcon) {
7606			this.getPane().appendChild(this._icon);
7607		}
7608		this._initInteraction();
7609		if (newShadow && addShadow) {
7610			this.getPane('shadowPane').appendChild(this._shadow);
7611		}
7612	},
7613
7614	_removeIcon: function () {
7615		if (this.options.riseOnHover) {
7616			this.off({
7617				mouseover: this._bringToFront,
7618				mouseout: this._resetZIndex
7619			});
7620		}
7621
7622		remove(this._icon);
7623		this.removeInteractiveTarget(this._icon);
7624
7625		this._icon = null;
7626	},
7627
7628	_removeShadow: function () {
7629		if (this._shadow) {
7630			remove(this._shadow);
7631		}
7632		this._shadow = null;
7633	},
7634
7635	_setPos: function (pos) {
7636		setPosition(this._icon, pos);
7637
7638		if (this._shadow) {
7639			setPosition(this._shadow, pos);
7640		}
7641
7642		this._zIndex = pos.y + this.options.zIndexOffset;
7643
7644		this._resetZIndex();
7645	},
7646
7647	_updateZIndex: function (offset) {
7648		this._icon.style.zIndex = this._zIndex + offset;
7649	},
7650
7651	_animateZoom: function (opt) {
7652		var pos = this._map._latLngToNewLayerPoint(this._latlng, opt.zoom, opt.center).round();
7653
7654		this._setPos(pos);
7655	},
7656
7657	_initInteraction: function () {
7658
7659		if (!this.options.interactive) { return; }
7660
7661		addClass(this._icon, 'leaflet-interactive');
7662
7663		this.addInteractiveTarget(this._icon);
7664
7665		if (MarkerDrag) {
7666			var draggable = this.options.draggable;
7667			if (this.dragging) {
7668				draggable = this.dragging.enabled();
7669				this.dragging.disable();
7670			}
7671
7672			this.dragging = new MarkerDrag(this);
7673
7674			if (draggable) {
7675				this.dragging.enable();
7676			}
7677		}
7678	},
7679
7680	// @method setOpacity(opacity: Number): this
7681	// Changes the opacity of the marker.
7682	setOpacity: function (opacity) {
7683		this.options.opacity = opacity;
7684		if (this._map) {
7685			this._updateOpacity();
7686		}
7687
7688		return this;
7689	},
7690
7691	_updateOpacity: function () {
7692		var opacity = this.options.opacity;
7693
7694		setOpacity(this._icon, opacity);
7695
7696		if (this._shadow) {
7697			setOpacity(this._shadow, opacity);
7698		}
7699	},
7700
7701	_bringToFront: function () {
7702		this._updateZIndex(this.options.riseOffset);
7703	},
7704
7705	_resetZIndex: function () {
7706		this._updateZIndex(0);
7707	},
7708
7709	_getPopupAnchor: function () {
7710		return this.options.icon.options.popupAnchor;
7711	},
7712
7713	_getTooltipAnchor: function () {
7714		return this.options.icon.options.tooltipAnchor;
7715	}
7716});
7717
7718
7719// factory L.marker(latlng: LatLng, options? : Marker options)
7720
7721// @factory L.marker(latlng: LatLng, options? : Marker options)
7722// Instantiates a Marker object given a geographical point and optionally an options object.
7723function marker(latlng, options) {
7724	return new Marker(latlng, options);
7725}
7726
7727/*
7728 * @class Path
7729 * @aka L.Path
7730 * @inherits Interactive layer
7731 *
7732 * An abstract class that contains options and constants shared between vector
7733 * overlays (Polygon, Polyline, Circle). Do not use it directly. Extends `Layer`.
7734 */
7735
7736var Path = Layer.extend({
7737
7738	// @section
7739	// @aka Path options
7740	options: {
7741		// @option stroke: Boolean = true
7742		// Whether to draw stroke along the path. Set it to `false` to disable borders on polygons or circles.
7743		stroke: true,
7744
7745		// @option color: String = '#3388ff'
7746		// Stroke color
7747		color: '#3388ff',
7748
7749		// @option weight: Number = 3
7750		// Stroke width in pixels
7751		weight: 3,
7752
7753		// @option opacity: Number = 1.0
7754		// Stroke opacity
7755		opacity: 1,
7756
7757		// @option lineCap: String= 'round'
7758		// A string that defines [shape to be used at the end](https://developer.mozilla.org/docs/Web/SVG/Attribute/stroke-linecap) of the stroke.
7759		lineCap: 'round',
7760
7761		// @option lineJoin: String = 'round'
7762		// A string that defines [shape to be used at the corners](https://developer.mozilla.org/docs/Web/SVG/Attribute/stroke-linejoin) of the stroke.
7763		lineJoin: 'round',
7764
7765		// @option dashArray: String = null
7766		// 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).
7767		dashArray: null,
7768
7769		// @option dashOffset: String = null
7770		// 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).
7771		dashOffset: null,
7772
7773		// @option fill: Boolean = depends
7774		// Whether to fill the path with color. Set it to `false` to disable filling on polygons or circles.
7775		fill: false,
7776
7777		// @option fillColor: String = *
7778		// Fill color. Defaults to the value of the [`color`](#path-color) option
7779		fillColor: null,
7780
7781		// @option fillOpacity: Number = 0.2
7782		// Fill opacity.
7783		fillOpacity: 0.2,
7784
7785		// @option fillRule: String = 'evenodd'
7786		// A string that defines [how the inside of a shape](https://developer.mozilla.org/docs/Web/SVG/Attribute/fill-rule) is determined.
7787		fillRule: 'evenodd',
7788
7789		// className: '',
7790
7791		// Option inherited from "Interactive layer" abstract class
7792		interactive: true,
7793
7794		// @option bubblingMouseEvents: Boolean = true
7795		// When `true`, a mouse event on this path will trigger the same event on the map
7796		// (unless [`L.DomEvent.stopPropagation`](#domevent-stoppropagation) is used).
7797		bubblingMouseEvents: true
7798	},
7799
7800	beforeAdd: function (map) {
7801		// Renderer is set here because we need to call renderer.getEvents
7802		// before this.getEvents.
7803		this._renderer = map.getRenderer(this);
7804	},
7805
7806	onAdd: function () {
7807		this._renderer._initPath(this);
7808		this._reset();
7809		this._renderer._addPath(this);
7810	},
7811
7812	onRemove: function () {
7813		this._renderer._removePath(this);
7814	},
7815
7816	// @method redraw(): this
7817	// Redraws the layer. Sometimes useful after you changed the coordinates that the path uses.
7818	redraw: function () {
7819		if (this._map) {
7820			this._renderer._updatePath(this);
7821		}
7822		return this;
7823	},
7824
7825	// @method setStyle(style: Path options): this
7826	// Changes the appearance of a Path based on the options in the `Path options` object.
7827	setStyle: function (style) {
7828		setOptions(this, style);
7829		if (this._renderer) {
7830			this._renderer._updateStyle(this);
7831		}
7832		return this;
7833	},
7834
7835	// @method bringToFront(): this
7836	// Brings the layer to the top of all path layers.
7837	bringToFront: function () {
7838		if (this._renderer) {
7839			this._renderer._bringToFront(this);
7840		}
7841		return this;
7842	},
7843
7844	// @method bringToBack(): this
7845	// Brings the layer to the bottom of all path layers.
7846	bringToBack: function () {
7847		if (this._renderer) {
7848			this._renderer._bringToBack(this);
7849		}
7850		return this;
7851	},
7852
7853	getElement: function () {
7854		return this._path;
7855	},
7856
7857	_reset: function () {
7858		// defined in child classes
7859		this._project();
7860		this._update();
7861	},
7862
7863	_clickTolerance: function () {
7864		// used when doing hit detection for Canvas layers
7865		return (this.options.stroke ? this.options.weight / 2 : 0) + this._renderer.options.tolerance;
7866	}
7867});
7868
7869/*
7870 * @class CircleMarker
7871 * @aka L.CircleMarker
7872 * @inherits Path
7873 *
7874 * A circle of a fixed size with radius specified in pixels. Extends `Path`.
7875 */
7876
7877var CircleMarker = Path.extend({
7878
7879	// @section
7880	// @aka CircleMarker options
7881	options: {
7882		fill: true,
7883
7884		// @option radius: Number = 10
7885		// Radius of the circle marker, in pixels
7886		radius: 10
7887	},
7888
7889	initialize: function (latlng, options) {
7890		setOptions(this, options);
7891		this._latlng = toLatLng(latlng);
7892		this._radius = this.options.radius;
7893	},
7894
7895	// @method setLatLng(latLng: LatLng): this
7896	// Sets the position of a circle marker to a new location.
7897	setLatLng: function (latlng) {
7898		this._latlng = toLatLng(latlng);
7899		this.redraw();
7900		return this.fire('move', {latlng: this._latlng});
7901	},
7902
7903	// @method getLatLng(): LatLng
7904	// Returns the current geographical position of the circle marker
7905	getLatLng: function () {
7906		return this._latlng;
7907	},
7908
7909	// @method setRadius(radius: Number): this
7910	// Sets the radius of a circle marker. Units are in pixels.
7911	setRadius: function (radius) {
7912		this.options.radius = this._radius = radius;
7913		return this.redraw();
7914	},
7915
7916	// @method getRadius(): Number
7917	// Returns the current radius of the circle
7918	getRadius: function () {
7919		return this._radius;
7920	},
7921
7922	setStyle : function (options) {
7923		var radius = options && options.radius || this._radius;
7924		Path.prototype.setStyle.call(this, options);
7925		this.setRadius(radius);
7926		return this;
7927	},
7928
7929	_project: function () {
7930		this._point = this._map.latLngToLayerPoint(this._latlng);
7931		this._updateBounds();
7932	},
7933
7934	_updateBounds: function () {
7935		var r = this._radius,
7936		    r2 = this._radiusY || r,
7937		    w = this._clickTolerance(),
7938		    p = [r + w, r2 + w];
7939		this._pxBounds = new Bounds(this._point.subtract(p), this._point.add(p));
7940	},
7941
7942	_update: function () {
7943		if (this._map) {
7944			this._updatePath();
7945		}
7946	},
7947
7948	_updatePath: function () {
7949		this._renderer._updateCircle(this);
7950	},
7951
7952	_empty: function () {
7953		return this._radius && !this._renderer._bounds.intersects(this._pxBounds);
7954	},
7955
7956	// Needed by the `Canvas` renderer for interactivity
7957	_containsPoint: function (p) {
7958		return p.distanceTo(this._point) <= this._radius + this._clickTolerance();
7959	}
7960});
7961
7962
7963// @factory L.circleMarker(latlng: LatLng, options?: CircleMarker options)
7964// Instantiates a circle marker object given a geographical point, and an optional options object.
7965function circleMarker(latlng, options) {
7966	return new CircleMarker(latlng, options);
7967}
7968
7969/*
7970 * @class Circle
7971 * @aka L.Circle
7972 * @inherits CircleMarker
7973 *
7974 * A class for drawing circle overlays on a map. Extends `CircleMarker`.
7975 *
7976 * It's an approximation and starts to diverge from a real circle closer to poles (due to projection distortion).
7977 *
7978 * @example
7979 *
7980 * ```js
7981 * L.circle([50.5, 30.5], {radius: 200}).addTo(map);
7982 * ```
7983 */
7984
7985var Circle = CircleMarker.extend({
7986
7987	initialize: function (latlng, options, legacyOptions) {
7988		if (typeof options === 'number') {
7989			// Backwards compatibility with 0.7.x factory (latlng, radius, options?)
7990			options = extend({}, legacyOptions, {radius: options});
7991		}
7992		setOptions(this, options);
7993		this._latlng = toLatLng(latlng);
7994
7995		if (isNaN(this.options.radius)) { throw new Error('Circle radius cannot be NaN'); }
7996
7997		// @section
7998		// @aka Circle options
7999		// @option radius: Number; Radius of the circle, in meters.
8000		this._mRadius = this.options.radius;
8001	},
8002
8003	// @method setRadius(radius: Number): this
8004	// Sets the radius of a circle. Units are in meters.
8005	setRadius: function (radius) {
8006		this._mRadius = radius;
8007		return this.redraw();
8008	},
8009
8010	// @method getRadius(): Number
8011	// Returns the current radius of a circle. Units are in meters.
8012	getRadius: function () {
8013		return this._mRadius;
8014	},
8015
8016	// @method getBounds(): LatLngBounds
8017	// Returns the `LatLngBounds` of the path.
8018	getBounds: function () {
8019		var half = [this._radius, this._radiusY || this._radius];
8020
8021		return new LatLngBounds(
8022			this._map.layerPointToLatLng(this._point.subtract(half)),
8023			this._map.layerPointToLatLng(this._point.add(half)));
8024	},
8025
8026	setStyle: Path.prototype.setStyle,
8027
8028	_project: function () {
8029
8030		var lng = this._latlng.lng,
8031		    lat = this._latlng.lat,
8032		    map = this._map,
8033		    crs = map.options.crs;
8034
8035		if (crs.distance === Earth.distance) {
8036			var d = Math.PI / 180,
8037			    latR = (this._mRadius / Earth.R) / d,
8038			    top = map.project([lat + latR, lng]),
8039			    bottom = map.project([lat - latR, lng]),
8040			    p = top.add(bottom).divideBy(2),
8041			    lat2 = map.unproject(p).lat,
8042			    lngR = Math.acos((Math.cos(latR * d) - Math.sin(lat * d) * Math.sin(lat2 * d)) /
8043			            (Math.cos(lat * d) * Math.cos(lat2 * d))) / d;
8044
8045			if (isNaN(lngR) || lngR === 0) {
8046				lngR = latR / Math.cos(Math.PI / 180 * lat); // Fallback for edge case, #2425
8047			}
8048
8049			this._point = p.subtract(map.getPixelOrigin());
8050			this._radius = isNaN(lngR) ? 0 : p.x - map.project([lat2, lng - lngR]).x;
8051			this._radiusY = p.y - top.y;
8052
8053		} else {
8054			var latlng2 = crs.unproject(crs.project(this._latlng).subtract([this._mRadius, 0]));
8055
8056			this._point = map.latLngToLayerPoint(this._latlng);
8057			this._radius = this._point.x - map.latLngToLayerPoint(latlng2).x;
8058		}
8059
8060		this._updateBounds();
8061	}
8062});
8063
8064// @factory L.circle(latlng: LatLng, options?: Circle options)
8065// Instantiates a circle object given a geographical point, and an options object
8066// which contains the circle radius.
8067// @alternative
8068// @factory L.circle(latlng: LatLng, radius: Number, options?: Circle options)
8069// Obsolete way of instantiating a circle, for compatibility with 0.7.x code.
8070// Do not use in new applications or plugins.
8071function circle(latlng, options, legacyOptions) {
8072	return new Circle(latlng, options, legacyOptions);
8073}
8074
8075/*
8076 * @class Polyline
8077 * @aka L.Polyline
8078 * @inherits Path
8079 *
8080 * A class for drawing polyline overlays on a map. Extends `Path`.
8081 *
8082 * @example
8083 *
8084 * ```js
8085 * // create a red polyline from an array of LatLng points
8086 * var latlngs = [
8087 * 	[45.51, -122.68],
8088 * 	[37.77, -122.43],
8089 * 	[34.04, -118.2]
8090 * ];
8091 *
8092 * var polyline = L.polyline(latlngs, {color: 'red'}).addTo(map);
8093 *
8094 * // zoom the map to the polyline
8095 * map.fitBounds(polyline.getBounds());
8096 * ```
8097 *
8098 * You can also pass a multi-dimensional array to represent a `MultiPolyline` shape:
8099 *
8100 * ```js
8101 * // create a red polyline from an array of arrays of LatLng points
8102 * var latlngs = [
8103 * 	[[45.51, -122.68],
8104 * 	 [37.77, -122.43],
8105 * 	 [34.04, -118.2]],
8106 * 	[[40.78, -73.91],
8107 * 	 [41.83, -87.62],
8108 * 	 [32.76, -96.72]]
8109 * ];
8110 * ```
8111 */
8112
8113
8114var Polyline = Path.extend({
8115
8116	// @section
8117	// @aka Polyline options
8118	options: {
8119		// @option smoothFactor: Number = 1.0
8120		// How much to simplify the polyline on each zoom level. More means
8121		// better performance and smoother look, and less means more accurate representation.
8122		smoothFactor: 1.0,
8123
8124		// @option noClip: Boolean = false
8125		// Disable polyline clipping.
8126		noClip: false
8127	},
8128
8129	initialize: function (latlngs, options) {
8130		setOptions(this, options);
8131		this._setLatLngs(latlngs);
8132	},
8133
8134	// @method getLatLngs(): LatLng[]
8135	// Returns an array of the points in the path, or nested arrays of points in case of multi-polyline.
8136	getLatLngs: function () {
8137		return this._latlngs;
8138	},
8139
8140	// @method setLatLngs(latlngs: LatLng[]): this
8141	// Replaces all the points in the polyline with the given array of geographical points.
8142	setLatLngs: function (latlngs) {
8143		this._setLatLngs(latlngs);
8144		return this.redraw();
8145	},
8146
8147	// @method isEmpty(): Boolean
8148	// Returns `true` if the Polyline has no LatLngs.
8149	isEmpty: function () {
8150		return !this._latlngs.length;
8151	},
8152
8153	// @method closestLayerPoint(p: Point): Point
8154	// Returns the point closest to `p` on the Polyline.
8155	closestLayerPoint: function (p) {
8156		var minDistance = Infinity,
8157		    minPoint = null,
8158		    closest = _sqClosestPointOnSegment,
8159		    p1, p2;
8160
8161		for (var j = 0, jLen = this._parts.length; j < jLen; j++) {
8162			var points = this._parts[j];
8163
8164			for (var i = 1, len = points.length; i < len; i++) {
8165				p1 = points[i - 1];
8166				p2 = points[i];
8167
8168				var sqDist = closest(p, p1, p2, true);
8169
8170				if (sqDist < minDistance) {
8171					minDistance = sqDist;
8172					minPoint = closest(p, p1, p2);
8173				}
8174			}
8175		}
8176		if (minPoint) {
8177			minPoint.distance = Math.sqrt(minDistance);
8178		}
8179		return minPoint;
8180	},
8181
8182	// @method getCenter(): LatLng
8183	// Returns the center ([centroid](http://en.wikipedia.org/wiki/Centroid)) of the polyline.
8184	getCenter: function () {
8185		// throws error when not yet added to map as this center calculation requires projected coordinates
8186		if (!this._map) {
8187			throw new Error('Must add layer to map before using getCenter()');
8188		}
8189
8190		var i, halfDist, segDist, dist, p1, p2, ratio,
8191		    points = this._rings[0],
8192		    len = points.length;
8193
8194		if (!len) { return null; }
8195
8196		// polyline centroid algorithm; only uses the first ring if there are multiple
8197
8198		for (i = 0, halfDist = 0; i < len - 1; i++) {
8199			halfDist += points[i].distanceTo(points[i + 1]) / 2;
8200		}
8201
8202		// The line is so small in the current view that all points are on the same pixel.
8203		if (halfDist === 0) {
8204			return this._map.layerPointToLatLng(points[0]);
8205		}
8206
8207		for (i = 0, dist = 0; i < len - 1; i++) {
8208			p1 = points[i];
8209			p2 = points[i + 1];
8210			segDist = p1.distanceTo(p2);
8211			dist += segDist;
8212
8213			if (dist > halfDist) {
8214				ratio = (dist - halfDist) / segDist;
8215				return this._map.layerPointToLatLng([
8216					p2.x - ratio * (p2.x - p1.x),
8217					p2.y - ratio * (p2.y - p1.y)
8218				]);
8219			}
8220		}
8221	},
8222
8223	// @method getBounds(): LatLngBounds
8224	// Returns the `LatLngBounds` of the path.
8225	getBounds: function () {
8226		return this._bounds;
8227	},
8228
8229	// @method addLatLng(latlng: LatLng, latlngs? LatLng[]): this
8230	// Adds a given point to the polyline. By default, adds to the first ring of
8231	// the polyline in case of a multi-polyline, but can be overridden by passing
8232	// a specific ring as a LatLng array (that you can earlier access with [`getLatLngs`](#polyline-getlatlngs)).
8233	addLatLng: function (latlng, latlngs) {
8234		latlngs = latlngs || this._defaultShape();
8235		latlng = toLatLng(latlng);
8236		latlngs.push(latlng);
8237		this._bounds.extend(latlng);
8238		return this.redraw();
8239	},
8240
8241	_setLatLngs: function (latlngs) {
vendor: 12,310 bytes, lines 8242-8691
8242		this._bounds = new LatLngBounds();
8243		this._latlngs = this._convertLatLngs(latlngs);
8244	},
8245
8246	_defaultShape: function () {
8247		return isFlat(this._latlngs) ? this._latlngs : this._latlngs[0];
8248	},
8249
8250	// recursively convert latlngs input into actual LatLng instances; calculate bounds along the way
8251	_convertLatLngs: function (latlngs) {
8252		var result = [],
8253		    flat = isFlat(latlngs);
8254
8255		for (var i = 0, len = latlngs.length; i < len; i++) {
8256			if (flat) {
8257				result[i] = toLatLng(latlngs[i]);
8258				this._bounds.extend(result[i]);
8259			} else {
8260				result[i] = this._convertLatLngs(latlngs[i]);
8261			}
8262		}
8263
8264		return result;
8265	},
8266
8267	_project: function () {
8268		var pxBounds = new Bounds();
8269		this._rings = [];
8270		this._projectLatlngs(this._latlngs, this._rings, pxBounds);
8271
8272		var w = this._clickTolerance(),
8273		    p = new Point(w, w);
8274
8275		if (this._bounds.isValid() && pxBounds.isValid()) {
8276			pxBounds.min._subtract(p);
8277			pxBounds.max._add(p);
8278			this._pxBounds = pxBounds;
8279		}
8280	},
8281
8282	// recursively turns latlngs into a set of rings with projected coordinates
8283	_projectLatlngs: function (latlngs, result, projectedBounds) {
8284		var flat = latlngs[0] instanceof LatLng,
8285		    len = latlngs.length,
8286		    i, ring;
8287
8288		if (flat) {
8289			ring = [];
8290			for (i = 0; i < len; i++) {
8291				ring[i] = this._map.latLngToLayerPoint(latlngs[i]);
8292				projectedBounds.extend(ring[i]);
8293			}
8294			result.push(ring);
8295		} else {
8296			for (i = 0; i < len; i++) {
8297				this._projectLatlngs(latlngs[i], result, projectedBounds);
8298			}
8299		}
8300	},
8301
8302	// clip polyline by renderer bounds so that we have less to render for performance
8303	_clipPoints: function () {
8304		var bounds = this._renderer._bounds;
8305
8306		this._parts = [];
8307		if (!this._pxBounds || !this._pxBounds.intersects(bounds)) {
8308			return;
8309		}
8310
8311		if (this.options.noClip) {
8312			this._parts = this._rings;
8313			return;
8314		}
8315
8316		var parts = this._parts,
8317		    i, j, k, len, len2, segment, points;
8318
8319		for (i = 0, k = 0, len = this._rings.length; i < len; i++) {
8320			points = this._rings[i];
8321
8322			for (j = 0, len2 = points.length; j < len2 - 1; j++) {
8323				segment = clipSegment(points[j], points[j + 1], bounds, j, true);
8324
8325				if (!segment) { continue; }
8326
8327				parts[k] = parts[k] || [];
8328				parts[k].push(segment[0]);
8329
8330				// if segment goes out of screen, or it's the last one, it's the end of the line part
8331				if ((segment[1] !== points[j + 1]) || (j === len2 - 2)) {
8332					parts[k].push(segment[1]);
8333					k++;
8334				}
8335			}
8336		}
8337	},
8338
8339	// simplify each clipped part of the polyline for performance
8340	_simplifyPoints: function () {
8341		var parts = this._parts,
8342		    tolerance = this.options.smoothFactor;
8343
8344		for (var i = 0, len = parts.length; i < len; i++) {
8345			parts[i] = simplify(parts[i], tolerance);
8346		}
8347	},
8348
8349	_update: function () {
8350		if (!this._map) { return; }
8351
8352		this._clipPoints();
8353		this._simplifyPoints();
8354		this._updatePath();
8355	},
8356
8357	_updatePath: function () {
8358		this._renderer._updatePoly(this);
8359	},
8360
8361	// Needed by the `Canvas` renderer for interactivity
8362	_containsPoint: function (p, closed) {
8363		var i, j, k, len, len2, part,
8364		    w = this._clickTolerance();
8365
8366		if (!this._pxBounds || !this._pxBounds.contains(p)) { return false; }
8367
8368		// hit detection for polylines
8369		for (i = 0, len = this._parts.length; i < len; i++) {
8370			part = this._parts[i];
8371
8372			for (j = 0, len2 = part.length, k = len2 - 1; j < len2; k = j++) {
8373				if (!closed && (j === 0)) { continue; }
8374
8375				if (pointToSegmentDistance(p, part[k], part[j]) <= w) {
8376					return true;
8377				}
8378			}
8379		}
8380		return false;
8381	}
8382});
8383
8384// @factory L.polyline(latlngs: LatLng[], options?: Polyline options)
8385// Instantiates a polyline object given an array of geographical points and
8386// optionally an options object. You can create a `Polyline` object with
8387// multiple separate lines (`MultiPolyline`) by passing an array of arrays
8388// of geographic points.
8389function polyline(latlngs, options) {
8390	return new Polyline(latlngs, options);
8391}
8392
8393// Retrocompat. Allow plugins to support Leaflet versions before and after 1.1.
8394Polyline._flat = _flat;
8395
8396/*
8397 * @class Polygon
8398 * @aka L.Polygon
8399 * @inherits Polyline
8400 *
8401 * A class for drawing polygon overlays on a map. Extends `Polyline`.
8402 *
8403 * 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.
8404 *
8405 *
8406 * @example
8407 *
8408 * ```js
8409 * // create a red polygon from an array of LatLng points
8410 * var latlngs = [[37, -109.05],[41, -109.03],[41, -102.05],[37, -102.04]];
8411 *
8412 * var polygon = L.polygon(latlngs, {color: 'red'}).addTo(map);
8413 *
8414 * // zoom the map to the polygon
8415 * map.fitBounds(polygon.getBounds());
8416 * ```
8417 *
8418 * 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:
8419 *
8420 * ```js
8421 * var latlngs = [
8422 *   [[37, -109.05],[41, -109.03],[41, -102.05],[37, -102.04]], // outer ring
8423 *   [[37.29, -108.58],[40.71, -108.58],[40.71, -102.50],[37.29, -102.50]] // hole
8424 * ];
8425 * ```
8426 *
8427 * Additionally, you can pass a multi-dimensional array to represent a MultiPolygon shape.
8428 *
8429 * ```js
8430 * var latlngs = [
8431 *   [ // first polygon
8432 *     [[37, -109.05],[41, -109.03],[41, -102.05],[37, -102.04]], // outer ring
8433 *     [[37.29, -108.58],[40.71, -108.58],[40.71, -102.50],[37.29, -102.50]] // hole
8434 *   ],
8435 *   [ // second polygon
8436 *     [[41, -111.03],[45, -111.04],[45, -104.05],[41, -104.05]]
8437 *   ]
8438 * ];
8439 * ```
8440 */
8441
8442var Polygon = Polyline.extend({
8443
8444	options: {
8445		fill: true
8446	},
8447
8448	isEmpty: function () {
8449		return !this._latlngs.length || !this._latlngs[0].length;
8450	},
8451
8452	getCenter: function () {
8453		// throws error when not yet added to map as this center calculation requires projected coordinates
8454		if (!this._map) {
8455			throw new Error('Must add layer to map before using getCenter()');
8456		}
8457
8458		var i, j, p1, p2, f, area, x, y, center,
8459		    points = this._rings[0],
8460		    len = points.length;
8461
8462		if (!len) { return null; }
8463
8464		// polygon centroid algorithm; only uses the first ring if there are multiple
8465
8466		area = x = y = 0;
8467
8468		for (i = 0, j = len - 1; i < len; j = i++) {
8469			p1 = points[i];
8470			p2 = points[j];
8471
8472			f = p1.y * p2.x - p2.y * p1.x;
8473			x += (p1.x + p2.x) * f;
8474			y += (p1.y + p2.y) * f;
8475			area += f * 3;
8476		}
8477
8478		if (area === 0) {
8479			// Polygon is so small that all points are on same pixel.
8480			center = points[0];
8481		} else {
8482			center = [x / area, y / area];
8483		}
8484		return this._map.layerPointToLatLng(center);
8485	},
8486
8487	_convertLatLngs: function (latlngs) {
8488		var result = Polyline.prototype._convertLatLngs.call(this, latlngs),
8489		    len = result.length;
8490
8491		// remove last point if it equals first one
8492		if (len >= 2 && result[0] instanceof LatLng && result[0].equals(result[len - 1])) {
8493			result.pop();
8494		}
8495		return result;
8496	},
8497
8498	_setLatLngs: function (latlngs) {
8499		Polyline.prototype._setLatLngs.call(this, latlngs);
8500		if (isFlat(this._latlngs)) {
8501			this._latlngs = [this._latlngs];
8502		}
8503	},
8504
8505	_defaultShape: function () {
8506		return isFlat(this._latlngs[0]) ? this._latlngs[0] : this._latlngs[0][0];
8507	},
8508
8509	_clipPoints: function () {
8510		// polygons need a different clipping algorithm so we redefine that
8511
8512		var bounds = this._renderer._bounds,
8513		    w = this.options.weight,
8514		    p = new Point(w, w);
8515
8516		// increase clip padding by stroke width to avoid stroke on clip edges
8517		bounds = new Bounds(bounds.min.subtract(p), bounds.max.add(p));
8518
8519		this._parts = [];
8520		if (!this._pxBounds || !this._pxBounds.intersects(bounds)) {
8521			return;
8522		}
8523
8524		if (this.options.noClip) {
8525			this._parts = this._rings;
8526			return;
8527		}
8528
8529		for (var i = 0, len = this._rings.length, clipped; i < len; i++) {
8530			clipped = clipPolygon(this._rings[i], bounds, true);
8531			if (clipped.length) {
8532				this._parts.push(clipped);
8533			}
8534		}
8535	},
8536
8537	_updatePath: function () {
8538		this._renderer._updatePoly(this, true);
8539	},
8540
8541	// Needed by the `Canvas` renderer for interactivity
8542	_containsPoint: function (p) {
8543		var inside = false,
8544		    part, p1, p2, i, j, k, len, len2;
8545
8546		if (!this._pxBounds || !this._pxBounds.contains(p)) { return false; }
8547
8548		// ray casting algorithm for detecting if point is in polygon
8549		for (i = 0, len = this._parts.length; i < len; i++) {
8550			part = this._parts[i];
8551
8552			for (j = 0, len2 = part.length, k = len2 - 1; j < len2; k = j++) {
8553				p1 = part[j];
8554				p2 = part[k];
8555
8556				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)) {
8557					inside = !inside;
8558				}
8559			}
8560		}
8561
8562		// also check if it's on polygon stroke
8563		return inside || Polyline.prototype._containsPoint.call(this, p, true);
8564	}
8565
8566});
8567
8568
8569// @factory L.polygon(latlngs: LatLng[], options?: Polyline options)
8570function polygon(latlngs, options) {
8571	return new Polygon(latlngs, options);
8572}
8573
8574/*
8575 * @class GeoJSON
8576 * @aka L.GeoJSON
8577 * @inherits FeatureGroup
8578 *
8579 * Represents a GeoJSON object or an array of GeoJSON objects. Allows you to parse
8580 * GeoJSON data and display it on the map. Extends `FeatureGroup`.
8581 *
8582 * @example
8583 *
8584 * ```js
8585 * L.geoJSON(data, {
8586 * 	style: function (feature) {
8587 * 		return {color: feature.properties.color};
8588 * 	}
8589 * }).bindPopup(function (layer) {
8590 * 	return layer.feature.properties.description;
8591 * }).addTo(map);
8592 * ```
8593 */
8594
8595var GeoJSON = FeatureGroup.extend({
8596
8597	/* @section
8598	 * @aka GeoJSON options
8599	 *
8600	 * @option pointToLayer: Function = *
8601	 * A `Function` defining how GeoJSON points spawn Leaflet layers. It is internally
8602	 * called when data is added, passing the GeoJSON point feature and its `LatLng`.
8603	 * The default is to spawn a default `Marker`:
8604	 * ```js
8605	 * function(geoJsonPoint, latlng) {
8606	 * 	return L.marker(latlng);
8607	 * }
8608	 * ```
8609	 *
8610	 * @option style: Function = *
8611	 * A `Function` defining the `Path options` for styling GeoJSON lines and polygons,
8612	 * called internally when data is added.
8613	 * The default value is to not override any defaults:
8614	 * ```js
8615	 * function (geoJsonFeature) {
8616	 * 	return {}
8617	 * }
8618	 * ```
8619	 *
8620	 * @option onEachFeature: Function = *
8621	 * A `Function` that will be called once for each created `Feature`, after it has
8622	 * been created and styled. Useful for attaching events and popups to features.
8623	 * The default is to do nothing with the newly created layers:
8624	 * ```js
8625	 * function (feature, layer) {}
8626	 * ```
8627	 *
8628	 * @option filter: Function = *
8629	 * A `Function` that will be used to decide whether to include a feature or not.
8630	 * The default is to include all features:
8631	 * ```js
8632	 * function (geoJsonFeature) {
8633	 * 	return true;
8634	 * }
8635	 * ```
8636	 * Note: dynamically changing the `filter` option will have effect only on newly
8637	 * added data. It will _not_ re-evaluate already included features.
8638	 *
8639	 * @option coordsToLatLng: Function = *
8640	 * A `Function` that will be used for converting GeoJSON coordinates to `LatLng`s.
8641	 * The default is the `coordsToLatLng` static method.
8642	 */
8643
8644	initialize: function (geojson, options) {
8645		setOptions(this, options);
8646
8647		this._layers = {};
8648
8649		if (geojson) {
8650			this.addData(geojson);
8651		}
8652	},
8653
8654	// @method addData( <GeoJSON> data ): this
8655	// Adds a GeoJSON object to the layer.
8656	addData: function (geojson) {
8657		var features = isArray(geojson) ? geojson : geojson.features,
8658		    i, len, feature;
8659
8660		if (features) {
8661			for (i = 0, len = features.length; i < len; i++) {
8662				// only add this if geometry or geometries are set and not null
8663				feature = features[i];
8664				if (feature.geometries || feature.geometry || feature.features || feature.coordinates) {
8665					this.addData(feature);
8666				}
8667			}
8668			return this;
8669		}
8670
8671		var options = this.options;
8672
8673		if (options.filter && !options.filter(geojson)) { return this; }
8674
8675		var layer = geometryToLayer(geojson, options);
8676		if (!layer) {
8677			return this;
8678		}
8679		layer.feature = asFeature(geojson);
8680
8681		layer.defaultOptions = layer.options;
8682		this.resetStyle(layer);
8683
8684		if (options.onEachFeature) {
8685			options.onEachFeature(geojson, layer);
8686		}
8687
8688		return this.addLayer(layer);
8689	},
8690
8691	// @method resetStyle( <Path>
8691 layer ): this
8692	// Resets the given vector layer's style to the original GeoJSON style, useful for resetting style after hover events.
8693	resetStyle: function (layer) {
8694		// reset any custom styles
8695		layer.options = extend({}, layer.defaultOptions);
8696		this._setLayerStyle(layer, this.options.style);
8697		return this;
8698	},
8699
8700	// @method setStyle( <Function> style ): this
8701	// Changes styles of GeoJSON vector layers with the given style function.
8702	setStyle: function (style) {
8703		return this.eachLayer(function (layer) {
8704			this._setLayerStyle(layer, style);
8705		}, this);
8706	},
8707
8708	_setLayerStyle: function (layer, style) {
8709		if (typeof style === 'function') {
8710			style = style(layer.feature);
8711		}
8712		if (layer.setStyle) {
8713			layer.setStyle(style);
8714		}
8715	}
8716});
8717
8718// @section
8719// There are several static functions which can be called without instantiating L.GeoJSON:
8720
8721// @function geometryToLayer(featureData: Object, options?: GeoJSON options): Layer
8722// Creates a `Layer` from a given GeoJSON feature. Can use a custom
8723// [`pointToLayer`](#geojson-pointtolayer) and/or [`coordsToLatLng`](#geojson-coordstolatlng)
8724// functions if provided as options.
8725function geometryToLayer(geojson, options) {
8726
8727	var geometry = geojson.type === 'Feature' ? geojson.geometry : geojson,
8728	    coords = geometry ? geometry.coordinates : null,
8729	    layers = [],
8730	    pointToLayer = options && options.pointToLayer,
8731	    _coordsToLatLng = options && options.coordsToLatLng || coordsToLatLng,
8732	    latlng, latlngs, i, len;
8733
8734	if (!coords && !geometry) {
8735		return null;
8736	}
8737
8738	switch (geometry.type) {
8739	case 'Point':
8740		latlng = _coordsToLatLng(coords);
8741		return pointToLayer ? pointToLayer(geojson, latlng) : new Marker(latlng);
8742
8743	case 'MultiPoint':
8744		for (i = 0, len = coords.length; i < len; i++) {
8745			latlng = _coordsToLatLng(coords[i]);
8746			layers.push(pointToLayer ? pointToLayer(geojson, latlng) : new Marker(latlng));
8747		}
8748		return new FeatureGroup(layers);
8749
8750	case 'LineString':
8751	case 'MultiLineString':
8752		latlngs = coordsToLatLngs(coords, geometry.type === 'LineString' ? 0 : 1, _coordsToLatLng);
8753		return new Polyline(latlngs, options);
8754
8755	case 'Polygon':
8756	case 'MultiPolygon':
8757		latlngs = coordsToLatLngs(coords, geometry.type === 'Polygon' ? 1 : 2, _coordsToLatLng);
8758		return new Polygon(latlngs, options);
8759
8760	case 'GeometryCollection':
8761		for (i = 0, len = geometry.geometries.length; i < len; i++) {
8762			var layer = geometryToLayer({
8763				geometry: geometry.geometries[i],
8764				type: 'Feature',
8765				properties: geojson.properties
8766			}, options);
8767
8768			if (layer) {
8769				layers.push(layer);
8770			}
8771		}
8772		return new FeatureGroup(layers);
8773
8774	default:
8775		throw new Error('Invalid GeoJSON object.');
8776	}
8777}
8778
8779// @function coordsToLatLng(coords: Array): LatLng
8780// Creates a `LatLng` object from an array of 2 numbers (longitude, latitude)
8781// or 3 numbers (longitude, latitude, altitude) used in GeoJSON for points.
8782function coordsToLatLng(coords) {
8783	return new LatLng(coords[1], coords[0], coords[2]);
8784}
8785
8786// @function coordsToLatLngs(coords: Array, levelsDeep?: Number, coordsToLatLng?: Function): Array
8787// Creates a multidimensional array of `LatLng`s from a GeoJSON coordinates array.
8788// `levelsDeep` specifies the nesting level (0 is for an array of points, 1 for an array of arrays of points, etc., 0 by default).
8789// Can use a custom [`coordsToLatLng`](#geojson-coordstolatlng) function.
8790function coordsToLatLngs(coords, levelsDeep, _coordsToLatLng) {
8791	var latlngs = [];
8792
8793	for (var i = 0, len = coords.length, latlng; i < len; i++) {
8794		latlng = levelsDeep ?
8795			coordsToLatLngs(coords[i], levelsDeep - 1, _coordsToLatLng) :
8796			(_coordsToLatLng || coordsToLatLng)(coords[i]);
8797
8798		latlngs.push(latlng);
8799	}
8800
8801	return latlngs;
8802}
8803
8804// @function latLngToCoords(latlng: LatLng, precision?: Number): Array
8805// Reverse of [`coordsToLatLng`](#geojson-coordstolatlng)
8806function latLngToCoords(latlng, precision) {
8807	precision = typeof precision === 'number' ? precision : 6;
8808	return latlng.alt !== undefined ?
vendor: 12,134 bytes, lines 8809-9245
8809		[formatNum(latlng.lng, precision), formatNum(latlng.lat, precision), formatNum(latlng.alt, precision)] :
8810		[formatNum(latlng.lng, precision), formatNum(latlng.lat, precision)];
8811}
8812
8813// @function latLngsToCoords(latlngs: Array, levelsDeep?: Number, closed?: Boolean): Array
8814// Reverse of [`coordsToLatLngs`](#geojson-coordstolatlngs)
8815// `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.
8816function latLngsToCoords(latlngs, levelsDeep, closed, precision) {
8817	var coords = [];
8818
8819	for (var i = 0, len = latlngs.length; i < len; i++) {
8820		coords.push(levelsDeep ?
8821			latLngsToCoords(latlngs[i], levelsDeep - 1, closed, precision) :
8822			latLngToCoords(latlngs[i], precision));
8823	}
8824
8825	if (!levelsDeep && closed) {
8826		coords.push(coords[0]);
8827	}
8828
8829	return coords;
8830}
8831
8832function getFeature(layer, newGeometry) {
8833	return layer.feature ?
8834		extend({}, layer.feature, {geometry: newGeometry}) :
8835		asFeature(newGeometry);
8836}
8837
8838// @function asFeature(geojson: Object): Object
8839// Normalize GeoJSON geometries/features into GeoJSON features.
8840function asFeature(geojson) {
8841	if (geojson.type === 'Feature' || geojson.type === 'FeatureCollection') {
8842		return geojson;
8843	}
8844
8845	return {
8846		type: 'Feature',
8847		properties: {},
8848		geometry: geojson
8849	};
8850}
8851
8852var PointToGeoJSON = {
8853	toGeoJSON: function (precision) {
8854		return getFeature(this, {
8855			type: 'Point',
8856			coordinates: latLngToCoords(this.getLatLng(), precision)
8857		});
8858	}
8859};
8860
8861// @namespace Marker
8862// @method toGeoJSON(): Object
8863// Returns a [`GeoJSON`](http://en.wikipedia.org/wiki/GeoJSON) representation of the marker (as a GeoJSON `Point` Feature).
8864Marker.include(PointToGeoJSON);
8865
8866// @namespace CircleMarker
8867// @method toGeoJSON(): Object
8868// Returns a [`GeoJSON`](http://en.wikipedia.org/wiki/GeoJSON) representation of the circle marker (as a GeoJSON `Point` Feature).
8869Circle.include(PointToGeoJSON);
8870CircleMarker.include(PointToGeoJSON);
8871
8872
8873// @namespace Polyline
8874// @method toGeoJSON(): Object
8875// Returns a [`GeoJSON`](http://en.wikipedia.org/wiki/GeoJSON) representation of the polyline (as a GeoJSON `LineString` or `MultiLineString` Feature).
8876Polyline.include({
8877	toGeoJSON: function (precision) {
8878		var multi = !isFlat(this._latlngs);
8879
8880		var coords = latLngsToCoords(this._latlngs, multi ? 1 : 0, false, precision);
8881
8882		return getFeature(this, {
8883			type: (multi ? 'Multi' : '') + 'LineString',
8884			coordinates: coords
8885		});
8886	}
8887});
8888
8889// @namespace Polygon
8890// @method toGeoJSON(): Object
8891// Returns a [`GeoJSON`](http://en.wikipedia.org/wiki/GeoJSON) representation of the polygon (as a GeoJSON `Polygon` or `MultiPolygon` Feature).
8892Polygon.include({
8893	toGeoJSON: function (precision) {
8894		var holes = !isFlat(this._latlngs),
8895		    multi = holes && !isFlat(this._latlngs[0]);
8896
8897		var coords = latLngsToCoords(this._latlngs, multi ? 2 : holes ? 1 : 0, true, precision);
8898
8899		if (!holes) {
8900			coords = [coords];
8901		}
8902
8903		return getFeature(this, {
8904			type: (multi ? 'Multi' : '') + 'Polygon',
8905			coordinates: coords
8906		});
8907	}
8908});
8909
8910
8911// @namespace LayerGroup
8912LayerGroup.include({
8913	toMultiPoint: function (precision) {
8914		var coords = [];
8915
8916		this.eachLayer(function (layer) {
8917			coords.push(layer.toGeoJSON(precision).geometry.coordinates);
8918		});
8919
8920		return getFeature(this, {
8921			type: 'MultiPoint',
8922			coordinates: coords
8923		});
8924	},
8925
8926	// @method toGeoJSON(): Object
8927	// Returns a [`GeoJSON`](http://en.wikipedia.org/wiki/GeoJSON) representation of the layer group (as a GeoJSON `FeatureCollection`, `GeometryCollection`, or `MultiPoint`).
8928	toGeoJSON: function (precision) {
8929
8930		var type = this.feature && this.feature.geometry && this.feature.geometry.type;
8931
8932		if (type === 'MultiPoint') {
8933			return this.toMultiPoint(precision);
8934		}
8935
8936		var isGeometryCollection = type === 'GeometryCollection',
8937		    jsons = [];
8938
8939		this.eachLayer(function (layer) {
8940			if (layer.toGeoJSON) {
8941				var json = layer.toGeoJSON(precision);
8942				if (isGeometryCollection) {
8943					jsons.push(json.geometry);
8944				} else {
8945					var feature = asFeature(json);
8946					// Squash nested feature collections
8947					if (feature.type === 'FeatureCollection') {
8948						jsons.push.apply(jsons, feature.features);
8949					} else {
8950						jsons.push(feature);
8951					}
8952				}
8953			}
8954		});
8955
8956		if (isGeometryCollection) {
8957			return getFeature(this, {
8958				geometries: jsons,
8959				type: 'GeometryCollection'
8960			});
8961		}
8962
8963		return {
8964			type: 'FeatureCollection',
8965			features: jsons
8966		};
8967	}
8968});
8969
8970// @namespace GeoJSON
8971// @factory L.geoJSON(geojson?: Object, options?: GeoJSON options)
8972// Creates a GeoJSON layer. Optionally accepts an object in
8973// [GeoJSON format](https://tools.ietf.org/html/rfc7946) to display on the map
8974// (you can alternatively add it later with `addData` method) and an `options` object.
8975function geoJSON(geojson, options) {
8976	return new GeoJSON(geojson, options);
8977}
8978
8979// Backward compatibility.
8980var geoJson = geoJSON;
8981
8982/*
8983 * @class ImageOverlay
8984 * @aka L.ImageOverlay
8985 * @inherits Interactive layer
8986 *
8987 * Used to load and display a single image over specific bounds of the map. Extends `Layer`.
8988 *
8989 * @example
8990 *
8991 * ```js
8992 * var imageUrl = 'http://www.lib.utexas.edu/maps/historical/newark_nj_1922.jpg',
8993 * 	imageBounds = [[40.712216, -74.22655], [40.773941, -74.12544]];
8994 * L.imageOverlay(imageUrl, imageBounds).addTo(map);
8995 * ```
8996 */
8997
8998var ImageOverlay = Layer.extend({
8999
9000	// @section
9001	// @aka ImageOverlay options
9002	options: {
9003		// @option opacity: Number = 1.0
9004		// The opacity of the image overlay.
9005		opacity: 1,
9006
9007		// @option alt: String = ''
9008		// Text for the `alt` attribute of the image (useful for accessibility).
9009		alt: '',
9010
9011		// @option interactive: Boolean = false
9012		// If `true`, the image overlay will emit [mouse events](#interactive-layer) when clicked or hovered.
9013		interactive: false,
9014
9015		// @option crossOrigin: Boolean|String = false
9016		// Whether the crossOrigin attribute will be added to the image.
9017		// If a String is provided, the image will have its crossOrigin attribute set to the String provided. This is needed if you want to access image pixel data.
9018		// Refer to [CORS Settings](https://developer.mozilla.org/en-US/docs/Web/HTML/CORS_settings_attributes) for valid String values.
9019		crossOrigin: false,
9020
9021		// @option errorOverlayUrl: String = ''
9022		// URL to the overlay image to show in place of the overlay that failed to load.
9023		errorOverlayUrl: '',
9024
9025		// @option zIndex: Number = 1
9026		// The explicit [zIndex](https://developer.mozilla.org/docs/Web/CSS/CSS_Positioning/Understanding_z_index) of the overlay layer.
9027		zIndex: 1,
9028
9029		// @option className: String = ''
9030		// A custom class name to assign to the image. Empty by default.
9031		className: ''
9032	},
9033
9034	initialize: function (url, bounds, options) { // (String, LatLngBounds, Object)
9035		this._url = url;
9036		this._bounds = toLatLngBounds(bounds);
9037
9038		setOptions(this, options);
9039	},
9040
9041	onAdd: function () {
9042		if (!this._image) {
9043			this._initImage();
9044
9045			if (this.options.opacity < 1) {
9046				this._updateOpacity();
9047			}
9048		}
9049
9050		if (this.options.interactive) {
9051			addClass(this._image, 'leaflet-interactive');
9052			this.addInteractiveTarget(this._image);
9053		}
9054
9055		this.getPane().appendChild(this._image);
9056		this._reset();
9057	},
9058
9059	onRemove: function () {
9060		remove(this._image);
9061		if (this.options.interactive) {
9062			this.removeInteractiveTarget(this._image);
9063		}
9064	},
9065
9066	// @method setOpacity(opacity: Number): this
9067	// Sets the opacity of the overlay.
9068	setOpacity: function (opacity) {
9069		this.options.opacity = opacity;
9070
9071		if (this._image) {
9072			this._updateOpacity();
9073		}
9074		return this;
9075	},
9076
9077	setStyle: function (styleOpts) {
9078		if (styleOpts.opacity) {
9079			this.setOpacity(styleOpts.opacity);
9080		}
9081		return this;
9082	},
9083
9084	// @method bringToFront(): this
9085	// Brings the layer to the top of all overlays.
9086	bringToFront: function () {
9087		if (this._map) {
9088			toFront(this._image);
9089		}
9090		return this;
9091	},
9092
9093	// @method bringToBack(): this
9094	// Brings the layer to the bottom of all overlays.
9095	bringToBack: function () {
9096		if (this._map) {
9097			toBack(this._image);
9098		}
9099		return this;
9100	},
9101
9102	// @method setUrl(url: String): this
9103	// Changes the URL of the image.
9104	setUrl: function (url) {
9105		this._url = url;
9106
9107		if (this._image) {
9108			this._image.src = url;
9109		}
9110		return this;
9111	},
9112
9113	// @method setBounds(bounds: LatLngBounds): this
9114	// Update the bounds that this ImageOverlay covers
9115	setBounds: function (bounds) {
9116		this._bounds = toLatLngBounds(bounds);
9117
9118		if (this._map) {
9119			this._reset();
9120		}
9121		return this;
9122	},
9123
9124	getEvents: function () {
9125		var events = {
9126			zoom: this._reset,
9127			viewreset: this._reset
9128		};
9129
9130		if (this._zoomAnimated) {
9131			events.zoomanim = this._animateZoom;
9132		}
9133
9134		return events;
9135	},
9136
9137	// @method setZIndex(value: Number): this
9138	// Changes the [zIndex](#imageoverlay-zindex) of the image overlay.
9139	setZIndex: function (value) {
9140		this.options.zIndex = value;
9141		this._updateZIndex();
9142		return this;
9143	},
9144
9145	// @method getBounds(): LatLngBounds
9146	// Get the bounds that this ImageOverlay covers
9147	getBounds: function () {
9148		return this._bounds;
9149	},
9150
9151	// @method getElement(): HTMLElement
9152	// Returns the instance of [`HTMLImageElement`](https://developer.mozilla.org/docs/Web/API/HTMLImageElement)
9153	// used by this overlay.
9154	getElement: function () {
9155		return this._image;
9156	},
9157
9158	_initImage: function () {
9159		var wasElementSupplied = this._url.tagName === 'IMG';
9160		var img = this._image = wasElementSupplied ? this._url : create$1('img');
9161
9162		addClass(img, 'leaflet-image-layer');
9163		if (this._zoomAnimated) { addClass(img, 'leaflet-zoom-animated'); }
9164		if (this.options.className) { addClass(img, this.options.className); }
9165
9166		img.onselectstart = falseFn;
9167		img.onmousemove = falseFn;
9168
9169		// @event load: Event
9170		// Fired when the ImageOverlay layer has loaded its image
9171		img.onload = bind(this.fire, this, 'load');
9172		img.onerror = bind(this._overlayOnError, this, 'error');
9173
9174		if (this.options.crossOrigin || this.options.crossOrigin === '') {
9175			img.crossOrigin = this.options.crossOrigin === true ? '' : this.options.crossOrigin;
9176		}
9177
9178		if (this.options.zIndex) {
9179			this._updateZIndex();
9180		}
9181
9182		if (wasElementSupplied) {
9183			this._url = img.src;
9184			return;
9185		}
9186
9187		img.src = this._url;
9188		img.alt = this.options.alt;
9189	},
9190
9191	_animateZoom: function (e) {
9192		var scale = this._map.getZoomScale(e.zoom),
9193		    offset = this._map._latLngBoundsToNewLayerBounds(this._bounds, e.zoom, e.center).min;
9194
9195		setTransform(this._image, offset, scale);
9196	},
9197
9198	_reset: function () {
9199		var image = this._image,
9200		    bounds = new Bounds(
9201		        this._map.latLngToLayerPoint(this._bounds.getNorthWest()),
9202		        this._map.latLngToLayerPoint(this._bounds.getSouthEast())),
9203		    size = bounds.getSize();
9204
9205		setPosition(image, bounds.min);
9206
9207		image.style.width  = size.x + 'px';
9208		image.style.height = size.y + 'px';
9209	},
9210
9211	_updateOpacity: function () {
9212		setOpacity(this._image, this.options.opacity);
9213	},
9214
9215	_updateZIndex: function () {
9216		if (this._image && this.options.zIndex !== undefined && this.options.zIndex !== null) {
9217			this._image.style.zIndex = this.options.zIndex;
9218		}
9219	},
9220
9221	_overlayOnError: function () {
9222		// @event error: Event
9223		// Fired when the ImageOverlay layer fails to load its image
9224		this.fire('error');
9225
9226		var errorUrl = this.options.errorOverlayUrl;
9227		if (errorUrl && this._url !== errorUrl) {
9228			this._url = errorUrl;
9229			this._image.src = errorUrl;
9230		}
9231	}
9232});
9233
9234// @factory L.imageOverlay(imageUrl: String, bounds: LatLngBounds, options?: ImageOverlay options)
9235// Instantiates an image overlay object given the URL of the image and the
9236// geographical bounds it is tied to.
9237var imageOverlay = function (url, bounds, options) {
9238	return new ImageOverlay(url, bounds, options);
9239};
9240
9241/*
9242 * @class VideoOverlay
9243 * @aka L.VideoOverlay
9244 * @inherits ImageOverlay
9245 *
9246 * Used to load and display a video player over specific bounds of the map. Extends `ImageOverlay`.
9247 *
9248 * A video overlay uses the [`<video>`](https://developer.mozilla.org/docs/Web/HTML/Element/video)
9249 * HTML5 element.
9250 *
9251 * @example
9252 *
9253 * ```js
9254 * var videoUrl = 'https://www.mapbox.com/bites/00188/patricia_nasa.webm',
9255 * 	videoBounds = [[ 32, -130], [ 13, -100]];
9256 * L.videoOverlay(videoUrl, videoBounds ).addTo(map);
9257 * ```
9258 */
9259
9260var VideoOverlay = ImageOverlay.extend({
9261
9262	// @section
9263	// @aka VideoOverlay options
9264	options: {
9265		// @option autoplay: Boolean = true
9266		// Whether the video starts playing automatically when loaded.
9267		autoplay: true,
9268
9269		// @option loop: Boolean = true
9270		// Whether the video will loop back to the beginning when played.
9271		loop: true
9272	},
9273
9274	_initImage: function () {
9275		var wasElementSupplied = this._url.tagName === 'VIDEO';
9276		var vid = this._image = wasElementSupplied ? this._url : create$1('video');
9277
9278		addClass(vid, 'leaflet-image-layer');
9279		if (this._zoomAnimated) { addClass(vid, 'leaflet-zoom-animated'); }
9280
9281		vid.onselectstart = falseFn;
9282		vid.onmousemove = falseFn;
9283
9284		// @event load: Event
9285		// Fired when the video has finished loading the first frame
9286		vid.onloadeddata = bind(this.fire, this, 'load');
9287
9288		if (wasElementSupplied) {
9289			var sourceElements = vid.getElementsByTagName('source');
9290			var sources = [];
9291			for (var j = 0; j < sourceElements.length; j++) {
9292				sources.push(sourceElements[j].src);
9293			}
9294
9295			this._url = (sourceElements.length > 0) ? sources : [vid.src];
9296			return;
9297		}
9298
9299		if (!isArray(this._url)) { this._url = [this._url]; }
9300
9301		vid.autoplay = !!this.options.autoplay;
9302		vid.loop = !!this.options.loop;
9303		for (var i = 0; i < this._url.length; i++) {
9304			var source = create$1('source');
9305			source.src = this._url[i];
9306			vid.appendChild(source);
9307		}
9308	}
9309
9310	// @method getElement(): HTMLVideoElement
9311	// Returns the instance of [`HTMLVideoElement`](https://developer.mozilla.org/docs/Web/API/HTMLVideoElement)
9312	// used by this overlay.
9313});
9314
9315
9316// @factory L.videoOverlay(video: String|Array|HTMLVideoElement, bounds: LatLngBounds, options?: VideoOverlay options)
9317// Instantiates an image overlay object given the URL of the video (or array of URLs, or even a video element) and the
9318// geographical bounds it is tied to.
9319
9320function videoOverlay(video, bounds, options) {
9321	return new VideoOverlay(video, bounds, options);
9322}
9323
9324/*
9325 * @class DivOverlay
9326 * @inherits Layer
9327 * @aka L.DivOverlay
9328 * Base model for L.Popup and L.Tooltip. Inherit from it for custom popup like plugins.
9329 */
9330
9331// @namespace DivOverlay
9332var DivOverlay = Layer.extend({
9333
9334	// @section
9335	// @aka DivOverlay options
9336	options: {
9337		// @option offset: Point = Point(0, 7)
9338		// The offset of the popup position. Useful to control the anchor
9339		// of the popup when opening it on some overlays.
9340		offset: [0, 7],
9341
9342		// @option className: String = ''
9343		// A custom CSS class name to assign to the popup.
9344		className: '',
9345
9346		// @option pane: String = 'popupPane'
9347		// `Map pane` where the popup will be added.
9348		pane: 'popupPane'
9349	},
9350
9351	initialize: function (options, source) {
9352		setOptions(this, options);
9353
9354		this._source = source;
9355	},
9356
9357	onAdd: function (map) {
9358		this._zoomAnimated = map._zoomAnimated;
9359
9360		if (!this._container) {
9361			this._initLayout();
9362		}
9363
9364		if (map._fadeAnimated) {
9365			setOpacity(this._container, 0);
9366		}
9367
9368		clearTimeout(this._removeTimeout);
9369		this.getPane().appendChild(this._container);
9370		this.update();
9371
9372		if (map._fadeAnimated) {
9373			setOpacity(this._container, 1);
9374		}
9375
9376		this.bringToFront();
9377	},
9378
9379	onRemove: function (map) {
9380		if (map._fadeAnimated) {
9381			setOpacity(this._container, 0);
9382			this._removeTimeout = setTimeout(bind(remove, undefined, this._container), 200);
9383		} else {
9384			remove(this._container);
9385		}
9386	},
9387
9388	// @namespace Popup
9389	// @method getLatLng: LatLng
9390	// Returns the geographical point of popup.
9391	getLatLng: function () {
9392		return this._latlng;
9393	},
9394
9395	// @method setLatLng(latlng: LatLng): this
9396	// Sets the geographical point where the popup will open.
9397	setLatLng: function (latlng) {
9398		this._latlng = toLatLng(latlng);
9399		if (this._map) {
9400			this._updatePosition();
9401			this._adjustPan();
9402		}
9403		return this;
9404	},
9405
9406	// @method getContent: String|HTMLElement
9407	// Returns the content of the popup.
9408	getContent: function () {
9409		return this._content;
9410	},
9411
9412	// @method setContent(htmlContent: String|HTMLElement|Function): this
9413	// 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.
9414	setContent: function (content) {
9415		this._content = content;
9416		this.update();
9417		return this;
9418	},
9419
9420	// @method getElement: String|HTMLElement
9421	// Alias for [getContent()](#popup-getcontent)
9422	getElement: function () {
9423		return this._container;
9424	},
9425
9426	// @method update: null
vendor: 9,805 bytes, lines 9427-9758
9427	// Updates the popup content, layout and position. Useful for updating the popup after something inside changed, e.g. image loaded.
9428	update: function () {
9429		if (!this._map) { return; }
9430
9431		this._container.style.visibility = 'hidden';
9432
9433		this._updateContent();
9434		this._updateLayout();
9435		this._updatePosition();
9436
9437		this._container.style.visibility = '';
9438
9439		this._adjustPan();
9440	},
9441
9442	getEvents: function () {
9443		var events = {
9444			zoom: this._updatePosition,
9445			viewreset: this._updatePosition
9446		};
9447
9448		if (this._zoomAnimated) {
9449			events.zoomanim = this._animateZoom;
9450		}
9451		return events;
9452	},
9453
9454	// @method isOpen: Boolean
9455	// Returns `true` when the popup is visible on the map.
9456	isOpen: function () {
9457		return !!this._map && this._map.hasLayer(this);
9458	},
9459
9460	// @method bringToFront: this
9461	// Brings this popup in front of other popups (in the same map pane).
9462	bringToFront: function () {
9463		if (this._map) {
9464			toFront(this._container);
9465		}
9466		return this;
9467	},
9468
9469	// @method bringToBack: this
9470	// Brings this popup to the back of other popups (in the same map pane).
9471	bringToBack: function () {
9472		if (this._map) {
9473			toBack(this._container);
9474		}
9475		return this;
9476	},
9477
9478	_updateContent: function () {
9479		if (!this._content) { return; }
9480
9481		var node = this._contentNode;
9482		var content = (typeof this._content === 'function') ? this._content(this._source || this) : this._content;
9483
9484		if (typeof content === 'string') {
9485			node.innerHTML = content;
9486		} else {
9487			while (node.hasChildNodes()) {
9488				node.removeChild(node.firstChild);
9489			}
9490			node.appendChild(content);
9491		}
9492		this.fire('contentupdate');
9493	},
9494
9495	_updatePosition: function () {
9496		if (!this._map) { return; }
9497
9498		var pos = this._map.latLngToLayerPoint(this._latlng),
9499		    offset = toPoint(this.options.offset),
9500		    anchor = this._getAnchor();
9501
9502		if (this._zoomAnimated) {
9503			setPosition(this._container, pos.add(anchor));
9504		} else {
9505			offset = offset.add(pos).add(anchor);
9506		}
9507
9508		var bottom = this._containerBottom = -offset.y,
9509		    left = this._containerLeft = -Math.round(this._containerWidth / 2) + offset.x;
9510
9511		// bottom position the popup in case the height of the popup changes (images loading etc)
9512		this._container.style.bottom = bottom + 'px';
9513		this._container.style.left = left + 'px';
9514	},
9515
9516	_getAnchor: function () {
9517		return [0, 0];
9518	}
9519
9520});
9521
9522/*
9523 * @class Popup
9524 * @inherits DivOverlay
9525 * @aka L.Popup
9526 * Used to open popups in certain places of the map. Use [Map.openPopup](#map-openpopup) to
9527 * open popups while making sure that only one popup is open at one time
9528 * (recommended for usability), or use [Map.addLayer](#map-addlayer) to open as many as you want.
9529 *
9530 * @example
9531 *
9532 * If you want to just bind a popup to marker click and then open it, it's really easy:
9533 *
9534 * ```js
9535 * marker.bindPopup(popupContent).openPopup();
9536 * ```
9537 * Path overlays like polylines also have a `bindPopup` method.
9538 * Here's a more complicated way to open a popup on a map:
9539 *
9540 * ```js
9541 * var popup = L.popup()
9542 * 	.setLatLng(latlng)
9543 * 	.setContent('<p>Hello world!<br />This is a nice popup.</p>')
9544 * 	.openOn(map);
9545 * ```
9546 */
9547
9548
9549// @namespace Popup
9550var Popup = DivOverlay.extend({
9551
9552	// @section
9553	// @aka Popup options
9554	options: {
9555		// @option maxWidth: Number = 300
9556		// Max width of the popup, in pixels.
9557		maxWidth: 300,
9558
9559		// @option minWidth: Number = 50
9560		// Min width of the popup, in pixels.
9561		minWidth: 50,
9562
9563		// @option maxHeight: Number = null
9564		// If set, creates a scrollable container of the given height
9565		// inside a popup if its content exceeds it.
9566		maxHeight: null,
9567
9568		// @option autoPan: Boolean = true
9569		// Set it to `false` if you don't want the map to do panning animation
9570		// to fit the opened popup.
9571		autoPan: true,
9572
9573		// @option autoPanPaddingTopLeft: Point = null
9574		// The margin between the popup and the top left corner of the map
9575		// view after autopanning was performed.
9576		autoPanPaddingTopLeft: null,
9577
9578		// @option autoPanPaddingBottomRight: Point = null
9579		// The margin between the popup and the bottom right corner of the map
9580		// view after autopanning was performed.
9581		autoPanPaddingBottomRight: null,
9582
9583		// @option autoPanPadding: Point = Point(5, 5)
9584		// Equivalent of setting both top left and bottom right autopan padding to the same value.
9585		autoPanPadding: [5, 5],
9586
9587		// @option keepInView: Boolean = false
9588		// Set it to `true` if you want to prevent users from panning the popup
9589		// off of the screen while it is open.
9590		keepInView: false,
9591
9592		// @option closeButton: Boolean = true
9593		// Controls the presence of a close button in the popup.
9594		closeButton: true,
9595
9596		// @option autoClose: Boolean = true
9597		// Set it to `false` if you want to override the default behavior of
9598		// the popup closing when another popup is opened.
9599		autoClose: true,
9600
9601		// @option closeOnEscapeKey: Boolean = true
9602		// Set it to `false` if you want to override the default behavior of
9603		// the ESC key for closing of the popup.
9604		closeOnEscapeKey: true,
9605
9606		// @option closeOnClick: Boolean = *
9607		// Set it if you want to override the default behavior of the popup closing when user clicks
9608		// on the map. Defaults to the map's [`closePopupOnClick`](#map-closepopuponclick) option.
9609
9610		// @option className: String = ''
9611		// A custom CSS class name to assign to the popup.
9612		className: ''
9613	},
9614
9615	// @namespace Popup
9616	// @method openOn(map: Map): this
9617	// Adds the popup to the map and closes the previous one. The same as `map.openPopup(popup)`.
9618	openOn: function (map) {
9619		map.openPopup(this);
9620		return this;
9621	},
9622
9623	onAdd: function (map) {
9624		DivOverlay.prototype.onAdd.call(this, map);
9625
9626		// @namespace Map
9627		// @section Popup events
9628		// @event popupopen: PopupEvent
9629		// Fired when a popup is opened in the map
9630		map.fire('popupopen', {popup: this});
9631
9632		if (this._source) {
9633			// @namespace Layer
9634			// @section Popup events
9635			// @event popupopen: PopupEvent
9636			// Fired when a popup bound to this layer is opened
9637			this._source.fire('popupopen', {popup: this}, true);
9638			// For non-path layers, we toggle the popup when clicking
9639			// again the layer, so prevent the map to reopen it.
9640			if (!(this._source instanceof Path)) {
9641				this._source.on('preclick', stopPropagation);
9642			}
9643		}
9644	},
9645
9646	onRemove: function (map) {
9647		DivOverlay.prototype.onRemove.call(this, map);
9648
9649		// @namespace Map
9650		// @section Popup events
9651		// @event popupclose: PopupEvent
9652		// Fired when a popup in the map is closed
9653		map.fire('popupclose', {popup: this});
9654
9655		if (this._source) {
9656			// @namespace Layer
9657			// @section Popup events
9658			// @event popupclose: PopupEvent
9659			// Fired when a popup bound to this layer is closed
9660			this._source.fire('popupclose', {popup: this}, true);
9661			if (!(this._source instanceof Path)) {
9662				this._source.off('preclick', stopPropagation);
9663			}
9664		}
9665	},
9666
9667	getEvents: function () {
9668		var events = DivOverlay.prototype.getEvents.call(this);
9669
9670		if (this.options.closeOnClick !== undefined ? this.options.closeOnClick : this._map.options.closePopupOnClick) {
9671			events.preclick = this._close;
9672		}
9673
9674		if (this.options.keepInView) {
9675			events.moveend = this._adjustPan;
9676		}
9677
9678		return events;
9679	},
9680
9681	_close: function () {
9682		if (this._map) {
9683			this._map.closePopup(this);
9684		}
9685	},
9686
9687	_initLayout: function () {
9688		var prefix = 'leaflet-popup',
9689		    container = this._container = create$1('div',
9690			prefix + ' ' + (this.options.className || '') +
9691			' leaflet-zoom-animated');
9692
9693		var wrapper = this._wrapper = create$1('div', prefix + '-content-wrapper', container);
9694		this._contentNode = create$1('div', prefix + '-content', wrapper);
9695
9696		disableClickPropagation(wrapper);
9697		disableScrollPropagation(this._contentNode);
9698		on(wrapper, 'contextmenu', stopPropagation);
9699
9700		this._tipContainer = create$1('div', prefix + '-tip-container', container);
9701		this._tip = create$1('div', prefix + '-tip', this._tipContainer);
9702
9703		if (this.options.closeButton) {
9704			var closeButton = this._closeButton = create$1('a', prefix + '-close-button', container);
9705			closeButton.href = '#close';
9706			closeButton.innerHTML = '&#215;';
9707
9708			on(closeButton, 'click', this._onCloseButtonClick, this);
9709		}
9710	},
9711
9712	_updateLayout: function () {
9713		var container = this._contentNode,
9714		    style = container.style;
9715
9716		style.width = '';
9717		style.whiteSpace = 'nowrap';
9718
9719		var width = container.offsetWidth;
9720		width = Math.min(width, this.options.maxWidth);
9721		width = Math.max(width, this.options.minWidth);
9722
9723		style.width = (width + 1) + 'px';
9724		style.whiteSpace = '';
9725
9726		style.height = '';
9727
9728		var height = container.offsetHeight,
9729		    maxHeight = this.options.maxHeight,
9730		    scrolledClass = 'leaflet-popup-scrolled';
9731
9732		if (maxHeight && height > maxHeight) {
9733			style.height = maxHeight + 'px';
9734			addClass(container, scrolledClass);
9735		} else {
9736			removeClass(container, scrolledClass);
9737		}
9738
9739		this._containerWidth = this._container.offsetWidth;
9740	},
9741
9742	_animateZoom: function (e) {
9743		var pos = this._map._latLngToNewLayerPoint(this._latlng, e.zoom, e.center),
9744		    anchor = this._getAnchor();
9745		setPosition(this._container, pos.add(anchor));
9746	},
9747
9748	_adjustPan: function () {
9749		if (!this.options.autoPan) { return; }
9750		if (this._map._panAnim) { this._map._panAnim.stop(); }
9751
9752		var map = this._map,
9753		    marginBottom = parseInt(getStyle(this._container, 'marginBottom'), 10) || 0,
9754		    containerHeight = this._container.offsetHeight + marginBottom,
9755		    containerWidth = this._containerWidth,
9756		    layerPos = new Point(this._containerLeft, -containerHeight - this._containerBottom);
9757
9758		layerPos._add(getPosition(this._container));
9759
9760		var containerPos = map.layerPointToContainerPoint(layerPos),
9761		    padding = toPoint(this.options.autoPanPadding),
9762		    paddingTL = toPoint(this.options.autoPanPaddingTopLeft || padding),
9763		    paddingBR = toPoint(this.options.autoPanPaddingBottomRight || padding),
9764		    size = map.getSize(),
9765		    dx = 0,
9766		    dy = 0;
9767
9768		if (containerPos.x + containerWidth + paddingBR.x > size.x) { // right
9769			dx = containerPos.x + containerWidth - size.x + paddingBR.x;
9770		}
9771		if (containerPos.x - dx - paddingTL.x < 0) { // left
9772			dx = containerPos.x - paddingTL.x;
9773		}
9774		if (containerPos.y + containerHeight + paddingBR.y > size.y) { // bottom
9775			dy = containerPos.y + containerHeight - size.y + paddingBR.y;
9776		}
9777		if (containerPos.y - dy - paddingTL.y < 0) { // top
9778			dy = containerPos.y - paddingTL.y;
9779		}
9780
9781		// @namespace Map
9782		// @section Popup events
9783		// @event autopanstart: Event
9784		// Fired when the map starts autopanning when opening a popup.
9785		if (dx || dy) {
9786			map
9787			    .fire('autopanstart')
9788			    .panBy([dx, dy]);
9789		}
9790	},
9791
9792	_onCloseButtonClick: function (e) {
9793		this._close();
9794		stop(e);
9795	},
9796
9797	_getAnchor: function () {
9798		// Where should we anchor the popup on the source layer?
9799		return toPoint(this._source && this._source._getPopupAnchor ? this._source._getPopupAnchor() : [0, 0]);
9800	}
9801
9802});
9803
9804// @namespace Popup
9805// @factory L.popup(options?: Popup options, source?: Layer)
9806// 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.
9807var popup = function (options, source) {
9808	return new Popup(options, source);
9809};
9810
9811
9812/* @namespace Map
9813 * @section Interaction Options
9814 * @option closePopupOnClick: Boolean = true
9815 * Set it to `false` if you don't want popups to close when user clicks the map.
9816 */
9817Map.mergeOptions({
9818	closePopupOnClick: true
9819});
9820
9821
9822// @namespace Map
9823// @section Methods for Layers and Controls
9824Map.include({
9825	// @method openPopup(popup: Popup): this
9826	// Opens the specified popup while closing the previously opened (to make sure only one is opened at one time for usability).
9827	// @alternative
9828	// @method openPopup(content: String|HTMLElement, latlng: LatLng, options?: Popup options): this
9829	// Creates a popup with the specified content and options and opens it in the given point on a map.
9830	openPopup: function (popup, latlng, options) {
9831		if (!(popup instanceof Popup)) {
9832			popup = new Popup(options).setContent(popup);
9833		}
9834
9835		if (latlng) {
9836			popup.setLatLng(latlng);
9837		}
9838
9839		if (this.hasLayer(popup)) {
9840			return this;
9841		}
9842
9843		if (this._popup && this._popup.options.autoClose) {
9844			this.closePopup();
9845		}
9846
9847		this._popup = popup;
9848		return this.addLayer(popup);
9849	},
9850
9851	// @method closePopup(popup?: Popup): this
9852	// Closes the popup previously opened with [openPopup](#map-openpopup) (or the given one).
9853	closePopup: function (popup) {
9854		if (!popup || popup === this._popup) {
9855			popup = this._popup;
9856			this._popup = null;
9857		}
9858		if (popup) {
9859			this.removeLayer(popup);
9860		}
9861		return this;
9862	}
9863});
9864
9865/*
9866 * @namespace Layer
9867 * @section Popup methods example
9868 *
9869 * All layers share a set of methods convenient for binding popups to it.
9870 *
9871 * ```js
9872 * var layer = L.Polygon(latlngs).bindPopup('Hi There!').addTo(map);
9873 * layer.openPopup();
9874 * layer.closePopup();
9875 * ```
9876 *
9877 * 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.
9878 */
9879
9880// @section Popup methods
9881Layer.include({
9882
9883	// @method bindPopup(content: String|HTMLElement|Function|Popup, options?: Popup options): this
9884	// Binds a popup to the layer with the passed `content` and sets up the
9885	// necessary event listeners. If a `Function` is passed it will receive
9886	// the layer as the first argument and should return a `String` or `HTMLElement`.
9887	bindPopup: function (content, options) {
9888
9889		if (content instanceof Popup) {
9890			setOptions(content, options);
9891			this._popup = content;
9892			content._source = this;
9893		} else {
9894			if (!this._popup || options) {
9895				this._popup = new Popup(options, this);
9896			}
9897			this._popup.setContent(content);
9898		}
9899
9900		if (!this._popupHandlersAdded) {
9901			this.on({
9902				click: this._openPopup,
9903				keypress: this._onKeyPress,
9904				remove: this.closePopup,
9905				move: this._movePopup
9906			});
9907			this._popupHandlersAdded = true;
9908		}
9909
9910		return this;
9911	},
9912
9913	// @method unbindPopup(): this
9914	// Removes the popup previously bound with `bindPopup`.
9915	unbindPopup: function () {
9916		if (this._popup) {
9917			this.off({
9918				click: this._openPopup,
9919				keypress: this._onKeyPress,
9920				remove: this.closePopup,
9921				move: this._movePopup
9922			});
9923			this._popupHandlersAdded = false;
9924			this._popup = null;
9925		}
9926		return this;
9927	},
9928
9929	// @method openPopup(latlng?: LatLng): this
9930	// Opens the bound popup at the specified `latlng` or at the default popup anchor if no `latlng` is passed.
9931	openPopup: function (layer, latlng) {
9932		if (!(layer instanceof Layer)) {
9933			latlng = layer;
9934			layer = this;
9935		}
9936
9937		if (layer instanceof FeatureGroup) {
9938			for (var id in this._layers) {
9939				layer = this._layers[id];
9940				break;
9941			}
9942		}
9943
9944		if (!latlng) {
9945			latlng = layer.getCenter ? layer.getCenter() : layer.getLatLng();
9946		}
9947
9948		if (this._popup && this._map) {
9949			// set popup source to this layer
9950			this._popup._source = layer;
9951
9952			// update the popup (content, layout, ect...)
9953			this._popup.update();
9954
9955			// open the popup on the map
9956			this._map.openPopup(this._popup, latlng);
9957		}
9958
9959		return this;
9960	},
9961
9962	// @method closePopup(): this
9963	// Closes the popup bound to this layer if it is open.
9964	closePopup: function () {
9965		if (this._popup) {
9966			this._popup._close();
9967		}
9968		return this;
9969	},
9970
9971	// @method togglePopup(): this
9972	// Opens or closes the popup bound to this layer depending on its current state.
9973	togglePopup: function (target) {
9974		if (this._popup) {
9975			if (this._popup._map) {
9976				this.closePopup();
9977			} else {
9978				this.openPopup(target);
9979			}
9980		}
9981		return this;
9982	},
9983
9984	// @method isPopupOpen(): boolean
9985	// Returns `true` if the popup bound to this layer is currently open.
9986	isPopupOpen: function () {
9987		return (this._popup ? this._popup.isOpen() : false);
9988	},
9989
9990	// @method setPopupContent(content: String|HTMLElement|Popup): this
9991	// Sets the content of the popup bound to this layer.
9992	setPopupContent: function (content) {
9993		if (this._popup) {
9994			this._popup.setContent(content);
9995		}
9996		return this;
9997	},
9998
9999	// @method getPopup(): Popup
10000	// Returns the popup bound to this layer.
10001	getPopup: function () {
10002		return this._popup;
10003	},
10004
10005	_openPopup: function (e) {
10006		var layer = e.layer || e.target;
10007
10008		if (!this._popup) {
10009			return;
10010		}
10011
10012		if (!this._map) {
10013			return;
10014		}
10015
10016		// prevent map click
10017		stop(e);
10018
10019		// if this inherits from Path its a vector and we can just
10020		// open the popup at the new location
10021		if (layer instanceof Path) {
10022			this.openPopup(e.layer || e.target, e.latlng);
10023			return;
10024		}
10025
10026		// otherwise treat it like a marker and figure out
10027		// if we should toggle it open/closed
10028		if (this._map.hasLayer(this._popup) && this._popup._source === layer) {
10029			this.closePopup();
10030		} else {
10031			this.openPopup(layer, e.latlng);
10032		}
10033	},
10034
10035	_movePopup: function (e) {
10036		this._popup.setLatLng(e.latlng);
10037	},
10038
10039	_onKeyPress: function (e) {
10040		if (e.originalEvent.keyCode === 13) {
10041			this._openPopup(e);
10042		}
10043	}
10044});
10045
10046/*
10047 * @class Tooltip
10048 * @inherits DivOverlay
10049 * @aka L.Tooltip
10050 * Used to display small texts on top of map layers.
10051 *
10052 * @example
10053 *
10054 * ```js
10055 * marker.bindTooltip("my tooltip text").openTooltip();
10056 * ```
10057 * Note about tooltip offset. Leaflet takes two options in consideration
10058 * for computing tooltip offsetting:
10059 * - the `offset` Tooltip option: it defaults to [0, 0], and it's specific to one tooltip.
10060 *   Add a positive x offset to move the tooltip to the right, and a positive y offset to
10061 *   move it to the bottom. Negatives will move to the left and top.
10062 * - the `tooltipAnchor` Icon option: this will only be considered for Marker. You
10063 *   should adapt this value if you use a custom icon.
10064 */
10065
10066
10067// @namespace Tooltip
10068var Tooltip = DivOverlay.extend({
10069
10070	// @section
10071	// @aka Tooltip options
10072	options: {
10073		// @option pane: String = 'tooltipPane'
10074		// `Map pane` where the tooltip will be added.
10075		pane: 'tooltipPane',
10076
10077		// @option offset: Point = Point(0, 0)
10078		// Optional offset of the tooltip position.
10079		offset: [0, 0],
10080
10081		// @option direction: String = 'auto'
10082		// Direction where to open the tooltip. Possible values are: `right`, `left`,
10083		// `top`, `bottom`, `center`, `auto`.
10084		// `auto` will dynamically switch between `right` and `left` according to the tooltip
10085		// position on the map.
10086		direction: 'auto',
10087
10088		// @option permanent: Boolean = false
10089		// Whether to open the tooltip permanently or only on mouseover.
10090		permanent: false,
10091
10092		// @option sticky: Boolean = false
10093		// If true, the tooltip will follow the mouse instead of being fixed at the feature center.
10094		sticky: false,
10095
10096		// @option interactive: Boolean = false
10097		// If true, the tooltip will listen to the feature events.
10098		interactive: false,
10099
10100		// @option opacity: Number = 0.9
10101		// Tooltip container opacity.
10102		opacity: 0.9
10103	},
10104
10105	onAdd: function (map) {
10106		DivOverlay.prototype.onAdd.call(this, map);
10107		this.setOpacity(this.options.opacity);
10108
10109		// @namespace Map
10110		// @section Tooltip events
10111		// @event tooltipopen: TooltipEvent
10112		// Fired when a tooltip is opened in the map.
10113		map.fire('tooltipopen', {tooltip: this});
10114
10115		if (this._source) {
10116			// @namespace Layer
10117			// @section Tooltip events
10118			// @event tooltipopen: TooltipEvent
10119			// Fired when a tooltip bound to this layer is opened.
10120			this._source.fire('tooltipopen', {tooltip: this}, true);
10121		}
10122	},
10123
10124	onRemove: function (map) {
10125		DivOverlay.prototype.onRemove.call(this, map);
10126
10127		// @namespace Map
10128		// @section Tooltip events
10129		// @event tooltipclose: TooltipEvent
10130		// Fired when a tooltip in the map is closed.
10131		map.fire('tooltipclose', {tooltip: this});
10132
10133		if (this._source) {
10134			// @namespace Layer
10135			// @section Tooltip events
10136			// @event tooltipclose: TooltipEvent
10137			// Fired when a tooltip bound to this layer is closed.
10138			this._source.fire('tooltipclose', {tooltip: this}, true);
10139		}
10140	},
10141
10142	getEvents: function () {
10143		var events = DivOverlay.prototype.getEvents.call(this);
10144
10145		if (touch && !this.options.permanent) {
10146			events.preclick = this._close;
10147		}
10148
10149		return events;
10150	},
10151
10152	_close: function () {
10153		if (this._map) {
10154			this._map.closeTooltip(this);
10155		}
10156	},
10157
10158	_initLayout: function () {
10159		var prefix = 'leaflet-tooltip',
10160		    className = prefix + ' ' + (this.options.className || '') + ' leaflet-zoom-' + (this._zoomAnimated ? 'animated' : 'hide');
10161
10162		this._contentNode = this._container = create$1('div', className);
10163	},
10164
10165	_updateLayout: function () {},
10166
10167	_adjustPan: function () {},
10168
10169	_setPosition: function (pos) {
10170		var map = this._map,
10171		    container = this._container,
10172		    centerPoint = map.latLngToContainerPoint(map.getCenter()),
10173		    tooltipPoint = map.layerPointToContainerPoint(pos),
10174		    direction = this.options.direction,
10175		    tooltipWidth = container.offsetWidth,
10176		    tooltipHeight = container.offsetHeight,
10177		    offset = toPoint(this.options.offset),
10178		    anchor = this._getAnchor();
10179
10180		if (direction === 'top') {
10181			pos = pos.add(toPoint(-tooltipWidth / 2 + offset.x, -tooltipHeight + offset.y + anchor.y, true));
10182		} else if (direction === 'bottom') {
10183			pos = pos.subtract(toPoint(tooltipWidth / 2 - offset.x, -offset.y, true));
10184		} else if (direction === 'center') {
10185			pos = pos.subtract(toPoint(tooltipWidth / 2 + offset.x, tooltipHeight / 2 - anchor.y + offset.y, true));
10186		} else if (direction === 'right' || direction === 'auto' && tooltipPoint.x < centerPoint.x) {
10187			direction = 'right';
10188			pos = pos.add(toPoint(offset.x + anchor.x, anchor.y - tooltipHeight / 2 + offset.y, true));
10189		} else {
10190			direction = 'left';
10191			pos = pos.subtract(toPoint(tooltipWidth + anchor.x - offset.x, tooltipHeight / 2 - anchor.y - offset.y, true));
10192		}
10193
10194		removeClass(container, 'leaflet-tooltip-right');
10195		removeClass(container, 'leaflet-tooltip-left');
10196		removeClass(container, 'leaflet-tooltip-top');
10197		removeClass(container, 'leaflet-tooltip-bottom');
10198		addClass(container, 'leaflet-tooltip-' + direction);
10199		setPosition(container, pos);
10200	},
10201
10202	_updatePosition: function () {
10203		var pos = this._map.latLngToLayerPoint(this._latlng);
10204		this._setPosition(pos);
10205	},
10206
10207	setOpacity: function (opacity) {
10208		this.options.opacity = opacity;
10209
10210		if (this._container) {
10211			setOpacity(this._container, opacity);
10212		}
10213	},
10214
10215	_animateZoom: function (e) {
10216		var pos = this._map._latLngToNewLayerPoint(this._latlng, e.zoom, e.center);
10217		this._setPosition(pos);
10218	},
10219
10220	_getAnchor: function () {
10221		// Where should we anchor the tooltip on the source layer?
10222		return toPoint(this._source && this._source._getTooltipAnchor && !this.options.sticky ? this._source._getTooltipAnchor() : [0, 0]);
10223	}
10224
10225});
10226
10227// @namespace Tooltip
10228// @factory L.tooltip(options?: Tooltip options, source?: Layer)
10229// 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.
10230var tooltip = function (options, source) {
10231	return new Tooltip(options, source);
10232};
10233
10234// @namespace Map
10235// @section Methods for Layers and Controls
10236Map.include({
10237
10238	// @method openTooltip(tooltip: Tooltip): this
10239	// Opens the specified tooltip.
10240	// @alternative
10241	// @method openTooltip(content: String|HTMLElement, latlng: LatLng, options?: Tooltip options): this
10242	// Creates a tooltip with the specified content and options and open it.
10243	openTooltip: function (tooltip, latlng, options) {
10244		if (!(tooltip instanceof Tooltip)) {
10245			tooltip = new Tooltip(options).setContent(tooltip);
10246		}
10247
10248		if (latlng) {
10249			tooltip.setLatLng(latlng);
10250		}
10251
10252		if (this.hasLayer(tooltip)) {
10253			return this;
10254		}
10255
10256		return this.addLayer(tooltip);
10257	},
10258
10259	// @method closeTooltip(tooltip?: Tooltip): this
10260	// Closes the tooltip given as parameter.
10261	closeTooltip: function (tooltip) {
10262		if (tooltip) {
10263			this.removeLayer(tooltip);
10264		}
10265		return this;
10266	}
10267
10268});
10269
10270/*
10271 * @namespace Layer
10272 * @section Tooltip methods example
10273 *
10274 * All layers share a set of methods convenient for binding tooltips to it.
10275 *
10276 * ```js
10277 * var layer = L.Polygon(latlngs).bindTooltip('Hi There!').addTo(map);
10278 * layer.openTooltip();
10279 * layer.closeTooltip();
10280 * ```
10281 */
10282
10283// @section Tooltip methods
10284Layer.include({
10285
10286	// @method bindTooltip(content: String|HTMLElement|Function|Tooltip, options?: Tooltip options): this
10287	// Binds a tooltip to the layer with the passed `content` and sets up the
10288	// necessary event listeners. If a `Function` is passed it will receive
10289	// the layer as the first argument and should return a `String` or `HTMLElement`.
10290	bindTooltip: function (content, options) {
10291
10292		if (content instanceof Tooltip) {
10293			setOptions(content, options);
10294			this._tooltip = content;
10295			content._source = this;
10296		} else {
10297			if (!this._tooltip || options) {
10298				this._tooltip = new Tooltip(options, this);
10299			}
10300			this._tooltip.setContent(content);
10301
10302		}
10303
10304		this._initTooltipInteractions();
10305
10306		if (this._tooltip.options.permanent && this._map && this._map.hasLayer(this)) {
10307			this.openTooltip();
10308		}
10309
10310		return this;
10311	},
10312
10313	// @method unbindTooltip(): this
10314	// Removes the tooltip previously bound with `bindTooltip`.
10315	unbindTooltip: function () {
10316		if (this._tooltip) {
10317			this._initTooltipInteractions(true);
10318			this.closeTooltip();
10319			this._tooltip = null;
10320		}
10321		return this;
10322	},
10323
10324	_initTooltipInteractions: function (remove$$1) {
10325		if (!remove$$1 && this._tooltipHandlersAdded) { return; }
10326		var onOff = remove$$1 ? 'off' : 'on',
10327		    events = {
10328			remove: this.closeTooltip,
10329			move: this._moveTooltip
10330		    };
10331		if (!this._tooltip.options.permanent) {
10332			events.mouseover = this._openTooltip;
10333			events.mouseout = this.closeTooltip;
10334			if (this._tooltip.options.sticky) {
10335				events.mousemove = this._moveTooltip;
10336			}
10337			if (touch) {
10338				events.click = this._openTooltip;
10339			}
10340		} else {
10341			events.add = this._openTooltip;
10342		}
10343		this[onOff](events);
10344		this._tooltipHandlersAdded = !remove$$1;
10345	},
10346
10347	// @method openTooltip(latlng?: LatLng): this
10348	// Opens the bound tooltip at the specified `latlng` or at the default tooltip anchor if no `latlng` is passed.
10349	openTooltip: function (layer, latlng) {
10350		if (!(layer instanceof Layer)) {
10351			latlng = layer;
10352			layer = this;
10353		}
10354
10355		if (layer instanceof FeatureGroup) {
10356			for (var id in this._layers) {
10357				layer = this._layers[id];
10358				break;
10359			}
10360		}
10361
10362		if (!latlng) {
10363			latlng = layer.getCenter ? layer.getCenter() : layer.getLatLng();
10364		}
10365
10366		if (this._tooltip && this._map) {
10367
10368			// set tooltip source to this layer
10369			this._tooltip._source = layer;
10370
10371			// update the tooltip (content, layout, ect...)
10372			this._tooltip.update();
10373
10374			// open the tooltip on the map
10375			this._map.openTooltip(this._tooltip, latlng);
10376
10377			// Tooltip container may not be defined if not permanent and never
10378			// opened.
10379			if (this._tooltip.options.interactive && this._tooltip._container) {
10380				addClass(this._tooltip._container, 'leaflet-clickable');
10381				this.addInteractiveTarget(this._tooltip._container);
10382			}
10383		}
10384
10385		return this;
10386	},
10387
10388	// @method closeTooltip(): this
10389	// Closes the tooltip bound to this layer if it is open.
10390	closeTooltip: function () {
10391		if (this._tooltip) {
10392			this._tooltip._close();
10393			if (this._tooltip.options.interactive && this._tooltip._container) {
10394				removeClass(this._tooltip._container, 'leaflet-clickable');
10395				this.removeInteractiveTarget(this._tooltip._container);
10396			}
10397		}
10398		return this;
10399	},
10400
10401	// @method toggleTooltip(): this
10402	// Opens or closes the tooltip bound to this layer depending on its current state.
10403	toggleTooltip: function (target) {
10404		if (this._tooltip) {
10405			if (this._tooltip._map) {
10406				this.closeTooltip();
10407			} else {
10408				this.openTooltip(target);
10409			}
10410		}
10411		return this;
10412	},
10413
10414	// @method isTooltipOpen(): boolean
10415	// Returns `true` if the tooltip bound to this layer is currently open.
10416	isTooltipOpen: function () {
10417		return this._tooltip.isOpen();
10418	},
10419
10420	// @method setTooltipContent(content: String|HTMLElement|Tooltip): this
10421	// Sets the content of the tooltip bound to this layer.
10422	setTooltipContent: function (content) {
10423		if (this._tooltip) {
10424			this._tooltip.setContent(content);
10425		}
10426		return this;
10427	},
10428
10429	// @method getTooltip(): Tooltip
10430	// Returns the tooltip bound to this layer.
10431	getTooltip: function () {
10432		return this._tooltip;
10433	},
10434
10435	_openTooltip: function (e) {
10436		var layer = e.layer || e.target;
10437
10438		if (!this._tooltip || !this._map) {
10439			return;
10440		}
10441		this.openTooltip(layer, this._tooltip.options.sticky ? e.latlng : undefined);
10442	},
10443
10444	_moveTooltip: function (e) {
10445		var latlng = e.latlng, containerPoint, layerPoint;
10446		if (this._tooltip.options.sticky && e.originalEvent) {
10447			containerPoint = this._map.mouseEventToContainerPoint(e.originalEvent);
10448			layerPoint = this._map.containerPointToLayerPoint(containerPoint);
10449			latlng = this._map.layerPointToLatLng(layerPoint);
10450		}
10451		this._tooltip.setLatLng(latlng);
10452	}
10453});
10454
10455/*
10456 * @class DivIcon
10457 * @aka L.DivIcon
10458 * @inherits Icon
10459 *
10460 * Represents a lightweight icon for markers that uses a simple `<div>`
10461 * element instead of an image. Inherits from `Icon` but ignores the `iconUrl` and shadow options.
10462 *
10463 * @example
10464 * ```js
10465 * var myIcon = L.divIcon({className: 'my-div-icon'});
10466 * // you can set .my-div-icon styles in CSS
10467 *
10468 * L.marker([50.505, 30.57], {icon: myIcon}).addTo(map);
10469 * ```
10470 *
10471 * By default, it has a 'leaflet-div-icon' CSS class and is styled as a little white square with a shadow.
10472 */
10473
10474var DivIcon = Icon.extend({
10475	options: {
10476		// @section
10477		// @aka DivIcon options
10478		iconSize: [12, 12], // also can be set through CSS
10479
10480		// iconAnchor: (Point),
10481		// popupAnchor: (Point),
10482
10483		// @option html: String = ''
10484		// Custom HTML code to put inside the div element, empty by default.
10485		html: false,
10486
10487		// @option bgPos: Point = [0, 0]
10488		// Optional relative position of the background, in pixels
10489		bgPos: null,
10490
10491		className: 'leaflet-div-icon'
10492	},
10493
10494	createIcon: function (oldIcon) {
10495		var div = (oldIcon && oldIcon.tagName === 'DIV') ? oldIcon : document.createElement('div'),
10496		    options = this.options;
10497
10498		div.innerHTML = options.html !== false ? options.html : '';
10499
10500		if (options.bgPos) {
10501			var bgPos = toPoint(options.bgPos);
10502			div.style.backgroundPosition = (-bgPos.x) + 'px ' + (-bgPos.y) + 'px';
10503		}
10504		this._setIconStyles(div, 'icon');
10505
10506		return div;
10507	},
10508
10509	createShadow: function () {
10510		return null;
10511	}
10512});
10513
10514// @factory L.divIcon(options: DivIcon options)
10515// Creates a `DivIcon` instance with the given options.
10516function divIcon(options) {
10517	return new DivIcon(options);
10518}
10519
10520Icon.Default = IconDefault;
10521
10522/*
10523 * @class GridLayer
10524 * @inherits Layer
10525 * @aka L.GridLayer
10526 *
10527 * Generic class for handling a tiled grid of HTML elements. This is the base class for all tile layers and replaces `TileLayer.Canvas`.
10528 * 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.
10529 *
10530 *
10531 * @section Synchronous usage
10532 * @example
10533 *
10534 * 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.
10535 *
10536 * ```js
10537 * var CanvasLayer = L.GridLayer.extend({
10538 *     createTile: function(coords){
10539 *         // create a <canvas> element for drawing
10540 *         var tile = L.DomUtil.create('canvas', 'leaflet-tile');
10541 *
10542 *         // setup tile width and height according to the options
10543 *         var size = this.getTileSize();
10544 *         tile.width = size.x;
10545 *         tile.height = size.y;
10546 *
10547 *         // get a canvas context and draw something on it using coords.x, coords.y and coords.z
10548 *         var ctx = tile.getContext('2d');
10549 *
10550 *         // return the tile so it can be rendered on screen
10551 *         return tile;
10552 *     }
10553 * });
10554 * ```
10555 *
10556 * @section Asynchronous usage
10557 * @example
10558 *
10559 * 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.
10560 *
10561 * ```js
10562 * var CanvasLayer = L.GridLayer.extend({
10563 *     createTile: function(coords, done){
10564 *         var error;
10565 *
10566 *         // create a <canvas> element for drawing
10567 *         var tile = L.DomUtil.create('canvas', 'leaflet-tile');
10568 *
10569 *         // setup tile width and height according to the options
10570 *         var size = this.getTileSize();
10571 *         tile.width = size.x;
10572 *         tile.height = size.y;
10573 *
10574 *         // draw something asynchronously and pass the tile to the done() callback
10575 *         setTimeout(function() {
10576 *             done(error, tile);
10577 *         }, 1000);
10578 *
10579 *         return tile;
10580 *     }
10581 * });
10582 * ```
10583 *
10584 * @section
10585 */
10586
10587
10588var GridLayer = Layer.extend({
10589
10590	// @section
10591	// @aka GridLayer options
10592	options: {
10593		// @option tileSize: Number|Point = 256
10594		// Width and height of tiles in the grid. Use a number if width and height are equal, or `L.point(width, height)` otherwise.
10595		tileSize: 256,
10596
10597		// @option opacity: Number = 1.0
10598		// Opacity of the tiles. Can be used in the `createTile()` function.
10599		opacity: 1,
10600
10601		// @option updateWhenIdle: Boolean = (depends)
10602		// Load new tiles only when panning ends.
10603		// `true` by default on mobile browsers, in order to avoid too many requests and keep smooth navigation.
10604		// `false` otherwise in order to display new tiles _during_ panning, since it is easy to pan outside the
10605		// [`keepBuffer`](#gridlayer-keepbuffer) option in desktop browsers.
10606		updateWhenIdle: mobile,
10607
vendor: 24,395 bytes, lines 10608-11472
10608		// @option updateWhenZooming: Boolean = true
10609		// 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.
10610		updateWhenZooming: true,
10611
10612		// @option updateInterval: Number = 200
10613		// Tiles will not update more than once every `updateInterval` milliseconds when panning.
10614		updateInterval: 200,
10615
10616		// @option zIndex: Number = 1
10617		// The explicit zIndex of the tile layer.
10618		zIndex: 1,
10619
10620		// @option bounds: LatLngBounds = undefined
10621		// If set, tiles will only be loaded inside the set `LatLngBounds`.
10622		bounds: null,
10623
10624		// @option minZoom: Number = 0
10625		// The minimum zoom level down to which this layer will be displayed (inclusive).
10626		minZoom: 0,
10627
10628		// @option maxZoom: Number = undefined
10629		// The maximum zoom level up to which this layer will be displayed (inclusive).
10630		maxZoom: undefined,
10631
10632		// @option maxNativeZoom: Number = undefined
10633		// Maximum zoom number the tile source has available. If it is specified,
10634		// the tiles on all zoom levels higher than `maxNativeZoom` will be loaded
10635		// from `maxNativeZoom` level and auto-scaled.
10636		maxNativeZoom: undefined,
10637
10638		// @option minNativeZoom: Number = undefined
10639		// Minimum zoom number the tile source has available. If it is specified,
10640		// the tiles on all zoom levels lower than `minNativeZoom` will be loaded
10641		// from `minNativeZoom` level and auto-scaled.
10642		minNativeZoom: undefined,
10643
10644		// @option noWrap: Boolean = false
10645		// Whether the layer is wrapped around the antimeridian. If `true`, the
10646		// GridLayer will only be displayed once at low zoom levels. Has no
10647		// effect when the [map CRS](#map-crs) doesn't wrap around. Can be used
10648		// in combination with [`bounds`](#gridlayer-bounds) to prevent requesting
10649		// tiles outside the CRS limits.
10650		noWrap: false,
10651
10652		// @option pane: String = 'tilePane'
10653		// `Map pane` where the grid layer will be added.
10654		pane: 'tilePane',
10655
10656		// @option className: String = ''
10657		// A custom class name to assign to the tile layer. Empty by default.
10658		className: '',
10659
10660		// @option keepBuffer: Number = 2
10661		// When panning the map, keep this many rows and columns of tiles before unloading them.
10662		keepBuffer: 2
10663	},
10664
10665	initialize: function (options) {
10666		setOptions(this, options);
10667	},
10668
10669	onAdd: function () {
10670		this._initContainer();
10671
10672		this._levels = {};
10673		this._tiles = {};
10674
10675		this._resetView();
10676		this._update();
10677	},
10678
10679	beforeAdd: function (map) {
10680		map._addZoomLimit(this);
10681	},
10682
10683	onRemove: function (map) {
10684		this._removeAllTiles();
10685		remove(this._container);
10686		map._removeZoomLimit(this);
10687		this._container = null;
10688		this._tileZoom = undefined;
10689	},
10690
10691	// @method bringToFront: this
10692	// Brings the tile layer to the top of all tile layers.
10693	bringToFront: function () {
10694		if (this._map) {
10695			toFront(this._container);
10696			this._setAutoZIndex(Math.max);
10697		}
10698		return this;
10699	},
10700
10701	// @method bringToBack: this
10702	// Brings the tile layer to the bottom of all tile layers.
10703	bringToBack: function () {
10704		if (this._map) {
10705			toBack(this._container);
10706			this._setAutoZIndex(Math.min);
10707		}
10708		return this;
10709	},
10710
10711	// @method getContainer: HTMLElement
10712	// Returns the HTML element that contains the tiles for this layer.
10713	getContainer: function () {
10714		return this._container;
10715	},
10716
10717	// @method setOpacity(opacity: Number): this
10718	// Changes the [opacity](#gridlayer-opacity) of the grid layer.
10719	setOpacity: function (opacity) {
10720		this.options.opacity = opacity;
10721		this._updateOpacity();
10722		return this;
10723	},
10724
10725	// @method setZIndex(zIndex: Number): this
10726	// Changes the [zIndex](#gridlayer-zindex) of the grid layer.
10727	setZIndex: function (zIndex) {
10728		this.options.zIndex = zIndex;
10729		this._updateZIndex();
10730
10731		return this;
10732	},
10733
10734	// @method isLoading: Boolean
10735	// Returns `true` if any tile in the grid layer has not finished loading.
10736	isLoading: function () {
10737		return this._loading;
10738	},
10739
10740	// @method redraw: this
10741	// Causes the layer to clear all the tiles and request them again.
10742	redraw: function () {
10743		if (this._map) {
10744			this._removeAllTiles();
10745			this._update();
10746		}
10747		return this;
10748	},
10749
10750	getEvents: function () {
10751		var events = {
10752			viewprereset: this._invalidateAll,
10753			viewreset: this._resetView,
10754			zoom: this._resetView,
10755			moveend: this._onMoveEnd
10756		};
10757
10758		if (!this.options.updateWhenIdle) {
10759			// update tiles on move, but not more often than once per given interval
10760			if (!this._onMove) {
10761				this._onMove = throttle(this._onMoveEnd, this.options.updateInterval, this);
10762			}
10763
10764			events.move = this._onMove;
10765		}
10766
10767		if (this._zoomAnimated) {
10768			events.zoomanim = this._animateZoom;
10769		}
10770
10771		return events;
10772	},
10773
10774	// @section Extension methods
10775	// Layers extending `GridLayer` shall reimplement the following method.
10776	// @method createTile(coords: Object, done?: Function): HTMLElement
10777	// Called only internally, must be overridden by classes extending `GridLayer`.
10778	// Returns the `HTMLElement` corresponding to the given `coords`. If the `done` callback
10779	// is specified, it must be called when the tile has finished loading and drawing.
10780	createTile: function () {
10781		return document.createElement('div');
10782	},
10783
10784	// @section
10785	// @method getTileSize: Point
10786	// Normalizes the [tileSize option](#gridlayer-tilesize) into a point. Used by the `createTile()` method.
10787	getTileSize: function () {
10788		var s = this.options.tileSize;
10789		return s instanceof Point ? s : new Point(s, s);
10790	},
10791
10792	_updateZIndex: function () {
10793		if (this._container && this.options.zIndex !== undefined && this.options.zIndex !== null) {
10794			this._container.style.zIndex = this.options.zIndex;
10795		}
10796	},
10797
10798	_setAutoZIndex: function (compare) {
10799		// go through all other layers of the same pane, set zIndex to max + 1 (front) or min - 1 (back)
10800
10801		var layers = this.getPane().children,
10802		    edgeZIndex = -compare(-Infinity, Infinity); // -Infinity for max, Infinity for min
10803
10804		for (var i = 0, len = layers.length, zIndex; i < len; i++) {
10805
10806			zIndex = layers[i].style.zIndex;
10807
10808			if (layers[i] !== this._container && zIndex) {
10809				edgeZIndex = compare(edgeZIndex, +zIndex);
10810			}
10811		}
10812
10813		if (isFinite(edgeZIndex)) {
10814			this.options.zIndex = edgeZIndex + compare(-1, 1);
10815			this._updateZIndex();
10816		}
10817	},
10818
10819	_updateOpacity: function () {
10820		if (!this._map) { return; }
10821
10822		// IE doesn't inherit filter opacity properly, so we're forced to set it on tiles
10823		if (ielt9) { return; }
10824
10825		setOpacity(this._container, this.options.opacity);
10826
10827		var now = +new Date(),
10828		    nextFrame = false,
10829		    willPrune = false;
10830
10831		for (var key in this._tiles) {
10832			var tile = this._tiles[key];
10833			if (!tile.current || !tile.loaded) { continue; }
10834
10835			var fade = Math.min(1, (now - tile.loaded) / 200);
10836
10837			setOpacity(tile.el, fade);
10838			if (fade < 1) {
10839				nextFrame = true;
10840			} else {
10841				if (tile.active) {
10842					willPrune = true;
10843				} else {
10844					this._onOpaqueTile(tile);
10845				}
10846				tile.active = true;
10847			}
10848		}
10849
10850		if (willPrune && !this._noPrune) { this._pruneTiles(); }
10851
10852		if (nextFrame) {
10853			cancelAnimFrame(this._fadeFrame);
10854			this._fadeFrame = requestAnimFrame(this._updateOpacity, this);
10855		}
10856	},
10857
10858	_onOpaqueTile: falseFn,
10859
10860	_initContainer: function () {
10861		if (this._container) { return; }
10862
10863		this._container = create$1('div', 'leaflet-layer ' + (this.options.className || ''));
10864		this._updateZIndex();
10865
10866		if (this.options.opacity < 1) {
10867			this._updateOpacity();
10868		}
10869
10870		this.getPane().appendChild(this._container);
10871	},
10872
10873	_updateLevels: function () {
10874
10875		var zoom = this._tileZoom,
10876		    maxZoom = this.options.maxZoom;
10877
10878		if (zoom === undefined) { return undefined; }
10879
10880		for (var z in this._levels) {
10881			if (this._levels[z].el.children.length || z === zoom) {
10882				this._levels[z].el.style.zIndex = maxZoom - Math.abs(zoom - z);
10883				this._onUpdateLevel(z);
10884			} else {
10885				remove(this._levels[z].el);
10886				this._removeTilesAtZoom(z);
10887				this._onRemoveLevel(z);
10888				delete this._levels[z];
10889			}
10890		}
10891
10892		var level = this._levels[zoom],
10893		    map = this._map;
10894
10895		if (!level) {
10896			level = this._levels[zoom] = {};
10897
10898			level.el = create$1('div', 'leaflet-tile-container leaflet-zoom-animated', this._container);
10899			level.el.style.zIndex = maxZoom;
10900
10901			level.origin = map.project(map.unproject(map.getPixelOrigin()), zoom).round();
10902			level.zoom = zoom;
10903
10904			this._setZoomTransform(level, map.getCenter(), map.getZoom());
10905
10906			// force the browser to consider the newly added element for transition
10907			falseFn(level.el.offsetWidth);
10908
10909			this._onCreateLevel(level);
10910		}
10911
10912		this._level = level;
10913
10914		return level;
10915	},
10916
10917	_onUpdateLevel: falseFn,
10918
10919	_onRemoveLevel: falseFn,
10920
10921	_onCreateLevel: falseFn,
10922
10923	_pruneTiles: function () {
10924		if (!this._map) {
10925			return;
10926		}
10927
10928		var key, tile;
10929
10930		var zoom = this._map.getZoom();
10931		if (zoom > this.options.maxZoom ||
10932			zoom < this.options.minZoom) {
10933			this._removeAllTiles();
10934			return;
10935		}
10936
10937		for (key in this._tiles) {
10938			tile = this._tiles[key];
10939			tile.retain = tile.current;
10940		}
10941
10942		for (key in this._tiles) {
10943			tile = this._tiles[key];
10944			if (tile.current && !tile.active) {
10945				var coords = tile.coords;
10946				if (!this._retainParent(coords.x, coords.y, coords.z, coords.z - 5)) {
10947					this._retainChildren(coords.x, coords.y, coords.z, coords.z + 2);
10948				}
10949			}
10950		}
10951
10952		for (key in this._tiles) {
10953			if (!this._tiles[key].retain) {
10954				this._removeTile(key);
10955			}
10956		}
10957	},
10958
10959	_removeTilesAtZoom: function (zoom) {
10960		for (var key in this._tiles) {
10961			if (this._tiles[key].coords.z !== zoom) {
10962				continue;
10963			}
10964			this._removeTile(key);
10965		}
10966	},
10967
10968	_removeAllTiles: function () {
10969		for (var key in this._tiles) {
10970			this._removeTile(key);
10971		}
10972	},
10973
10974	_invalidateAll: function () {
10975		for (var z in this._levels) {
10976			remove(this._levels[z].el);
10977			this._onRemoveLevel(z);
10978			delete this._levels[z];
10979		}
10980		this._removeAllTiles();
10981
10982		this._tileZoom = undefined;
10983	},
10984
10985	_retainParent: function (x, y, z, minZoom) {
10986		var x2 = Math.floor(x / 2),
10987		    y2 = Math.floor(y / 2),
10988		    z2 = z - 1,
10989		    coords2 = new Point(+x2, +y2);
10990		coords2.z = +z2;
10991
10992		var key = this._tileCoordsToKey(coords2),
10993		    tile = this._tiles[key];
10994
10995		if (tile && tile.active) {
10996			tile.retain = true;
10997			return true;
10998
10999		} else if (tile && tile.loaded) {
11000			tile.retain = true;
11001		}
11002
11003		if (z2 > minZoom) {
11004			return this._retainParent(x2, y2, z2, minZoom);
11005		}
11006
11007		return false;
11008	},
11009
11010	_retainChildren: function (x, y, z, maxZoom) {
11011
11012		for (var i = 2 * x; i < 2 * x + 2; i++) {
11013			for (var j = 2 * y; j < 2 * y + 2; j++) {
11014
11015				var coords = new Point(i, j);
11016				coords.z = z + 1;
11017
11018				var key = this._tileCoordsToKey(coords),
11019				    tile = this._tiles[key];
11020
11021				if (tile && tile.active) {
11022					tile.retain = true;
11023					continue;
11024
11025				} else if (tile && tile.loaded) {
11026					tile.retain = true;
11027				}
11028
11029				if (z + 1 < maxZoom) {
11030					this._retainChildren(i, j, z + 1, maxZoom);
11031				}
11032			}
11033		}
11034	},
11035
11036	_resetView: function (e) {
11037		var animating = e && (e.pinch || e.flyTo);
11038		this._setView(this._map.getCenter(), this._map.getZoom(), animating, animating);
11039	},
11040
11041	_animateZoom: function (e) {
11042		this._setView(e.center, e.zoom, true, e.noUpdate);
11043	},
11044
11045	_clampZoom: function (zoom) {
11046		var options = this.options;
11047
11048		if (undefined !== options.minNativeZoom && zoom < options.minNativeZoom) {
11049			return options.minNativeZoom;
11050		}
11051
11052		if (undefined !== options.maxNativeZoom && options.maxNativeZoom < zoom) {
11053			return options.maxNativeZoom;
11054		}
11055
11056		return zoom;
11057	},
11058
11059	_setView: function (center, zoom, noPrune, noUpdate) {
11060		var tileZoom = this._clampZoom(Math.round(zoom));
11061		if ((this.options.maxZoom !== undefined && tileZoom > this.options.maxZoom) ||
11062		    (this.options.minZoom !== undefined && tileZoom < this.options.minZoom)) {
11063			tileZoom = undefined;
11064		}
11065
11066		var tileZoomChanged = this.options.updateWhenZooming && (tileZoom !== this._tileZoom);
11067
11068		if (!noUpdate || tileZoomChanged) {
11069
11070			this._tileZoom = tileZoom;
11071
11072			if (this._abortLoading) {
11073				this._abortLoading();
11074			}
11075
11076			this._updateLevels();
11077			this._resetGrid();
11078
11079			if (tileZoom !== undefined) {
11080				this._update(center);
11081			}
11082
11083			if (!noPrune) {
11084				this._pruneTiles();
11085			}
11086
11087			// Flag to prevent _updateOpacity from pruning tiles during
11088			// a zoom anim or a pinch gesture
11089			this._noPrune = !!noPrune;
11090		}
11091
11092		this._setZoomTransforms(center, zoom);
11093	},
11094
11095	_setZoomTransforms: function (center, zoom) {
11096		for (var i in this._levels) {
11097			this._setZoomTransform(this._levels[i], center, zoom);
11098		}
11099	},
11100
11101	_setZoomTransform: function (level, center, zoom) {
11102		var scale = this._map.getZoomScale(zoom, level.zoom),
11103		    translate = level.origin.multiplyBy(scale)
11104		        .subtract(this._map._getNewPixelOrigin(center, zoom)).round();
11105
11106		if (any3d) {
11107			setTransform(level.el, translate, scale);
11108		} else {
11109			setPosition(level.el, translate);
11110		}
11111	},
11112
11113	_resetGrid: function () {
11114		var map = this._map,
11115		    crs = map.options.crs,
11116		    tileSize = this._tileSize = this.getTileSize(),
11117		    tileZoom = this._tileZoom;
11118
11119		var bounds = this._map.getPixelWorldBounds(this._tileZoom);
11120		if (bounds) {
11121			this._globalTileRange = this._pxBoundsToTileRange(bounds);
11122		}
11123
11124		this._wrapX = crs.wrapLng && !this.options.noWrap && [
11125			Math.floor(map.project([0, crs.wrapLng[0]], tileZoom).x / tileSize.x),
11126			Math.ceil(map.project([0, crs.wrapLng[1]], tileZoom).x / tileSize.y)
11127		];
11128		this._wrapY = crs.wrapLat && !this.options.noWrap && [
11129			Math.floor(map.project([crs.wrapLat[0], 0], tileZoom).y / tileSize.x),
11130			Math.ceil(map.project([crs.wrapLat[1], 0], tileZoom).y / tileSize.y)
11131		];
11132	},
11133
11134	_onMoveEnd: function () {
11135		if (!this._map || this._map._animatingZoom) { return; }
11136
11137		this._update();
11138	},
11139
11140	_getTiledPixelBounds: function (center) {
11141		var map = this._map,
11142		    mapZoom = map._animatingZoom ? Math.max(map._animateToZoom, map.getZoom()) : map.getZoom(),
11143		    scale = map.getZoomScale(mapZoom, this._tileZoom),
11144		    pixelCenter = map.project(center, this._tileZoom).floor(),
11145		    halfSize = map.getSize().divideBy(scale * 2);
11146
11147		return new Bounds(pixelCenter.subtract(halfSize), pixelCenter.add(halfSize));
11148	},
11149
11150	// Private method to load tiles in the grid's active zoom level according to map bounds
11151	_update: function (center) {
11152		var map = this._map;
11153		if (!map) { return; }
11154		var zoom = this._clampZoom(map.getZoom());
11155
11156		if (center === undefined) { center = map.getCenter(); }
11157		if (this._tileZoom === undefined) { return; }	// if out of minzoom/maxzoom
11158
11159		var pixelBounds = this._getTiledPixelBounds(center),
11160		    tileRange = this._pxBoundsToTileRange(pixelBounds),
11161		    tileCenter = tileRange.getCenter(),
11162		    queue = [],
11163		    margin = this.options.keepBuffer,
11164		    noPruneRange = new Bounds(tileRange.getBottomLeft().subtract([margin, -margin]),
11165		                              tileRange.getTopRight().add([margin, -margin]));
11166
11167		// Sanity check: panic if the tile range contains Infinity somewhere.
11168		if (!(isFinite(tileRange.min.x) &&
11169		      isFinite(tileRange.min.y) &&
11170		      isFinite(tileRange.max.x) &&
11171		      isFinite(tileRange.max.y))) { throw new Error('Attempted to load an infinite number of tiles'); }
11172
11173		for (var key in this._tiles) {
11174			var c = this._tiles[key].coords;
11175			if (c.z !== this._tileZoom || !noPruneRange.contains(new Point(c.x, c.y))) {
11176				this._tiles[key].current = false;
11177			}
11178		}
11179
11180		// _update just loads more tiles. If the tile zoom level differs too much
11181		// from the map's, let _setView reset levels and prune old tiles.
11182		if (Math.abs(zoom - this._tileZoom) > 1) { this._setView(center, zoom); return; }
11183
11184		// create a queue of coordinates to load tiles from
11185		for (var j = tileRange.min.y; j <= tileRange.max.y; j++) {
11186			for (var i = tileRange.min.x; i <= tileRange.max.x; i++) {
11187				var coords = new Point(i, j);
11188				coords.z = this._tileZoom;
11189
11190				if (!this._isValidTile(coords)) { continue; }
11191
11192				var tile = this._tiles[this._tileCoordsToKey(coords)];
11193				if (tile) {
11194					tile.current = true;
11195				} else {
11196					queue.push(coords);
11197				}
11198			}
11199		}
11200
11201		// sort tile queue to load tiles in order of their distance to center
11202		queue.sort(function (a, b) {
11203			return a.distanceTo(tileCenter) - b.distanceTo(tileCenter);
11204		});
11205
11206		if (queue.length !== 0) {
11207			// if it's the first batch of tiles to load
11208			if (!this._loading) {
11209				this._loading = true;
11210				// @event loading: Event
11211				// Fired when the grid layer starts loading tiles.
11212				this.fire('loading');
11213			}
11214
11215			// create DOM fragment to append tiles in one batch
11216			var fragment = document.createDocumentFragment();
11217
11218			for (i = 0; i < queue.length; i++) {
11219				this._addTile(queue[i], fragment);
11220			}
11221
11222			this._level.el.appendChild(fragment);
11223		}
11224	},
11225
11226	_isValidTile: function (coords) {
11227		var crs = this._map.options.crs;
11228
11229		if (!crs.infinite) {
11230			// don't load tile if it's out of bounds and not wrapped
11231			var bounds = this._globalTileRange;
11232			if ((!crs.wrapLng && (coords.x < bounds.min.x || coords.x > bounds.max.x)) ||
11233			    (!crs.wrapLat && (coords.y < bounds.min.y || coords.y > bounds.max.y))) { return false; }
11234		}
11235
11236		if (!this.options.bounds) { return true; }
11237
11238		// don't load tile if it doesn't intersect the bounds in options
11239		var tileBounds = this._tileCoordsToBounds(coords);
11240		return toLatLngBounds(this.options.bounds).overlaps(tileBounds);
11241	},
11242
11243	_keyToBounds: function (key) {
11244		return this._tileCoordsToBounds(this._keyToTileCoords(key));
11245	},
11246
11247	_tileCoordsToNwSe: function (coords) {
11248		var map = this._map,
11249		    tileSize = this.getTileSize(),
11250		    nwPoint = coords.scaleBy(tileSize),
11251		    sePoint = nwPoint.add(tileSize),
11252		    nw = map.unproject(nwPoint, coords.z),
11253		    se = map.unproject(sePoint, coords.z);
11254		return [nw, se];
11255	},
11256
11257	// converts tile coordinates to its geographical bounds
11258	_tileCoordsToBounds: function (coords) {
11259		var bp = this._tileCoordsToNwSe(coords),
11260		    bounds = new LatLngBounds(bp[0], bp[1]);
11261
11262		if (!this.options.noWrap) {
11263			bounds = this._map.wrapLatLngBounds(bounds);
11264		}
11265		return bounds;
11266	},
11267	// converts tile coordinates to key for the tile cache
11268	_tileCoordsToKey: function (coords) {
11269		return coords.x + ':' + coords.y + ':' + coords.z;
11270	},
11271
11272	// converts tile cache key to coordinates
11273	_keyToTileCoords: function (key) {
11274		var k = key.split(':'),
11275		    coords = new Point(+k[0], +k[1]);
11276		coords.z = +k[2];
11277		return coords;
11278	},
11279
11280	_removeTile: function (key) {
11281		var tile = this._tiles[key];
11282		if (!tile) { return; }
11283
11284		remove(tile.el);
11285
11286		delete this._tiles[key];
11287
11288		// @event tileunload: TileEvent
11289		// Fired when a tile is removed (e.g. when a tile goes off the screen).
11290		this.fire('tileunload', {
11291			tile: tile.el,
11292			coords: this._keyToTileCoords(key)
11293		});
11294	},
11295
11296	_initTile: function (tile) {
11297		addClass(tile, 'leaflet-tile');
11298
11299		var tileSize = this.getTileSize();
11300		tile.style.width = tileSize.x + 'px';
11301		tile.style.height = tileSize.y + 'px';
11302
11303		tile.onselectstart = falseFn;
11304		tile.onmousemove = falseFn;
11305
11306		// update opacity on tiles in IE7-8 because of filter inheritance problems
11307		if (ielt9 && this.options.opacity < 1) {
11308			setOpacity(tile, this.options.opacity);
11309		}
11310
11311		// without this hack, tiles disappear after zoom on Chrome for Android
11312		// https://github.com/Leaflet/Leaflet/issues/2078
11313		if (android && !android23) {
11314			tile.style.WebkitBackfaceVisibility = 'hidden';
11315		}
11316	},
11317
11318	_addTile: function (coords, container) {
11319		var tilePos = this._getTilePos(coords),
11320		    key = this._tileCoordsToKey(coords);
11321
11322		var tile = this.createTile(this._wrapCoords(coords), bind(this._tileReady, this, coords));
11323
11324		this._initTile(tile);
11325
11326		// if createTile is defined with a second argument ("done" callback),
11327		// we know that tile is async and will be ready later; otherwise
11328		if (this.createTile.length < 2) {
11329			// mark tile as ready, but delay one frame for opacity animation to happen
11330			requestAnimFrame(bind(this._tileReady, this, coords, null, tile));
11331		}
11332
11333		setPosition(tile, tilePos);
11334
11335		// save tile in cache
11336		this._tiles[key] = {
11337			el: tile,
11338			coords: coords,
11339			current: true
11340		};
11341
11342		container.appendChild(tile);
11343		// @event tileloadstart: TileEvent
11344		// Fired when a tile is requested and starts loading.
11345		this.fire('tileloadstart', {
11346			tile: tile,
11347			coords: coords
11348		});
11349	},
11350
11351	_tileReady: function (coords, err, tile) {
11352		if (err) {
11353			// @event tileerror: TileErrorEvent
11354			// Fired when there is an error loading a tile.
11355			this.fire('tileerror', {
11356				error: err,
11357				tile: tile,
11358				coords: coords
11359			});
11360		}
11361
11362		var key = this._tileCoordsToKey(coords);
11363
11364		tile = this._tiles[key];
11365		if (!tile) { return; }
11366
11367		tile.loaded = +new Date();
11368		if (this._map._fadeAnimated) {
11369			setOpacity(tile.el, 0);
11370			cancelAnimFrame(this._fadeFrame);
11371			this._fadeFrame = requestAnimFrame(this._updateOpacity, this);
11372		} else {
11373			tile.active = true;
11374			this._pruneTiles();
11375		}
11376
11377		if (!err) {
11378			addClass(tile.el, 'leaflet-tile-loaded');
11379
11380			// @event tileload: TileEvent
11381			// Fired when a tile loads.
11382			this.fire('tileload', {
11383				tile: tile.el,
11384				coords: coords
11385			});
11386		}
11387
11388		if (this._noTilesToLoad()) {
11389			this._loading = false;
11390			// @event load: Event
11391			// Fired when the grid layer loaded all visible tiles.
11392			this.fire('load');
11393
11394			if (ielt9 || !this._map._fadeAnimated) {
11395				requestAnimFrame(this._pruneTiles, this);
11396			} else {
11397				// Wait a bit more than 0.2 secs (the duration of the tile fade-in)
11398				// to trigger a pruning.
11399				setTimeout(bind(this._pruneTiles, this), 250);
11400			}
11401		}
11402	},
11403
11404	_getTilePos: function (coords) {
11405		return coords.scaleBy(this.getTileSize()).subtract(this._level.origin);
11406	},
11407
11408	_wrapCoords: function (coords) {
11409		var newCoords = new Point(
11410			this._wrapX ? wrapNum(coords.x, this._wrapX) : coords.x,
11411			this._wrapY ? wrapNum(coords.y, this._wrapY) : coords.y);
11412		newCoords.z = coords.z;
11413		return newCoords;
11414	},
11415
11416	_pxBoundsToTileRange: function (bounds) {
11417		var tileSize = this.getTileSize();
11418		return new Bounds(
11419			bounds.min.unscaleBy(tileSize).floor(),
11420			bounds.max.unscaleBy(tileSize).ceil().subtract([1, 1]));
11421	},
11422
11423	_noTilesToLoad: function () {
11424		for (var key in this._tiles) {
11425			if (!this._tiles[key].loaded) { return false; }
11426		}
11427		return true;
11428	}
11429});
11430
11431// @factory L.gridLayer(options?: GridLayer options)
11432// Creates a new instance of GridLayer with the supplied options.
11433function gridLayer(options) {
11434	return new GridLayer(options);
11435}
11436
11437/*
11438 * @class TileLayer
11439 * @inherits GridLayer
11440 * @aka L.TileLayer
11441 * Used to load and display tile layers on the map. Note that most tile servers require attribution, which you can set under `Layer`. Extends `GridLayer`.
11442 *
11443 * @example
11444 *
11445 * ```js
11446 * L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png?{foo}', {foo: 'bar', attribution: 'Map data &copy; <a href="https://www.openstreetmap.org/">OpenStreetMap</a> contributors, <a href="https://creativecommons.org/licenses/by-sa/2.0/">CC-BY-SA</a>'}).addTo(map);
11447 * ```
11448 *
11449 * @section URL template
11450 * @example
11451 *
11452 * A string of the following form:
11453 *
11454 * ```
11455 * 'http://{s}.somedomain.com/blabla/{z}/{x}/{y}{r}.png'
11456 * ```
11457 *
11458 * `{s}` means one of the available subdomains (used sequentially to help with browser parallel requests per domain limitation; subdomain values are specified in options; `a`, `b` or `c` by default, can be omitted), `{z}` — zoom level, `{x}` and `{y}` — tile coordinates. `{r}` can be used to add "&commat;2x" to the URL to load retina tiles.
11459 *
11460 * You can use custom keys in the template, which will be [evaluated](#util-template) from TileLayer options, like this:
11461 *
11462 * ```
11463 * L.tileLayer('http://{s}.somedomain.com/{foo}/{z}/{x}/{y}.png', {foo: 'bar'});
11464 * ```
11465 */
11466
11467
11468var TileLayer = GridLayer.extend({
11469
11470	// @section
11471	// @aka TileLayer options
11472	options: {
11473		// @option minZoom: Number = 0
11474		// The minimum zoom level down to which this layer will be displayed (inclusive).
11475		minZoom: 0,
11476
11477		// @option maxZoom: Number = 18
11478		// The maximum zoom level up to which this layer will be displayed (inclusive).
11479		maxZoom: 18,
11480
11481		// @option subdomains: String|String[] = 'abc'
11482		// 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.
11483		subdomains: 'abc',
11484
11485		// @option errorTileUrl: String = ''
11486		// URL to the tile image to show in place of the tile that failed to load.
11487		errorTileUrl: '',
11488
11489		// @option zoomOffset: Number = 0
11490		// The zoom number used in tile URLs will be offset with this value.
11491		zoomOffset: 0,
11492
11493		// @option tms: Boolean = false
11494		// If `true`, inverses Y axis numbering for tiles (turn this on for [TMS](https://en.wikipedia.org/wiki/Tile_Map_Service) services).
11495		tms: false,
11496
11497		// @option zoomReverse: Boolean = false
11498		// If set to true, the zoom number used in tile URLs will be reversed (`maxZoom - zoom` instead of `zoom`)
11499		zoomReverse: false,
11500
11501		// @option detectRetina: Boolean = false
11502		// 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.
11503		detectRetina: false,
11504
11505		// @option crossOrigin: Boolean|String = false
11506		// Whether the crossOrigin attribute will be added to the tiles.
11507		// If a String is provided, all tiles will have their crossOrigin attribute set to the String provided. This is needed if you want to access tile pixel data.
11508		// Refer to [CORS Settings](https://developer.mozilla.org/en-US/docs/Web/HTML/CORS_settings_attributes) for valid String values.
11509		crossOrigin: false
11510	},
11511
11512	initialize: function (url, options) {
11513
11514		this._url = url;
11515
11516		options = setOptions(this, options);
11517
11518		// detecting retina displays, adjusting tileSize and zoom levels
11519		if (options.detectRetina && retina && options.maxZoom > 0) {
11520
11521			options.tileSize = Math.floor(options.tileSize / 2);
11522
11523			if (!options.zoomReverse) {
11524				options.zoomOffset++;
11525				options.maxZoom--;
11526			} else {
11527				options.zoomOffset--;
11528				options.minZoom++;
11529			}
11530
11531			options.minZoom = Math.max(0, options.minZoom);
11532		}
11533
11534		if (typeof options.subdomains === 'string') {
11535			options.subdomains = options.subdomains.split('');
11536		}
11537
11538		// for https://github.com/Leaflet/Leaflet/issues/137
11539		if (!android) {
11540			this.on('tileunload', this._onTileRemove);
11541		}
11542	},
11543
11544	// @method setUrl(url: String, noRedraw?: Boolean): this
11545	// Updates the layer's URL template and redraws it (unless `noRedraw` is set to `true`).
11546	// If the URL does not change, the layer will not be redrawn unless
11547	// the noRedraw parameter is set to false.
11548	setUrl: function (url, noRedraw) {
11549		if (this._url === url && noRedraw === undefined) {
11550			noRedraw = true;
11551		}
11552
11553		this._url = url;
11554
11555		if (!noRedraw) {
11556			this.redraw();
11557		}
11558		return this;
11559	},
11560
11561	// @method createTile(coords: Object, done?: Function): HTMLElement
11562	// Called only internally, overrides GridLayer's [`createTile()`](#gridlayer-createtile)
11563	// to return an `<img>` HTML element with the appropriate image URL given `coords`. The `done`
11564	// callback is called when the tile has been loaded.
11565	createTile: function (coords, done) {
11566		var tile = document.createElement('img');
11567
11568		on(tile, 'load', bind(this._tileOnLoad, this, done, tile));
11569		on(tile, 'error', bind(this._tileOnError, this, done, tile));
11570
11571		if (this.options.crossOrigin || this.options.crossOrigin === '') {
11572			tile.crossOrigin = this.options.crossOrigin === true ? '' : this.options.crossOrigin;
11573		}
11574
11575		/*
11576		 Alt tag is set to empty string to keep screen readers from reading URL and for compliance reasons
11577		 http://www.w3.org/TR/WCAG20-TECHS/H67
11578		*/
11579		tile.alt = '';
11580
11581		/*
11582		 Set role="presentation" to force screen readers to ignore this
11583		 https://www.w3.org/TR/wai-aria/roles#textalternativecomputation
11584		*/
11585		tile.setAttribute('role', 'presentation');
11586
11587		tile.src = this.getTileUrl(coords);
vendor: 50,258 bytes, lines 11588-13459
11588
11589		return tile;
11590	},
11591
11592	// @section Extension methods
11593	// @uninheritable
11594	// Layers extending `TileLayer` might reimplement the following method.
11595	// @method getTileUrl(coords: Object): String
11596	// Called only internally, returns the URL for a tile given its coordinates.
11597	// Classes extending `TileLayer` can override this function to provide custom tile URL naming schemes.
11598	getTileUrl: function (coords) {
11599		var data = {
11600			r: retina ? '@2x' : '',
11601			s: this._getSubdomain(coords),
11602			x: coords.x,
11603			y: coords.y,
11604			z: this._getZoomForUrl()
11605		};
11606		if (this._map && !this._map.options.crs.infinite) {
11607			var invertedY = this._globalTileRange.max.y - coords.y;
11608			if (this.options.tms) {
11609				data['y'] = invertedY;
11610			}
11611			data['-y'] = invertedY;
11612		}
11613
11614		return template(this._url, extend(data, this.options));
11615	},
11616
11617	_tileOnLoad: function (done, tile) {
11618		// For https://github.com/Leaflet/Leaflet/issues/3332
11619		if (ielt9) {
11620			setTimeout(bind(done, this, null, tile), 0);
11621		} else {
11622			done(null, tile);
11623		}
11624	},
11625
11626	_tileOnError: function (done, tile, e) {
11627		var errorUrl = this.options.errorTileUrl;
11628		if (errorUrl && tile.getAttribute('src') !== errorUrl) {
11629			tile.src = errorUrl;
11630		}
11631		done(e, tile);
11632	},
11633
11634	_onTileRemove: function (e) {
11635		e.tile.onload = null;
11636	},
11637
11638	_getZoomForUrl: function () {
11639		var zoom = this._tileZoom,
11640		maxZoom = this.options.maxZoom,
11641		zoomReverse = this.options.zoomReverse,
11642		zoomOffset = this.options.zoomOffset;
11643
11644		if (zoomReverse) {
11645			zoom = maxZoom - zoom;
11646		}
11647
11648		return zoom + zoomOffset;
11649	},
11650
11651	_getSubdomain: function (tilePoint) {
11652		var index = Math.abs(tilePoint.x + tilePoint.y) % this.options.subdomains.length;
11653		return this.options.subdomains[index];
11654	},
11655
11656	// stops loading all tiles in the background layer
11657	_abortLoading: function () {
11658		var i, tile;
11659		for (i in this._tiles) {
11660			if (this._tiles[i].coords.z !== this._tileZoom) {
11661				tile = this._tiles[i].el;
11662
11663				tile.onload = falseFn;
11664				tile.onerror = falseFn;
11665
11666				if (!tile.complete) {
11667					tile.src = emptyImageUrl;
11668					remove(tile);
11669					delete this._tiles[i];
11670				}
11671			}
11672		}
11673	},
11674
11675	_removeTile: function (key) {
11676		var tile = this._tiles[key];
11677		if (!tile) { return; }
11678
11679		// Cancels any pending http requests associated with the tile
11680		// unless we're on Android's stock browser,
11681		// see https://github.com/Leaflet/Leaflet/issues/137
11682		if (!androidStock) {
11683			tile.el.setAttribute('src', emptyImageUrl);
11684		}
11685
11686		return GridLayer.prototype._removeTile.call(this, key);
11687	},
11688
11689	_tileReady: function (coords, err, tile) {
11690		if (!this._map || (tile && tile.getAttribute('src') === emptyImageUrl)) {
11691			return;
11692		}
11693
11694		return GridLayer.prototype._tileReady.call(this, coords, err, tile);
11695	}
11696});
11697
11698
11699// @factory L.tilelayer(urlTemplate: String, options?: TileLayer options)
11700// Instantiates a tile layer object given a `URL template` and optionally an options object.
11701
11702function tileLayer(url, options) {
11703	return new TileLayer(url, options);
11704}
11705
11706/*
11707 * @class TileLayer.WMS
11708 * @inherits TileLayer
11709 * @aka L.TileLayer.WMS
11710 * Used to display [WMS](https://en.wikipedia.org/wiki/Web_Map_Service) services as tile layers on the map. Extends `TileLayer`.
11711 *
11712 * @example
11713 *
11714 * ```js
11715 * var nexrad = L.tileLayer.wms("http://mesonet.agron.iastate.edu/cgi-bin/wms/nexrad/n0r.cgi", {
11716 * 	layers: 'nexrad-n0r-900913',
11717 * 	format: 'image/png',
11718 * 	transparent: true,
11719 * 	attribution: "Weather data © 2012 IEM Nexrad"
11720 * });
11721 * ```
11722 */
11723
11724var TileLayerWMS = TileLayer.extend({
11725
11726	// @section
11727	// @aka TileLayer.WMS options
11728	// If any custom options not documented here are used, they will be sent to the
11729	// WMS server as extra parameters in each request URL. This can be useful for
11730	// [non-standard vendor WMS parameters](http://docs.geoserver.org/stable/en/user/services/wms/vendor.html).
11731	defaultWmsParams: {
11732		service: 'WMS',
11733		request: 'GetMap',
11734
11735		// @option layers: String = ''
11736		// **(required)** Comma-separated list of WMS layers to show.
11737		layers: '',
11738
11739		// @option styles: String = ''
11740		// Comma-separated list of WMS styles.
11741		styles: '',
11742
11743		// @option format: String = 'image/jpeg'
11744		// WMS image format (use `'image/png'` for layers with transparency).
11745		format: 'image/jpeg',
11746
11747		// @option transparent: Boolean = false
11748		// If `true`, the WMS service will return images with transparency.
11749		transparent: false,
11750
11751		// @option version: String = '1.1.1'
11752		// Version of the WMS service to use
11753		version: '1.1.1'
11754	},
11755
11756	options: {
11757		// @option crs: CRS = null
11758		// Coordinate Reference System to use for the WMS requests, defaults to
11759		// map CRS. Don't change this if you're not sure what it means.
11760		crs: null,
11761
11762		// @option uppercase: Boolean = false
11763		// If `true`, WMS request parameter keys will be uppercase.
11764		uppercase: false
11765	},
11766
11767	initialize: function (url, options) {
11768
11769		this._url = url;
11770
11771		var wmsParams = extend({}, this.defaultWmsParams);
11772
11773		// all keys that are not TileLayer options go to WMS params
11774		for (var i in options) {
11775			if (!(i in this.options)) {
11776				wmsParams[i] = options[i];
11777			}
11778		}
11779
11780		options = setOptions(this, options);
11781
11782		var realRetina = options.detectRetina && retina ? 2 : 1;
11783		var tileSize = this.getTileSize();
11784		wmsParams.width = tileSize.x * realRetina;
11785		wmsParams.height = tileSize.y * realRetina;
11786
11787		this.wmsParams = wmsParams;
11788	},
11789
11790	onAdd: function (map) {
11791
11792		this._crs = this.options.crs || map.options.crs;
11793		this._wmsVersion = parseFloat(this.wmsParams.version);
11794
11795		var projectionKey = this._wmsVersion >= 1.3 ? 'crs' : 'srs';
11796		this.wmsParams[projectionKey] = this._crs.code;
11797
11798		TileLayer.prototype.onAdd.call(this, map);
11799	},
11800
11801	getTileUrl: function (coords) {
11802
11803		var tileBounds = this._tileCoordsToNwSe(coords),
11804		    crs = this._crs,
11805		    bounds = toBounds(crs.project(tileBounds[0]), crs.project(tileBounds[1])),
11806		    min = bounds.min,
11807		    max = bounds.max,
11808		    bbox = (this._wmsVersion >= 1.3 && this._crs === EPSG4326 ?
11809		    [min.y, min.x, max.y, max.x] :
11810		    [min.x, min.y, max.x, max.y]).join(','),
11811		    url = TileLayer.prototype.getTileUrl.call(this, coords);
11812		return url +
11813			getParamString(this.wmsParams, url, this.options.uppercase) +
11814			(this.options.uppercase ? '&BBOX=' : '&bbox=') + bbox;
11815	},
11816
11817	// @method setParams(params: Object, noRedraw?: Boolean): this
11818	// Merges an object with the new parameters and re-requests tiles on the current screen (unless `noRedraw` was set to true).
11819	setParams: function (params, noRedraw) {
11820
11821		extend(this.wmsParams, params);
11822
11823		if (!noRedraw) {
11824			this.redraw();
11825		}
11826
11827		return this;
11828	}
11829});
11830
11831
11832// @factory L.tileLayer.wms(baseUrl: String, options: TileLayer.WMS options)
11833// Instantiates a WMS tile layer object given a base URL of the WMS service and a WMS parameters/options object.
11834function tileLayerWMS(url, options) {
11835	return new TileLayerWMS(url, options);
11836}
11837
11838TileLayer.WMS = TileLayerWMS;
11839tileLayer.wms = tileLayerWMS;
11840
11841/*
11842 * @class Renderer
11843 * @inherits Layer
11844 * @aka L.Renderer
11845 *
11846 * Base class for vector renderer implementations (`SVG`, `Canvas`). Handles the
11847 * DOM container of the renderer, its bounds, and its zoom animation.
11848 *
11849 * A `Renderer` works as an implicit layer group for all `Path`s - the renderer
11850 * itself can be added or removed to the map. All paths use a renderer, which can
11851 * be implicit (the map will decide the type of renderer and use it automatically)
11852 * or explicit (using the [`renderer`](#path-renderer) option of the path).
11853 *
11854 * Do not use this class directly, use `SVG` and `Canvas` instead.
11855 *
11856 * @event update: Event
11857 * Fired when the renderer updates its bounds, center and zoom, for example when
11858 * its map has moved
11859 */
11860
11861var Renderer = Layer.extend({
11862
11863	// @section
11864	// @aka Renderer options
11865	options: {
11866		// @option padding: Number = 0.1
11867		// How much to extend the clip area around the map view (relative to its size)
11868		// e.g. 0.1 would be 10% of map view in each direction
11869		padding: 0.1,
11870
11871		// @option tolerance: Number = 0
11872		// How much to extend click tolerance round a path/object on the map
11873		tolerance : 0
11874	},
11875
11876	initialize: function (options) {
11877		setOptions(this, options);
11878		stamp(this);
11879		this._layers = this._layers || {};
11880	},
11881
11882	onAdd: function () {
11883		if (!this._container) {
11884			this._initContainer(); // defined by renderer implementations
11885
11886			if (this._zoomAnimated) {
11887				addClass(this._container, 'leaflet-zoom-animated');
11888			}
11889		}
11890
11891		this.getPane().appendChild(this._container);
11892		this._update();
11893		this.on('update', this._updatePaths, this);
11894	},
11895
11896	onRemove: function () {
11897		this.off('update', this._updatePaths, this);
11898		this._destroyContainer();
11899	},
11900
11901	getEvents: function () {
11902		var events = {
11903			viewreset: this._reset,
11904			zoom: this._onZoom,
11905			moveend: this._update,
11906			zoomend: this._onZoomEnd
11907		};
11908		if (this._zoomAnimated) {
11909			events.zoomanim = this._onAnimZoom;
11910		}
11911		return events;
11912	},
11913
11914	_onAnimZoom: function (ev) {
11915		this._updateTransform(ev.center, ev.zoom);
11916	},
11917
11918	_onZoom: function () {
11919		this._updateTransform(this._map.getCenter(), this._map.getZoom());
11920	},
11921
11922	_updateTransform: function (center, zoom) {
11923		var scale = this._map.getZoomScale(zoom, this._zoom),
11924		    position = getPosition(this._container),
11925		    viewHalf = this._map.getSize().multiplyBy(0.5 + this.options.padding),
11926		    currentCenterPoint = this._map.project(this._center, zoom),
11927		    destCenterPoint = this._map.project(center, zoom),
11928		    centerOffset = destCenterPoint.subtract(currentCenterPoint),
11929
11930		    topLeftOffset = viewHalf.multiplyBy(-scale).add(position).add(viewHalf).subtract(centerOffset);
11931
11932		if (any3d) {
11933			setTransform(this._container, topLeftOffset, scale);
11934		} else {
11935			setPosition(this._container, topLeftOffset);
11936		}
11937	},
11938
11939	_reset: function () {
11940		this._update();
11941		this._updateTransform(this._center, this._zoom);
11942
11943		for (var id in this._layers) {
11944			this._layers[id]._reset();
11945		}
11946	},
11947
11948	_onZoomEnd: function () {
11949		for (var id in this._layers) {
11950			this._layers[id]._project();
11951		}
11952	},
11953
11954	_updatePaths: function () {
11955		for (var id in this._layers) {
11956			this._layers[id]._update();
11957		}
11958	},
11959
11960	_update: function () {
11961		// Update pixel bounds of renderer container (for positioning/sizing/clipping later)
11962		// Subclasses are responsible of firing the 'update' event.
11963		var p = this.options.padding,
11964		    size = this._map.getSize(),
11965		    min = this._map.containerPointToLayerPoint(size.multiplyBy(-p)).round();
11966
11967		this._bounds = new Bounds(min, min.add(size.multiplyBy(1 + p * 2)).round());
11968
11969		this._center = this._map.getCenter();
11970		this._zoom = this._map.getZoom();
11971	}
11972});
11973
11974/*
11975 * @class Canvas
11976 * @inherits Renderer
11977 * @aka L.Canvas
11978 *
11979 * Allows vector layers to be displayed with [`<canvas>`](https://developer.mozilla.org/docs/Web/API/Canvas_API).
11980 * Inherits `Renderer`.
11981 *
11982 * Due to [technical limitations](http://caniuse.com/#search=canvas), Canvas is not
11983 * available in all web browsers, notably IE8, and overlapping geometries might
11984 * not display properly in some edge cases.
11985 *
11986 * @example
11987 *
11988 * Use Canvas by default for all paths in the map:
11989 *
11990 * ```js
11991 * var map = L.map('map', {
11992 * 	renderer: L.canvas()
11993 * });
11994 * ```
11995 *
11996 * Use a Canvas renderer with extra padding for specific vector geometries:
11997 *
11998 * ```js
11999 * var map = L.map('map');
12000 * var myRenderer = L.canvas({ padding: 0.5 });
12001 * var line = L.polyline( coordinates, { renderer: myRenderer } );
12002 * var circle = L.circle( center, { renderer: myRenderer } );
12003 * ```
12004 */
12005
12006var Canvas = Renderer.extend({
12007	getEvents: function () {
12008		var events = Renderer.prototype.getEvents.call(this);
12009		events.viewprereset = this._onViewPreReset;
12010		return events;
12011	},
12012
12013	_onViewPreReset: function () {
12014		// Set a flag so that a viewprereset+moveend+viewreset only updates&redraws once
12015		this._postponeUpdatePaths = true;
12016	},
12017
12018	onAdd: function () {
12019		Renderer.prototype.onAdd.call(this);
12020
12021		// Redraw vectors since canvas is cleared upon removal,
12022		// in case of removing the renderer itself from the map.
12023		this._draw();
12024	},
12025
12026	_initContainer: function () {
12027		var container = this._container = document.createElement('canvas');
12028
12029		on(container, 'mousemove', throttle(this._onMouseMove, 32, this), this);
12030		on(container, 'click dblclick mousedown mouseup contextmenu', this._onClick, this);
12031		on(container, 'mouseout', this._handleMouseOut, this);
12032
12033		this._ctx = container.getContext('2d');
12034	},
12035
12036	_destroyContainer: function () {
12037		cancelAnimFrame(this._redrawRequest);
12038		delete this._ctx;
12039		remove(this._container);
12040		off(this._container);
12041		delete this._container;
12042	},
12043
12044	_updatePaths: function () {
12045		if (this._postponeUpdatePaths) { return; }
12046
12047		var layer;
12048		this._redrawBounds = null;
12049		for (var id in this._layers) {
12050			layer = this._layers[id];
12051			layer._update();
12052		}
12053		this._redraw();
12054	},
12055
12056	_update: function () {
12057		if (this._map._animatingZoom && this._bounds) { return; }
12058
12059		Renderer.prototype._update.call(this);
12060
12061		var b = this._bounds,
12062		    container = this._container,
12063		    size = b.getSize(),
12064		    m = retina ? 2 : 1;
12065
12066		setPosition(container, b.min);
12067
12068		// set canvas size (also clearing it); use double size on retina
12069		container.width = m * size.x;
12070		container.height = m * size.y;
12071		container.style.width = size.x + 'px';
12072		container.style.height = size.y + 'px';
12073
12074		if (retina) {
12075			this._ctx.scale(2, 2);
12076		}
12077
12078		// translate so we use the same path coordinates after canvas element moves
12079		this._ctx.translate(-b.min.x, -b.min.y);
12080
12081		// Tell paths to redraw themselves
12082		this.fire('update');
12083	},
12084
12085	_reset: function () {
12086		Renderer.prototype._reset.call(this);
12087
12088		if (this._postponeUpdatePaths) {
12089			this._postponeUpdatePaths = false;
12090			this._updatePaths();
12091		}
12092	},
12093
12094	_initPath: function (layer) {
12095		this._updateDashArray(layer);
12096		this._layers[stamp(layer)] = layer;
12097
12098		var order = layer._order = {
12099			layer: layer,
12100			prev: this._drawLast,
12101			next: null
12102		};
12103		if (this._drawLast) { this._drawLast.next = order; }
12104		this._drawLast = order;
12105		this._drawFirst = this._drawFirst || this._drawLast;
12106	},
12107
12108	_addPath: function (layer) {
12109		this._requestRedraw(layer);
12110	},
12111
12112	_removePath: function (layer) {
12113		var order = layer._order;
12114		var next = order.next;
12115		var prev = order.prev;
12116
12117		if (next) {
12118			next.prev = prev;
12119		} else {
12120			this._drawLast = prev;
12121		}
12122		if (prev) {
12123			prev.next = next;
12124		} else {
12125			this._drawFirst = next;
12126		}
12127
12128		delete layer._order;
12129
12130		delete this._layers[stamp(layer)];
12131
12132		this._requestRedraw(layer);
12133	},
12134
12135	_updatePath: function (layer) {
12136		// Redraw the union of the layer's old pixel
12137		// bounds and the new pixel bounds.
12138		this._extendRedrawBounds(layer);
12139		layer._project();
12140		layer._update();
12141		// The redraw will extend the redraw bounds
12142		// with the new pixel bounds.
12143		this._requestRedraw(layer);
12144	},
12145
12146	_updateStyle: function (layer) {
12147		this._updateDashArray(layer);
12148		this._requestRedraw(layer);
12149	},
12150
12151	_updateDashArray: function (layer) {
12152		if (typeof layer.options.dashArray === 'string') {
12153			var parts = layer.options.dashArray.split(/[, ]+/),
12154			    dashArray = [],
12155			    dashValue,
12156			    i;
12157			for (i = 0; i < parts.length; i++) {
12158				dashValue = Number(parts[i]);
12159				// Ignore dash array containing invalid lengths
12160				if (isNaN(dashValue)) { return; }
12161				dashArray.push(dashValue);
12162			}
12163			layer.options._dashArray = dashArray;
12164		} else {
12165			layer.options._dashArray = layer.options.dashArray;
12166		}
12167	},
12168
12169	_requestRedraw: function (layer) {
12170		if (!this._map) { return; }
12171
12172		this._extendRedrawBounds(layer);
12173		this._redrawRequest = this._redrawRequest || requestAnimFrame(this._redraw, this);
12174	},
12175
12176	_extendRedrawBounds: function (layer) {
12177		if (layer._pxBounds) {
12178			var padding = (layer.options.weight || 0) + 1;
12179			this._redrawBounds = this._redrawBounds || new Bounds();
12180			this._redrawBounds.extend(layer._pxBounds.min.subtract([padding, padding]));
12181			this._redrawBounds.extend(layer._pxBounds.max.add([padding, padding]));
12182		}
12183	},
12184
12185	_redraw: function () {
12186		this._redrawRequest = null;
12187
12188		if (this._redrawBounds) {
12189			this._redrawBounds.min._floor();
12190			this._redrawBounds.max._ceil();
12191		}
12192
12193		this._clear(); // clear layers in redraw bounds
12194		this._draw(); // draw layers
12195
12196		this._redrawBounds = null;
12197	},
12198
12199	_clear: function () {
12200		var bounds = this._redrawBounds;
12201		if (bounds) {
12202			var size = bounds.getSize();
12203			this._ctx.clearRect(bounds.min.x, bounds.min.y, size.x, size.y);
12204		} else {
12205			this._ctx.clearRect(0, 0, this._container.width, this._container.height);
12206		}
12207	},
12208
12209	_draw: function () {
12210		var layer, bounds = this._redrawBounds;
12211		this._ctx.save();
12212		if (bounds) {
12213			var size = bounds.getSize();
12214			this._ctx.beginPath();
12215			this._ctx.rect(bounds.min.x, bounds.min.y, size.x, size.y);
12216			this._ctx.clip();
12217		}
12218
12219		this._drawing = true;
12220
12221		for (var order = this._drawFirst; order; order = order.next) {
12222			layer = order.layer;
12223			if (!bounds || (layer._pxBounds && layer._pxBounds.intersects(bounds))) {
12224				layer._updatePath();
12225			}
12226		}
12227
12228		this._drawing = false;
12229
12230		this._ctx.restore();  // Restore state before clipping.
12231	},
12232
12233	_updatePoly: function (layer, closed) {
12234		if (!this._drawing) { return; }
12235
12236		var i, j, len2, p,
12237		    parts = layer._parts,
12238		    len = parts.length,
12239		    ctx = this._ctx;
12240
12241		if (!len) { return; }
12242
12243		ctx.beginPath();
12244
12245		for (i = 0; i < len; i++) {
12246			for (j = 0, len2 = parts[i].length; j < len2; j++) {
12247				p = parts[i][j];
12248				ctx[j ? 'lineTo' : 'moveTo'](p.x, p.y);
12249			}
12250			if (closed) {
12251				ctx.closePath();
12252			}
12253		}
12254
12255		this._fillStroke(ctx, layer);
12256
12257		// TODO optimization: 1 fill/stroke for all features with equal style instead of 1 for each feature
12258	},
12259
12260	_updateCircle: function (layer) {
12261
12262		if (!this._drawing || layer._empty()) { return; }
12263
12264		var p = layer._point,
12265		    ctx = this._ctx,
12266		    r = Math.max(Math.round(layer._radius), 1),
12267		    s = (Math.max(Math.round(layer._radiusY), 1) || r) / r;
12268
12269		if (s !== 1) {
12270			ctx.save();
12271			ctx.scale(1, s);
12272		}
12273
12274		ctx.beginPath();
12275		ctx.arc(p.x, p.y / s, r, 0, Math.PI * 2, false);
12276
12277		if (s !== 1) {
12278			ctx.restore();
12279		}
12280
12281		this._fillStroke(ctx, layer);
12282	},
12283
12284	_fillStroke: function (ctx, layer) {
12285		var options = layer.options;
12286
12287		if (options.fill) {
12288			ctx.globalAlpha = options.fillOpacity;
12289			ctx.fillStyle = options.fillColor || options.color;
12290			ctx.fill(options.fillRule || 'evenodd');
12291		}
12292
12293		if (options.stroke && options.weight !== 0) {
12294			if (ctx.setLineDash) {
12295				ctx.setLineDash(layer.options && layer.options._dashArray || []);
12296			}
12297			ctx.globalAlpha = options.opacity;
12298			ctx.lineWidth = options.weight;
12299			ctx.strokeStyle = options.color;
12300			ctx.lineCap = options.lineCap;
12301			ctx.lineJoin = options.lineJoin;
12302			ctx.stroke();
12303		}
12304	},
12305
12306	// Canvas obviously doesn't have mouse events for individual drawn objects,
12307	// so we emulate that by calculating what's under the mouse on mousemove/click manually
12308
12309	_onClick: function (e) {
12310		var point = this._map.mouseEventToLayerPoint(e), layer, clickedLayer;
12311
12312		for (var order = this._drawFirst; order; order = order.next) {
12313			layer = order.layer;
12314			if (layer.options.interactive && layer._containsPoint(point) && !this._map._draggableMoved(layer)) {
12315				clickedLayer = layer;
12316			}
12317		}
12318		if (clickedLayer)  {
12319			fakeStop(e);
12320			this._fireEvent([clickedLayer], e);
12321		}
12322	},
12323
12324	_onMouseMove: function (e) {
12325		if (!this._map || this._map.dragging.moving() || this._map._animatingZoom) { return; }
12326
12327		var point = this._map.mouseEventToLayerPoint(e);
12328		this._handleMouseHover(e, point);
12329	},
12330
12331
12332	_handleMouseOut: function (e) {
12333		var layer = this._hoveredLayer;
12334		if (layer) {
12335			// if we're leaving the layer, fire mouseout
12336			removeClass(this._container, 'leaflet-interactive');
12337			this._fireEvent([layer], e, 'mouseout');
12338			this._hoveredLayer = null;
12339		}
12340	},
12341
12342	_handleMouseHover: function (e, point) {
12343		var layer, candidateHoveredLayer;
12344
12345		for (var order = this._drawFirst; order; order = order.next) {
12346			layer = order.layer;
12347			if (layer.options.interactive && layer._containsPoint(point)) {
12348				candidateHoveredLayer = layer;
12349			}
12350		}
12351
12352		if (candidateHoveredLayer !== this._hoveredLayer) {
12353			this._handleMouseOut(e);
12354
12355			if (candidateHoveredLayer) {
12356				addClass(this._container, 'leaflet-interactive'); // change cursor
12357				this._fireEvent([candidateHoveredLayer], e, 'mouseover');
12358				this._hoveredLayer = candidateHoveredLayer;
12359			}
12360		}
12361
12362		if (this._hoveredLayer) {
12363			this._fireEvent([this._hoveredLayer], e);
12364		}
12365	},
12366
12367	_fireEvent: function (layers, e, type) {
12368		this._map._fireDOMEvent(e, type || e.type, layers);
12369	},
12370
12371	_bringToFront: function (layer) {
12372		var order = layer._order;
12373
12374		if (!order) { return; }
12375
12376		var next = order.next;
12377		var prev = order.prev;
12378
12379		if (next) {
12380			next.prev = prev;
12381		} else {
12382			// Already last
12383			return;
12384		}
12385		if (prev) {
12386			prev.next = next;
12387		} else if (next) {
12388			// Update first entry unless this is the
12389			// single entry
12390			this._drawFirst = next;
12391		}
12392
12393		order.prev = this._drawLast;
12394		this._drawLast.next = order;
12395
12396		order.next = null;
12397		this._drawLast = order;
12398
12399		this._requestRedraw(layer);
12400	},
12401
12402	_bringToBack: function (layer) {
12403		var order = layer._order;
12404
12405		if (!order) { return; }
12406
12407		var next = order.next;
12408		var prev = order.prev;
12409
12410		if (prev) {
12411			prev.next = next;
12412		} else {
12413			// Already first
12414			return;
12415		}
12416		if (next) {
12417			next.prev = prev;
12418		} else if (prev) {
12419			// Update last entry unless this is the
12420			// single entry
12421			this._drawLast = prev;
12422		}
12423
12424		order.prev = null;
12425
12426		order.next = this._drawFirst;
12427		this._drawFirst.prev = order;
12428		this._drawFirst = order;
12429
12430		this._requestRedraw(layer);
12431	}
12432});
12433
12434// @factory L.canvas(options?: Renderer options)
12435// Creates a Canvas renderer with the given options.
12436function canvas$1(options) {
12437	return canvas ? new Canvas(options) : null;
12438}
12439
12440/*
12441 * Thanks to Dmitry Baranovsky and his Raphael library for inspiration!
12442 */
12443
12444
12445var vmlCreate = (function () {
12446	try {
12447		document.namespaces.add('lvml', 'urn:schemas-microsoft-com:vml');
12448		return function (name) {
12449			return document.createElement('<lvml:' + name + ' class="lvml">');
12450		};
12451	} catch (e) {
12452		return function (name) {
12453			return document.createElement('<' + name + ' xmlns="urn:schemas-microsoft.com:vml" class="lvml">');
12454		};
12455	}
12456})();
12457
12458
12459/*
12460 * @class SVG
12461 *
12462 *
12463 * VML was deprecated in 2012, which means VML functionality exists only for backwards compatibility
12464 * with old versions of Internet Explorer.
12465 */
12466
12467// mixin to redefine some SVG methods to handle VML syntax which is similar but with some differences
12468var vmlMixin = {
12469
12470	_initContainer: function () {
12471		this._container = create$1('div', 'leaflet-vml-container');
12472	},
12473
12474	_update: function () {
12475		if (this._map._animatingZoom) { return; }
12476		Renderer.prototype._update.call(this);
12477		this.fire('update');
12478	},
12479
12480	_initPath: function (layer) {
12481		var container = layer._container = vmlCreate('shape');
12482
12483		addClass(container, 'leaflet-vml-shape ' + (this.options.className || ''));
12484
12485		container.coordsize = '1 1';
12486
12487		layer._path = vmlCreate('path');
12488		container.appendChild(layer._path);
12489
12490		this._updateStyle(layer);
12491		this._layers[stamp(layer)] = layer;
12492	},
12493
12494	_addPath: function (layer) {
12495		var container = layer._container;
12496		this._container.appendChild(container);
12497
12498		if (layer.options.interactive) {
12499			layer.addInteractiveTarget(container);
12500		}
12501	},
12502
12503	_removePath: function (layer) {
12504		var container = layer._container;
12505		remove(container);
12506		layer.removeInteractiveTarget(container);
12507		delete this._layers[stamp(layer)];
12508	},
12509
12510	_updateStyle: function (layer) {
12511		var stroke = layer._stroke,
12512		    fill = layer._fill,
12513		    options = layer.options,
12514		    container = layer._container;
12515
12516		container.stroked = !!options.stroke;
12517		container.filled = !!options.fill;
12518
12519		if (options.stroke) {
12520			if (!stroke) {
12521				stroke = layer._stroke = vmlCreate('stroke');
12522			}
12523			container.appendChild(stroke);
12524			stroke.weight = options.weight + 'px';
12525			stroke.color = options.color;
12526			stroke.opacity = options.opacity;
12527
12528			if (options.dashArray) {
12529				stroke.dashStyle = isArray(options.dashArray) ?
12530				    options.dashArray.join(' ') :
12531				    options.dashArray.replace(/( *, *)/g, ' ');
12532			} else {
12533				stroke.dashStyle = '';
12534			}
12535			stroke.endcap = options.lineCap.replace('butt', 'flat');
12536			stroke.joinstyle = options.lineJoin;
12537
12538		} else if (stroke) {
12539			container.removeChild(stroke);
12540			layer._stroke = null;
12541		}
12542
12543		if (options.fill) {
12544			if (!fill) {
12545				fill = layer._fill = vmlCreate('fill');
12546			}
12547			container.appendChild(fill);
12548			fill.color = options.fillColor || options.color;
12549			fill.opacity = options.fillOpacity;
12550
12551		} else if (fill) {
12552			container.removeChild(fill);
12553			layer._fill = null;
12554		}
12555	},
12556
12557	_updateCircle: function (layer) {
12558		var p = layer._point.round(),
12559		    r = Math.round(layer._radius),
12560		    r2 = Math.round(layer._radiusY || r);
12561
12562		this._setPath(layer, layer._empty() ? 'M0 0' :
12563			'AL ' + p.x + ',' + p.y + ' ' + r + ',' + r2 + ' 0,' + (65535 * 360));
12564	},
12565
12566	_setPath: function (layer, path) {
12567		layer._path.v = path;
12568	},
12569
12570	_bringToFront: function (layer) {
12571		toFront(layer._container);
12572	},
12573
12574	_bringToBack: function (layer) {
12575		toBack(layer._container);
12576	}
12577};
12578
12579var create$2 = vml ? vmlCreate : svgCreate;
12580
12581/*
12582 * @class SVG
12583 * @inherits Renderer
12584 * @aka L.SVG
12585 *
12586 * Allows vector layers to be displayed with [SVG](https://developer.mozilla.org/docs/Web/SVG).
12587 * Inherits `Renderer`.
12588 *
12589 * Due to [technical limitations](http://caniuse.com/#search=svg), SVG is not
12590 * available in all web browsers, notably Android 2.x and 3.x.
12591 *
12592 * Although SVG is not available on IE7 and IE8, these browsers support
12593 * [VML](https://en.wikipedia.org/wiki/Vector_Markup_Language)
12594 * (a now deprecated technology), and the SVG renderer will fall back to VML in
12595 * this case.
12596 *
12597 * @example
12598 *
12599 * Use SVG by default for all paths in the map:
12600 *
12601 * ```js
12602 * var map = L.map('map', {
12603 * 	renderer: L.svg()
12604 * });
12605 * ```
12606 *
12607 * Use a SVG renderer with extra padding for specific vector geometries:
12608 *
12609 * ```js
12610 * var map = L.map('map');
12611 * var myRenderer = L.svg({ padding: 0.5 });
12612 * var line = L.polyline( coordinates, { renderer: myRenderer } );
12613 * var circle = L.circle( center, { renderer: myRenderer } );
12614 * ```
12615 */
12616
12617var SVG = Renderer.extend({
12618
12619	getEvents: function () {
12620		var events = Renderer.prototype.getEvents.call(this);
12621		events.zoomstart = this._onZoomStart;
12622		return events;
12623	},
12624
12625	_initContainer: function () {
12626		this._container = create$2('svg');
12627
12628		// makes it possible to click through svg root; we'll reset it back in individual paths
12629		this._container.setAttribute('pointer-events', 'none');
12630
12631		this._rootGroup = create$2('g');
12632		this._container.appendChild(this._rootGroup);
12633	},
12634
12635	_destroyContainer: function () {
12636		remove(this._container);
12637		off(this._container);
12638		delete this._container;
12639		delete this._rootGroup;
12640		delete this._svgSize;
12641	},
12642
12643	_onZoomStart: function () {
12644		// Drag-then-pinch interactions might mess up the center and zoom.
12645		// In this case, the easiest way to prevent this is re-do the renderer
12646		//   bounds and padding when the zooming starts.
12647		this._update();
12648	},
12649
12650	_update: function () {
12651		if (this._map._animatingZoom && this._bounds) { return; }
12652
12653		Renderer.prototype._update.call(this);
12654
12655		var b = this._bounds,
12656		    size = b.getSize(),
12657		    container = this._container;
12658
12659		// set size of svg-container if changed
12660		if (!this._svgSize || !this._svgSize.equals(size)) {
12661			this._svgSize = size;
12662			container.setAttribute('width', size.x);
12663			container.setAttribute('height', size.y);
12664		}
12665
12666		// movement: update container viewBox so that we don't have to change coordinates of individual layers
12667		setPosition(container, b.min);
12668		container.setAttribute('viewBox', [b.min.x, b.min.y, size.x, size.y].join(' '));
12669
12670		this.fire('update');
12671	},
12672
12673	// methods below are called by vector layers implementations
12674
12675	_initPath: function (layer) {
12676		var path = layer._path = create$2('path');
12677
12678		// @namespace Path
12679		// @option className: String = null
12680		// Custom class name set on an element. Only for SVG renderer.
12681		if (layer.options.className) {
12682			addClass(path, layer.options.className);
12683		}
12684
12685		if (layer.options.interactive) {
12686			addClass(path, 'leaflet-interactive');
12687		}
12688
12689		this._updateStyle(layer);
12690		this._layers[stamp(layer)] = layer;
12691	},
12692
12693	_addPath: function (layer) {
12694		if (!this._rootGroup) { this._initContainer(); }
12695		this._rootGroup.appendChild(layer._path);
12696		layer.addInteractiveTarget(layer._path);
12697	},
12698
12699	_removePath: function (layer) {
12700		remove(layer._path);
12701		layer.removeInteractiveTarget(layer._path);
12702		delete this._layers[stamp(layer)];
12703	},
12704
12705	_updatePath: function (layer) {
12706		layer._project();
12707		layer._update();
12708	},
12709
12710	_updateStyle: function (layer) {
12711		var path = layer._path,
12712		    options = layer.options;
12713
12714		if (!path) { return; }
12715
12716		if (options.stroke) {
12717			path.setAttribute('stroke', options.color);
12718			path.setAttribute('stroke-opacity', options.opacity);
12719			path.setAttribute('stroke-width', options.weight);
12720			path.setAttribute('stroke-linecap', options.lineCap);
12721			path.setAttribute('stroke-linejoin', options.lineJoin);
12722
12723			if (options.dashArray) {
12724				path.setAttribute('stroke-dasharray', options.dashArray);
12725			} else {
12726				path.removeAttribute('stroke-dasharray');
12727			}
12728
12729			if (options.dashOffset) {
12730				path.setAttribute('stroke-dashoffset', options.dashOffset);
12731			} else {
12732				path.removeAttribute('stroke-dashoffset');
12733			}
12734		} else {
12735			path.setAttribute('stroke', 'none');
12736		}
12737
12738		if (options.fill) {
12739			path.setAttribute('fill', options.fillColor || options.color);
12740			path.setAttribute('fill-opacity', options.fillOpacity);
12741			path.setAttribute('fill-rule', options.fillRule || 'evenodd');
12742		} else {
12743			path.setAttribute('fill', 'none');
12744		}
12745	},
12746
12747	_updatePoly: function (layer, closed) {
12748		this._setPath(layer, pointsToPath(layer._parts, closed));
12749	},
12750
12751	_updateCircle: function (layer) {
12752		var p = layer._point,
12753		    r = Math.max(Math.round(layer._radius), 1),
12754		    r2 = Math.max(Math.round(layer._radiusY), 1) || r,
12755		    arc = 'a' + r + ',' + r2 + ' 0 1,0 ';
12756
12757		// drawing a circle with two half-arcs
12758		var d = layer._empty() ? 'M0 0' :
12759			'M' + (p.x - r) + ',' + p.y +
12760			arc + (r * 2) + ',0 ' +
12761			arc + (-r * 2) + ',0 ';
12762
12763		this._setPath(layer, d);
12764	},
12765
12766	_setPath: function (layer, path) {
12767		layer._path.setAttribute('d', path);
12768	},
12769
12770	// SVG does not have the concept of zIndex so we resort to changing the DOM order of elements
12771	_bringToFront: function (layer) {
12772		toFront(layer._path);
12773	},
12774
12775	_bringToBack: function (layer) {
12776		toBack(layer._path);
12777	}
12778});
12779
12780if (vml) {
12781	SVG.include(vmlMixin);
12782}
12783
12784// @namespace SVG
12785// @factory L.svg(options?: Renderer options)
12786// Creates a SVG renderer with the given options.
12787function svg$1(options) {
12788	return svg || vml ? new SVG(options) : null;
12789}
12790
12791Map.include({
12792	// @namespace Map; @method getRenderer(layer: Path): Renderer
12793	// Returns the instance of `Renderer` that should be used to render the given
12794	// `Path`. It will ensure that the `renderer` options of the map and paths
12795	// are respected, and that the renderers do exist on the map.
12796	getRenderer: function (layer) {
12797		// @namespace Path; @option renderer: Renderer
12798		// Use this specific instance of `Renderer` for this path. Takes
12799		// precedence over the map's [default renderer](#map-renderer).
12800		var renderer = layer.options.renderer || this._getPaneRenderer(layer.options.pane) || this.options.renderer || this._renderer;
12801
12802		if (!renderer) {
12803			renderer = this._renderer = this._createRenderer();
12804		}
12805
12806		if (!this.hasLayer(renderer)) {
12807			this.addLayer(renderer);
12808		}
12809		return renderer;
12810	},
12811
12812	_getPaneRenderer: function (name) {
12813		if (name === 'overlayPane' || name === undefined) {
12814			return false;
12815		}
12816
12817		var renderer = this._paneRenderers[name];
12818		if (renderer === undefined) {
12819			renderer = this._createRenderer({pane: name});
12820			this._paneRenderers[name] = renderer;
12821		}
12822		return renderer;
12823	},
12824
12825	_createRenderer: function (options) {
12826		// @namespace Map; @option preferCanvas: Boolean = false
12827		// Whether `Path`s should be rendered on a `Canvas` renderer.
12828		// By default, all `Path`s are rendered in a `SVG` renderer.
12829		return (this.options.preferCanvas && canvas$1(options)) || svg$1(options);
12830	}
12831});
12832
12833/*
12834 * L.Rectangle extends Polygon and creates a rectangle when passed a LatLngBounds object.
12835 */
12836
12837/*
12838 * @class Rectangle
12839 * @aka L.Rectangle
12840 * @inherits Polygon
12841 *
12842 * A class for drawing rectangle overlays on a map. Extends `Polygon`.
12843 *
12844 * @example
12845 *
12846 * ```js
12847 * // define rectangle geographical bounds
12848 * var bounds = [[54.559322, -5.767822], [56.1210604, -3.021240]];
12849 *
12850 * // create an orange rectangle
12851 * L.rectangle(bounds, {color: "#ff7800", weight: 1}).addTo(map);
12852 *
12853 * // zoom the map to the rectangle bounds
12854 * map.fitBounds(bounds);
12855 * ```
12856 *
12857 */
12858
12859
12860var Rectangle = Polygon.extend({
12861	initialize: function (latLngBounds, options) {
12862		Polygon.prototype.initialize.call(this, this._boundsToLatLngs(latLngBounds), options);
12863	},
12864
12865	// @method setBounds(latLngBounds: LatLngBounds): this
12866	// Redraws the rectangle with the passed bounds.
12867	setBounds: function (latLngBounds) {
12868		return this.setLatLngs(this._boundsToLatLngs(latLngBounds));
12869	},
12870
12871	_boundsToLatLngs: function (latLngBounds) {
12872		latLngBounds = toLatLngBounds(latLngBounds);
12873		return [
12874			latLngBounds.getSouthWest(),
12875			latLngBounds.getNorthWest(),
12876			latLngBounds.getNorthEast(),
12877			latLngBounds.getSouthEast()
12878		];
12879	}
12880});
12881
12882
12883// @factory L.rectangle(latLngBounds: LatLngBounds, options?: Polyline options)
12884function rectangle(latLngBounds, options) {
12885	return new Rectangle(latLngBounds, options);
12886}
12887
12888SVG.create = create$2;
12889SVG.pointsToPath = pointsToPath;
12890
12891GeoJSON.geometryToLayer = geometryToLayer;
12892GeoJSON.coordsToLatLng = coordsToLatLng;
12893GeoJSON.coordsToLatLngs = coordsToLatLngs;
12894GeoJSON.latLngToCoords = latLngToCoords;
12895GeoJSON.latLngsToCoords = latLngsToCoords;
12896GeoJSON.getFeature = getFeature;
12897GeoJSON.asFeature = asFeature;
12898
12899/*
12900 * L.Handler.BoxZoom is used to add shift-drag zoom interaction to the map
12901 * (zoom to a selected bounding box), enabled by default.
12902 */
12903
12904// @namespace Map
12905// @section Interaction Options
12906Map.mergeOptions({
12907	// @option boxZoom: Boolean = true
12908	// Whether the map can be zoomed to a rectangular area specified by
12909	// dragging the mouse while pressing the shift key.
12910	boxZoom: true
12911});
12912
12913var BoxZoom = Handler.extend({
12914	initialize: function (map) {
12915		this._map = map;
12916		this._container = map._container;
12917		this._pane = map._panes.overlayPane;
12918		this._resetStateTimeout = 0;
12919		map.on('unload', this._destroy, this);
12920	},
12921
12922	addHooks: function () {
12923		on(this._container, 'mousedown', this._onMouseDown, this);
12924	},
12925
12926	removeHooks: function () {
12927		off(this._container, 'mousedown', this._onMouseDown, this);
12928	},
12929
12930	moved: function () {
12931		return this._moved;
12932	},
12933
12934	_destroy: function () {
12935		remove(this._pane);
12936		delete this._pane;
12937	},
12938
12939	_resetState: function () {
12940		this._resetStateTimeout = 0;
12941		this._moved = false;
12942	},
12943
12944	_clearDeferredResetState: function () {
12945		if (this._resetStateTimeout !== 0) {
12946			clearTimeout(this._resetStateTimeout);
12947			this._resetStateTimeout = 0;
12948		}
12949	},
12950
12951	_onMouseDown: function (e) {
12952		if (!e.shiftKey || ((e.which !== 1) && (e.button !== 1))) { return false; }
12953
12954		// Clear the deferred resetState if it hasn't executed yet, otherwise it
12955		// will interrupt the interaction and orphan a box element in the container.
12956		this._clearDeferredResetState();
12957		this._resetState();
12958
12959		disableTextSelection();
12960		disableImageDrag();
12961
12962		this._startPoint = this._map.mouseEventToContainerPoint(e);
12963
12964		on(document, {
12965			contextmenu: stop,
12966			mousemove: this._onMouseMove,
12967			mouseup: this._onMouseUp,
12968			keydown: this._onKeyDown
12969		}, this);
12970	},
12971
12972	_onMouseMove: function (e) {
12973		if (!this._moved) {
12974			this._moved = true;
12975
12976			this._box = create$1('div', 'leaflet-zoom-box', this._container);
12977			addClass(this._container, 'leaflet-crosshair');
12978
12979			this._map.fire('boxzoomstart');
12980		}
12981
12982		this._point = this._map.mouseEventToContainerPoint(e);
12983
12984		var bounds = new Bounds(this._point, this._startPoint),
12985		    size = bounds.getSize();
12986
12987		setPosition(this._box, bounds.min);
12988
12989		this._box.style.width  = size.x + 'px';
12990		this._box.style.height = size.y + 'px';
12991	},
12992
12993	_finish: function () {
12994		if (this._moved) {
12995			remove(this._box);
12996			removeClass(this._container, 'leaflet-crosshair');
12997		}
12998
12999		enableTextSelection();
13000		enableImageDrag();
13001
13002		off(document, {
13003			contextmenu: stop,
13004			mousemove: this._onMouseMove,
13005			mouseup: this._onMouseUp,
13006			keydown: this._onKeyDown
13007		}, this);
13008	},
13009
13010	_onMouseUp: function (e) {
13011		if ((e.which !== 1) && (e.button !== 1)) { return; }
13012
13013		this._finish();
13014
13015		if (!this._moved) { return; }
13016		// Postpone to next JS tick so internal click event handling
13017		// still see it as "moved".
13018		this._clearDeferredResetState();
13019		this._resetStateTimeout = setTimeout(bind(this._resetState, this), 0);
13020
13021		var bounds = new LatLngBounds(
13022		        this._map.containerPointToLatLng(this._startPoint),
13023		        this._map.containerPointToLatLng(this._point));
13024
13025		this._map
13026			.fitBounds(bounds)
13027			.fire('boxzoomend', {boxZoomBounds: bounds});
13028	},
13029
13030	_onKeyDown: function (e) {
13031		if (e.keyCode === 27) {
13032			this._finish();
13033		}
13034	}
13035});
13036
13037// @section Handlers
13038// @property boxZoom: Handler
13039// Box (shift-drag with mouse) zoom handler.
13040Map.addInitHook('addHandler', 'boxZoom', BoxZoom);
13041
13042/*
13043 * L.Handler.DoubleClickZoom is used to handle double-click zoom on the map, enabled by default.
13044 */
13045
13046// @namespace Map
13047// @section Interaction Options
13048
13049Map.mergeOptions({
13050	// @option doubleClickZoom: Boolean|String = true
13051	// Whether the map can be zoomed in by double clicking on it and
13052	// zoomed out by double clicking while holding shift. If passed
13053	// `'center'`, double-click zoom will zoom to the center of the
13054	//  view regardless of where the mouse was.
13055	doubleClickZoom: true
13056});
13057
13058var DoubleClickZoom = Handler.extend({
13059	addHooks: function () {
13060		this._map.on('dblclick', this._onDoubleClick, this);
13061	},
13062
13063	removeHooks: function () {
13064		this._map.off('dblclick', this._onDoubleClick, this);
13065	},
13066
13067	_onDoubleClick: function (e) {
13068		var map = this._map,
13069		    oldZoom = map.getZoom(),
13070		    delta = map.options.zoomDelta,
13071		    zoom = e.originalEvent.shiftKey ? oldZoom - delta : oldZoom + delta;
13072
13073		if (map.options.doubleClickZoom === 'center') {
13074			map.setZoom(zoom);
13075		} else {
13076			map.setZoomAround(e.containerPoint, zoom);
13077		}
13078	}
13079});
13080
13081// @section Handlers
13082//
13083// Map properties include interaction handlers that allow you to control
13084// interaction behavior in runtime, enabling or disabling certain features such
13085// as dragging or touch zoom (see `Handler` methods). For example:
13086//
13087// ```js
13088// map.doubleClickZoom.disable();
13089// ```
13090//
13091// @property doubleClickZoom: Handler
13092// Double click zoom handler.
13093Map.addInitHook('addHandler', 'doubleClickZoom', DoubleClickZoom);
13094
13095/*
13096 * L.Handler.MapDrag is used to make the map draggable (with panning inertia), enabled by default.
13097 */
13098
13099// @namespace Map
13100// @section Interaction Options
13101Map.mergeOptions({
13102	// @option dragging: Boolean = true
13103	// Whether the map be draggable with mouse/touch or not.
13104	dragging: true,
13105
13106	// @section Panning Inertia Options
13107	// @option inertia: Boolean = *
13108	// If enabled, panning of the map will have an inertia effect where
13109	// the map builds momentum while dragging and continues moving in
13110	// the same direction for some time. Feels especially nice on touch
13111	// devices. Enabled by default unless running on old Android devices.
13112	inertia: !android23,
13113
13114	// @option inertiaDeceleration: Number = 3000
13115	// The rate with which the inertial movement slows down, in pixels/second².
13116	inertiaDeceleration: 3400, // px/s^2
13117
13118	// @option inertiaMaxSpeed: Number = Infinity
13119	// Max speed of the inertial movement, in pixels/second.
13120	inertiaMaxSpeed: Infinity, // px/s
13121
13122	// @option easeLinearity: Number = 0.2
13123	easeLinearity: 0.2,
13124
13125	// TODO refactor, move to CRS
13126	// @option worldCopyJump: Boolean = false
13127	// With this option enabled, the map tracks when you pan to another "copy"
13128	// of the world and seamlessly jumps to the original one so that all overlays
13129	// like markers and vector layers are still visible.
13130	worldCopyJump: false,
13131
13132	// @option maxBoundsViscosity: Number = 0.0
13133	// If `maxBounds` is set, this option will control how solid the bounds
13134	// are when dragging the map around. The default value of `0.0` allows the
13135	// user to drag outside the bounds at normal speed, higher values will
13136	// slow down map dragging outside bounds, and `1.0` makes the bounds fully
13137	// solid, preventing the user from dragging outside the bounds.
13138	maxBoundsViscosity: 0.0
13139});
13140
13141var Drag = Handler.extend({
13142	addHooks: function () {
13143		if (!this._draggable) {
13144			var map = this._map;
13145
13146			this._draggable = new Draggable(map._mapPane, map._container);
13147
13148			this._draggable.on({
13149				dragstart: this._onDragStart,
13150				drag: this._onDrag,
13151				dragend: this._onDragEnd
13152			}, this);
13153
13154			this._draggable.on('predrag', this._onPreDragLimit, this);
13155			if (map.options.worldCopyJump) {
13156				this._draggable.on('predrag', this._onPreDragWrap, this);
13157				map.on('zoomend', this._onZoomEnd, this);
13158
13159				map.whenReady(this._onZoomEnd, this);
13160			}
13161		}
13162		addClass(this._map._container, 'leaflet-grab leaflet-touch-drag');
13163		this._draggable.enable();
13164		this._positions = [];
13165		this._times = [];
13166	},
13167
13168	removeHooks: function () {
13169		removeClass(this._map._container, 'leaflet-grab');
13170		removeClass(this._map._container, 'leaflet-touch-drag');
13171		this._draggable.disable();
13172	},
13173
13174	moved: function () {
13175		return this._draggable && this._draggable._moved;
13176	},
13177
13178	moving: function () {
13179		return this._draggable && this._draggable._moving;
13180	},
13181
13182	_onDragStart: function () {
13183		var map = this._map;
13184
13185		map._stop();
13186		if (this._map.options.maxBounds && this._map.options.maxBoundsViscosity) {
13187			var bounds = toLatLngBounds(this._map.options.maxBounds);
13188
13189			this._offsetLimit = toBounds(
13190				this._map.latLngToContainerPoint(bounds.getNorthWest()).multiplyBy(-1),
13191				this._map.latLngToContainerPoint(bounds.getSouthEast()).multiplyBy(-1)
13192					.add(this._map.getSize()));
13193
13194			this._viscosity = Math.min(1.0, Math.max(0.0, this._map.options.maxBoundsViscosity));
13195		} else {
13196			this._offsetLimit = null;
13197		}
13198
13199		map
13200		    .fire('movestart')
13201		    .fire('dragstart');
13202
13203		if (map.options.inertia) {
13204			this._positions = [];
13205			this._times = [];
13206		}
13207	},
13208
13209	_onDrag: function (e) {
13210		if (this._map.options.inertia) {
13211			var time = this._lastTime = +new Date(),
13212			    pos = this._lastPos = this._draggable._absPos || this._draggable._newPos;
13213
13214			this._positions.push(pos);
13215			this._times.push(time);
13216
13217			this._prunePositions(time);
13218		}
13219
13220		this._map
13221		    .fire('move', e)
13222		    .fire('drag', e);
13223	},
13224
13225	_prunePositions: function (time) {
13226		while (this._positions.length > 1 && time - this._times[0] > 50) {
13227			this._positions.shift();
13228			this._times.shift();
13229		}
13230	},
13231
13232	_onZoomEnd: function () {
13233		var pxCenter = this._map.getSize().divideBy(2),
13234		    pxWorldCenter = this._map.latLngToLayerPoint([0, 0]);
13235
13236		this._initialWorldOffset = pxWorldCenter.subtract(pxCenter).x;
13237		this._worldWidth = this._map.getPixelWorldBounds().getSize().x;
13238	},
13239
13240	_viscousLimit: function (value, threshold) {
13241		return value - (value - threshold) * this._viscosity;
13242	},
13243
13244	_onPreDragLimit: function () {
13245		if (!this._viscosity || !this._offsetLimit) { return; }
13246
13247		var offset = this._draggable._newPos.subtract(this._draggable._startPos);
13248
13249		var limit = this._offsetLimit;
13250		if (offset.x < limit.min.x) { offset.x = this._viscousLimit(offset.x, limit.min.x); }
13251		if (offset.y < limit.min.y) { offset.y = this._viscousLimit(offset.y, limit.min.y); }
13252		if (offset.x > limit.max.x) { offset.x = this._viscousLimit(offset.x, limit.max.x); }
13253		if (offset.y > limit.max.y) { offset.y = this._viscousLimit(offset.y, limit.max.y); }
13254
13255		this._draggable._newPos = this._draggable._startPos.add(offset);
13256	},
13257
13258	_onPreDragWrap: function () {
13259		// TODO refactor to be able to adjust map pane position after zoom
13260		var worldWidth = this._worldWidth,
13261		    halfWidth = Math.round(worldWidth / 2),
13262		    dx = this._initialWorldOffset,
13263		    x = this._draggable._newPos.x,
13264		    newX1 = (x - halfWidth + dx) % worldWidth + halfWidth - dx,
13265		    newX2 = (x + halfWidth + dx) % worldWidth - halfWidth - dx,
13266		    newX = Math.abs(newX1 + dx) < Math.abs(newX2 + dx) ? newX1 : newX2;
13267
13268		this._draggable._absPos = this._draggable._newPos.clone();
13269		this._draggable._newPos.x = newX;
13270	},
13271
13272	_onDragEnd: function (e) {
13273		var map = this._map,
13274		    options = map.options,
13275
13276		    noInertia = !options.inertia || this._times.length < 2;
13277
13278		map.fire('dragend', e);
13279
13280		if (noInertia) {
13281			map.fire('moveend');
13282
13283		} else {
13284			this._prunePositions(+new Date());
13285
13286			var direction = this._lastPos.subtract(this._positions[0]),
13287			    duration = (this._lastTime - this._times[0]) / 1000,
13288			    ease = options.easeLinearity,
13289
13290			    speedVector = direction.multiplyBy(ease / duration),
13291			    speed = speedVector.distanceTo([0, 0]),
13292
13293			    limitedSpeed = Math.min(options.inertiaMaxSpeed, speed),
13294			    limitedSpeedVector = speedVector.multiplyBy(limitedSpeed / speed),
13295
13296			    decelerationDuration = limitedSpeed / (options.inertiaDeceleration * ease),
13297			    offset = limitedSpeedVector.multiplyBy(-decelerationDuration / 2).round();
13298
13299			if (!offset.x && !offset.y) {
13300				map.fire('moveend');
13301
13302			} else {
13303				offset = map._limitOffset(offset, map.options.maxBounds);
13304
13305				requestAnimFrame(function () {
13306					map.panBy(offset, {
13307						duration: decelerationDuration,
13308						easeLinearity: ease,
13309						noMoveStart: true,
13310						animate: true
13311					});
13312				});
13313			}
13314		}
13315	}
13316});
13317
13318// @section Handlers
13319// @property dragging: Handler
13320// Map dragging handler (by both mouse and touch).
13321Map.addInitHook('addHandler', 'dragging', Drag);
13322
13323/*
13324 * L.Map.Keyboard is handling keyboard interaction with the map, enabled by default.
13325 */
13326
13327// @namespace Map
13328// @section Keyboard Navigation Options
13329Map.mergeOptions({
13330	// @option keyboard: Boolean = true
13331	// Makes the map focusable and allows users to navigate the map with keyboard
13332	// arrows and `+`/`-` keys.
13333	keyboard: true,
13334
13335	// @option keyboardPanDelta: Number = 80
13336	// Amount of pixels to pan when pressing an arrow key.
13337	keyboardPanDelta: 80
13338});
13339
13340var Keyboard = Handler.extend({
13341
13342	keyCodes: {
13343		left:    [37],
13344		right:   [39],
13345		down:    [40],
13346		up:      [38],
13347		zoomIn:  [187, 107, 61, 171],
13348		zoomOut: [189, 109, 54, 173]
13349	},
13350
13351	initialize: function (map) {
13352		this._map = map;
13353
13354		this._setPanDelta(map.options.keyboardPanDelta);
13355		this._setZoomDelta(map.options.zoomDelta);
13356	},
13357
13358	addHooks: function () {
13359		var container = this._map._container;
13360
13361		// make the container focusable by tabbing
13362		if (container.tabIndex <= 0) {
13363			container.tabIndex = '0';
13364		}
13365
13366		on(container, {
13367			focus: this._onFocus,
13368			blur: this._onBlur,
13369			mousedown: this._onMouseDown
13370		}, this);
13371
13372		this._map.on({
13373			focus: this._addHooks,
13374			blur: this._removeHooks
13375		}, this);
13376	},
13377
13378	removeHooks: function () {
13379		this._removeHooks();
13380
13381		off(this._map._container, {
13382			focus: this._onFocus,
13383			blur: this._onBlur,
13384			mousedown: this._onMouseDown
13385		}, this);
13386
13387		this._map.off({
13388			focus: this._addHooks,
13389			blur: this._removeHooks
13390		}, this);
13391	},
13392
13393	_onMouseDown: function () {
13394		if (this._focused) { return; }
13395
13396		var body = document.body,
13397		    docEl = document.documentElement,
13398		    top = body.scrollTop || docEl.scrollTop,
13399		    left = body.scrollLeft || docEl.scrollLeft;
13400
13401		this._map._container.focus();
13402
13403		window.scrollTo(left, top);
13404	},
13405
13406	_onFocus: function () {
13407		this._focused = true;
13408		this._map.fire('focus');
13409	},
13410
13411	_onBlur: function () {
13412		this._focused = false;
13413		this._map.fire('blur');
13414	},
13415
13416	_setPanDelta: function (panDelta) {
13417		var keys = this._panKeys = {},
13418		    codes = this.keyCodes,
13419		    i, len;
13420
13421		for (i = 0, len = codes.left.length; i < len; i++) {
13422			keys[codes.left[i]] = [-1 * panDelta, 0];
13423		}
13424		for (i = 0, len = codes.right.length; i < len; i++) {
13425			keys[codes.right[i]] = [panDelta, 0];
13426		}
13427		for (i = 0, len = codes.down.length; i < len; i++) {
13428			keys[codes.down[i]] = [0, panDelta];
13429		}
13430		for (i = 0, len = codes.up.length; i < len; i++) {
13431			keys[codes.up[i]] = [0, -1 * panDelta];
13432		}
13433	},
13434
13435	_setZoomDelta: function (zoomDelta) {
13436		var keys = this._zoomKeys = {},
13437		    codes = this.keyCodes,
13438		    i, len;
13439
13440		for (i = 0, len = codes.zoomIn.length; i < len; i++) {
13441			keys[codes.zoomIn[i]] = zoomDelta;
13442		}
13443		for (i = 0, len = codes.zoomOut.length; i < len; i++) {
13444			keys[codes.zoomOut[i]] = -zoomDelta;
13445		}
13446	},
13447
13448	_addHooks: function () {
13449		on(document, 'keydown', this._onKeyDown, this);
13450	},
13451
13452	_removeHooks: function () {
13453		off(document, 'keydown', this._onKeyDown, this);
13454	},
13455
13456	_onKeyDown: function (e) {
13457		if (e.altKey || e.ctrlKey || e.metaKey) { return; }
13458
13459		var key = e.keyCode,
13460		    map = this._map,
13461		    offset;
13462
13463		if (key in this._panKeys) {
13464			if (!map._panAnim || !map._panAnim._inProgress) {
13465				offset = this._panKeys[key];
13466				if (e.shiftKey) {
13467					offset = toPoint(offset).multiplyBy(3);
13468				}
13469
13470				map.panBy(offset);
13471
13472				if (map.options.maxBounds) {
13473					map.panInsideBounds(map.options.maxBounds);
13474				}
13475			}
13476		} else if (key in this._zoomKeys) {
13477			map.setZoom(map.getZoom() + (e.shiftKey ? 3 : 1) * this._zoomKeys[key]);
13478
13479		} else if (key === 27 && map._popup && map._popup.options.closeOnEscapeKey) {
13480			map.closePopup();
13481
13482		} else {
13483			return;
13484		}
13485
13486		stop(e);
13487	}
13488});
13489
13490// @section Handlers
13491// @section Handlers
13492// @property keyboard: Handler
13493// Keyboard navigation handler.
13494Map.addInitHook('addHandler', 'keyboard', Keyboard);
13495
13496/*
13497 * L.Handler.ScrollWheelZoom is used by L.Map to enable mouse scroll wheel zoom on the map.
13498 */
13499
13500// @namespace Map
13501// @section Interaction Options
13502Map.mergeOptions({
13503	// @section Mousewheel options
13504	// @option scrollWheelZoom: Boolean|String = true
13505	// Whether the map can be zoomed by using the mouse wheel. If passed `'center'`,
13506	// it will zoom to the center of the view regardless of where the mouse was.
13507	scrollWheelZoom: true,
13508
13509	// @option wheelDebounceTime: Number = 40
13510	// Limits the rate at which a wheel can fire (in milliseconds). By default
13511	// user can't zoom via wheel more often than once per 40 ms.
13512	wheelDebounceTime: 40,
13513
13514	// @option wheelPxPerZoomLevel: Number = 60
13515	// How many scroll pixels (as reported by [L.DomEvent.getWheelDelta](#domevent-getwheeldelta))
13516	// mean a change of one full zoom level. Smaller values will make wheel-zooming
13517	// faster (and vice versa).
13518	wheelPxPerZoomLevel: 60
13519});
13520
13521var ScrollWheelZoom = Handler.extend({
13522	addHooks: function () {
13523		on(this._map._container, 'mousewheel', this._onWheelScroll, this);
13524
13525		this._delta = 0;
13526	},
13527
13528	removeHooks: function () {
13529		off(this._map._container, 'mousewheel', this._onWheelScroll, this);
13530	},
13531
13532	_onWheelScroll: function (e) {
13533		var delta = getWheelDelta(e);
13534
13535		var debounce = this._map.options.wheelDebounceTime;
13536
13537		this._delta += delta;
13538		this._lastMousePos = this._map.mouseEventToContainerPoint(e);
13539
13540		if (!this._startTime) {
13541			this._startTime = +new Date();
13542		}
13543
13544		var left = Math.max(debounce - (+new Date() - this._startTime), 0);
13545
13546		clearTimeout(this._timer);
13547		this._timer = setTimeout(bind(this._performZoom, this), left);
13548
13549		stop(e);
13550	},
13551
13552	_performZoom: function () {
13553		var map = this._map,
13554		    zoom = map.getZoom(),
13555		    snap = this._map.options.zoomSnap || 0;
13556
13557		map._stop(); // stop panning and fly animations if any
13558
13559		// map the delta with a sigmoid function to -4..4 range leaning on -1..1
13560		var d2 = this._delta / (this._map.options.wheelPxPerZoomLevel * 4),
13561		    d3 = 4 * Math.log(2 / (1 + Math.exp(-Math.abs(d2)))) / Math.LN2,
13562		    d4 = snap ? Math.ceil(d3 / snap) * snap : d3,
13563		    delta = map._limitZoom(zoom + (this._delta > 0 ? d4 : -d4)) - zoom;
13564
13565		this._delta = 0;
13566		this._startTime = null;
13567
13568		if (!delta) { return; }
13569
13570		if (map.options.scrollWheelZoom === 'center') {
13571			map.setZoom(zoom + delta);
13572		} else {
13573			map.setZoomAround(this._lastMousePos, zoom + delta);
13574		}
13575	}
13576});
13577
13578// @section Handlers
13579// @property scrollWheelZoom: Handler
13580// Scroll wheel zoom handler.
13581Map.addInitHook('addHandler', 'scrollWheelZoom', ScrollWheelZoom);
13582
13583/*
13584 * L.Map.Tap is used to enable mobile hacks like quick taps and long hold.
13585 */
13586
13587// @namespace Map
13588// @section Interaction Options
13589Map.mergeOptions({
13590	// @section Touch interaction options
13591	// @option tap: Boolean = true
13592	// Enables mobile hacks for supporting instant taps (fixing 200ms click
13593	// delay on iOS/Android) and touch holds (fired as `contextmenu` events).
13594	tap: true,
13595
13596	// @option tapTolerance: Number = 15
13597	// The max number of pixels a user can shift his finger during touch
13598	// for it to be considered a valid tap.
13599	tapTolerance: 15
13600});
13601
13602var Tap = Handler.extend({
13603	addHooks: function () {
13604		on(this._map._container, 'touchstart', this._onDown, this);
13605	},
13606
13607	removeHooks: function () {
13608		off(this._map._container, 'touchstart', this._onDown, this);
13609	},
13610
13611	_onDown: function (e) {
13612		if (!e.touches) { return; }
13613
13614		preventDefault(e);
13615
13616		this._fireClick = true;
13617
13618		// don't simulate click or track longpress if more than 1 touch
13619		if (e.touches.length > 1) {
13620			this._fireClick = false;
13621			clearTimeout(this._holdTimeout);
13622			return;
13623		}
13624
13625		var first = e.touches[0],
13626		    el = first.target;
13627
13628		this._startPos = this._newPos = new Point(first.clientX, first.clientY);
13629
13630		// if touching a link, highlight it
13631		if (el.tagName && el.tagName.toLowerCase() === 'a') {
13632			addClass(el, 'leaflet-active');
13633		}
13634
13635		// simulate long hold but setting a timeout
13636		this._holdTimeout = setTimeout(bind(function () {
13637			if (this._isTapValid()) {
13638				this._fireClick = false;
13639				this._onUp();
13640				this._simulateEvent('contextmenu', first);
13641			}
13642		}, this), 1000);
13643
13644		this._simulateEvent('mousedown', first);
13645
13646		on(document, {
13647			touchmove: this._onMove,
13648			touchend: this._onUp
13649		}, this);
13650	},
13651
13652	_onUp: function (e) {
13653		clearTimeout(this._holdTimeout);
13654
13655		off(document, {
13656			touchmove: this._onMove,
13657			touchend: this._onUp
13658		}, this);
13659
13660		if (this._fireClick && e && e.changedTouches) {
13661
13662			var first = e.changedTouches[0],
13663			    el = first.target;
13664
13665			if (el && el.tagName && el.tagName.toLowerCase() === 'a') {
13666				removeClass(el, 'leaflet-active');
13667			}
13668
13669			this._simulateEvent('mouseup', first);
13670
13671			// simulate click if the touch didn't move too much
13672			if (this._isTapValid()) {
13673				this._simulateEvent('click', first);
13674			}
13675		}
13676	},
13677
13678	_isTapValid: function () {
13679		return this._newPos.distanceTo(this._startPos) <= this._map.options.tapTolerance;
13680	},
13681
13682	_onMove: function (e) {
13683		var first = e.touches[0];
13684		this._newPos = new Point(first.clientX, first.clientY);
13685		this._simulateEvent('mousemove', first);
13686	},
13687
13688	_simulateEvent: function (type, e) {
13689		var simulatedEvent = document.createEvent('MouseEvents');
13690
13691		simulatedEvent._simulated = true;
13692		e.target._simulatedClick = true;
13693
13694		simulatedEvent.initMouseEvent(
13695		        type, true, true, window, 1,
13696		        e.screenX, e.screenY,
13697		        e.clientX, e.clientY,
13698		        false, false, false, false, 0, null);
13699
13700		e.target.dispatchEvent(simulatedEvent);
13701	}
13702});
13703
13704// @section Handlers
13705// @property tap: Handler
13706// Mobile touch hacks (quick tap and touch hold) handler.
13707if (touch && !pointer) {
13708	Map.addInitHook('addHandler', 'tap', Tap);
13709}
13710
13711/*
13712 * L.Handler.TouchZoom is used by L.Map to add pinch zoom on supp
vendor: 4,150 bytes, lines 13712-13840
13712orted mobile browsers.
13713 */
13714
13715// @namespace Map
13716// @section Interaction Options
13717Map.mergeOptions({
13718	// @section Touch interaction options
13719	// @option touchZoom: Boolean|String = *
13720	// Whether the map can be zoomed by touch-dragging with two fingers. If
13721	// passed `'center'`, it will zoom to the center of the view regardless of
13722	// where the touch events (fingers) were. Enabled for touch-capable web
13723	// browsers except for old Androids.
13724	touchZoom: touch && !android23,
13725
13726	// @option bounceAtZoomLimits: Boolean = true
13727	// Set it to false if you don't want the map to zoom beyond min/max zoom
13728	// and then bounce back when pinch-zooming.
13729	bounceAtZoomLimits: true
13730});
13731
13732var TouchZoom = Handler.extend({
13733	addHooks: function () {
13734		addClass(this._map._container, 'leaflet-touch-zoom');
13735		on(this._map._container, 'touchstart', this._onTouchStart, this);
13736	},
13737
13738	removeHooks: function () {
13739		removeClass(this._map._container, 'leaflet-touch-zoom');
13740		off(this._map._container, 'touchstart', this._onTouchStart, this);
13741	},
13742
13743	_onTouchStart: function (e) {
13744		var map = this._map;
13745		if (!e.touches || e.touches.length !== 2 || map._animatingZoom || this._zooming) { return; }
13746
13747		var p1 = map.mouseEventToContainerPoint(e.touches[0]),
13748		    p2 = map.mouseEventToContainerPoint(e.touches[1]);
13749
13750		this._centerPoint = map.getSize()._divideBy(2);
13751		this._startLatLng = map.containerPointToLatLng(this._centerPoint);
13752		if (map.options.touchZoom !== 'center') {
13753			this._pinchStartLatLng = map.containerPointToLatLng(p1.add(p2)._divideBy(2));
13754		}
13755
13756		this._startDist = p1.distanceTo(p2);
13757		this._startZoom = map.getZoom();
13758
13759		this._moved = false;
13760		this._zooming = true;
13761
13762		map._stop();
13763
13764		on(document, 'touchmove', this._onTouchMove, this);
13765		on(document, 'touchend', this._onTouchEnd, this);
13766
13767		preventDefault(e);
13768	},
13769
13770	_onTouchMove: function (e) {
13771		if (!e.touches || e.touches.length !== 2 || !this._zooming) { return; }
13772
13773		var map = this._map,
13774		    p1 = map.mouseEventToContainerPoint(e.touches[0]),
13775		    p2 = map.mouseEventToContainerPoint(e.touches[1]),
13776		    scale = p1.distanceTo(p2) / this._startDist;
13777
13778		this._zoom = map.getScaleZoom(scale, this._startZoom);
13779
13780		if (!map.options.bounceAtZoomLimits && (
13781			(this._zoom < map.getMinZoom() && scale < 1) ||
13782			(this._zoom > map.getMaxZoom() && scale > 1))) {
13783			this._zoom = map._limitZoom(this._zoom);
13784		}
13785
13786		if (map.options.touchZoom === 'center') {
13787			this._center = this._startLatLng;
13788			if (scale === 1) { return; }
13789		} else {
13790			// Get delta from pinch to center, so centerLatLng is delta applied to initial pinchLatLng
13791			var delta = p1._add(p2)._divideBy(2)._subtract(this._centerPoint);
13792			if (scale === 1 && delta.x === 0 && delta.y === 0) { return; }
13793			this._center = map.unproject(map.project(this._pinchStartLatLng, this._zoom).subtract(delta), this._zoom);
13794		}
13795
13796		if (!this._moved) {
13797			map._moveStart(true, false);
13798			this._moved = true;
13799		}
13800
13801		cancelAnimFrame(this._animRequest);
13802
13803		var moveFn = bind(map._move, map, this._center, this._zoom, {pinch: true, round: false});
13804		this._animRequest = requestAnimFrame(moveFn, this, true);
13805
13806		preventDefault(e);
13807	},
13808
13809	_onTouchEnd: function () {
13810		if (!this._moved || !this._zooming) {
13811			this._zooming = false;
13812			return;
13813		}
13814
13815		this._zooming = false;
13816		cancelAnimFrame(this._animRequest);
13817
13818		off(document, 'touchmove', this._onTouchMove);
13819		off(document, 'touchend', this._onTouchEnd);
13820
13821		// Pinch updates GridLayers' levels only when zoomSnap is off, so zoomSnap becomes noUpdate.
13822		if (this._map.options.zoomAnimation) {
13823			this._map._animateZoom(this._center, this._map._limitZoom(this._zoom), true, this._map.options.zoomSnap);
13824		} else {
13825			this._map._resetView(this._center, this._map._limitZoom(this._zoom));
13826		}
13827	}
13828});
13829
13830// @section Handlers
13831// @property touchZoom: Handler
13832// Touch zoom handler.
13833Map.addInitHook('addHandler', 'touchZoom', TouchZoom);
13834
13835Map.BoxZoom = BoxZoom;
13836Map.DoubleClickZoom = DoubleClickZoom;
13837Map.Drag = Drag;
13838Map.Keyboard = Keyboard;
13839Map.ScrollWheelZoom = ScrollWheelZoom;
13840Map.Tap = Tap;
13841Map.TouchZoom = TouchZoom;
13842
13843Object.freeze = freeze;
13844
13845exports.version = version;
13846exports.Control = Control;
13847exports.control = control;
13848exports.Browser = Browser;
13849exports.Evented = Evented;
13850exports.Mixin = Mixin;
13851exports.Util = Util;
13852exports.Class = Class;
13853exports.Handler = Handler;
13854exports.extend = extend;
13855exports.bind = bind;
13856exports.stamp = stamp;
13857exports.setOptions = setOptions;
13858exports.DomEvent = DomEvent;
13859exports.DomUtil = DomUtil;
13860exports.PosAnimation = PosAnimation;
13861exports.Draggable = Draggable;
13862exports.LineUtil = LineUtil;
13863exports.PolyUtil = PolyUtil;
13864exports.Point = Point;
13865exports.point = toPoint;
13866exports.Bounds = Bounds;
13867exports.bounds = toBounds;
13868exports.Transformation = Transformation;
13869exports.transformation = toTransformation;
13870exports.Projection = index;
13871exports.LatLng = LatLng;
13872exports.latLng = toLatLng;
13873exports.LatLngBounds = LatLngBounds;
13874exports.latLngBounds = toLatLngBounds;
13875exports.CRS = CRS;
13876exports.GeoJSON = GeoJSON;
13877exports.geoJSON = geoJSON;
13878exports.geoJson = geoJson;
13879exports.Layer = Layer;
13880exports.LayerGroup = LayerGroup;
13881exports.layerGroup = layerGroup;
13882exports.FeatureGroup = FeatureGroup;
13883exports.featureGroup = featureGroup;
13884exports.ImageOverlay = ImageOverlay;
13885exports.imageOverlay = imageOverlay;
13886exports.VideoOverlay = VideoOverlay;
13887exports.videoOverlay = videoOverlay;
13888exports.DivOverlay = DivOverlay;
13889exports.Popup = Popup;
13890exports.popup = popup;
13891exports.Tooltip = Tooltip;
13892exports.tooltip = tooltip;
13893exports.Icon = Icon;
13894exports.icon = icon;
13895exports.DivIcon = DivIcon;
13896exports.divIcon = divIcon;
13897exports.Marker = Marker;
13898exports.marker = marker;
13899exports.TileLayer = TileLayer;
13900exports.tileLayer = tileLayer;
13901exports.GridLayer = GridLayer;
13902exports.gridLayer = gridLayer;
13903exports.SVG = SVG;
13904exports.svg = svg$1;
13905exports.Renderer = Renderer;
13906exports.Canvas = Canvas;
13907exports.canvas = canvas$1;
13908exports.Path = Path;
13909exports.CircleMarker = CircleMarker;
13910exports.circleMarker = circleMarker;
13911exports.Circle = Circle;
13912exports.circle = circle;
13913exports.Polyline = Polyline;
13914exports.polyline = polyline;
13915exports.Polygon = Polygon;
13916exports.polygon = polygon;
13917exports.Rectangle = Rectangle;
13918exports.rectangle = rectangle;
13919exports.Map = Map;
13920exports.map = createMap;
13921
13922var oldL = window.L;
13923exports.noConflict = function() {
13924	window.L = oldL;
13925	return this;
13926}
13927
13928// Always export us to window global (see #2364)
13929window.L = exports;
13930
13931})));
13932//# sourceMappingURL=leaflet-src.js.map

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