fff1e831cc5c51edfed4859b5526e9155166bf8f
[rails.git] / vendor / assets / leaflet / leaflet.js
1 /* @preserve
2  * Leaflet 1.3.0, a JS library for interactive maps. http://leafletjs.com
3  * (c) 2010-2017 Vladimir Agafonkin, (c) 2010-2011 CloudMade
4  */
5
6 (function (global, factory) {
7         typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) :
8         typeof define === 'function' && define.amd ? define(['exports'], factory) :
9         (factory((global.L = {})));
10 }(this, (function (exports) { 'use strict';
11
12 var version = "1.3.0";
13
14 /*
15  * @namespace Util
16  *
17  * Various utility functions, used by Leaflet internally.
18  */
19
20 var freeze = Object.freeze;
21 Object.freeze = function (obj) { return obj; };
22
23 // @function extend(dest: Object, src?: Object): Object
24 // Merges the properties of the `src` object (or multiple objects) into `dest` object and returns the latter. Has an `L.extend` shortcut.
25 function extend(dest) {
26         var i, j, len, src;
27
28         for (j = 1, len = arguments.length; j < len; j++) {
29                 src = arguments[j];
30                 for (i in src) {
31                         dest[i] = src[i];
32                 }
33         }
34         return dest;
35 }
36
37 // @function create(proto: Object, properties?: Object): Object
38 // Compatibility polyfill for [Object.create](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Object/create)
39 var create = Object.create || (function () {
40         function F() {}
41         return function (proto) {
42                 F.prototype = proto;
43                 return new F();
44         };
45 })();
46
47 // @function bind(fn: Function, …): Function
48 // Returns a new function bound to the arguments passed, like [Function.prototype.bind](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Function/bind).
49 // Has a `L.bind()` shortcut.
50 function bind(fn, obj) {
51         var slice = Array.prototype.slice;
52
53         if (fn.bind) {
54                 return fn.bind.apply(fn, slice.call(arguments, 1));
55         }
56
57         var args = slice.call(arguments, 2);
58
59         return function () {
60                 return fn.apply(obj, args.length ? args.concat(slice.call(arguments)) : arguments);
61         };
62 }
63
64 // @property lastId: Number
65 // Last unique ID used by [`stamp()`](#util-stamp)
66 var lastId = 0;
67
68 // @function stamp(obj: Object): Number
69 // Returns the unique ID of an object, assigning it one if it doesn't have it.
70 function stamp(obj) {
71         /*eslint-disable */
72         obj._leaflet_id = obj._leaflet_id || ++lastId;
73         return obj._leaflet_id;
74         /* eslint-enable */
75 }
76
77 // @function throttle(fn: Function, time: Number, context: Object): Function
78 // Returns a function which executes function `fn` with the given scope `context`
79 // (so that the `this` keyword refers to `context` inside `fn`'s code). The function
80 // `fn` will be called no more than one time per given amount of `time`. The arguments
81 // received by the bound function will be any arguments passed when binding the
82 // function, followed by any arguments passed when invoking the bound function.
83 // Has an `L.throttle` shortcut.
84 function throttle(fn, time, context) {
85         var lock, args, wrapperFn, later;
86
87         later = function () {
88                 // reset lock and call if queued
89                 lock = false;
90                 if (args) {
91                         wrapperFn.apply(context, args);
92                         args = false;
93                 }
94         };
95
96         wrapperFn = function () {
97                 if (lock) {
98                         // called too soon, queue to call later
99                         args = arguments;
100
101                 } else {
102                         // call and lock until later
103                         fn.apply(context, arguments);
104                         setTimeout(later, time);
105                         lock = true;
106                 }
107         };
108
109         return wrapperFn;
110 }
111
112 // @function wrapNum(num: Number, range: Number[], includeMax?: Boolean): Number
113 // Returns the number `num` modulo `range` in such a way so it lies within
114 // `range[0]` and `range[1]`. The returned value will be always smaller than
115 // `range[1]` unless `includeMax` is set to `true`.
116 function wrapNum(x, range, includeMax) {
117         var max = range[1],
118             min = range[0],
119             d = max - min;
120         return x === max && includeMax ? x : ((x - min) % d + d) % d + min;
121 }
122
123 // @function falseFn(): Function
124 // Returns a function which always returns `false`.
125 function falseFn() { return false; }
126
127 // @function formatNum(num: Number, digits?: Number): Number
128 // Returns the number `num` rounded to `digits` decimals, or to 6 decimals by default.
129 function formatNum(num, digits) {
130         var pow = Math.pow(10, (digits === undefined ? 6 : digits));
131         return Math.round(num * pow) / pow;
132 }
133
134 // @function trim(str: String): String
135 // Compatibility polyfill for [String.prototype.trim](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String/Trim)
136 function trim(str) {
137         return str.trim ? str.trim() : str.replace(/^\s+|\s+$/g, '');
138 }
139
140 // @function splitWords(str: String): String[]
141 // Trims and splits the string on whitespace and returns the array of parts.
142 function splitWords(str) {
143         return trim(str).split(/\s+/);
144 }
145
146 // @function setOptions(obj: Object, options: Object): Object
147 // Merges the given properties to the `options` of the `obj` object, returning the resulting options. See `Class options`. Has an `L.setOptions` shortcut.
148 function setOptions(obj, options) {
149         if (!obj.hasOwnProperty('options')) {
150                 obj.options = obj.options ? create(obj.options) : {};
151         }
152         for (var i in options) {
153                 obj.options[i] = options[i];
154         }
155         return obj.options;
156 }
157
158 // @function getParamString(obj: Object, existingUrl?: String, uppercase?: Boolean): String
159 // Converts an object into a parameter URL string, e.g. `{a: "foo", b: "bar"}`
160 // translates to `'?a=foo&b=bar'`. If `existingUrl` is set, the parameters will
161 // be appended at the end. If `uppercase` is `true`, the parameter names will
162 // be uppercased (e.g. `'?A=foo&B=bar'`)
163 function getParamString(obj, existingUrl, uppercase) {
164         var params = [];
165         for (var i in obj) {
166                 params.push(encodeURIComponent(uppercase ? i.toUpperCase() : i) + '=' + encodeURIComponent(obj[i]));
167         }
168         return ((!existingUrl || existingUrl.indexOf('?') === -1) ? '?' : '&') + params.join('&');
169 }
170
171 var templateRe = /\{ *([\w_-]+) *\}/g;
172
173 // @function template(str: String, data: Object): String
174 // Simple templating facility, accepts a template string of the form `'Hello {a}, {b}'`
175 // and a data object like `{a: 'foo', b: 'bar'}`, returns evaluated string
176 // `('Hello foo, bar')`. You can also specify functions instead of strings for
177 // data values — they will be evaluated passing `data` as an argument.
178 function template(str, data) {
179         return str.replace(templateRe, function (str, key) {
180                 var value = data[key];
181
182                 if (value === undefined) {
183                         throw new Error('No value provided for variable ' + str);
184
185                 } else if (typeof value === 'function') {
186                         value = value(data);
187                 }
188                 return value;
189         });
190 }
191
192 // @function isArray(obj): Boolean
193 // Compatibility polyfill for [Array.isArray](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Array/isArray)
194 var isArray = Array.isArray || function (obj) {
195         return (Object.prototype.toString.call(obj) === '[object Array]');
196 };
197
198 // @function indexOf(array: Array, el: Object): Number
199 // Compatibility polyfill for [Array.prototype.indexOf](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Array/indexOf)
200 function indexOf(array, el) {
201         for (var i = 0; i < array.length; i++) {
202                 if (array[i] === el) { return i; }
203         }
204         return -1;
205 }
206
207 // @property emptyImageUrl: String
208 // Data URI string containing a base64-encoded empty GIF image.
209 // Used as a hack to free memory from unused images on WebKit-powered
210 // mobile devices (by setting image `src` to this string).
211 var emptyImageUrl = 'data:image/gif;base64,R0lGODlhAQABAAD/ACwAAAAAAQABAAACADs=';
212
213 // inspired by http://paulirish.com/2011/requestanimationframe-for-smart-animating/
214
215 function getPrefixed(name) {
216         return window['webkit' + name] || window['moz' + name] || window['ms' + name];
217 }
218
219 var lastTime = 0;
220
221 // fallback for IE 7-8
222 function timeoutDefer(fn) {
223         var time = +new Date(),
224             timeToCall = Math.max(0, 16 - (time - lastTime));
225
226         lastTime = time + timeToCall;
227         return window.setTimeout(fn, timeToCall);
228 }
229
230 var requestFn = window.requestAnimationFrame || getPrefixed('RequestAnimationFrame') || timeoutDefer;
231 var cancelFn = window.cancelAnimationFrame || getPrefixed('CancelAnimationFrame') ||
232                 getPrefixed('CancelRequestAnimationFrame') || function (id) { window.clearTimeout(id); };
233
234 // @function requestAnimFrame(fn: Function, context?: Object, immediate?: Boolean): Number
235 // Schedules `fn` to be executed when the browser repaints. `fn` is bound to
236 // `context` if given. When `immediate` is set, `fn` is called immediately if
237 // the browser doesn't have native support for
238 // [`window.requestAnimationFrame`](https://developer.mozilla.org/docs/Web/API/window/requestAnimationFrame),
239 // otherwise it's delayed. Returns a request ID that can be used to cancel the request.
240 function requestAnimFrame(fn, context, immediate) {
241         if (immediate && requestFn === timeoutDefer) {
242                 fn.call(context);
243         } else {
244                 return requestFn.call(window, bind(fn, context));
245         }
246 }
247
248 // @function cancelAnimFrame(id: Number): undefined
249 // Cancels a previous `requestAnimFrame`. See also [window.cancelAnimationFrame](https://developer.mozilla.org/docs/Web/API/window/cancelAnimationFrame).
250 function cancelAnimFrame(id) {
251         if (id) {
252                 cancelFn.call(window, id);
253         }
254 }
255
256
257 var Util = (Object.freeze || Object)({
258         freeze: freeze,
259         extend: extend,
260         create: create,
261         bind: bind,
262         lastId: lastId,
263         stamp: stamp,
264         throttle: throttle,
265         wrapNum: wrapNum,
266         falseFn: falseFn,
267         formatNum: formatNum,
268         trim: trim,
269         splitWords: splitWords,
270         setOptions: setOptions,
271         getParamString: getParamString,
272         template: template,
273         isArray: isArray,
274         indexOf: indexOf,
275         emptyImageUrl: emptyImageUrl,
276         requestFn: requestFn,
277         cancelFn: cancelFn,
278         requestAnimFrame: requestAnimFrame,
279         cancelAnimFrame: cancelAnimFrame
280 });
281
282 // @class Class
283 // @aka L.Class
284
285 // @section
286 // @uninheritable
287
288 // Thanks to John Resig and Dean Edwards for inspiration!
289
290 function Class() {}
291
292 Class.extend = function (props) {
293
294         // @function extend(props: Object): Function
295         // [Extends the current class](#class-inheritance) given the properties to be included.
296         // Returns a Javascript function that is a class constructor (to be called with `new`).
297         var NewClass = function () {
298
299                 // call the constructor
300                 if (this.initialize) {
301                         this.initialize.apply(this, arguments);
302                 }
303
304                 // call all constructor hooks
305                 this.callInitHooks();
306         };
307
308         var parentProto = NewClass.__super__ = this.prototype;
309
310         var proto = create(parentProto);
311         proto.constructor = NewClass;
312
313         NewClass.prototype = proto;
314
315         // inherit parent's statics
316         for (var i in this) {
317                 if (this.hasOwnProperty(i) && i !== 'prototype' && i !== '__super__') {
318                         NewClass[i] = this[i];
319                 }
320         }
321
322         // mix static properties into the class
323         if (props.statics) {
324                 extend(NewClass, props.statics);
325                 delete props.statics;
326         }
327
328         // mix includes into the prototype
329         if (props.includes) {
330                 checkDeprecatedMixinEvents(props.includes);
331                 extend.apply(null, [proto].concat(props.includes));
332                 delete props.includes;
333         }
334
335         // merge options
336         if (proto.options) {
337                 props.options = extend(create(proto.options), props.options);
338         }
339
340         // mix given properties into the prototype
341         extend(proto, props);
342
343         proto._initHooks = [];
344
345         // add method for calling all hooks
346         proto.callInitHooks = function () {
347
348                 if (this._initHooksCalled) { return; }
349
350                 if (parentProto.callInitHooks) {
351                         parentProto.callInitHooks.call(this);
352                 }
353
354                 this._initHooksCalled = true;
355
356                 for (var i = 0, len = proto._initHooks.length; i < len; i++) {
357                         proto._initHooks[i].call(this);
358                 }
359         };
360
361         return NewClass;
362 };
363
364
365 // @function include(properties: Object): this
366 // [Includes a mixin](#class-includes) into the current class.
367 Class.include = function (props) {
368         extend(this.prototype, props);
369         return this;
370 };
371
372 // @function mergeOptions(options: Object): this
373 // [Merges `options`](#class-options) into the defaults of the class.
374 Class.mergeOptions = function (options) {
375         extend(this.prototype.options, options);
376         return this;
377 };
378
379 // @function addInitHook(fn: Function): this
380 // Adds a [constructor hook](#class-constructor-hooks) to the class.
381 Class.addInitHook = function (fn) { // (Function) || (String, args...)
382         var args = Array.prototype.slice.call(arguments, 1);
383
384         var init = typeof fn === 'function' ? fn : function () {
385                 this[fn].apply(this, args);
386         };
387
388         this.prototype._initHooks = this.prototype._initHooks || [];
389         this.prototype._initHooks.push(init);
390         return this;
391 };
392
393 function checkDeprecatedMixinEvents(includes) {
394         if (typeof L === 'undefined' || !L || !L.Mixin) { return; }
395
396         includes = isArray(includes) ? includes : [includes];
397
398         for (var i = 0; i < includes.length; i++) {
399                 if (includes[i] === L.Mixin.Events) {
400                         console.warn('Deprecated include of L.Mixin.Events: ' +
401                                 'this property will be removed in future releases, ' +
402                                 'please inherit from L.Evented instead.', new Error().stack);
403                 }
404         }
405 }
406
407 /*
408  * @class Evented
409  * @aka L.Evented
410  * @inherits Class
411  *
412  * A set of methods shared between event-powered classes (like `Map` and `Marker`). Generally, events allow you to execute some function when something happens with an object (e.g. the user clicks on the map, causing the map to fire `'click'` event).
413  *
414  * @example
415  *
416  * ```js
417  * map.on('click', function(e) {
418  *      alert(e.latlng);
419  * } );
420  * ```
421  *
422  * Leaflet deals with event listeners by reference, so if you want to add a listener and then remove it, define it as a function:
423  *
424  * ```js
425  * function onClick(e) { ... }
426  *
427  * map.on('click', onClick);
428  * map.off('click', onClick);
429  * ```
430  */
431
432 var Events = {
433         /* @method on(type: String, fn: Function, context?: Object): this
434          * Adds a listener function (`fn`) to a particular event type of the object. You can optionally specify the context of the listener (object the this keyword will point to). You can also pass several space-separated types (e.g. `'click dblclick'`).
435          *
436          * @alternative
437          * @method on(eventMap: Object): this
438          * Adds a set of type/listener pairs, e.g. `{click: onClick, mousemove: onMouseMove}`
439          */
440         on: function (types, fn, context) {
441
442                 // types can be a map of types/handlers
443                 if (typeof types === 'object') {
444                         for (var type in types) {
445                                 // we don't process space-separated events here for performance;
446                                 // it's a hot path since Layer uses the on(obj) syntax
447                                 this._on(type, types[type], fn);
448                         }
449
450                 } else {
451                         // types can be a string of space-separated words
452                         types = splitWords(types);
453
454                         for (var i = 0, len = types.length; i < len; i++) {
455                                 this._on(types[i], fn, context);
456                         }
457                 }
458
459                 return this;
460         },
461
462         /* @method off(type: String, fn?: Function, context?: Object): this
463          * Removes a previously added listener function. If no function is specified, it will remove all the listeners of that particular event from the object. Note that if you passed a custom context to `on`, you must pass the same context to `off` in order to remove the listener.
464          *
465          * @alternative
466          * @method off(eventMap: Object): this
467          * Removes a set of type/listener pairs.
468          *
469          * @alternative
470          * @method off: this
471          * Removes all listeners to all events on the object.
472          */
473         off: function (types, fn, context) {
474
475                 if (!types) {
476                         // clear all listeners if called without arguments
477                         delete this._events;
478
479                 } else if (typeof types === 'object') {
480                         for (var type in types) {
481                                 this._off(type, types[type], fn);
482                         }
483
484                 } else {
485                         types = splitWords(types);
486
487                         for (var i = 0, len = types.length; i < len; i++) {
488                                 this._off(types[i], fn, context);
489                         }
490                 }
491
492                 return this;
493         },
494
495         // attach listener (without syntactic sugar now)
496         _on: function (type, fn, context) {
497                 this._events = this._events || {};
498
499                 /* get/init listeners for type */
500                 var typeListeners = this._events[type];
501                 if (!typeListeners) {
502                         typeListeners = [];
503                         this._events[type] = typeListeners;
504                 }
505
506                 if (context === this) {
507                         // Less memory footprint.
508                         context = undefined;
509                 }
510                 var newListener = {fn: fn, ctx: context},
511                     listeners = typeListeners;
512
513                 // check if fn already there
514                 for (var i = 0, len = listeners.length; i < len; i++) {
515                         if (listeners[i].fn === fn && listeners[i].ctx === context) {
516                                 return;
517                         }
518                 }
519
520                 listeners.push(newListener);
521         },
522
523         _off: function (type, fn, context) {
524                 var listeners,
525                     i,
526                     len;
527
528                 if (!this._events) { return; }
529
530                 listeners = this._events[type];
531
532                 if (!listeners) {
533                         return;
534                 }
535
536                 if (!fn) {
537                         // Set all removed listeners to noop so they are not called if remove happens in fire
538                         for (i = 0, len = listeners.length; i < len; i++) {
539                                 listeners[i].fn = falseFn;
540                         }
541                         // clear all listeners for a type if function isn't specified
542                         delete this._events[type];
543                         return;
544                 }
545
546                 if (context === this) {
547                         context = undefined;
548                 }
549
550                 if (listeners) {
551
552                         // find fn and remove it
553                         for (i = 0, len = listeners.length; i < len; i++) {
554                                 var l = listeners[i];
555                                 if (l.ctx !== context) { continue; }
556                                 if (l.fn === fn) {
557
558                                         // set the removed listener to noop so that's not called if remove happens in fire
559                                         l.fn = falseFn;
560
561                                         if (this._firingCount) {
562                                                 /* copy array in case events are being fired */
563                                                 this._events[type] = listeners = listeners.slice();
564                                         }
565                                         listeners.splice(i, 1);
566
567                                         return;
568                                 }
569                         }
570                 }
571         },
572
573         // @method fire(type: String, data?: Object, propagate?: Boolean): this
574         // Fires an event of the specified type. You can optionally provide an data
575         // object — the first argument of the listener function will contain its
576         // properties. The event can optionally be propagated to event parents.
577         fire: function (type, data, propagate) {
578                 if (!this.listens(type, propagate)) { return this; }
579
580                 var event = extend({}, data, {
581                         type: type,
582                         target: this,
583                         sourceTarget: data && data.sourceTarget || this
584                 });
585
586                 if (this._events) {
587                         var listeners = this._events[type];
588
589                         if (listeners) {
590                                 this._firingCount = (this._firingCount + 1) || 1;
591                                 for (var i = 0, len = listeners.length; i < len; i++) {
592                                         var l = listeners[i];
593                                         l.fn.call(l.ctx || this, event);
594                                 }
595
596                                 this._firingCount--;
597                         }
598                 }
599
600                 if (propagate) {
601                         // propagate the event to parents (set with addEventParent)
602                         this._propagateEvent(event);
603                 }
604
605                 return this;
606         },
607
608         // @method listens(type: String): Boolean
609         // Returns `true` if a particular event type has any listeners attached to it.
610         listens: function (type, propagate) {
611                 var listeners = this._events && this._events[type];
612                 if (listeners && listeners.length) { return true; }
613
614                 if (propagate) {
615                         // also check parents for listeners if event propagates
616                         for (var id in this._eventParents) {
617                                 if (this._eventParents[id].listens(type, propagate)) { return true; }
618                         }
619                 }
620                 return false;
621         },
622
623         // @method once(…): this
624         // Behaves as [`on(…)`](#evented-on), except the listener will only get fired once and then removed.
625         once: function (types, fn, context) {
626
627                 if (typeof types === 'object') {
628                         for (var type in types) {
629                                 this.once(type, types[type], fn);
630                         }
631                         return this;
632                 }
633
634                 var handler = bind(function () {
635                         this
636                             .off(types, fn, context)
637                             .off(types, handler, context);
638                 }, this);
639
640                 // add a listener that's executed once and removed after that
641                 return this
642                     .on(types, fn, context)
643                     .on(types, handler, context);
644         },
645
646         // @method addEventParent(obj: Evented): this
647         // Adds an event parent - an `Evented` that will receive propagated events
648         addEventParent: function (obj) {
649                 this._eventParents = this._eventParents || {};
650                 this._eventParents[stamp(obj)] = obj;
651                 return this;
652         },
653
654         // @method removeEventParent(obj: Evented): this
655         // Removes an event parent, so it will stop receiving propagated events
656         removeEventParent: function (obj) {
657                 if (this._eventParents) {
658                         delete this._eventParents[stamp(obj)];
659                 }
660                 return this;
661         },
662
663         _propagateEvent: function (e) {
664                 for (var id in this._eventParents) {
665                         this._eventParents[id].fire(e.type, extend({
666                                 layer: e.target,
667                                 propagatedFrom: e.target
668                         }, e), true);
669                 }
670         }
671 };
672
673 // aliases; we should ditch those eventually
674
675 // @method addEventListener(…): this
676 // Alias to [`on(…)`](#evented-on)
677 Events.addEventListener = Events.on;
678
679 // @method removeEventListener(…): this
680 // Alias to [`off(…)`](#evented-off)
681
682 // @method clearAllEventListeners(…): this
683 // Alias to [`off()`](#evented-off)
684 Events.removeEventListener = Events.clearAllEventListeners = Events.off;
685
686 // @method addOneTimeEventListener(…): this
687 // Alias to [`once(…)`](#evented-once)
688 Events.addOneTimeEventListener = Events.once;
689
690 // @method fireEvent(…): this
691 // Alias to [`fire(…)`](#evented-fire)
692 Events.fireEvent = Events.fire;
693
694 // @method hasEventListeners(…): Boolean
695 // Alias to [`listens(…)`](#evented-listens)
696 Events.hasEventListeners = Events.listens;
697
698 var Evented = Class.extend(Events);
699
700 /*
701  * @class Point
702  * @aka L.Point
703  *
704  * Represents a point with `x` and `y` coordinates in pixels.
705  *
706  * @example
707  *
708  * ```js
709  * var point = L.point(200, 300);
710  * ```
711  *
712  * All Leaflet methods and options that accept `Point` objects also accept them in a simple Array form (unless noted otherwise), so these lines are equivalent:
713  *
714  * ```js
715  * map.panBy([200, 300]);
716  * map.panBy(L.point(200, 300));
717  * ```
718  *
719  * Note that `Point` does not inherit from Leafet's `Class` object,
720  * which means new classes can't inherit from it, and new methods
721  * can't be added to it with the `include` function.
722  */
723
724 function Point(x, y, round) {
725         // @property x: Number; The `x` coordinate of the point
726         this.x = (round ? Math.round(x) : x);
727         // @property y: Number; The `y` coordinate of the point
728         this.y = (round ? Math.round(y) : y);
729 }
730
731 var trunc = Math.trunc || function (v) {
732         return v > 0 ? Math.floor(v) : Math.ceil(v);
733 };
734
735 Point.prototype = {
736
737         // @method clone(): Point
738         // Returns a copy of the current point.
739         clone: function () {
740                 return new Point(this.x, this.y);
741         },
742
743         // @method add(otherPoint: Point): Point
744         // Returns the result of addition of the current and the given points.
745         add: function (point) {
746                 // non-destructive, returns a new point
747                 return this.clone()._add(toPoint(point));
748         },
749
750         _add: function (point) {
751                 // destructive, used directly for performance in situations where it's safe to modify existing point
752                 this.x += point.x;
753                 this.y += point.y;
754                 return this;
755         },
756
757         // @method subtract(otherPoint: Point): Point
758         // Returns the result of subtraction of the given point from the current.
759         subtract: function (point) {
760                 return this.clone()._subtract(toPoint(point));
761         },
762
763         _subtract: function (point) {
764                 this.x -= point.x;
765                 this.y -= point.y;
766                 return this;
767         },
768
769         // @method divideBy(num: Number): Point
770         // Returns the result of division of the current point by the given number.
771         divideBy: function (num) {
772                 return this.clone()._divideBy(num);
773         },
774
775         _divideBy: function (num) {
776                 this.x /= num;
777                 this.y /= num;
778                 return this;
779         },
780
781         // @method multiplyBy(num: Number): Point
782         // Returns the result of multiplication of the current point by the given number.
783         multiplyBy: function (num) {
784                 return this.clone()._multiplyBy(num);
785         },
786
787         _multiplyBy: function (num) {
788                 this.x *= num;
789                 this.y *= num;
790                 return this;
791         },
792
793         // @method scaleBy(scale: Point): Point
794         // Multiply each coordinate of the current point by each coordinate of
795         // `scale`. In linear algebra terms, multiply the point by the
796         // [scaling matrix](https://en.wikipedia.org/wiki/Scaling_%28geometry%29#Matrix_representation)
797         // defined by `scale`.
798         scaleBy: function (point) {
799                 return new Point(this.x * point.x, this.y * point.y);
800         },
801
802         // @method unscaleBy(scale: Point): Point
803         // Inverse of `scaleBy`. Divide each coordinate of the current point by
804         // each coordinate of `scale`.
805         unscaleBy: function (point) {
806                 return new Point(this.x / point.x, this.y / point.y);
807         },
808
809         // @method round(): Point
810         // Returns a copy of the current point with rounded coordinates.
811         round: function () {
812                 return this.clone()._round();
813         },
814
815         _round: function () {
816                 this.x = Math.round(this.x);
817                 this.y = Math.round(this.y);
818                 return this;
819         },
820
821         // @method floor(): Point
822         // Returns a copy of the current point with floored coordinates (rounded down).
823         floor: function () {
824                 return this.clone()._floor();
825         },
826
827         _floor: function () {
828                 this.x = Math.floor(this.x);
829                 this.y = Math.floor(this.y);
830                 return this;
831         },
832
833         // @method ceil(): Point
834         // Returns a copy of the current point with ceiled coordinates (rounded up).
835         ceil: function () {
836                 return this.clone()._ceil();
837         },
838
839         _ceil: function () {
840                 this.x = Math.ceil(this.x);
841                 this.y = Math.ceil(this.y);
842                 return this;
843         },
844
845         // @method trunc(): Point
846         // Returns a copy of the current point with truncated coordinates (rounded towards zero).
847         trunc: function () {
848                 return this.clone()._trunc();
849         },
850
851         _trunc: function () {
852                 this.x = trunc(this.x);
853                 this.y = trunc(this.y);
854                 return this;
855         },
856
857         // @method distanceTo(otherPoint: Point): Number
858         // Returns the cartesian distance between the current and the given points.
859         distanceTo: function (point) {
860                 point = toPoint(point);
861
862                 var x = point.x - this.x,
863                     y = point.y - this.y;
864
865                 return Math.sqrt(x * x + y * y);
866         },
867
868         // @method equals(otherPoint: Point): Boolean
869         // Returns `true` if the given point has the same coordinates.
870         equals: function (point) {
871                 point = toPoint(point);
872
873                 return point.x === this.x &&
874                        point.y === this.y;
875         },
876
877         // @method contains(otherPoint: Point): Boolean
878         // Returns `true` if both coordinates of the given point are less than the corresponding current point coordinates (in absolute values).
879         contains: function (point) {
880                 point = toPoint(point);
881
882                 return Math.abs(point.x) <= Math.abs(this.x) &&
883                        Math.abs(point.y) <= Math.abs(this.y);
884         },
885
886         // @method toString(): String
887         // Returns a string representation of the point for debugging purposes.
888         toString: function () {
889                 return 'Point(' +
890                         formatNum(this.x) + ', ' +
891                         formatNum(this.y) + ')';
892         }
893 };
894
895 // @factory L.point(x: Number, y: Number, round?: Boolean)
896 // Creates a Point object with the given `x` and `y` coordinates. If optional `round` is set to true, rounds the `x` and `y` values.
897
898 // @alternative
899 // @factory L.point(coords: Number[])
900 // Expects an array of the form `[x, y]` instead.
901
902 // @alternative
903 // @factory L.point(coords: Object)
904 // Expects a plain object of the form `{x: Number, y: Number}` instead.
905 function toPoint(x, y, round) {
906         if (x instanceof Point) {
907                 return x;
908         }
909         if (isArray(x)) {
910                 return new Point(x[0], x[1]);
911         }
912         if (x === undefined || x === null) {
913                 return x;
914         }
915         if (typeof x === 'object' && 'x' in x && 'y' in x) {
916                 return new Point(x.x, x.y);
917         }
918         return new Point(x, y, round);
919 }
920
921 /*
922  * @class Bounds
923  * @aka L.Bounds
924  *
925  * Represents a rectangular area in pixel coordinates.
926  *
927  * @example
928  *
929  * ```js
930  * var p1 = L.point(10, 10),
931  * p2 = L.point(40, 60),
932  * bounds = L.bounds(p1, p2);
933  * ```
934  *
935  * All Leaflet methods that accept `Bounds` objects also accept them in a simple Array form (unless noted otherwise), so the bounds example above can be passed like this:
936  *
937  * ```js
938  * otherBounds.intersects([[10, 10], [40, 60]]);
939  * ```
940  *
941  * Note that `Bounds` does not inherit from Leafet's `Class` object,
942  * which means new classes can't inherit from it, and new methods
943  * can't be added to it with the `include` function.
944  */
945
946 function Bounds(a, b) {
947         if (!a) { return; }
948
949         var points = b ? [a, b] : a;
950
951         for (var i = 0, len = points.length; i < len; i++) {
952                 this.extend(points[i]);
953         }
954 }
955
956 Bounds.prototype = {
957         // @method extend(point: Point): this
958         // Extends the bounds to contain the given point.
959         extend: function (point) { // (Point)
960                 point = toPoint(point);
961
962                 // @property min: Point
963                 // The top left corner of the rectangle.
964                 // @property max: Point
965                 // The bottom right corner of the rectangle.
966                 if (!this.min && !this.max) {
967                         this.min = point.clone();
968                         this.max = point.clone();
969                 } else {
970                         this.min.x = Math.min(point.x, this.min.x);
971                         this.max.x = Math.max(point.x, this.max.x);
972                         this.min.y = Math.min(point.y, this.min.y);
973                         this.max.y = Math.max(point.y, this.max.y);
974                 }
975                 return this;
976         },
977
978         // @method getCenter(round?: Boolean): Point
979         // Returns the center point of the bounds.
980         getCenter: function (round) {
981                 return new Point(
982                         (this.min.x + this.max.x) / 2,
983                         (this.min.y + this.max.y) / 2, round);
984         },
985
986         // @method getBottomLeft(): Point
987         // Returns the bottom-left point of the bounds.
988         getBottomLeft: function () {
989                 return new Point(this.min.x, this.max.y);
990         },
991
992         // @method getTopRight(): Point
993         // Returns the top-right point of the bounds.
994         getTopRight: function () { // -> Point
995                 return new Point(this.max.x, this.min.y);
996         },
997
998         // @method getTopLeft(): Point
999         // Returns the top-left point of the bounds (i.e. [`this.min`](#bounds-min)).
1000         getTopLeft: function () {
1001                 return this.min; // left, top
1002         },
1003
1004         // @method getBottomRight(): Point
1005         // Returns the bottom-right point of the bounds (i.e. [`this.max`](#bounds-max)).
1006         getBottomRight: function () {
1007                 return this.max; // right, bottom
1008         },
1009
1010         // @method getSize(): Point
1011         // Returns the size of the given bounds
1012         getSize: function () {
1013                 return this.max.subtract(this.min);
1014         },
1015
1016         // @method contains(otherBounds: Bounds): Boolean
1017         // Returns `true` if the rectangle contains the given one.
1018         // @alternative
1019         // @method contains(point: Point): Boolean
1020         // Returns `true` if the rectangle contains the given point.
1021         contains: function (obj) {
1022                 var min, max;
1023
1024                 if (typeof obj[0] === 'number' || obj instanceof Point) {
1025                         obj = toPoint(obj);
1026                 } else {
1027                         obj = toBounds(obj);
1028                 }
1029
1030                 if (obj instanceof Bounds) {
1031                         min = obj.min;
1032                         max = obj.max;
1033                 } else {
1034                         min = max = obj;
1035                 }
1036
1037                 return (min.x >= this.min.x) &&
1038                        (max.x <= this.max.x) &&
1039                        (min.y >= this.min.y) &&
1040                        (max.y <= this.max.y);
1041         },
1042
1043         // @method intersects(otherBounds: Bounds): Boolean
1044         // Returns `true` if the rectangle intersects the given bounds. Two bounds
1045         // intersect if they have at least one point in common.
1046         intersects: function (bounds) { // (Bounds) -> Boolean
1047                 bounds = toBounds(bounds);
1048
1049                 var min = this.min,
1050                     max = this.max,
1051                     min2 = bounds.min,
1052                     max2 = bounds.max,
1053                     xIntersects = (max2.x >= min.x) && (min2.x <= max.x),
1054                     yIntersects = (max2.y >= min.y) && (min2.y <= max.y);
1055
1056                 return xIntersects && yIntersects;
1057         },
1058
1059         // @method overlaps(otherBounds: Bounds): Boolean
1060         // Returns `true` if the rectangle overlaps the given bounds. Two bounds
1061         // overlap if their intersection is an area.
1062         overlaps: function (bounds) { // (Bounds) -> Boolean
1063                 bounds = toBounds(bounds);
1064
1065                 var min = this.min,
1066                     max = this.max,
1067                     min2 = bounds.min,
1068                     max2 = bounds.max,
1069                     xOverlaps = (max2.x > min.x) && (min2.x < max.x),
1070                     yOverlaps = (max2.y > min.y) && (min2.y < max.y);
1071
1072                 return xOverlaps && yOverlaps;
1073         },
1074
1075         isValid: function () {
1076                 return !!(this.min && this.max);
1077         }
1078 };
1079
1080
1081 // @factory L.bounds(corner1: Point, corner2: Point)
1082 // Creates a Bounds object from two corners coordinate pairs.
1083 // @alternative
1084 // @factory L.bounds(points: Point[])
1085 // Creates a Bounds object from the given array of points.
1086 function toBounds(a, b) {
1087         if (!a || a instanceof Bounds) {
1088                 return a;
1089         }
1090         return new Bounds(a, b);
1091 }
1092
1093 /*
1094  * @class LatLngBounds
1095  * @aka L.LatLngBounds
1096  *
1097  * Represents a rectangular geographical area on a map.
1098  *
1099  * @example
1100  *
1101  * ```js
1102  * var corner1 = L.latLng(40.712, -74.227),
1103  * corner2 = L.latLng(40.774, -74.125),
1104  * bounds = L.latLngBounds(corner1, corner2);
1105  * ```
1106  *
1107  * All Leaflet methods that accept LatLngBounds objects also accept them in a simple Array form (unless noted otherwise), so the bounds example above can be passed like this:
1108  *
1109  * ```js
1110  * map.fitBounds([
1111  *      [40.712, -74.227],
1112  *      [40.774, -74.125]
1113  * ]);
1114  * ```
1115  *
1116  * Caution: if the area crosses the antimeridian (often confused with the International Date Line), you must specify corners _outside_ the [-180, 180] degrees longitude range.
1117  *
1118  * Note that `LatLngBounds` does not inherit from Leafet's `Class` object,
1119  * which means new classes can't inherit from it, and new methods
1120  * can't be added to it with the `include` function.
1121  */
1122
1123 function LatLngBounds(corner1, corner2) { // (LatLng, LatLng) or (LatLng[])
1124         if (!corner1) { return; }
1125
1126         var latlngs = corner2 ? [corner1, corner2] : corner1;
1127
1128         for (var i = 0, len = latlngs.length; i < len; i++) {
1129                 this.extend(latlngs[i]);
1130         }
1131 }
1132
1133 LatLngBounds.prototype = {
1134
1135         // @method extend(latlng: LatLng): this
1136         // Extend the bounds to contain the given point
1137
1138         // @alternative
1139         // @method extend(otherBounds: LatLngBounds): this
1140         // Extend the bounds to contain the given bounds
1141         extend: function (obj) {
1142                 var sw = this._southWest,
1143                     ne = this._northEast,
1144                     sw2, ne2;
1145
1146                 if (obj instanceof LatLng) {
1147                         sw2 = obj;
1148                         ne2 = obj;
1149
1150                 } else if (obj instanceof LatLngBounds) {
1151                         sw2 = obj._southWest;
1152                         ne2 = obj._northEast;
1153
1154                         if (!sw2 || !ne2) { return this; }
1155
1156                 } else {
1157                         return obj ? this.extend(toLatLng(obj) || toLatLngBounds(obj)) : this;
1158                 }
1159
1160                 if (!sw && !ne) {
1161                         this._southWest = new LatLng(sw2.lat, sw2.lng);
1162                         this._northEast = new LatLng(ne2.lat, ne2.lng);
1163                 } else {
1164                         sw.lat = Math.min(sw2.lat, sw.lat);
1165                         sw.lng = Math.min(sw2.lng, sw.lng);
1166                         ne.lat = Math.max(ne2.lat, ne.lat);
1167                         ne.lng = Math.max(ne2.lng, ne.lng);
1168                 }
1169
1170                 return this;
1171         },
1172
1173         // @method pad(bufferRatio: Number): LatLngBounds
1174         // Returns bounds created by extending or retracting the current bounds by a given ratio in each direction.
1175         // For example, a ratio of 0.5 extends the bounds by 50% in each direction.
1176         // Negative values will retract the bounds.
1177         pad: function (bufferRatio) {
1178                 var sw = this._southWest,
1179                     ne = this._northEast,
1180                     heightBuffer = Math.abs(sw.lat - ne.lat) * bufferRatio,
1181                     widthBuffer = Math.abs(sw.lng - ne.lng) * bufferRatio;
1182
1183                 return new LatLngBounds(
1184                         new LatLng(sw.lat - heightBuffer, sw.lng - widthBuffer),
1185                         new LatLng(ne.lat + heightBuffer, ne.lng + widthBuffer));
1186         },
1187
1188         // @method getCenter(): LatLng
1189         // Returns the center point of the bounds.
1190         getCenter: function () {
1191                 return new LatLng(
1192                         (this._southWest.lat + this._northEast.lat) / 2,
1193                         (this._southWest.lng + this._northEast.lng) / 2);
1194         },
1195
1196         // @method getSouthWest(): LatLng
1197         // Returns the south-west point of the bounds.
1198         getSouthWest: function () {
1199                 return this._southWest;
1200         },
1201
1202         // @method getNorthEast(): LatLng
1203         // Returns the north-east point of the bounds.
1204         getNorthEast: function () {
1205                 return this._northEast;
1206         },
1207
1208         // @method getNorthWest(): LatLng
1209         // Returns the north-west point of the bounds.
1210         getNorthWest: function () {
1211                 return new LatLng(this.getNorth(), this.getWest());
1212         },
1213
1214         // @method getSouthEast(): LatLng
1215         // Returns the south-east point of the bounds.
1216         getSouthEast: function () {
1217                 return new LatLng(this.getSouth(), this.getEast());
1218         },
1219
1220         // @method getWest(): Number
1221         // Returns the west longitude of the bounds
1222         getWest: function () {
1223                 return this._southWest.lng;
1224         },
1225
1226         // @method getSouth(): Number
1227         // Returns the south latitude of the bounds
1228         getSouth: function () {
1229                 return this._southWest.lat;
1230         },
1231
1232         // @method getEast(): Number
1233         // Returns the east longitude of the bounds
1234         getEast: function () {
1235                 return this._northEast.lng;
1236         },
1237
1238         // @method getNorth(): Number
1239         // Returns the north latitude of the bounds
1240         getNorth: function () {
1241                 return this._northEast.lat;
1242         },
1243
1244         // @method contains(otherBounds: LatLngBounds): Boolean
1245         // Returns `true` if the rectangle contains the given one.
1246
1247         // @alternative
1248         // @method contains (latlng: LatLng): Boolean
1249         // Returns `true` if the rectangle contains the given point.
1250         contains: function (obj) { // (LatLngBounds) or (LatLng) -> Boolean
1251                 if (typeof obj[0] === 'number' || obj instanceof LatLng || 'lat' in obj) {
1252                         obj = toLatLng(obj);
1253                 } else {
1254                         obj = toLatLngBounds(obj);
1255                 }
1256
1257                 var sw = this._southWest,
1258                     ne = this._northEast,
1259                     sw2, ne2;
1260
1261                 if (obj instanceof LatLngBounds) {
1262                         sw2 = obj.getSouthWest();
1263                         ne2 = obj.getNorthEast();
1264                 } else {
1265                         sw2 = ne2 = obj;
1266                 }
1267
1268                 return (sw2.lat >= sw.lat) && (ne2.lat <= ne.lat) &&
1269                        (sw2.lng >= sw.lng) && (ne2.lng <= ne.lng);
1270         },
1271
1272         // @method intersects(otherBounds: LatLngBounds): Boolean
1273         // Returns `true` if the rectangle intersects the given bounds. Two bounds intersect if they have at least one point in common.
1274         intersects: function (bounds) {
1275                 bounds = toLatLngBounds(bounds);
1276
1277                 var sw = this._southWest,
1278                     ne = this._northEast,
1279                     sw2 = bounds.getSouthWest(),
1280                     ne2 = bounds.getNorthEast(),
1281
1282                     latIntersects = (ne2.lat >= sw.lat) && (sw2.lat <= ne.lat),
1283                     lngIntersects = (ne2.lng >= sw.lng) && (sw2.lng <= ne.lng);
1284
1285                 return latIntersects && lngIntersects;
1286         },
1287
1288         // @method overlaps(otherBounds: Bounds): Boolean
1289         // Returns `true` if the rectangle overlaps the given bounds. Two bounds overlap if their intersection is an area.
1290         overlaps: function (bounds) {
1291                 bounds = toLatLngBounds(bounds);
1292
1293                 var sw = this._southWest,
1294                     ne = this._northEast,
1295                     sw2 = bounds.getSouthWest(),
1296                     ne2 = bounds.getNorthEast(),
1297
1298                     latOverlaps = (ne2.lat > sw.lat) && (sw2.lat < ne.lat),
1299                     lngOverlaps = (ne2.lng > sw.lng) && (sw2.lng < ne.lng);
1300
1301                 return latOverlaps && lngOverlaps;
1302         },
1303
1304         // @method toBBoxString(): String
1305         // Returns a string with bounding box coordinates in a 'southwest_lng,southwest_lat,northeast_lng,northeast_lat' format. Useful for sending requests to web services that return geo data.
1306         toBBoxString: function () {
1307                 return [this.getWest(), this.getSouth(), this.getEast(), this.getNorth()].join(',');
1308         },
1309
1310         // @method equals(otherBounds: LatLngBounds, maxMargin?: Number): Boolean
1311         // Returns `true` if the rectangle is equivalent (within a small margin of error) to the given bounds. The margin of error can be overridden by setting `maxMargin` to a small number.
1312         equals: function (bounds, maxMargin) {
1313                 if (!bounds) { return false; }
1314
1315                 bounds = toLatLngBounds(bounds);
1316
1317                 return this._southWest.equals(bounds.getSouthWest(), maxMargin) &&
1318                        this._northEast.equals(bounds.getNorthEast(), maxMargin);
1319         },
1320
1321         // @method isValid(): Boolean
1322         // Returns `true` if the bounds are properly initialized.
1323         isValid: function () {
1324                 return !!(this._southWest && this._northEast);
1325         }
1326 };
1327
1328 // TODO International date line?
1329
1330 // @factory L.latLngBounds(corner1: LatLng, corner2: LatLng)
1331 // Creates a `LatLngBounds` object by defining two diagonally opposite corners of the rectangle.
1332
1333 // @alternative
1334 // @factory L.latLngBounds(latlngs: LatLng[])
1335 // Creates a `LatLngBounds` object defined by the geographical points it contains. Very useful for zooming the map to fit a particular set of locations with [`fitBounds`](#map-fitbounds).
1336 function toLatLngBounds(a, b) {
1337         if (a instanceof LatLngBounds) {
1338                 return a;
1339         }
1340         return new LatLngBounds(a, b);
1341 }
1342
1343 /* @class LatLng
1344  * @aka L.LatLng
1345  *
1346  * Represents a geographical point with a certain latitude and longitude.
1347  *
1348  * @example
1349  *
1350  * ```
1351  * var latlng = L.latLng(50.5, 30.5);
1352  * ```
1353  *
1354  * All Leaflet methods that accept LatLng objects also accept them in a simple Array form and simple object form (unless noted otherwise), so these lines are equivalent:
1355  *
1356  * ```
1357  * map.panTo([50, 30]);
1358  * map.panTo({lon: 30, lat: 50});
1359  * map.panTo({lat: 50, lng: 30});
1360  * map.panTo(L.latLng(50, 30));
1361  * ```
1362  *
1363  * Note that `LatLng` does not inherit from Leafet's `Class` object,
1364  * which means new classes can't inherit from it, and new methods
1365  * can't be added to it with the `include` function.
1366  */
1367
1368 function LatLng(lat, lng, alt) {
1369         if (isNaN(lat) || isNaN(lng)) {
1370                 throw new Error('Invalid LatLng object: (' + lat + ', ' + lng + ')');
1371         }
1372
1373         // @property lat: Number
1374         // Latitude in degrees
1375         this.lat = +lat;
1376
1377         // @property lng: Number
1378         // Longitude in degrees
1379         this.lng = +lng;
1380
1381         // @property alt: Number
1382         // Altitude in meters (optional)
1383         if (alt !== undefined) {
1384                 this.alt = +alt;
1385         }
1386 }
1387
1388 LatLng.prototype = {
1389         // @method equals(otherLatLng: LatLng, maxMargin?: Number): Boolean
1390         // Returns `true` if the given `LatLng` point is at the same position (within a small margin of error). The margin of error can be overridden by setting `maxMargin` to a small number.
1391         equals: function (obj, maxMargin) {
1392                 if (!obj) { return false; }
1393
1394                 obj = toLatLng(obj);
1395
1396                 var margin = Math.max(
1397                         Math.abs(this.lat - obj.lat),
1398                         Math.abs(this.lng - obj.lng));
1399
1400                 return margin <= (maxMargin === undefined ? 1.0E-9 : maxMargin);
1401         },
1402
1403         // @method toString(): String
1404         // Returns a string representation of the point (for debugging purposes).
1405         toString: function (precision) {
1406                 return 'LatLng(' +
1407                         formatNum(this.lat, precision) + ', ' +
1408                         formatNum(this.lng, precision) + ')';
1409         },
1410
1411         // @method distanceTo(otherLatLng: LatLng): Number
1412         // Returns the distance (in meters) to the given `LatLng` calculated using the [Spherical Law of Cosines](https://en.wikipedia.org/wiki/Spherical_law_of_cosines).
1413         distanceTo: function (other) {
1414                 return Earth.distance(this, toLatLng(other));
1415         },
1416
1417         // @method wrap(): LatLng
1418         // Returns a new `LatLng` object with the longitude wrapped so it's always between -180 and +180 degrees.
1419         wrap: function () {
1420                 return Earth.wrapLatLng(this);
1421         },
1422
1423         // @method toBounds(sizeInMeters: Number): LatLngBounds
1424         // Returns a new `LatLngBounds` object in which each boundary is `sizeInMeters/2` meters apart from the `LatLng`.
1425         toBounds: function (sizeInMeters) {
1426                 var latAccuracy = 180 * sizeInMeters / 40075017,
1427                     lngAccuracy = latAccuracy / Math.cos((Math.PI / 180) * this.lat);
1428
1429                 return toLatLngBounds(
1430                         [this.lat - latAccuracy, this.lng - lngAccuracy],
1431                         [this.lat + latAccuracy, this.lng + lngAccuracy]);
1432         },
1433
1434         clone: function () {
1435                 return new LatLng(this.lat, this.lng, this.alt);
1436         }
1437 };
1438
1439
1440
1441 // @factory L.latLng(latitude: Number, longitude: Number, altitude?: Number): LatLng
1442 // Creates an object representing a geographical point with the given latitude and longitude (and optionally altitude).
1443
1444 // @alternative
1445 // @factory L.latLng(coords: Array): LatLng
1446 // Expects an array of the form `[Number, Number]` or `[Number, Number, Number]` instead.
1447
1448 // @alternative
1449 // @factory L.latLng(coords: Object): LatLng
1450 // Expects an plain object of the form `{lat: Number, lng: Number}` or `{lat: Number, lng: Number, alt: Number}` instead.
1451
1452 function toLatLng(a, b, c) {
1453         if (a instanceof LatLng) {
1454                 return a;
1455         }
1456         if (isArray(a) && typeof a[0] !== 'object') {
1457                 if (a.length === 3) {
1458                         return new LatLng(a[0], a[1], a[2]);
1459                 }
1460                 if (a.length === 2) {
1461                         return new LatLng(a[0], a[1]);
1462                 }
1463                 return null;
1464         }
1465         if (a === undefined || a === null) {
1466                 return a;
1467         }
1468         if (typeof a === 'object' && 'lat' in a) {
1469                 return new LatLng(a.lat, 'lng' in a ? a.lng : a.lon, a.alt);
1470         }
1471         if (b === undefined) {
1472                 return null;
1473         }
1474         return new LatLng(a, b, c);
1475 }
1476
1477 /*
1478  * @namespace CRS
1479  * @crs L.CRS.Base
1480  * Object that defines coordinate reference systems for projecting
1481  * geographical points into pixel (screen) coordinates and back (and to
1482  * coordinates in other units for [WMS](https://en.wikipedia.org/wiki/Web_Map_Service) services). See
1483  * [spatial reference system](http://en.wikipedia.org/wiki/Coordinate_reference_system).
1484  *
1485  * Leaflet defines the most usual CRSs by default. If you want to use a
1486  * CRS not defined by default, take a look at the
1487  * [Proj4Leaflet](https://github.com/kartena/Proj4Leaflet) plugin.
1488  *
1489  * Note that the CRS instances do not inherit from Leafet's `Class` object,
1490  * and can't be instantiated. Also, new classes can't inherit from them,
1491  * and methods can't be added to them with the `include` function.
1492  */
1493
1494 var CRS = {
1495         // @method latLngToPoint(latlng: LatLng, zoom: Number): Point
1496         // Projects geographical coordinates into pixel coordinates for a given zoom.
1497         latLngToPoint: function (latlng, zoom) {
1498                 var projectedPoint = this.projection.project(latlng),
1499                     scale = this.scale(zoom);
1500
1501                 return this.transformation._transform(projectedPoint, scale);
1502         },
1503
1504         // @method pointToLatLng(point: Point, zoom: Number): LatLng
1505         // The inverse of `latLngToPoint`. Projects pixel coordinates on a given
1506         // zoom into geographical coordinates.
1507         pointToLatLng: function (point, zoom) {
1508                 var scale = this.scale(zoom),
1509                     untransformedPoint = this.transformation.untransform(point, scale);
1510
1511                 return this.projection.unproject(untransformedPoint);
1512         },
1513
1514         // @method project(latlng: LatLng): Point
1515         // Projects geographical coordinates into coordinates in units accepted for
1516         // this CRS (e.g. meters for EPSG:3857, for passing it to WMS services).
1517         project: function (latlng) {
1518                 return this.projection.project(latlng);
1519         },
1520
1521         // @method unproject(point: Point): LatLng
1522         // Given a projected coordinate returns the corresponding LatLng.
1523         // The inverse of `project`.
1524         unproject: function (point) {
1525                 return this.projection.unproject(point);
1526         },
1527
1528         // @method scale(zoom: Number): Number
1529         // Returns the scale used when transforming projected coordinates into
1530         // pixel coordinates for a particular zoom. For example, it returns
1531         // `256 * 2^zoom` for Mercator-based CRS.
1532         scale: function (zoom) {
1533                 return 256 * Math.pow(2, zoom);
1534         },
1535
1536         // @method zoom(scale: Number): Number
1537         // Inverse of `scale()`, returns the zoom level corresponding to a scale
1538         // factor of `scale`.
1539         zoom: function (scale) {
1540                 return Math.log(scale / 256) / Math.LN2;
1541         },
1542
1543         // @method getProjectedBounds(zoom: Number): Bounds
1544         // Returns the projection's bounds scaled and transformed for the provided `zoom`.
1545         getProjectedBounds: function (zoom) {
1546                 if (this.infinite) { return null; }
1547
1548                 var b = this.projection.bounds,
1549                     s = this.scale(zoom),
1550                     min = this.transformation.transform(b.min, s),
1551                     max = this.transformation.transform(b.max, s);
1552
1553                 return new Bounds(min, max);
1554         },
1555
1556         // @method distance(latlng1: LatLng, latlng2: LatLng): Number
1557         // Returns the distance between two geographical coordinates.
1558
1559         // @property code: String
1560         // Standard code name of the CRS passed into WMS services (e.g. `'EPSG:3857'`)
1561         //
1562         // @property wrapLng: Number[]
1563         // An array of two numbers defining whether the longitude (horizontal) coordinate
1564         // axis wraps around a given range and how. Defaults to `[-180, 180]` in most
1565         // geographical CRSs. If `undefined`, the longitude axis does not wrap around.
1566         //
1567         // @property wrapLat: Number[]
1568         // Like `wrapLng`, but for the latitude (vertical) axis.
1569
1570         // wrapLng: [min, max],
1571         // wrapLat: [min, max],
1572
1573         // @property infinite: Boolean
1574         // If true, the coordinate space will be unbounded (infinite in both axes)
1575         infinite: false,
1576
1577         // @method wrapLatLng(latlng: LatLng): LatLng
1578         // Returns a `LatLng` where lat and lng has been wrapped according to the
1579         // CRS's `wrapLat` and `wrapLng` properties, if they are outside the CRS's bounds.
1580         wrapLatLng: function (latlng) {
1581                 var lng = this.wrapLng ? wrapNum(latlng.lng, this.wrapLng, true) : latlng.lng,
1582                     lat = this.wrapLat ? wrapNum(latlng.lat, this.wrapLat, true) : latlng.lat,
1583                     alt = latlng.alt;
1584
1585                 return new LatLng(lat, lng, alt);
1586         },
1587
1588         // @method wrapLatLngBounds(bounds: LatLngBounds): LatLngBounds
1589         // Returns a `LatLngBounds` with the same size as the given one, ensuring
1590         // that its center is within the CRS's bounds.
1591         // Only accepts actual `L.LatLngBounds` instances, not arrays.
1592         wrapLatLngBounds: function (bounds) {
1593                 var center = bounds.getCenter(),
1594                     newCenter = this.wrapLatLng(center),
1595                     latShift = center.lat - newCenter.lat,
1596                     lngShift = center.lng - newCenter.lng;
1597
1598                 if (latShift === 0 && lngShift === 0) {
1599                         return bounds;
1600                 }
1601
1602                 var sw = bounds.getSouthWest(),
1603                     ne = bounds.getNorthEast(),
1604                     newSw = new LatLng(sw.lat - latShift, sw.lng - lngShift),
1605                     newNe = new LatLng(ne.lat - latShift, ne.lng - lngShift);
1606
1607                 return new LatLngBounds(newSw, newNe);
1608         }
1609 };
1610
1611 /*
1612  * @namespace CRS
1613  * @crs L.CRS.Earth
1614  *
1615  * Serves as the base for CRS that are global such that they cover the earth.
1616  * Can only be used as the base for other CRS and cannot be used directly,
1617  * since it does not have a `code`, `projection` or `transformation`. `distance()` returns
1618  * meters.
1619  */
1620
1621 var Earth = extend({}, CRS, {
1622         wrapLng: [-180, 180],
1623
1624         // Mean Earth Radius, as recommended for use by
1625         // the International Union of Geodesy and Geophysics,
1626         // see http://rosettacode.org/wiki/Haversine_formula
1627         R: 6371000,
1628
1629         // distance between two geographical points using spherical law of cosines approximation
1630         distance: function (latlng1, latlng2) {
1631                 var rad = Math.PI / 180,
1632                     lat1 = latlng1.lat * rad,
1633                     lat2 = latlng2.lat * rad,
1634                     sinDLat = Math.sin((latlng2.lat - latlng1.lat) * rad / 2),
1635                     sinDLon = Math.sin((latlng2.lng - latlng1.lng) * rad / 2),
1636                     a = sinDLat * sinDLat + Math.cos(lat1) * Math.cos(lat2) * sinDLon * sinDLon,
1637                     c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
1638                 return this.R * c;
1639         }
1640 });
1641
1642 /*
1643  * @namespace Projection
1644  * @projection L.Projection.SphericalMercator
1645  *
1646  * Spherical Mercator projection — the most common projection for online maps,
1647  * used by almost all free and commercial tile providers. Assumes that Earth is
1648  * a sphere. Used by the `EPSG:3857` CRS.
1649  */
1650
1651 var SphericalMercator = {
1652
1653         R: 6378137,
1654         MAX_LATITUDE: 85.0511287798,
1655
1656         project: function (latlng) {
1657                 var d = Math.PI / 180,
1658                     max = this.MAX_LATITUDE,
1659                     lat = Math.max(Math.min(max, latlng.lat), -max),
1660                     sin = Math.sin(lat * d);
1661
1662                 return new Point(
1663                         this.R * latlng.lng * d,
1664                         this.R * Math.log((1 + sin) / (1 - sin)) / 2);
1665         },
1666
1667         unproject: function (point) {
1668                 var d = 180 / Math.PI;
1669
1670                 return new LatLng(
1671                         (2 * Math.atan(Math.exp(point.y / this.R)) - (Math.PI / 2)) * d,
1672                         point.x * d / this.R);
1673         },
1674
1675         bounds: (function () {
1676                 var d = 6378137 * Math.PI;
1677                 return new Bounds([-d, -d], [d, d]);
1678         })()
1679 };
1680
1681 /*
1682  * @class Transformation
1683  * @aka L.Transformation
1684  *
1685  * Represents an affine transformation: a set of coefficients `a`, `b`, `c`, `d`
1686  * for transforming a point of a form `(x, y)` into `(a*x + b, c*y + d)` and doing
1687  * the reverse. Used by Leaflet in its projections code.
1688  *
1689  * @example
1690  *
1691  * ```js
1692  * var transformation = L.transformation(2, 5, -1, 10),
1693  *      p = L.point(1, 2),
1694  *      p2 = transformation.transform(p), //  L.point(7, 8)
1695  *      p3 = transformation.untransform(p2); //  L.point(1, 2)
1696  * ```
1697  */
1698
1699
1700 // factory new L.Transformation(a: Number, b: Number, c: Number, d: Number)
1701 // Creates a `Transformation` object with the given coefficients.
1702 function Transformation(a, b, c, d) {
1703         if (isArray(a)) {
1704                 // use array properties
1705                 this._a = a[0];
1706                 this._b = a[1];
1707                 this._c = a[2];
1708                 this._d = a[3];
1709                 return;
1710         }
1711         this._a = a;
1712         this._b = b;
1713         this._c = c;
1714         this._d = d;
1715 }
1716
1717 Transformation.prototype = {
1718         // @method transform(point: Point, scale?: Number): Point
1719         // Returns a transformed point, optionally multiplied by the given scale.
1720         // Only accepts actual `L.Point` instances, not arrays.
1721         transform: function (point, scale) { // (Point, Number) -> Point
1722                 return this._transform(point.clone(), scale);
1723         },
1724
1725         // destructive transform (faster)
1726         _transform: function (point, scale) {
1727                 scale = scale || 1;
1728                 point.x = scale * (this._a * point.x + this._b);
1729                 point.y = scale * (this._c * point.y + this._d);
1730                 return point;
1731         },
1732
1733         // @method untransform(point: Point, scale?: Number): Point
1734         // Returns the reverse transformation of the given point, optionally divided
1735         // by the given scale. Only accepts actual `L.Point` instances, not arrays.
1736         untransform: function (point, scale) {
1737                 scale = scale || 1;
1738                 return new Point(
1739                         (point.x / scale - this._b) / this._a,
1740                         (point.y / scale - this._d) / this._c);
1741         }
1742 };
1743
1744 // factory L.transformation(a: Number, b: Number, c: Number, d: Number)
1745
1746 // @factory L.transformation(a: Number, b: Number, c: Number, d: Number)
1747 // Instantiates a Transformation object with the given coefficients.
1748
1749 // @alternative
1750 // @factory L.transformation(coefficients: Array): Transformation
1751 // Expects an coefficients array of the form
1752 // `[a: Number, b: Number, c: Number, d: Number]`.
1753
1754 function toTransformation(a, b, c, d) {
1755         return new Transformation(a, b, c, d);
1756 }
1757
1758 /*
1759  * @namespace CRS
1760  * @crs L.CRS.EPSG3857
1761  *
1762  * The most common CRS for online maps, used by almost all free and commercial
1763  * tile providers. Uses Spherical Mercator projection. Set in by default in
1764  * Map's `crs` option.
1765  */
1766
1767 var EPSG3857 = extend({}, Earth, {
1768         code: 'EPSG:3857',
1769         projection: SphericalMercator,
1770
1771         transformation: (function () {
1772                 var scale = 0.5 / (Math.PI * SphericalMercator.R);
1773                 return toTransformation(scale, 0.5, -scale, 0.5);
1774         }())
1775 });
1776
1777 var EPSG900913 = extend({}, EPSG3857, {
1778         code: 'EPSG:900913'
1779 });
1780
1781 // @namespace SVG; @section
1782 // There are several static functions which can be called without instantiating L.SVG:
1783
1784 // @function create(name: String): SVGElement
1785 // Returns a instance of [SVGElement](https://developer.mozilla.org/docs/Web/API/SVGElement),
1786 // corresponding to the class name passed. For example, using 'line' will return
1787 // an instance of [SVGLineElement](https://developer.mozilla.org/docs/Web/API/SVGLineElement).
1788 function svgCreate(name) {
1789         return document.createElementNS('http://www.w3.org/2000/svg', name);
1790 }
1791
1792 // @function pointsToPath(rings: Point[], closed: Boolean): String
1793 // Generates a SVG path string for multiple rings, with each ring turning
1794 // into "M..L..L.." instructions
1795 function pointsToPath(rings, closed) {
1796         var str = '',
1797         i, j, len, len2, points, p;
1798
1799         for (i = 0, len = rings.length; i < len; i++) {
1800                 points = rings[i];
1801
1802                 for (j = 0, len2 = points.length; j < len2; j++) {
1803                         p = points[j];
1804                         str += (j ? 'L' : 'M') + p.x + ' ' + p.y;
1805                 }
1806
1807                 // closes the ring for polygons; "x" is VML syntax
1808                 str += closed ? (svg ? 'z' : 'x') : '';
1809         }
1810
1811         // SVG complains about empty path strings
1812         return str || 'M0 0';
1813 }
1814
1815 /*
1816  * @namespace Browser
1817  * @aka L.Browser
1818  *
1819  * A namespace with static properties for browser/feature detection used by Leaflet internally.
1820  *
1821  * @example
1822  *
1823  * ```js
1824  * if (L.Browser.ielt9) {
1825  *   alert('Upgrade your browser, dude!');
1826  * }
1827  * ```
1828  */
1829
1830 var style$1 = document.documentElement.style;
1831
1832 // @property ie: Boolean; `true` for all Internet Explorer versions (not Edge).
1833 var ie = 'ActiveXObject' in window;
1834
1835 // @property ielt9: Boolean; `true` for Internet Explorer versions less than 9.
1836 var ielt9 = ie && !document.addEventListener;
1837
1838 // @property edge: Boolean; `true` for the Edge web browser.
1839 var edge = 'msLaunchUri' in navigator && !('documentMode' in document);
1840
1841 // @property webkit: Boolean;
1842 // `true` for webkit-based browsers like Chrome and Safari (including mobile versions).
1843 var webkit = userAgentContains('webkit');
1844
1845 // @property android: Boolean
1846 // `true` for any browser running on an Android platform.
1847 var android = userAgentContains('android');
1848
1849 // @property android23: Boolean; `true` for browsers running on Android 2 or Android 3.
1850 var android23 = userAgentContains('android 2') || userAgentContains('android 3');
1851
1852 /* See https://stackoverflow.com/a/17961266 for details on detecting stock Android */
1853 var webkitVer = parseInt(/WebKit\/([0-9]+)|$/.exec(navigator.userAgent)[1], 10); // also matches AppleWebKit
1854 // @property androidStock: Boolean; `true` for the Android stock browser (i.e. not Chrome)
1855 var androidStock = android && userAgentContains('Google') && webkitVer < 537 && !('AudioNode' in window);
1856
1857 // @property opera: Boolean; `true` for the Opera browser
1858 var opera = !!window.opera;
1859
1860 // @property chrome: Boolean; `true` for the Chrome browser.
1861 var chrome = userAgentContains('chrome');
1862
1863 // @property gecko: Boolean; `true` for gecko-based browsers like Firefox.
1864 var gecko = userAgentContains('gecko') && !webkit && !opera && !ie;
1865
1866 // @property safari: Boolean; `true` for the Safari browser.
1867 var safari = !chrome && userAgentContains('safari');
1868
1869 var phantom = userAgentContains('phantom');
1870
1871 // @property opera12: Boolean
1872 // `true` for the Opera browser supporting CSS transforms (version 12 or later).
1873 var opera12 = 'OTransition' in style$1;
1874
1875 // @property win: Boolean; `true` when the browser is running in a Windows platform
1876 var win = navigator.platform.indexOf('Win') === 0;
1877
1878 // @property ie3d: Boolean; `true` for all Internet Explorer versions supporting CSS transforms.
1879 var ie3d = ie && ('transition' in style$1);
1880
1881 // @property webkit3d: Boolean; `true` for webkit-based browsers supporting CSS transforms.
1882 var webkit3d = ('WebKitCSSMatrix' in window) && ('m11' in new window.WebKitCSSMatrix()) && !android23;
1883
1884 // @property gecko3d: Boolean; `true` for gecko-based browsers supporting CSS transforms.
1885 var gecko3d = 'MozPerspective' in style$1;
1886
1887 // @property any3d: Boolean
1888 // `true` for all browsers supporting CSS transforms.
1889 var any3d = !window.L_DISABLE_3D && (ie3d || webkit3d || gecko3d) && !opera12 && !phantom;
1890
1891 // @property mobile: Boolean; `true` for all browsers running in a mobile device.
1892 var mobile = typeof orientation !== 'undefined' || userAgentContains('mobile');
1893
1894 // @property mobileWebkit: Boolean; `true` for all webkit-based browsers in a mobile device.
1895 var mobileWebkit = mobile && webkit;
1896
1897 // @property mobileWebkit3d: Boolean
1898 // `true` for all webkit-based browsers in a mobile device supporting CSS transforms.
1899 var mobileWebkit3d = mobile && webkit3d;
1900
1901 // @property msPointer: Boolean
1902 // `true` for browsers implementing the Microsoft touch events model (notably IE10).
1903 var msPointer = !window.PointerEvent && window.MSPointerEvent;
1904
1905 // @property pointer: Boolean
1906 // `true` for all browsers supporting [pointer events](https://msdn.microsoft.com/en-us/library/dn433244%28v=vs.85%29.aspx).
1907 var pointer = !!(window.PointerEvent || msPointer);
1908
1909 // @property touch: Boolean
1910 // `true` for all browsers supporting [touch events](https://developer.mozilla.org/docs/Web/API/Touch_events).
1911 // This does not necessarily mean that the browser is running in a computer with
1912 // a touchscreen, it only means that the browser is capable of understanding
1913 // touch events.
1914 var touch = !window.L_NO_TOUCH && (pointer || 'ontouchstart' in window ||
1915                 (window.DocumentTouch && document instanceof window.DocumentTouch));
1916
1917 // @property mobileOpera: Boolean; `true` for the Opera browser in a mobile device.
1918 var mobileOpera = mobile && opera;
1919
1920 // @property mobileGecko: Boolean
1921 // `true` for gecko-based browsers running in a mobile device.
1922 var mobileGecko = mobile && gecko;
1923
1924 // @property retina: Boolean
1925 // `true` for browsers on a high-resolution "retina" screen.
1926 var retina = (window.devicePixelRatio || (window.screen.deviceXDPI / window.screen.logicalXDPI)) > 1;
1927
1928
1929 // @property canvas: Boolean
1930 // `true` when the browser supports [`<canvas>`](https://developer.mozilla.org/docs/Web/API/Canvas_API).
1931 var canvas = (function () {
1932         return !!document.createElement('canvas').getContext;
1933 }());
1934
1935 // @property svg: Boolean
1936 // `true` when the browser supports [SVG](https://developer.mozilla.org/docs/Web/SVG).
1937 var svg = !!(document.createElementNS && svgCreate('svg').createSVGRect);
1938
1939 // @property vml: Boolean
1940 // `true` if the browser supports [VML](https://en.wikipedia.org/wiki/Vector_Markup_Language).
1941 var vml = !svg && (function () {
1942         try {
1943                 var div = document.createElement('div');
1944                 div.innerHTML = '<v:shape adj="1"/>';
1945
1946                 var shape = div.firstChild;
1947                 shape.style.behavior = 'url(#default#VML)';
1948
1949                 return shape && (typeof shape.adj === 'object');
1950
1951         } catch (e) {
1952                 return false;
1953         }
1954 }());
1955
1956
1957 function userAgentContains(str) {
1958         return navigator.userAgent.toLowerCase().indexOf(str) >= 0;
1959 }
1960
1961
1962 var Browser = (Object.freeze || Object)({
1963         ie: ie,
1964         ielt9: ielt9,
1965         edge: edge,
1966         webkit: webkit,
1967         android: android,
1968         android23: android23,
1969         androidStock: androidStock,
1970         opera: opera,
1971         chrome: chrome,
1972         gecko: gecko,
1973         safari: safari,
1974         phantom: phantom,
1975         opera12: opera12,
1976         win: win,
1977         ie3d: ie3d,
1978         webkit3d: webkit3d,
1979         gecko3d: gecko3d,
1980         any3d: any3d,
1981         mobile: mobile,
1982         mobileWebkit: mobileWebkit,
1983         mobileWebkit3d: mobileWebkit3d,
1984         msPointer: msPointer,
1985         pointer: pointer,
1986         touch: touch,
1987         mobileOpera: mobileOpera,
1988         mobileGecko: mobileGecko,
1989         retina: retina,
1990         canvas: canvas,
1991         svg: svg,
1992         vml: vml
1993 });
1994
1995 /*
1996  * Extends L.DomEvent to provide touch support for Internet Explorer and Windows-based devices.
1997  */
1998
1999
2000 var POINTER_DOWN =   msPointer ? 'MSPointerDown'   : 'pointerdown';
2001 var POINTER_MOVE =   msPointer ? 'MSPointerMove'   : 'pointermove';
2002 var POINTER_UP =     msPointer ? 'MSPointerUp'     : 'pointerup';
2003 var POINTER_CANCEL = msPointer ? 'MSPointerCancel' : 'pointercancel';
2004 var TAG_WHITE_LIST = ['INPUT', 'SELECT', 'OPTION'];
2005
2006 var _pointers = {};
2007 var _pointerDocListener = false;
2008
2009 // DomEvent.DoubleTap needs to know about this
2010 var _pointersCount = 0;
2011
2012 // Provides a touch events wrapper for (ms)pointer events.
2013 // ref http://www.w3.org/TR/pointerevents/ https://www.w3.org/Bugs/Public/show_bug.cgi?id=22890
2014
2015 function addPointerListener(obj, type, handler, id) {
2016         if (type === 'touchstart') {
2017                 _addPointerStart(obj, handler, id);
2018
2019         } else if (type === 'touchmove') {
2020                 _addPointerMove(obj, handler, id);
2021
2022         } else if (type === 'touchend') {
2023                 _addPointerEnd(obj, handler, id);
2024         }
2025
2026         return this;
2027 }
2028
2029 function removePointerListener(obj, type, id) {
2030         var handler = obj['_leaflet_' + type + id];
2031
2032         if (type === 'touchstart') {
2033                 obj.removeEventListener(POINTER_DOWN, handler, false);
2034
2035         } else if (type === 'touchmove') {
2036                 obj.removeEventListener(POINTER_MOVE, handler, false);
2037
2038         } else if (type === 'touchend') {
2039                 obj.removeEventListener(POINTER_UP, handler, false);
2040                 obj.removeEventListener(POINTER_CANCEL, handler, false);
2041         }
2042
2043         return this;
2044 }
2045
2046 function _addPointerStart(obj, handler, id) {
2047         var onDown = bind(function (e) {
2048                 if (e.pointerType !== 'mouse' && e.MSPOINTER_TYPE_MOUSE && e.pointerType !== e.MSPOINTER_TYPE_MOUSE) {
2049                         // In IE11, some touch events needs to fire for form controls, or
2050                         // the controls will stop working. We keep a whitelist of tag names that
2051                         // need these events. For other target tags, we prevent default on the event.
2052                         if (TAG_WHITE_LIST.indexOf(e.target.tagName) < 0) {
2053                                 preventDefault(e);
2054                         } else {
2055                                 return;
2056                         }
2057                 }
2058
2059                 _handlePointer(e, handler);
2060         });
2061
2062         obj['_leaflet_touchstart' + id] = onDown;
2063         obj.addEventListener(POINTER_DOWN, onDown, false);
2064
2065         // need to keep track of what pointers and how many are active to provide e.touches emulation
2066         if (!_pointerDocListener) {
2067                 // we listen documentElement as any drags that end by moving the touch off the screen get fired there
2068                 document.documentElement.addEventListener(POINTER_DOWN, _globalPointerDown, true);
2069                 document.documentElement.addEventListener(POINTER_MOVE, _globalPointerMove, true);
2070                 document.documentElement.addEventListener(POINTER_UP, _globalPointerUp, true);
2071                 document.documentElement.addEventListener(POINTER_CANCEL, _globalPointerUp, true);
2072
2073                 _pointerDocListener = true;
2074         }
2075 }
2076
2077 function _globalPointerDown(e) {
2078         _pointers[e.pointerId] = e;
2079         _pointersCount++;
2080 }
2081
2082 function _globalPointerMove(e) {
2083         if (_pointers[e.pointerId]) {
2084                 _pointers[e.pointerId] = e;
2085         }
2086 }
2087
2088 function _globalPointerUp(e) {
2089         delete _pointers[e.pointerId];
2090         _pointersCount--;
2091 }
2092
2093 function _handlePointer(e, handler) {
2094         e.touches = [];
2095         for (var i in _pointers) {
2096                 e.touches.push(_pointers[i]);
2097         }
2098         e.changedTouches = [e];
2099
2100         handler(e);
2101 }
2102
2103 function _addPointerMove(obj, handler, id) {
2104         var onMove = function (e) {
2105                 // don't fire touch moves when mouse isn't down
2106                 if ((e.pointerType === e.MSPOINTER_TYPE_MOUSE || e.pointerType === 'mouse') && e.buttons === 0) { return; }
2107
2108                 _handlePointer(e, handler);
2109         };
2110
2111         obj['_leaflet_touchmove' + id] = onMove;
2112         obj.addEventListener(POINTER_MOVE, onMove, false);
2113 }
2114
2115 function _addPointerEnd(obj, handler, id) {
2116         var onUp = function (e) {
2117                 _handlePointer(e, handler);
2118         };
2119
2120         obj['_leaflet_touchend' + id] = onUp;
2121         obj.addEventListener(POINTER_UP, onUp, false);
2122         obj.addEventListener(POINTER_CANCEL, onUp, false);
2123 }
2124
2125 /*
2126  * Extends the event handling code with double tap support for mobile browsers.
2127  */
2128
2129 var _touchstart = msPointer ? 'MSPointerDown' : pointer ? 'pointerdown' : 'touchstart';
2130 var _touchend = msPointer ? 'MSPointerUp' : pointer ? 'pointerup' : 'touchend';
2131 var _pre = '_leaflet_';
2132
2133 // inspired by Zepto touch code by Thomas Fuchs
2134 function addDoubleTapListener(obj, handler, id) {
2135         var last, touch$$1,
2136             doubleTap = false,
2137             delay = 250;
2138
2139         function onTouchStart(e) {
2140                 var count;
2141
2142                 if (pointer) {
2143                         if ((!edge) || e.pointerType === 'mouse') { return; }
2144                         count = _pointersCount;
2145                 } else {
2146                         count = e.touches.length;
2147                 }
2148
2149                 if (count > 1) { return; }
2150
2151                 var now = Date.now(),
2152                     delta = now - (last || now);
2153
2154                 touch$$1 = e.touches ? e.touches[0] : e;
2155                 doubleTap = (delta > 0 && delta <= delay);
2156                 last = now;
2157         }
2158
2159         function onTouchEnd(e) {
2160                 if (doubleTap && !touch$$1.cancelBubble) {
2161                         if (pointer) {
2162                                 if ((!edge) || e.pointerType === 'mouse') { return; }
2163                                 // work around .type being readonly with MSPointer* events
2164                                 var newTouch = {},
2165                                     prop, i;
2166
2167                                 for (i in touch$$1) {
2168                                         prop = touch$$1[i];
2169                                         newTouch[i] = prop && prop.bind ? prop.bind(touch$$1) : prop;
2170                                 }
2171                                 touch$$1 = newTouch;
2172                         }
2173                         touch$$1.type = 'dblclick';
2174                         handler(touch$$1);
2175                         last = null;
2176                 }
2177         }
2178
2179         obj[_pre + _touchstart + id] = onTouchStart;
2180         obj[_pre + _touchend + id] = onTouchEnd;
2181         obj[_pre + 'dblclick' + id] = handler;
2182
2183         obj.addEventListener(_touchstart, onTouchStart, false);
2184         obj.addEventListener(_touchend, onTouchEnd, false);
2185
2186         // On some platforms (notably, chrome<55 on win10 + touchscreen + mouse),
2187         // the browser doesn't fire touchend/pointerup events but does fire
2188         // native dblclicks. See #4127.
2189         // Edge 14 also fires native dblclicks, but only for pointerType mouse, see #5180.
2190         obj.addEventListener('dblclick', handler, false);
2191
2192         return this;
2193 }
2194
2195 function removeDoubleTapListener(obj, id) {
2196         var touchstart = obj[_pre + _touchstart + id],
2197             touchend = obj[_pre + _touchend + id],
2198             dblclick = obj[_pre + 'dblclick' + id];
2199
2200         obj.removeEventListener(_touchstart, touchstart, false);
2201         obj.removeEventListener(_touchend, touchend, false);
2202         if (!edge) {
2203                 obj.removeEventListener('dblclick', dblclick, false);
2204         }
2205
2206         return this;
2207 }
2208
2209 /*
2210  * @namespace DomEvent
2211  * Utility functions to work with the [DOM events](https://developer.mozilla.org/docs/Web/API/Event), used by Leaflet internally.
2212  */
2213
2214 // Inspired by John Resig, Dean Edwards and YUI addEvent implementations.
2215
2216 // @function on(el: HTMLElement, types: String, fn: Function, context?: Object): this
2217 // Adds a listener function (`fn`) to a particular DOM event type of the
2218 // element `el`. You can optionally specify the context of the listener
2219 // (object the `this` keyword will point to). You can also pass several
2220 // space-separated types (e.g. `'click dblclick'`).
2221
2222 // @alternative
2223 // @function on(el: HTMLElement, eventMap: Object, context?: Object): this
2224 // Adds a set of type/listener pairs, e.g. `{click: onClick, mousemove: onMouseMove}`
2225 function on(obj, types, fn, context) {
2226
2227         if (typeof types === 'object') {
2228                 for (var type in types) {
2229                         addOne(obj, type, types[type], fn);
2230                 }
2231         } else {
2232                 types = splitWords(types);
2233
2234                 for (var i = 0, len = types.length; i < len; i++) {
2235                         addOne(obj, types[i], fn, context);
2236                 }
2237         }
2238
2239         return this;
2240 }
2241
2242 var eventsKey = '_leaflet_events';
2243
2244 // @function off(el: HTMLElement, types: String, fn: Function, context?: Object): this
2245 // Removes a previously added listener function.
2246 // Note that if you passed a custom context to on, you must pass the same
2247 // context to `off` in order to remove the listener.
2248
2249 // @alternative
2250 // @function off(el: HTMLElement, eventMap: Object, context?: Object): this
2251 // Removes a set of type/listener pairs, e.g. `{click: onClick, mousemove: onMouseMove}`
2252 function off(obj, types, fn, context) {
2253
2254         if (typeof types === 'object') {
2255                 for (var type in types) {
2256                         removeOne(obj, type, types[type], fn);
2257                 }
2258         } else if (types) {
2259                 types = splitWords(types);
2260
2261                 for (var i = 0, len = types.length; i < len; i++) {
2262                         removeOne(obj, types[i], fn, context);
2263                 }
2264         } else {
2265                 for (var j in obj[eventsKey]) {
2266                         removeOne(obj, j, obj[eventsKey][j]);
2267                 }
2268                 delete obj[eventsKey];
2269         }
2270
2271         return this;
2272 }
2273
2274 function addOne(obj, type, fn, context) {
2275         var id = type + stamp(fn) + (context ? '_' + stamp(context) : '');
2276
2277         if (obj[eventsKey] && obj[eventsKey][id]) { return this; }
2278
2279         var handler = function (e) {
2280                 return fn.call(context || obj, e || window.event);
2281         };
2282
2283         var originalHandler = handler;
2284
2285         if (pointer && type.indexOf('touch') === 0) {
2286                 // Needs DomEvent.Pointer.js
2287                 addPointerListener(obj, type, handler, id);
2288
2289         } else if (touch && (type === 'dblclick') && addDoubleTapListener &&
2290                    !(pointer && chrome)) {
2291                 // Chrome >55 does not need the synthetic dblclicks from addDoubleTapListener
2292                 // See #5180
2293                 addDoubleTapListener(obj, handler, id);
2294
2295         } else if ('addEventListener' in obj) {
2296
2297                 if (type === 'mousewheel') {
2298                         obj.addEventListener('onwheel' in obj ? 'wheel' : 'mousewheel', handler, false);
2299
2300                 } else if ((type === 'mouseenter') || (type === 'mouseleave')) {
2301                         handler = function (e) {
2302                                 e = e || window.event;
2303                                 if (isExternalTarget(obj, e)) {
2304                                         originalHandler(e);
2305                                 }
2306                         };
2307                         obj.addEventListener(type === 'mouseenter' ? 'mouseover' : 'mouseout', handler, false);
2308
2309                 } else {
2310                         if (type === 'click' && android) {
2311                                 handler = function (e) {
2312                                         filterClick(e, originalHandler);
2313                                 };
2314                         }
2315                         obj.addEventListener(type, handler, false);
2316                 }
2317
2318         } else if ('attachEvent' in obj) {
2319                 obj.attachEvent('on' + type, handler);
2320         }
2321
2322         obj[eventsKey] = obj[eventsKey] || {};
2323         obj[eventsKey][id] = handler;
2324 }
2325
2326 function removeOne(obj, type, fn, context) {
2327
2328         var id = type + stamp(fn) + (context ? '_' + stamp(context) : ''),
2329             handler = obj[eventsKey] && obj[eventsKey][id];
2330
2331         if (!handler) { return this; }
2332
2333         if (pointer && type.indexOf('touch') === 0) {
2334                 removePointerListener(obj, type, id);
2335
2336         } else if (touch && (type === 'dblclick') && removeDoubleTapListener &&
2337                    !(pointer && chrome)) {
2338                 removeDoubleTapListener(obj, id);
2339
2340         } else if ('removeEventListener' in obj) {
2341
2342                 if (type === 'mousewheel') {
2343                         obj.removeEventListener('onwheel' in obj ? 'wheel' : 'mousewheel', handler, false);
2344
2345                 } else {
2346                         obj.removeEventListener(
2347                                 type === 'mouseenter' ? 'mouseover' :
2348                                 type === 'mouseleave' ? 'mouseout' : type, handler, false);
2349                 }
2350
2351         } else if ('detachEvent' in obj) {
2352                 obj.detachEvent('on' + type, handler);
2353         }
2354
2355         obj[eventsKey][id] = null;
2356 }
2357
2358 // @function stopPropagation(ev: DOMEvent): this
2359 // Stop the given event from propagation to parent elements. Used inside the listener functions:
2360 // ```js
2361 // L.DomEvent.on(div, 'click', function (ev) {
2362 //      L.DomEvent.stopPropagation(ev);
2363 // });
2364 // ```
2365 function stopPropagation(e) {
2366
2367         if (e.stopPropagation) {
2368                 e.stopPropagation();
2369         } else if (e.originalEvent) {  // In case of Leaflet event.
2370                 e.originalEvent._stopped = true;
2371         } else {
2372                 e.cancelBubble = true;
2373         }
2374         skipped(e);
2375
2376         return this;
2377 }
2378
2379 // @function disableScrollPropagation(el: HTMLElement): this
2380 // Adds `stopPropagation` to the element's `'mousewheel'` events (plus browser variants).
2381 function disableScrollPropagation(el) {
2382         addOne(el, 'mousewheel', stopPropagation);
2383         return this;
2384 }
2385
2386 // @function disableClickPropagation(el: HTMLElement): this
2387 // Adds `stopPropagation` to the element's `'click'`, `'doubleclick'`,
2388 // `'mousedown'` and `'touchstart'` events (plus browser variants).
2389 function disableClickPropagation(el) {
2390         on(el, 'mousedown touchstart dblclick', stopPropagation);
2391         addOne(el, 'click', fakeStop);
2392         return this;
2393 }
2394
2395 // @function preventDefault(ev: DOMEvent): this
2396 // Prevents the default action of the DOM Event `ev` from happening (such as
2397 // following a link in the href of the a element, or doing a POST request
2398 // with page reload when a `<form>` is submitted).
2399 // Use it inside listener functions.
2400 function preventDefault(e) {
2401         if (e.preventDefault) {
2402                 e.preventDefault();
2403         } else {
2404                 e.returnValue = false;
2405         }
2406         return this;
2407 }
2408
2409 // @function stop(ev: DOMEvent): this
2410 // Does `stopPropagation` and `preventDefault` at the same time.
2411 function stop(e) {
2412         preventDefault(e);
2413         stopPropagation(e);
2414         return this;
2415 }
2416
2417 // @function getMousePosition(ev: DOMEvent, container?: HTMLElement): Point
2418 // Gets normalized mouse position from a DOM event relative to the
2419 // `container` or to the whole page if not specified.
2420 function getMousePosition(e, container) {
2421         if (!container) {
2422                 return new Point(e.clientX, e.clientY);
2423         }
2424
2425         var rect = container.getBoundingClientRect();
2426
2427         var scaleX = rect.width / container.offsetWidth || 1;
2428         var scaleY = rect.height / container.offsetHeight || 1;
2429         return new Point(
2430                 e.clientX / scaleX - rect.left - container.clientLeft,
2431                 e.clientY / scaleY - rect.top - container.clientTop);
2432 }
2433
2434 // Chrome on Win scrolls double the pixels as in other platforms (see #4538),
2435 // and Firefox scrolls device pixels, not CSS pixels
2436 var wheelPxFactor =
2437         (win && chrome) ? 2 * window.devicePixelRatio :
2438         gecko ? window.devicePixelRatio : 1;
2439
2440 // @function getWheelDelta(ev: DOMEvent): Number
2441 // Gets normalized wheel delta from a mousewheel DOM event, in vertical
2442 // pixels scrolled (negative if scrolling down).
2443 // Events from pointing devices without precise scrolling are mapped to
2444 // a best guess of 60 pixels.
2445 function getWheelDelta(e) {
2446         return (edge) ? e.wheelDeltaY / 2 : // Don't trust window-geometry-based delta
2447                (e.deltaY && e.deltaMode === 0) ? -e.deltaY / wheelPxFactor : // Pixels
2448                (e.deltaY && e.deltaMode === 1) ? -e.deltaY * 20 : // Lines
2449                (e.deltaY && e.deltaMode === 2) ? -e.deltaY * 60 : // Pages
2450                (e.deltaX || e.deltaZ) ? 0 :     // Skip horizontal/depth wheel events
2451                e.wheelDelta ? (e.wheelDeltaY || e.wheelDelta) / 2 : // Legacy IE pixels
2452                (e.detail && Math.abs(e.detail) < 32765) ? -e.detail * 20 : // Legacy Moz lines
2453                e.detail ? e.detail / -32765 * 60 : // Legacy Moz pages
2454                0;
2455 }
2456
2457 var skipEvents = {};
2458
2459 function fakeStop(e) {
2460         // fakes stopPropagation by setting a special event flag, checked/reset with skipped(e)
2461         skipEvents[e.type] = true;
2462 }
2463
2464 function skipped(e) {
2465         var events = skipEvents[e.type];
2466         // reset when checking, as it's only used in map container and propagates outside of the map
2467         skipEvents[e.type] = false;
2468         return events;
2469 }
2470
2471 // check if element really left/entered the event target (for mouseenter/mouseleave)
2472 function isExternalTarget(el, e) {
2473
2474         var related = e.relatedTarget;
2475
2476         if (!related) { return true; }
2477
2478         try {
2479                 while (related && (related !== el)) {
2480                         related = related.parentNode;
2481                 }
2482         } catch (err) {
2483                 return false;
2484         }
2485         return (related !== el);
2486 }
2487
2488 var lastClick;
2489
2490 // this is a horrible workaround for a bug in Android where a single touch triggers two click events
2491 function filterClick(e, handler) {
2492         var timeStamp = (e.timeStamp || (e.originalEvent && e.originalEvent.timeStamp)),
2493             elapsed = lastClick && (timeStamp - lastClick);
2494
2495         // are they closer together than 500ms yet more than 100ms?
2496         // Android typically triggers them ~300ms apart while multiple listeners
2497         // on the same event should be triggered far faster;
2498         // or check if click is simulated on the element, and if it is, reject any non-simulated events
2499
2500         if ((elapsed && elapsed > 100 && elapsed < 500) || (e.target._simulatedClick && !e._simulated)) {
2501                 stop(e);
2502                 return;
2503         }
2504         lastClick = timeStamp;
2505
2506         handler(e);
2507 }
2508
2509
2510
2511
2512 var DomEvent = (Object.freeze || Object)({
2513         on: on,
2514         off: off,
2515         stopPropagation: stopPropagation,
2516         disableScrollPropagation: disableScrollPropagation,
2517         disableClickPropagation: disableClickPropagation,
2518         preventDefault: preventDefault,
2519         stop: stop,
2520         getMousePosition: getMousePosition,
2521         getWheelDelta: getWheelDelta,
2522         fakeStop: fakeStop,
2523         skipped: skipped,
2524         isExternalTarget: isExternalTarget,
2525         addListener: on,
2526         removeListener: off
2527 });
2528
2529 /*
2530  * @namespace DomUtil
2531  *
2532  * Utility functions to work with the [DOM](https://developer.mozilla.org/docs/Web/API/Document_Object_Model)
2533  * tree, used by Leaflet internally.
2534  *
2535  * Most functions expecting or returning a `HTMLElement` also work for
2536  * SVG elements. The only difference is that classes refer to CSS classes
2537  * in HTML and SVG classes in SVG.
2538  */
2539
2540
2541 // @property TRANSFORM: String
2542 // Vendor-prefixed transform style name (e.g. `'webkitTransform'` for WebKit).
2543 var TRANSFORM = testProp(
2544         ['transform', 'WebkitTransform', 'OTransform', 'MozTransform', 'msTransform']);
2545
2546 // webkitTransition comes first because some browser versions that drop vendor prefix don't do
2547 // the same for the transitionend event, in particular the Android 4.1 stock browser
2548
2549 // @property TRANSITION: String
2550 // Vendor-prefixed transition style name.
2551 var TRANSITION = testProp(
2552         ['webkitTransition', 'transition', 'OTransition', 'MozTransition', 'msTransition']);
2553
2554 // @property TRANSITION_END: String
2555 // Vendor-prefixed transitionend event name.
2556 var TRANSITION_END =
2557         TRANSITION === 'webkitTransition' || TRANSITION === 'OTransition' ? TRANSITION + 'End' : 'transitionend';
2558
2559
2560 // @function get(id: String|HTMLElement): HTMLElement
2561 // Returns an element given its DOM id, or returns the element itself
2562 // if it was passed directly.
2563 function get(id) {
2564         return typeof id === 'string' ? document.getElementById(id) : id;
2565 }
2566
2567 // @function getStyle(el: HTMLElement, styleAttrib: String): String
2568 // Returns the value for a certain style attribute on an element,
2569 // including computed values or values set through CSS.
2570 function getStyle(el, style) {
2571         var value = el.style[style] || (el.currentStyle && el.currentStyle[style]);
2572
2573         if ((!value || value === 'auto') && document.defaultView) {
2574                 var css = document.defaultView.getComputedStyle(el, null);
2575                 value = css ? css[style] : null;
2576         }
2577         return value === 'auto' ? null : value;
2578 }
2579
2580 // @function create(tagName: String, className?: String, container?: HTMLElement): HTMLElement
2581 // Creates an HTML element with `tagName`, sets its class to `className`, and optionally appends it to `container` element.
2582 function create$1(tagName, className, container) {
2583         var el = document.createElement(tagName);
2584         el.className = className || '';
2585
2586         if (container) {
2587                 container.appendChild(el);
2588         }
2589         return el;
2590 }
2591
2592 // @function remove(el: HTMLElement)
2593 // Removes `el` from its parent element
2594 function remove(el) {
2595         var parent = el.parentNode;
2596         if (parent) {
2597                 parent.removeChild(el);
2598         }
2599 }
2600
2601 // @function empty(el: HTMLElement)
2602 // Removes all of `el`'s children elements from `el`
2603 function empty(el) {
2604         while (el.firstChild) {
2605                 el.removeChild(el.firstChild);
2606         }
2607 }
2608
2609 // @function toFront(el: HTMLElement)
2610 // Makes `el` the last child of its parent, so it renders in front of the other children.
2611 function toFront(el) {
2612         var parent = el.parentNode;
2613         if (parent.lastChild !== el) {
2614                 parent.appendChild(el);
2615         }
2616 }
2617
2618 // @function toBack(el: HTMLElement)
2619 // Makes `el` the first child of its parent, so it renders behind the other children.
2620 function toBack(el) {
2621         var parent = el.parentNode;
2622         if (parent.firstChild !== el) {
2623                 parent.insertBefore(el, parent.firstChild);
2624         }
2625 }
2626
2627 // @function hasClass(el: HTMLElement, name: String): Boolean
2628 // Returns `true` if the element's class attribute contains `name`.
2629 function hasClass(el, name) {
2630         if (el.classList !== undefined) {
2631                 return el.classList.contains(name);
2632         }
2633         var className = getClass(el);
2634         return className.length > 0 && new RegExp('(^|\\s)' + name + '(\\s|$)').test(className);
2635 }
2636
2637 // @function addClass(el: HTMLElement, name: String)
2638 // Adds `name` to the element's class attribute.
2639 function addClass(el, name) {
2640         if (el.classList !== undefined) {
2641                 var classes = splitWords(name);
2642                 for (var i = 0, len = classes.length; i < len; i++) {
2643                         el.classList.add(classes[i]);
2644                 }
2645         } else if (!hasClass(el, name)) {
2646                 var className = getClass(el);
2647                 setClass(el, (className ? className + ' ' : '') + name);
2648         }
2649 }
2650
2651 // @function removeClass(el: HTMLElement, name: String)
2652 // Removes `name` from the element's class attribute.
2653 function removeClass(el, name) {
2654         if (el.classList !== undefined) {
2655                 el.classList.remove(name);
2656         } else {
2657                 setClass(el, trim((' ' + getClass(el) + ' ').replace(' ' + name + ' ', ' ')));
2658         }
2659 }
2660
2661 // @function setClass(el: HTMLElement, name: String)
2662 // Sets the element's class.
2663 function setClass(el, name) {
2664         if (el.className.baseVal === undefined) {
2665                 el.className = name;
2666         } else {
2667                 // in case of SVG element
2668                 el.className.baseVal = name;
2669         }
2670 }
2671
2672 // @function getClass(el: HTMLElement): String
2673 // Returns the element's class.
2674 function getClass(el) {
2675         return el.className.baseVal === undefined ? el.className : el.className.baseVal;
2676 }
2677
2678 // @function setOpacity(el: HTMLElement, opacity: Number)
2679 // Set the opacity of an element (including old IE support).
2680 // `opacity` must be a number from `0` to `1`.
2681 function setOpacity(el, value) {
2682         if ('opacity' in el.style) {
2683                 el.style.opacity = value;
2684         } else if ('filter' in el.style) {
2685                 _setOpacityIE(el, value);
2686         }
2687 }
2688
2689 function _setOpacityIE(el, value) {
2690         var filter = false,
2691             filterName = 'DXImageTransform.Microsoft.Alpha';
2692
2693         // filters collection throws an error if we try to retrieve a filter that doesn't exist
2694         try {
2695                 filter = el.filters.item(filterName);
2696         } catch (e) {
2697                 // don't set opacity to 1 if we haven't already set an opacity,
2698                 // it isn't needed and breaks transparent pngs.
2699                 if (value === 1) { return; }
2700         }
2701
2702         value = Math.round(value * 100);
2703
2704         if (filter) {
2705                 filter.Enabled = (value !== 100);
2706                 filter.Opacity = value;
2707         } else {
2708                 el.style.filter += ' progid:' + filterName + '(opacity=' + value + ')';
2709         }
2710 }
2711
2712 // @function testProp(props: String[]): String|false
2713 // Goes through the array of style names and returns the first name
2714 // that is a valid style name for an element. If no such name is found,
2715 // it returns false. Useful for vendor-prefixed styles like `transform`.
2716 function testProp(props) {
2717         var style = document.documentElement.style;
2718
2719         for (var i = 0; i < props.length; i++) {
2720                 if (props[i] in style) {
2721                         return props[i];
2722                 }
2723         }
2724         return false;
2725 }
2726
2727 // @function setTransform(el: HTMLElement, offset: Point, scale?: Number)
2728 // Resets the 3D CSS transform of `el` so it is translated by `offset` pixels
2729 // and optionally scaled by `scale`. Does not have an effect if the
2730 // browser doesn't support 3D CSS transforms.
2731 function setTransform(el, offset, scale) {
2732         var pos = offset || new Point(0, 0);
2733
2734         el.style[TRANSFORM] =
2735                 (ie3d ?
2736                         'translate(' + pos.x + 'px,' + pos.y + 'px)' :
2737                         'translate3d(' + pos.x + 'px,' + pos.y + 'px,0)') +
2738                 (scale ? ' scale(' + scale + ')' : '');
2739 }
2740
2741 // @function setPosition(el: HTMLElement, position: Point)
2742 // Sets the position of `el` to coordinates specified by `position`,
2743 // using CSS translate or top/left positioning depending on the browser
2744 // (used by Leaflet internally to position its layers).
2745 function setPosition(el, point) {
2746
2747         /*eslint-disable */
2748         el._leaflet_pos = point;
2749         /* eslint-enable */
2750
2751         if (any3d) {
2752                 setTransform(el, point);
2753         } else {
2754                 el.style.left = point.x + 'px';
2755                 el.style.top = point.y + 'px';
2756         }
2757 }
2758
2759 // @function getPosition(el: HTMLElement): Point
2760 // Returns the coordinates of an element previously positioned with setPosition.
2761 function getPosition(el) {
2762         // this method is only used for elements previously positioned using setPosition,
2763         // so it's safe to cache the position for performance
2764
2765         return el._leaflet_pos || new Point(0, 0);
2766 }
2767
2768 // @function disableTextSelection()
2769 // Prevents the user from generating `selectstart` DOM events, usually generated
2770 // when the user drags the mouse through a page with text. Used internally
2771 // by Leaflet to override the behaviour of any click-and-drag interaction on
2772 // the map. Affects drag interactions on the whole document.
2773
2774 // @function enableTextSelection()
2775 // Cancels the effects of a previous [`L.DomUtil.disableTextSelection`](#domutil-disabletextselection).
2776 var disableTextSelection;
2777 var enableTextSelection;
2778 var _userSelect;
2779 if ('onselectstart' in document) {
2780         disableTextSelection = function () {
2781                 on(window, 'selectstart', preventDefault);
2782         };
2783         enableTextSelection = function () {
2784                 off(window, 'selectstart', preventDefault);
2785         };
2786 } else {
2787         var userSelectProperty = testProp(
2788                 ['userSelect', 'WebkitUserSelect', 'OUserSelect', 'MozUserSelect', 'msUserSelect']);
2789
2790         disableTextSelection = function () {
2791                 if (userSelectProperty) {
2792                         var style = document.documentElement.style;
2793                         _userSelect = style[userSelectProperty];
2794                         style[userSelectProperty] = 'none';
2795                 }
2796         };
2797         enableTextSelection = function () {
2798                 if (userSelectProperty) {
2799                         document.documentElement.style[userSelectProperty] = _userSelect;
2800                         _userSelect = undefined;
2801                 }
2802         };
2803 }
2804
2805 // @function disableImageDrag()
2806 // As [`L.DomUtil.disableTextSelection`](#domutil-disabletextselection), but
2807 // for `dragstart` DOM events, usually generated when the user drags an image.
2808 function disableImageDrag() {
2809         on(window, 'dragstart', preventDefault);
2810 }
2811
2812 // @function enableImageDrag()
2813 // Cancels the effects of a previous [`L.DomUtil.disableImageDrag`](#domutil-disabletextselection).
2814 function enableImageDrag() {
2815         off(window, 'dragstart', preventDefault);
2816 }
2817
2818 var _outlineElement;
2819 var _outlineStyle;
2820 // @function preventOutline(el: HTMLElement)
2821 // Makes the [outline](https://developer.mozilla.org/docs/Web/CSS/outline)
2822 // of the element `el` invisible. Used internally by Leaflet to prevent
2823 // focusable elements from displaying an outline when the user performs a
2824 // drag interaction on them.
2825 function preventOutline(element) {
2826         while (element.tabIndex === -1) {
2827                 element = element.parentNode;
2828         }
2829         if (!element.style) { return; }
2830         restoreOutline();
2831         _outlineElement = element;
2832         _outlineStyle = element.style.outline;
2833         element.style.outline = 'none';
2834         on(window, 'keydown', restoreOutline);
2835 }
2836
2837 // @function restoreOutline()
2838 // Cancels the effects of a previous [`L.DomUtil.preventOutline`]().
2839 function restoreOutline() {
2840         if (!_outlineElement) { return; }
2841         _outlineElement.style.outline = _outlineStyle;
2842         _outlineElement = undefined;
2843         _outlineStyle = undefined;
2844         off(window, 'keydown', restoreOutline);
2845 }
2846
2847
2848 var DomUtil = (Object.freeze || Object)({
2849         TRANSFORM: TRANSFORM,
2850         TRANSITION: TRANSITION,
2851         TRANSITION_END: TRANSITION_END,
2852         get: get,
2853         getStyle: getStyle,
2854         create: create$1,
2855         remove: remove,
2856         empty: empty,
2857         toFront: toFront,
2858         toBack: toBack,
2859         hasClass: hasClass,
2860         addClass: addClass,
2861         removeClass: removeClass,
2862         setClass: setClass,
2863         getClass: getClass,
2864         setOpacity: setOpacity,
2865         testProp: testProp,
2866         setTransform: setTransform,
2867         setPosition: setPosition,
2868         getPosition: getPosition,
2869         disableTextSelection: disableTextSelection,
2870         enableTextSelection: enableTextSelection,
2871         disableImageDrag: disableImageDrag,
2872         enableImageDrag: enableImageDrag,
2873         preventOutline: preventOutline,
2874         restoreOutline: restoreOutline
2875 });
2876
2877 /*
2878  * @class PosAnimation
2879  * @aka L.PosAnimation
2880  * @inherits Evented
2881  * Used internally for panning animations, utilizing CSS3 Transitions for modern browsers and a timer fallback for IE6-9.
2882  *
2883  * @example
2884  * ```js
2885  * var fx = new L.PosAnimation();
2886  * fx.run(el, [300, 500], 0.5);
2887  * ```
2888  *
2889  * @constructor L.PosAnimation()
2890  * Creates a `PosAnimation` object.
2891  *
2892  */
2893
2894 var PosAnimation = Evented.extend({
2895
2896         // @method run(el: HTMLElement, newPos: Point, duration?: Number, easeLinearity?: Number)
2897         // Run an animation of a given element to a new position, optionally setting
2898         // duration in seconds (`0.25` by default) and easing linearity factor (3rd
2899         // argument of the [cubic bezier curve](http://cubic-bezier.com/#0,0,.5,1),
2900         // `0.5` by default).
2901         run: function (el, newPos, duration, easeLinearity) {
2902                 this.stop();
2903
2904                 this._el = el;
2905                 this._inProgress = true;
2906                 this._duration = duration || 0.25;
2907                 this._easeOutPower = 1 / Math.max(easeLinearity || 0.5, 0.2);
2908
2909                 this._startPos = getPosition(el);
2910                 this._offset = newPos.subtract(this._startPos);
2911                 this._startTime = +new Date();
2912
2913                 // @event start: Event
2914                 // Fired when the animation starts
2915                 this.fire('start');
2916
2917                 this._animate();
2918         },
2919
2920         // @method stop()
2921         // Stops the animation (if currently running).
2922         stop: function () {
2923                 if (!this._inProgress) { return; }
2924
2925                 this._step(true);
2926                 this._complete();
2927         },
2928
2929         _animate: function () {
2930                 // animation loop
2931                 this._animId = requestAnimFrame(this._animate, this);
2932                 this._step();
2933         },
2934
2935         _step: function (round) {
2936                 var elapsed = (+new Date()) - this._startTime,
2937                     duration = this._duration * 1000;
2938
2939                 if (elapsed < duration) {
2940                         this._runFrame(this._easeOut(elapsed / duration), round);
2941                 } else {
2942                         this._runFrame(1);
2943                         this._complete();
2944                 }
2945         },
2946
2947         _runFrame: function (progress, round) {
2948                 var pos = this._startPos.add(this._offset.multiplyBy(progress));
2949                 if (round) {
2950                         pos._round();
2951                 }
2952                 setPosition(this._el, pos);
2953
2954                 // @event step: Event
2955                 // Fired continuously during the animation.
2956                 this.fire('step');
2957         },
2958
2959         _complete: function () {
2960                 cancelAnimFrame(this._animId);
2961
2962                 this._inProgress = false;
2963                 // @event end: Event
2964                 // Fired when the animation ends.
2965                 this.fire('end');
2966         },
2967
2968         _easeOut: function (t) {
2969                 return 1 - Math.pow(1 - t, this._easeOutPower);
2970         }
2971 });
2972
2973 /*
2974  * @class Map
2975  * @aka L.Map
2976  * @inherits Evented
2977  *
2978  * The central class of the API — it is used to create a map on a page and manipulate it.
2979  *
2980  * @example
2981  *
2982  * ```js
2983  * // initialize the map on the "map" div with a given center and zoom
2984  * var map = L.map('map', {
2985  *      center: [51.505, -0.09],
2986  *      zoom: 13
2987  * });
2988  * ```
2989  *
2990  */
2991
2992 var Map = Evented.extend({
2993
2994         options: {
2995                 // @section Map State Options
2996                 // @option crs: CRS = L.CRS.EPSG3857
2997                 // The [Coordinate Reference System](#crs) to use. Don't change this if you're not
2998                 // sure what it means.
2999                 crs: EPSG3857,
3000
3001                 // @option center: LatLng = undefined
3002                 // Initial geographic center of the map
3003                 center: undefined,
3004
3005                 // @option zoom: Number = undefined
3006                 // Initial map zoom level
3007                 zoom: undefined,
3008
3009                 // @option minZoom: Number = *
3010                 // Minimum zoom level of the map.
3011                 // If not specified and at least one `GridLayer` or `TileLayer` is in the map,
3012                 // the lowest of their `minZoom` options will be used instead.
3013                 minZoom: undefined,
3014
3015                 // @option maxZoom: Number = *
3016                 // Maximum zoom level of the map.
3017                 // If not specified and at least one `GridLayer` or `TileLayer` is in the map,
3018                 // the highest of their `maxZoom` options will be used instead.
3019                 maxZoom: undefined,
3020
3021                 // @option layers: Layer[] = []
3022                 // Array of layers that will be added to the map initially
3023                 layers: [],
3024
3025                 // @option maxBounds: LatLngBounds = null
3026                 // When this option is set, the map restricts the view to the given
3027                 // geographical bounds, bouncing the user back if the user tries to pan
3028                 // outside the view. To set the restriction dynamically, use
3029                 // [`setMaxBounds`](#map-setmaxbounds) method.
3030                 maxBounds: undefined,
3031
3032                 // @option renderer: Renderer = *
3033                 // The default method for drawing vector layers on the map. `L.SVG`
3034                 // or `L.Canvas` by default depending on browser support.
3035                 renderer: undefined,
3036
3037
3038                 // @section Animation Options
3039                 // @option zoomAnimation: Boolean = true
3040                 // Whether the map zoom animation is enabled. By default it's enabled
3041                 // in all browsers that support CSS3 Transitions except Android.
3042                 zoomAnimation: true,
3043
3044                 // @option zoomAnimationThreshold: Number = 4
3045                 // Won't animate zoom if the zoom difference exceeds this value.
3046                 zoomAnimationThreshold: 4,
3047
3048                 // @option fadeAnimation: Boolean = true
3049                 // Whether the tile fade animation is enabled. By default it's enabled
3050                 // in all browsers that support CSS3 Transitions except Android.
3051                 fadeAnimation: true,
3052
3053                 // @option markerZoomAnimation: Boolean = true
3054                 // Whether markers animate their zoom with the zoom animation, if disabled
3055                 // they will disappear for the length of the animation. By default it's
3056                 // enabled in all browsers that support CSS3 Transitions except Android.
3057                 markerZoomAnimation: true,
3058
3059                 // @option transform3DLimit: Number = 2^23
3060                 // Defines the maximum size of a CSS translation transform. The default
3061                 // value should not be changed unless a web browser positions layers in
3062                 // the wrong place after doing a large `panBy`.
3063                 transform3DLimit: 8388608, // Precision limit of a 32-bit float
3064
3065                 // @section Interaction Options
3066                 // @option zoomSnap: Number = 1
3067                 // Forces the map's zoom level to always be a multiple of this, particularly
3068                 // right after a [`fitBounds()`](#map-fitbounds) or a pinch-zoom.
3069                 // By default, the zoom level snaps to the nearest integer; lower values
3070                 // (e.g. `0.5` or `0.1`) allow for greater granularity. A value of `0`
3071                 // means the zoom level will not be snapped after `fitBounds` or a pinch-zoom.
3072                 zoomSnap: 1,
3073
3074                 // @option zoomDelta: Number = 1
3075                 // Controls how much the map's zoom level will change after a
3076                 // [`zoomIn()`](#map-zoomin), [`zoomOut()`](#map-zoomout), pressing `+`
3077                 // or `-` on the keyboard, or using the [zoom controls](#control-zoom).
3078                 // Values smaller than `1` (e.g. `0.5`) allow for greater granularity.
3079                 zoomDelta: 1,
3080
3081                 // @option trackResize: Boolean = true
3082                 // Whether the map automatically handles browser window resize to update itself.
3083                 trackResize: true
3084         },
3085
3086         initialize: function (id, options) { // (HTMLElement or String, Object)
3087                 options = setOptions(this, options);
3088
3089                 this._initContainer(id);
3090                 this._initLayout();
3091
3092                 // hack for https://github.com/Leaflet/Leaflet/issues/1980
3093                 this._onResize = bind(this._onResize, this);
3094
3095                 this._initEvents();
3096
3097                 if (options.maxBounds) {
3098                         this.setMaxBounds(options.maxBounds);
3099                 }
3100
3101                 if (options.zoom !== undefined) {
3102                         this._zoom = this._limitZoom(options.zoom);
3103                 }
3104
3105                 if (options.center && options.zoom !== undefined) {
3106                         this.setView(toLatLng(options.center), options.zoom, {reset: true});
3107                 }
3108
3109                 this._handlers = [];
3110                 this._layers = {};
3111                 this._zoomBoundLayers = {};
3112                 this._sizeChanged = true;
3113
3114                 this.callInitHooks();
3115
3116                 // don't animate on browsers without hardware-accelerated transitions or old Android/Opera
3117                 this._zoomAnimated = TRANSITION && any3d && !mobileOpera &&
3118                                 this.options.zoomAnimation;
3119
3120                 // zoom transitions run with the same duration for all layers, so if one of transitionend events
3121                 // happens after starting zoom animation (propagating to the map pane), we know that it ended globally
3122                 if (this._zoomAnimated) {
3123                         this._createAnimProxy();
3124                         on(this._proxy, TRANSITION_END, this._catchTransitionEnd, this);
3125                 }
3126
3127                 this._addLayers(this.options.layers);
3128         },
3129
3130
3131         // @section Methods for modifying map state
3132
3133         // @method setView(center: LatLng, zoom: Number, options?: Zoom/pan options): this
3134         // Sets the view of the map (geographical center and zoom) with the given
3135         // animation options.
3136         setView: function (center, zoom, options) {
3137
3138                 zoom = zoom === undefined ? this._zoom : this._limitZoom(zoom);
3139                 center = this._limitCenter(toLatLng(center), zoom, this.options.maxBounds);
3140                 options = options || {};
3141
3142                 this._stop();
3143
3144                 if (this._loaded && !options.reset && options !== true) {
3145
3146                         if (options.animate !== undefined) {
3147                                 options.zoom = extend({animate: options.animate}, options.zoom);
3148                                 options.pan = extend({animate: options.animate, duration: options.duration}, options.pan);
3149                         }
3150
3151                         // try animating pan or zoom
3152                         var moved = (this._zoom !== zoom) ?
3153                                 this._tryAnimatedZoom && this._tryAnimatedZoom(center, zoom, options.zoom) :
3154                                 this._tryAnimatedPan(center, options.pan);
3155
3156                         if (moved) {
3157                                 // prevent resize handler call, the view will refresh after animation anyway
3158                                 clearTimeout(this._sizeTimer);
3159                                 return this;
3160                         }
3161                 }
3162
3163                 // animation didn't start, just reset the map view
3164                 this._resetView(center, zoom);
3165
3166                 return this;
3167         },
3168
3169         // @method setZoom(zoom: Number, options?: Zoom/pan options): this
3170         // Sets the zoom of the map.
3171         setZoom: function (zoom, options) {
3172                 if (!this._loaded) {
3173                         this._zoom = zoom;
3174                         return this;
3175                 }
3176                 return this.setView(this.getCenter(), zoom, {zoom: options});
3177         },
3178
3179         // @method zoomIn(delta?: Number, options?: Zoom options): this
3180         // Increases the zoom of the map by `delta` ([`zoomDelta`](#map-zoomdelta) by default).
3181         zoomIn: function (delta, options) {
3182                 delta = delta || (any3d ? this.options.zoomDelta : 1);
3183                 return this.setZoom(this._zoom + delta, options);
3184         },
3185
3186         // @method zoomOut(delta?: Number, options?: Zoom options): this
3187         // Decreases the zoom of the map by `delta` ([`zoomDelta`](#map-zoomdelta) by default).
3188         zoomOut: function (delta, options) {
3189                 delta = delta || (any3d ? this.options.zoomDelta : 1);
3190                 return this.setZoom(this._zoom - delta, options);
3191         },
3192
3193         // @method setZoomAround(latlng: LatLng, zoom: Number, options: Zoom options): this
3194         // Zooms the map while keeping a specified geographical point on the map
3195         // stationary (e.g. used internally for scroll zoom and double-click zoom).
3196         // @alternative
3197         // @method setZoomAround(offset: Point, zoom: Number, options: Zoom options): this
3198         // Zooms the map while keeping a specified pixel on the map (relative to the top-left corner) stationary.
3199         setZoomAround: function (latlng, zoom, options) {
3200                 var scale = this.getZoomScale(zoom),
3201                     viewHalf = this.getSize().divideBy(2),
3202                     containerPoint = latlng instanceof Point ? latlng : this.latLngToContainerPoint(latlng),
3203
3204                     centerOffset = containerPoint.subtract(viewHalf).multiplyBy(1 - 1 / scale),
3205                     newCenter = this.containerPointToLatLng(viewHalf.add(centerOffset));
3206
3207                 return this.setView(newCenter, zoom, {zoom: options});
3208         },
3209
3210         _getBoundsCenterZoom: function (bounds, options) {
3211
3212                 options = options || {};
3213                 bounds = bounds.getBounds ? bounds.getBounds() : toLatLngBounds(bounds);
3214
3215                 var paddingTL = toPoint(options.paddingTopLeft || options.padding || [0, 0]),
3216                     paddingBR = toPoint(options.paddingBottomRight || options.padding || [0, 0]),
3217
3218                     zoom = this.getBoundsZoom(bounds, false, paddingTL.add(paddingBR));
3219
3220                 zoom = (typeof options.maxZoom === 'number') ? Math.min(options.maxZoom, zoom) : zoom;
3221
3222                 if (zoom === Infinity) {
3223                         return {
3224                                 center: bounds.getCenter(),
3225                                 zoom: zoom
3226                         };
3227                 }
3228
3229                 var paddingOffset = paddingBR.subtract(paddingTL).divideBy(2),
3230
3231                     swPoint = this.project(bounds.getSouthWest(), zoom),
3232                     nePoint = this.project(bounds.getNorthEast(), zoom),
3233                     center = this.unproject(swPoint.add(nePoint).divideBy(2).add(paddingOffset), zoom);
3234
3235                 return {
3236                         center: center,
3237                         zoom: zoom
3238                 };
3239         },
3240
3241         // @method fitBounds(bounds: LatLngBounds, options?: fitBounds options): this
3242         // Sets a map view that contains the given geographical bounds with the
3243         // maximum zoom level possible.
3244         fitBounds: function (bounds, options) {
3245
3246                 bounds = toLatLngBounds(bounds);
3247
3248                 if (!bounds.isValid()) {
3249                         throw new Error('Bounds are not valid.');
3250                 }
3251
3252                 var target = this._getBoundsCenterZoom(bounds, options);
3253                 return this.setView(target.center, target.zoom, options);
3254         },
3255
3256         // @method fitWorld(options?: fitBounds options): this
3257         // Sets a map view that mostly contains the whole world with the maximum
3258         // zoom level possible.
3259         fitWorld: function (options) {
3260                 return this.fitBounds([[-90, -180], [90, 180]], options);
3261         },
3262
3263         // @method panTo(latlng: LatLng, options?: Pan options): this
3264         // Pans the map to a given center.
3265         panTo: function (center, options) { // (LatLng)
3266                 return this.setView(center, this._zoom, {pan: options});
3267         },
3268
3269         // @method panBy(offset: Point, options?: Pan options): this
3270         // Pans the map by a given number of pixels (animated).
3271         panBy: function (offset, options) {
3272                 offset = toPoint(offset).round();
3273                 options = options || {};
3274
3275                 if (!offset.x && !offset.y) {
3276                         return this.fire('moveend');
3277                 }
3278                 // If we pan too far, Chrome gets issues with tiles
3279                 // and makes them disappear or appear in the wrong place (slightly offset) #2602
3280                 if (options.animate !== true && !this.getSize().contains(offset)) {
3281                         this._resetView(this.unproject(this.project(this.getCenter()).add(offset)), this.getZoom());
3282                         return this;
3283                 }
3284
3285                 if (!this._panAnim) {
3286                         this._panAnim = new PosAnimation();
3287
3288                         this._panAnim.on({
3289                                 'step': this._onPanTransitionStep,
3290                                 'end': this._onPanTransitionEnd
3291                         }, this);
3292                 }
3293
3294                 // don't fire movestart if animating inertia
3295                 if (!options.noMoveStart) {
3296                         this.fire('movestart');
3297                 }
3298
3299                 // animate pan unless animate: false specified
3300                 if (options.animate !== false) {
3301                         addClass(this._mapPane, 'leaflet-pan-anim');
3302
3303                         var newPos = this._getMapPanePos().subtract(offset).round();
3304                         this._panAnim.run(this._mapPane, newPos, options.duration || 0.25, options.easeLinearity);
3305                 } else {
3306                         this._rawPanBy(offset);
3307                         this.fire('move').fire('moveend');
3308                 }
3309
3310                 return this;
3311         },
3312
3313         // @method flyTo(latlng: LatLng, zoom?: Number, options?: Zoom/pan options): this
3314         // Sets the view of the map (geographical center and zoom) performing a smooth
3315         // pan-zoom animation.
3316         flyTo: function (targetCenter, targetZoom, options) {
3317
3318                 options = options || {};
3319                 if (options.animate === false || !any3d) {
3320                         return this.setView(targetCenter, targetZoom, options);
3321                 }
3322
3323                 this._stop();
3324
3325                 var from = this.project(this.getCenter()),
3326                     to = this.project(targetCenter),
3327                     size = this.getSize(),
3328                     startZoom = this._zoom;
3329
3330                 targetCenter = toLatLng(targetCenter);
3331                 targetZoom = targetZoom === undefined ? startZoom : targetZoom;
3332
3333                 var w0 = Math.max(size.x, size.y),
3334                     w1 = w0 * this.getZoomScale(startZoom, targetZoom),
3335                     u1 = (to.distanceTo(from)) || 1,
3336                     rho = 1.42,
3337                     rho2 = rho * rho;
3338
3339                 function r(i) {
3340                         var s1 = i ? -1 : 1,
3341                             s2 = i ? w1 : w0,
3342                             t1 = w1 * w1 - w0 * w0 + s1 * rho2 * rho2 * u1 * u1,
3343                             b1 = 2 * s2 * rho2 * u1,
3344                             b = t1 / b1,
3345                             sq = Math.sqrt(b * b + 1) - b;
3346
3347                             // workaround for floating point precision bug when sq = 0, log = -Infinite,
3348                             // thus triggering an infinite loop in flyTo
3349                             var log = sq < 0.000000001 ? -18 : Math.log(sq);
3350
3351                         return log;
3352                 }
3353
3354                 function sinh(n) { return (Math.exp(n) - Math.exp(-n)) / 2; }
3355                 function cosh(n) { return (Math.exp(n) + Math.exp(-n)) / 2; }
3356                 function tanh(n) { return sinh(n) / cosh(n); }
3357
3358                 var r0 = r(0);
3359
3360                 function w(s) { return w0 * (cosh(r0) / cosh(r0 + rho * s)); }
3361                 function u(s) { return w0 * (cosh(r0) * tanh(r0 + rho * s) - sinh(r0)) / rho2; }
3362
3363                 function easeOut(t) { return 1 - Math.pow(1 - t, 1.5); }
3364
3365                 var start = Date.now(),
3366                     S = (r(1) - r0) / rho,
3367                     duration = options.duration ? 1000 * options.duration : 1000 * S * 0.8;
3368
3369                 function frame() {
3370                         var t = (Date.now() - start) / duration,
3371                             s = easeOut(t) * S;
3372
3373                         if (t <= 1) {
3374                                 this._flyToFrame = requestAnimFrame(frame, this);
3375
3376                                 this._move(
3377                                         this.unproject(from.add(to.subtract(from).multiplyBy(u(s) / u1)), startZoom),
3378                                         this.getScaleZoom(w0 / w(s), startZoom),
3379                                         {flyTo: true});
3380
3381                         } else {
3382                                 this
3383                                         ._move(targetCenter, targetZoom)
3384                                         ._moveEnd(true);
3385                         }
3386                 }
3387
3388                 this._moveStart(true, options.noMoveStart);
3389
3390                 frame.call(this);
3391                 return this;
3392         },
3393
3394         // @method flyToBounds(bounds: LatLngBounds, options?: fitBounds options): this
3395         // Sets the view of the map with a smooth animation like [`flyTo`](#map-flyto),
3396         // but takes a bounds parameter like [`fitBounds`](#map-fitbounds).
3397         flyToBounds: function (bounds, options) {
3398                 var target = this._getBoundsCenterZoom(bounds, options);
3399                 return this.flyTo(target.center, target.zoom, options);
3400         },
3401
3402         // @method setMaxBounds(bounds: Bounds): this
3403         // Restricts the map view to the given bounds (see the [maxBounds](#map-maxbounds) option).
3404         setMaxBounds: function (bounds) {
3405                 bounds = toLatLngBounds(bounds);
3406
3407                 if (!bounds.isValid()) {
3408                         this.options.maxBounds = null;
3409                         return this.off('moveend', this._panInsideMaxBounds);
3410                 } else if (this.options.maxBounds) {
3411                         this.off('moveend', this._panInsideMaxBounds);
3412                 }
3413
3414                 this.options.maxBounds = bounds;
3415
3416                 if (this._loaded) {
3417                         this._panInsideMaxBounds();
3418                 }
3419
3420                 return this.on('moveend', this._panInsideMaxBounds);
3421         },
3422
3423         // @method setMinZoom(zoom: Number): this
3424         // Sets the lower limit for the available zoom levels (see the [minZoom](#map-minzoom) option).
3425         setMinZoom: function (zoom) {
3426                 var oldZoom = this.options.minZoom;
3427                 this.options.minZoom = zoom;
3428
3429                 if (this._loaded && oldZoom !== zoom) {
3430                         this.fire('zoomlevelschange');
3431
3432                         if (this.getZoom() < this.options.minZoom) {
3433                                 return this.setZoom(zoom);
3434                         }
3435                 }
3436
3437                 return this;
3438         },
3439
3440         // @method setMaxZoom(zoom: Number): this
3441         // Sets the upper limit for the available zoom levels (see the [maxZoom](#map-maxzoom) option).
3442         setMaxZoom: function (zoom) {
3443                 var oldZoom = this.options.maxZoom;
3444                 this.options.maxZoom = zoom;
3445
3446                 if (this._loaded && oldZoom !== zoom) {
3447                         this.fire('zoomlevelschange');
3448
3449                         if (this.getZoom() > this.options.maxZoom) {
3450                                 return this.setZoom(zoom);
3451                         }
3452                 }
3453
3454                 return this;
3455         },
3456
3457         // @method panInsideBounds(bounds: LatLngBounds, options?: Pan options): this
3458         // 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.
3459         panInsideBounds: function (bounds, options) {
3460                 this._enforcingBounds = true;
3461                 var center = this.getCenter(),
3462                     newCenter = this._limitCenter(center, this._zoom, toLatLngBounds(bounds));
3463
3464                 if (!center.equals(newCenter)) {
3465                         this.panTo(newCenter, options);
3466                 }
3467
3468                 this._enforcingBounds = false;
3469                 return this;
3470         },
3471
3472         // @method invalidateSize(options: Zoom/pan options): this
3473         // Checks if the map container size changed and updates the map if so —
3474         // call it after you've changed the map size dynamically, also animating
3475         // pan by default. If `options.pan` is `false`, panning will not occur.
3476         // If `options.debounceMoveend` is `true`, it will delay `moveend` event so
3477         // that it doesn't happen often even if the method is called many
3478         // times in a row.
3479
3480         // @alternative
3481         // @method invalidateSize(animate: Boolean): this
3482         // Checks if the map container size changed and updates the map if so —
3483         // call it after you've changed the map size dynamically, also animating
3484         // pan by default.
3485         invalidateSize: function (options) {
3486                 if (!this._loaded) { return this; }
3487
3488                 options = extend({
3489                         animate: false,
3490                         pan: true
3491                 }, options === true ? {animate: true} : options);
3492
3493                 var oldSize = this.getSize();
3494                 this._sizeChanged = true;
3495                 this._lastCenter = null;
3496
3497                 var newSize = this.getSize(),
3498                     oldCenter = oldSize.divideBy(2).round(),
3499                     newCenter = newSize.divideBy(2).round(),
3500                     offset = oldCenter.subtract(newCenter);
3501
3502                 if (!offset.x && !offset.y) { return this; }
3503
3504                 if (options.animate && options.pan) {
3505                         this.panBy(offset);
3506
3507                 } else {
3508                         if (options.pan) {
3509                                 this._rawPanBy(offset);
3510                         }
3511
3512                         this.fire('move');
3513
3514                         if (options.debounceMoveend) {
3515                                 clearTimeout(this._sizeTimer);
3516                                 this._sizeTimer = setTimeout(bind(this.fire, this, 'moveend'), 200);
3517                         } else {
3518                                 this.fire('moveend');
3519                         }
3520                 }
3521
3522                 // @section Map state change events
3523                 // @event resize: ResizeEvent
3524                 // Fired when the map is resized.
3525                 return this.fire('resize', {
3526                         oldSize: oldSize,
3527                         newSize: newSize
3528                 });
3529         },
3530
3531         // @section Methods for modifying map state
3532         // @method stop(): this
3533         // Stops the currently running `panTo` or `flyTo` animation, if any.
3534         stop: function () {
3535                 this.setZoom(this._limitZoom(this._zoom));
3536                 if (!this.options.zoomSnap) {
3537                         this.fire('viewreset');
3538                 }
3539                 return this._stop();
3540         },
3541
3542         // @section Geolocation methods
3543         // @method locate(options?: Locate options): this
3544         // Tries to locate the user using the Geolocation API, firing a [`locationfound`](#map-locationfound)
3545         // event with location data on success or a [`locationerror`](#map-locationerror) event on failure,
3546         // and optionally sets the map view to the user's location with respect to
3547         // detection accuracy (or to the world view if geolocation failed).
3548         // Note that, if your page doesn't use HTTPS, this method will fail in
3549         // modern browsers ([Chrome 50 and newer](https://sites.google.com/a/chromium.org/dev/Home/chromium-security/deprecating-powerful-features-on-insecure-origins))
3550         // See `Locate options` for more details.
3551         locate: function (options) {
3552
3553                 options = this._locateOptions = extend({
3554                         timeout: 10000,
3555                         watch: false
3556                         // setView: false
3557                         // maxZoom: <Number>
3558                         // maximumAge: 0
3559                         // enableHighAccuracy: false
3560                 }, options);
3561
3562                 if (!('geolocation' in navigator)) {
3563                         this._handleGeolocationError({
3564                                 code: 0,
3565                                 message: 'Geolocation not supported.'
3566                         });
3567                         return this;
3568                 }
3569
3570                 var onResponse = bind(this._handleGeolocationResponse, this),
3571                     onError = bind(this._handleGeolocationError, this);
3572
3573                 if (options.watch) {
3574                         this._locationWatchId =
3575                                 navigator.geolocation.watchPosition(onResponse, onError, options);
3576                 } else {
3577                         navigator.geolocation.getCurrentPosition(onResponse, onError, options);
3578                 }
3579                 return this;
3580         },
3581
3582         // @method stopLocate(): this
3583         // Stops watching location previously initiated by `map.locate({watch: true})`
3584         // and aborts resetting the map view if map.locate was called with
3585         // `{setView: true}`.
3586         stopLocate: function () {
3587                 if (navigator.geolocation && navigator.geolocation.clearWatch) {
3588                         navigator.geolocation.clearWatch(this._locationWatchId);
3589                 }
3590                 if (this._locateOptions) {
3591                         this._locateOptions.setView = false;
3592                 }
3593                 return this;
3594         },
3595
3596         _handleGeolocationError: function (error) {
3597                 var c = error.code,
3598                     message = error.message ||
3599                             (c === 1 ? 'permission denied' :
3600                             (c === 2 ? 'position unavailable' : 'timeout'));
3601
3602                 if (this._locateOptions.setView && !this._loaded) {
3603                         this.fitWorld();
3604                 }
3605
3606                 // @section Location events
3607                 // @event locationerror: ErrorEvent
3608                 // Fired when geolocation (using the [`locate`](#map-locate) method) failed.
3609                 this.fire('locationerror', {
3610                         code: c,
3611                         message: 'Geolocation error: ' + message + '.'
3612                 });
3613         },
3614
3615         _handleGeolocationResponse: function (pos) {
3616                 var lat = pos.coords.latitude,
3617                     lng = pos.coords.longitude,
3618                     latlng = new LatLng(lat, lng),
3619                     bounds = latlng.toBounds(pos.coords.accuracy),
3620                     options = this._locateOptions;
3621
3622                 if (options.setView) {
3623                         var zoom = this.getBoundsZoom(bounds);
3624                         this.setView(latlng, options.maxZoom ? Math.min(zoom, options.maxZoom) : zoom);
3625                 }
3626
3627                 var data = {
3628                         latlng: latlng,
3629                         bounds: bounds,
3630                         timestamp: pos.timestamp
3631                 };
3632
3633                 for (var i in pos.coords) {
3634                         if (typeof pos.coords[i] === 'number') {
3635                                 data[i] = pos.coords[i];
3636                         }
3637                 }
3638
3639                 // @event locationfound: LocationEvent
3640                 // Fired when geolocation (using the [`locate`](#map-locate) method)
3641                 // went successfully.
3642                 this.fire('locationfound', data);
3643         },
3644
3645         // TODO Appropriate docs section?
3646         // @section Other Methods
3647         // @method addHandler(name: String, HandlerClass: Function): this
3648         // Adds a new `Handler` to the map, given its name and constructor function.
3649         addHandler: function (name, HandlerClass) {
3650                 if (!HandlerClass) { return this; }
3651
3652                 var handler = this[name] = new HandlerClass(this);
3653
3654                 this._handlers.push(handler);
3655
3656                 if (this.options[name]) {
3657                         handler.enable();
3658                 }
3659
3660                 return this;
3661         },
3662
3663         // @method remove(): this
3664         // Destroys the map and clears all related event listeners.
3665         remove: function () {
3666
3667                 this._initEvents(true);
3668
3669                 if (this._containerId !== this._container._leaflet_id) {
3670                         throw new Error('Map container is being reused by another instance');
3671                 }
3672
3673                 try {
3674                         // throws error in IE6-8
3675                         delete this._container._leaflet_id;
3676                         delete this._containerId;
3677                 } catch (e) {
3678                         /*eslint-disable */
3679                         this._container._leaflet_id = undefined;
3680                         /* eslint-enable */
3681                         this._containerId = undefined;
3682                 }
3683
3684                 if (this._locationWatchId !== undefined) {
3685                         this.stopLocate();
3686                 }
3687
3688                 this._stop();
3689
3690                 remove(this._mapPane);
3691
3692                 if (this._clearControlPos) {
3693                         this._clearControlPos();
3694                 }
3695
3696                 this._clearHandlers();
3697
3698                 if (this._loaded) {
3699                         // @section Map state change events
3700                         // @event unload: Event
3701                         // Fired when the map is destroyed with [remove](#map-remove) method.
3702                         this.fire('unload');
3703                 }
3704
3705                 var i;
3706                 for (i in this._layers) {
3707                         this._layers[i].remove();
3708                 }
3709                 for (i in this._panes) {
3710                         remove(this._panes[i]);
3711                 }
3712
3713                 this._layers = [];
3714                 this._panes = [];
3715                 delete this._mapPane;
3716                 delete this._renderer;
3717
3718                 return this;
3719         },
3720
3721         // @section Other Methods
3722         // @method createPane(name: String, container?: HTMLElement): HTMLElement
3723         // Creates a new [map pane](#map-pane) with the given name if it doesn't exist already,
3724         // then returns it. The pane is created as a child of `container`, or
3725         // as a child of the main map pane if not set.
3726         createPane: function (name, container) {
3727                 var className = 'leaflet-pane' + (name ? ' leaflet-' + name.replace('Pane', '') + '-pane' : ''),
3728                     pane = create$1('div', className, container || this._mapPane);
3729
3730                 if (name) {
3731                         this._panes[name] = pane;
3732                 }
3733                 return pane;
3734         },
3735
3736         // @section Methods for Getting Map State
3737
3738         // @method getCenter(): LatLng
3739         // Returns the geographical center of the map view
3740         getCenter: function () {
3741                 this._checkIfLoaded();
3742
3743                 if (this._lastCenter && !this._moved()) {
3744                         return this._lastCenter;
3745                 }
3746                 return this.layerPointToLatLng(this._getCenterLayerPoint());
3747         },
3748
3749         // @method getZoom(): Number
3750         // Returns the current zoom level of the map view
3751         getZoom: function () {
3752                 return this._zoom;
3753         },
3754
3755         // @method getBounds(): LatLngBounds
3756         // Returns the geographical bounds visible in the current map view
3757         getBounds: function () {
3758                 var bounds = this.getPixelBounds(),
3759                     sw = this.unproject(bounds.getBottomLeft()),
3760                     ne = this.unproject(bounds.getTopRight());
3761
3762                 return new LatLngBounds(sw, ne);
3763         },
3764
3765         // @method getMinZoom(): Number
3766         // 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.
3767         getMinZoom: function () {
3768                 return this.options.minZoom === undefined ? this._layersMinZoom || 0 : this.options.minZoom;
3769         },
3770
3771         // @method getMaxZoom(): Number
3772         // Returns the maximum zoom level of the map (if set in the `maxZoom` option of the map or of any layers).
3773         getMaxZoom: function () {
3774                 return this.options.maxZoom === undefined ?
3775                         (this._layersMaxZoom === undefined ? Infinity : this._layersMaxZoom) :
3776                         this.options.maxZoom;
3777         },
3778
3779         // @method getBoundsZoom(bounds: LatLngBounds, inside?: Boolean): Number
3780         // Returns the maximum zoom level on which the given bounds fit to the map
3781         // view in its entirety. If `inside` (optional) is set to `true`, the method
3782         // instead returns the minimum zoom level on which the map view fits into
3783         // the given bounds in its entirety.
3784         getBoundsZoom: function (bounds, inside, padding) { // (LatLngBounds[, Boolean, Point]) -> Number
3785                 bounds = toLatLngBounds(bounds);
3786                 padding = toPoint(padding || [0, 0]);
3787
3788                 var zoom = this.getZoom() || 0,
3789                     min = this.getMinZoom(),
3790                     max = this.getMaxZoom(),
3791                     nw = bounds.getNorthWest(),
3792                     se = bounds.getSouthEast(),
3793                     size = this.getSize().subtract(padding),
3794                     boundsSize = toBounds(this.project(se, zoom), this.project(nw, zoom)).getSize(),
3795                     snap = any3d ? this.options.zoomSnap : 1,
3796                     scalex = size.x / boundsSize.x,
3797                     scaley = size.y / boundsSize.y,
3798                     scale = inside ? Math.max(scalex, scaley) : Math.min(scalex, scaley);
3799
3800                 zoom = this.getScaleZoom(scale, zoom);
3801
3802                 if (snap) {
3803                         zoom = Math.round(zoom / (snap / 100)) * (snap / 100); // don't jump if within 1% of a snap level
3804                         zoom = inside ? Math.ceil(zoom / snap) * snap : Math.floor(zoom / snap) * snap;
3805                 }
3806
3807                 return Math.max(min, Math.min(max, zoom));
3808         },
3809
3810         // @method getSize(): Point
3811         // Returns the current size of the map container (in pixels).
3812         getSize: function () {
3813                 if (!this._size || this._sizeChanged) {
3814                         this._size = new Point(
3815                                 this._container.clientWidth || 0,
3816                                 this._container.clientHeight || 0);
3817
3818                         this._sizeChanged = false;
3819                 }
3820                 return this._size.clone();
3821         },
3822
3823         // @method getPixelBounds(): Bounds
3824         // Returns the bounds of the current map view in projected pixel
3825         // coordinates (sometimes useful in layer and overlay implementations).
3826         getPixelBounds: function (center, zoom) {
3827                 var topLeftPoint = this._getTopLeftPoint(center, zoom);
3828                 return new Bounds(topLeftPoint, topLeftPoint.add(this.getSize()));
3829         },
3830
3831         // TODO: Check semantics - isn't the pixel origin the 0,0 coord relative to
3832         // the map pane? "left point of the map layer" can be confusing, specially
3833         // since there can be negative offsets.
3834         // @method getPixelOrigin(): Point
3835         // Returns the projected pixel coordinates of the top left point of
3836         // the map layer (useful in custom layer and overlay implementations).
3837         getPixelOrigin: function () {
3838                 this._checkIfLoaded();
3839                 return this._pixelOrigin;
3840         },
3841
3842         // @method getPixelWorldBounds(zoom?: Number): Bounds
3843         // Returns the world's bounds in pixel coordinates for zoom level `zoom`.
3844         // If `zoom` is omitted, the map's current zoom level is used.
3845         getPixelWorldBounds: function (zoom) {
3846                 return this.options.crs.getProjectedBounds(zoom === undefined ? this.getZoom() : zoom);
3847         },
3848
3849         // @section Other Methods
3850
3851         // @method getPane(pane: String|HTMLElement): HTMLElement
3852         // Returns a [map pane](#map-pane), given its name or its HTML element (its identity).
3853         getPane: function (pane) {
3854                 return typeof pane === 'string' ? this._panes[pane] : pane;
3855         },
3856
3857         // @method getPanes(): Object
3858         // Returns a plain object containing the names of all [panes](#map-pane) as keys and
3859         // the panes as values.
3860         getPanes: function () {
3861                 return this._panes;
3862         },
3863
3864         // @method getContainer: HTMLElement
3865         // Returns the HTML element that contains the map.
3866         getContainer: function () {
3867                 return this._container;
3868         },
3869
3870
3871         // @section Conversion Methods
3872
3873         // @method getZoomScale(toZoom: Number, fromZoom: Number): Number
3874         // Returns the scale factor to be applied to a map transition from zoom level
3875         // `fromZoom` to `toZoom`. Used internally to help with zoom animations.
3876         getZoomScale: function (toZoom, fromZoom) {
3877                 // TODO replace with universal implementation after refactoring projections
3878                 var crs = this.options.crs;
3879                 fromZoom = fromZoom === undefined ? this._zoom : fromZoom;
3880                 return crs.scale(toZoom) / crs.scale(fromZoom);
3881         },
3882
3883         // @method getScaleZoom(scale: Number, fromZoom: Number): Number
3884         // Returns the zoom level that the map would end up at, if it is at `fromZoom`
3885         // level and everything is scaled by a factor of `scale`. Inverse of
3886         // [`getZoomScale`](#map-getZoomScale).
3887         getScaleZoom: function (scale, fromZoom) {
3888                 var crs = this.options.crs;
3889                 fromZoom = fromZoom === undefined ? this._zoom : fromZoom;
3890                 var zoom = crs.zoom(scale * crs.scale(fromZoom));
3891                 return isNaN(zoom) ? Infinity : zoom;
3892         },
3893
3894         // @method project(latlng: LatLng, zoom: Number): Point
3895         // Projects a geographical coordinate `LatLng` according to the projection
3896         // of the map's CRS, then scales it according to `zoom` and the CRS's
3897         // `Transformation`. The result is pixel coordinate relative to
3898         // the CRS origin.
3899         project: function (latlng, zoom) {
3900                 zoom = zoom === undefined ? this._zoom : zoom;
3901                 return this.options.crs.latLngToPoint(toLatLng(latlng), zoom);
3902         },
3903
3904         // @method unproject(point: Point, zoom: Number): LatLng
3905         // Inverse of [`project`](#map-project).
3906         unproject: function (point, zoom) {
3907                 zoom = zoom === undefined ? this._zoom : zoom;
3908                 return this.options.crs.pointToLatLng(toPoint(point), zoom);
3909         },
3910
3911         // @method layerPointToLatLng(point: Point): LatLng
3912         // Given a pixel coordinate relative to the [origin pixel](#map-getpixelorigin),
3913         // returns the corresponding geographical coordinate (for the current zoom level).
3914         layerPointToLatLng: function (point) {
3915                 var projectedPoint = toPoint(point).add(this.getPixelOrigin());
3916                 return this.unproject(projectedPoint);
3917         },
3918
3919         // @method latLngToLayerPoint(latlng: LatLng): Point
3920         // Given a geographical coordinate, returns the corresponding pixel coordinate
3921         // relative to the [origin pixel](#map-getpixelorigin).
3922         latLngToLayerPoint: function (latlng) {
3923                 var projectedPoint = this.project(toLatLng(latlng))._round();
3924                 return projectedPoint._subtract(this.getPixelOrigin());
3925         },
3926
3927         // @method wrapLatLng(latlng: LatLng): LatLng
3928         // Returns a `LatLng` where `lat` and `lng` has been wrapped according to the
3929         // map's CRS's `wrapLat` and `wrapLng` properties, if they are outside the
3930         // CRS's bounds.
3931         // By default this means longitude is wrapped around the dateline so its
3932         // value is between -180 and +180 degrees.
3933         wrapLatLng: function (latlng) {
3934                 return this.options.crs.wrapLatLng(toLatLng(latlng));
3935         },
3936
3937         // @method wrapLatLngBounds(bounds: LatLngBounds): LatLngBounds
3938         // Returns a `LatLngBounds` with the same size as the given one, ensuring that
3939         // its center is within the CRS's bounds.
3940         // By default this means the center longitude is wrapped around the dateline so its
3941         // value is between -180 and +180 degrees, and the majority of the bounds
3942         // overlaps the CRS's bounds.
3943         wrapLatLngBounds: function (latlng) {
3944                 return this.options.crs.wrapLatLngBounds(toLatLngBounds(latlng));
3945         },
3946
3947         // @method distance(latlng1: LatLng, latlng2: LatLng): Number
3948         // Returns the distance between two geographical coordinates according to
3949         // the map's CRS. By default this measures distance in meters.
3950         distance: function (latlng1, latlng2) {
3951                 return this.options.crs.distance(toLatLng(latlng1), toLatLng(latlng2));
3952         },
3953
3954         // @method containerPointToLayerPoint(point: Point): Point
3955         // Given a pixel coordinate relative to the map container, returns the corresponding
3956         // pixel coordinate relative to the [origin pixel](#map-getpixelorigin).
3957         containerPointToLayerPoint: function (point) { // (Point)
3958                 return toPoint(point).subtract(this._getMapPanePos());
3959         },
3960
3961         // @method layerPointToContainerPoint(point: Point): Point
3962         // Given a pixel coordinate relative to the [origin pixel](#map-getpixelorigin),
3963         // returns the corresponding pixel coordinate relative to the map container.
3964         layerPointToContainerPoint: function (point) { // (Point)