f977f27d82c7fed247afb8b27c78476a3a84238f
[rails.git] / vendor / assets / leaflet / leaflet.js
1 /* @preserve
2  * Leaflet 1.3.3, a JS library for interactive maps. http://leafletjs.com
3  * (c) 2010-2018 Vladimir Agafonkin, (c) 2010-2011 CloudMade
4  */
5
6 (function (global, factory) {
7         typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) :
8         typeof define === 'function' && define.amd ? define(['exports'], factory) :
9         (factory((global.L = {})));
10 }(this, (function (exports) { 'use strict';
11
12 var version = "1.3.3";
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 Leaflet's `Class` object,
1364  * which means new classes can't inherit from it, and new methods
1365  * can't be added to it with the `include` function.
1366  */
1367
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 or on any screen when browser's display zoom is more than 100%.
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 DomUtil
2211  *
2212  * Utility functions to work with the [DOM](https://developer.mozilla.org/docs/Web/API/Document_Object_Model)
2213  * tree, used by Leaflet internally.
2214  *
2215  * Most functions expecting or returning a `HTMLElement` also work for
2216  * SVG elements. The only difference is that classes refer to CSS classes
2217  * in HTML and SVG classes in SVG.
2218  */
2219
2220
2221 // @property TRANSFORM: String
2222 // Vendor-prefixed transform style name (e.g. `'webkitTransform'` for WebKit).
2223 var TRANSFORM = testProp(
2224         ['transform', 'WebkitTransform', 'OTransform', 'MozTransform', 'msTransform']);
2225
2226 // webkitTransition comes first because some browser versions that drop vendor prefix don't do
2227 // the same for the transitionend event, in particular the Android 4.1 stock browser
2228
2229 // @property TRANSITION: String
2230 // Vendor-prefixed transition style name.
2231 var TRANSITION = testProp(
2232         ['webkitTransition', 'transition', 'OTransition', 'MozTransition', 'msTransition']);
2233
2234 // @property TRANSITION_END: String
2235 // Vendor-prefixed transitionend event name.
2236 var TRANSITION_END =
2237         TRANSITION === 'webkitTransition' || TRANSITION === 'OTransition' ? TRANSITION + 'End' : 'transitionend';
2238
2239
2240 // @function get(id: String|HTMLElement): HTMLElement
2241 // Returns an element given its DOM id, or returns the element itself
2242 // if it was passed directly.
2243 function get(id) {
2244         return typeof id === 'string' ? document.getElementById(id) : id;
2245 }
2246
2247 // @function getStyle(el: HTMLElement, styleAttrib: String): String
2248 // Returns the value for a certain style attribute on an element,
2249 // including computed values or values set through CSS.
2250 function getStyle(el, style) {
2251         var value = el.style[style] || (el.currentStyle && el.currentStyle[style]);
2252
2253         if ((!value || value === 'auto') && document.defaultView) {
2254                 var css = document.defaultView.getComputedStyle(el, null);
2255                 value = css ? css[style] : null;
2256         }
2257         return value === 'auto' ? null : value;
2258 }
2259
2260 // @function create(tagName: String, className?: String, container?: HTMLElement): HTMLElement
2261 // Creates an HTML element with `tagName`, sets its class to `className`, and optionally appends it to `container` element.
2262 function create$1(tagName, className, container) {
2263         var el = document.createElement(tagName);
2264         el.className = className || '';
2265
2266         if (container) {
2267                 container.appendChild(el);
2268         }
2269         return el;
2270 }
2271
2272 // @function remove(el: HTMLElement)
2273 // Removes `el` from its parent element
2274 function remove(el) {
2275         var parent = el.parentNode;
2276         if (parent) {
2277                 parent.removeChild(el);
2278         }
2279 }
2280
2281 // @function empty(el: HTMLElement)
2282 // Removes all of `el`'s children elements from `el`
2283 function empty(el) {
2284         while (el.firstChild) {
2285                 el.removeChild(el.firstChild);
2286         }
2287 }
2288
2289 // @function toFront(el: HTMLElement)
2290 // Makes `el` the last child of its parent, so it renders in front of the other children.
2291 function toFront(el) {
2292         var parent = el.parentNode;
2293         if (parent.lastChild !== el) {
2294                 parent.appendChild(el);
2295         }
2296 }
2297
2298 // @function toBack(el: HTMLElement)
2299 // Makes `el` the first child of its parent, so it renders behind the other children.
2300 function toBack(el) {
2301         var parent = el.parentNode;
2302         if (parent.firstChild !== el) {
2303                 parent.insertBefore(el, parent.firstChild);
2304         }
2305 }
2306
2307 // @function hasClass(el: HTMLElement, name: String): Boolean
2308 // Returns `true` if the element's class attribute contains `name`.
2309 function hasClass(el, name) {
2310         if (el.classList !== undefined) {
2311                 return el.classList.contains(name);
2312         }
2313         var className = getClass(el);
2314         return className.length > 0 && new RegExp('(^|\\s)' + name + '(\\s|$)').test(className);
2315 }
2316
2317 // @function addClass(el: HTMLElement, name: String)
2318 // Adds `name` to the element's class attribute.
2319 function addClass(el, name) {
2320         if (el.classList !== undefined) {
2321                 var classes = splitWords(name);
2322                 for (var i = 0, len = classes.length; i < len; i++) {
2323                         el.classList.add(classes[i]);
2324                 }
2325         } else if (!hasClass(el, name)) {
2326                 var className = getClass(el);
2327                 setClass(el, (className ? className + ' ' : '') + name);
2328         }
2329 }
2330
2331 // @function removeClass(el: HTMLElement, name: String)
2332 // Removes `name` from the element's class attribute.
2333 function removeClass(el, name) {
2334         if (el.classList !== undefined) {
2335                 el.classList.remove(name);
2336         } else {
2337                 setClass(el, trim((' ' + getClass(el) + ' ').replace(' ' + name + ' ', ' ')));
2338         }
2339 }
2340
2341 // @function setClass(el: HTMLElement, name: String)
2342 // Sets the element's class.
2343 function setClass(el, name) {
2344         if (el.className.baseVal === undefined) {
2345                 el.className = name;
2346         } else {
2347                 // in case of SVG element
2348                 el.className.baseVal = name;
2349         }
2350 }
2351
2352 // @function getClass(el: HTMLElement): String
2353 // Returns the element's class.
2354 function getClass(el) {
2355         return el.className.baseVal === undefined ? el.className : el.className.baseVal;
2356 }
2357
2358 // @function setOpacity(el: HTMLElement, opacity: Number)
2359 // Set the opacity of an element (including old IE support).
2360 // `opacity` must be a number from `0` to `1`.
2361 function setOpacity(el, value) {
2362         if ('opacity' in el.style) {
2363                 el.style.opacity = value;
2364         } else if ('filter' in el.style) {
2365                 _setOpacityIE(el, value);
2366         }
2367 }
2368
2369 function _setOpacityIE(el, value) {
2370         var filter = false,
2371             filterName = 'DXImageTransform.Microsoft.Alpha';
2372
2373         // filters collection throws an error if we try to retrieve a filter that doesn't exist
2374         try {
2375                 filter = el.filters.item(filterName);
2376         } catch (e) {
2377                 // don't set opacity to 1 if we haven't already set an opacity,
2378                 // it isn't needed and breaks transparent pngs.
2379                 if (value === 1) { return; }
2380         }
2381
2382         value = Math.round(value * 100);
2383
2384         if (filter) {
2385                 filter.Enabled = (value !== 100);
2386                 filter.Opacity = value;
2387         } else {
2388                 el.style.filter += ' progid:' + filterName + '(opacity=' + value + ')';
2389         }
2390 }
2391
2392 // @function testProp(props: String[]): String|false
2393 // Goes through the array of style names and returns the first name
2394 // that is a valid style name for an element. If no such name is found,
2395 // it returns false. Useful for vendor-prefixed styles like `transform`.
2396 function testProp(props) {
2397         var style = document.documentElement.style;
2398
2399         for (var i = 0; i < props.length; i++) {
2400                 if (props[i] in style) {
2401                         return props[i];
2402                 }
2403         }
2404         return false;
2405 }
2406
2407 // @function setTransform(el: HTMLElement, offset: Point, scale?: Number)
2408 // Resets the 3D CSS transform of `el` so it is translated by `offset` pixels
2409 // and optionally scaled by `scale`. Does not have an effect if the
2410 // browser doesn't support 3D CSS transforms.
2411 function setTransform(el, offset, scale) {
2412         var pos = offset || new Point(0, 0);
2413
2414         el.style[TRANSFORM] =
2415                 (ie3d ?
2416                         'translate(' + pos.x + 'px,' + pos.y + 'px)' :
2417                         'translate3d(' + pos.x + 'px,' + pos.y + 'px,0)') +
2418                 (scale ? ' scale(' + scale + ')' : '');
2419 }
2420
2421 // @function setPosition(el: HTMLElement, position: Point)
2422 // Sets the position of `el` to coordinates specified by `position`,
2423 // using CSS translate or top/left positioning depending on the browser
2424 // (used by Leaflet internally to position its layers).
2425 function setPosition(el, point) {
2426
2427         /*eslint-disable */
2428         el._leaflet_pos = point;
2429         /* eslint-enable */
2430
2431         if (any3d) {
2432                 setTransform(el, point);
2433         } else {
2434                 el.style.left = point.x + 'px';
2435                 el.style.top = point.y + 'px';
2436         }
2437 }
2438
2439 // @function getPosition(el: HTMLElement): Point
2440 // Returns the coordinates of an element previously positioned with setPosition.
2441 function getPosition(el) {
2442         // this method is only used for elements previously positioned using setPosition,
2443         // so it's safe to cache the position for performance
2444
2445         return el._leaflet_pos || new Point(0, 0);
2446 }
2447
2448 // @function disableTextSelection()
2449 // Prevents the user from generating `selectstart` DOM events, usually generated
2450 // when the user drags the mouse through a page with text. Used internally
2451 // by Leaflet to override the behaviour of any click-and-drag interaction on
2452 // the map. Affects drag interactions on the whole document.
2453
2454 // @function enableTextSelection()
2455 // Cancels the effects of a previous [`L.DomUtil.disableTextSelection`](#domutil-disabletextselection).
2456 var disableTextSelection;
2457 var enableTextSelection;
2458 var _userSelect;
2459 if ('onselectstart' in document) {
2460         disableTextSelection = function () {
2461                 on(window, 'selectstart', preventDefault);
2462         };
2463         enableTextSelection = function () {
2464                 off(window, 'selectstart', preventDefault);
2465         };
2466 } else {
2467         var userSelectProperty = testProp(
2468                 ['userSelect', 'WebkitUserSelect', 'OUserSelect', 'MozUserSelect', 'msUserSelect']);
2469
2470         disableTextSelection = function () {
2471                 if (userSelectProperty) {
2472                         var style = document.documentElement.style;
2473                         _userSelect = style[userSelectProperty];
2474                         style[userSelectProperty] = 'none';
2475                 }
2476         };
2477         enableTextSelection = function () {
2478                 if (userSelectProperty) {
2479                         document.documentElement.style[userSelectProperty] = _userSelect;
2480                         _userSelect = undefined;
2481                 }
2482         };
2483 }
2484
2485 // @function disableImageDrag()
2486 // As [`L.DomUtil.disableTextSelection`](#domutil-disabletextselection), but
2487 // for `dragstart` DOM events, usually generated when the user drags an image.
2488 function disableImageDrag() {
2489         on(window, 'dragstart', preventDefault);
2490 }
2491
2492 // @function enableImageDrag()
2493 // Cancels the effects of a previous [`L.DomUtil.disableImageDrag`](#domutil-disabletextselection).
2494 function enableImageDrag() {
2495         off(window, 'dragstart', preventDefault);
2496 }
2497
2498 var _outlineElement;
2499 var _outlineStyle;
2500 // @function preventOutline(el: HTMLElement)
2501 // Makes the [outline](https://developer.mozilla.org/docs/Web/CSS/outline)
2502 // of the element `el` invisible. Used internally by Leaflet to prevent
2503 // focusable elements from displaying an outline when the user performs a
2504 // drag interaction on them.
2505 function preventOutline(element) {
2506         while (element.tabIndex === -1) {
2507                 element = element.parentNode;
2508         }
2509         if (!element.style) { return; }
2510         restoreOutline();
2511         _outlineElement = element;
2512         _outlineStyle = element.style.outline;
2513         element.style.outline = 'none';
2514         on(window, 'keydown', restoreOutline);
2515 }
2516
2517 // @function restoreOutline()
2518 // Cancels the effects of a previous [`L.DomUtil.preventOutline`]().
2519 function restoreOutline() {
2520         if (!_outlineElement) { return; }
2521         _outlineElement.style.outline = _outlineStyle;
2522         _outlineElement = undefined;
2523         _outlineStyle = undefined;
2524         off(window, 'keydown', restoreOutline);
2525 }
2526
2527 // @function getSizedParentNode(el: HTMLElement): HTMLElement
2528 // Finds the closest parent node which size (width and height) is not null.
2529 function getSizedParentNode(element) {
2530         do {
2531                 element = element.parentNode;
2532         } while ((!element.offsetWidth || !element.offsetHeight) && element !== document.body);
2533         return element;
2534 }
2535
2536 // @function getScale(el: HTMLElement): Object
2537 // Computes the CSS scale currently applied on the element.
2538 // Returns an object with `x` and `y` members as horizontal and vertical scales respectively,
2539 // and `boundingClientRect` as the result of [`getBoundingClientRect()`](https://developer.mozilla.org/en-US/docs/Web/API/Element/getBoundingClientRect).
2540 function getScale(element) {
2541         var rect = element.getBoundingClientRect(); // Read-only in old browsers.
2542
2543         return {
2544                 x: rect.width / element.offsetWidth || 1,
2545                 y: rect.height / element.offsetHeight || 1,
2546                 boundingClientRect: rect
2547         };
2548 }
2549
2550
2551 var DomUtil = (Object.freeze || Object)({
2552         TRANSFORM: TRANSFORM,
2553         TRANSITION: TRANSITION,
2554         TRANSITION_END: TRANSITION_END,
2555         get: get,
2556         getStyle: getStyle,
2557         create: create$1,
2558         remove: remove,
2559         empty: empty,
2560         toFront: toFront,
2561         toBack: toBack,
2562         hasClass: hasClass,
2563         addClass: addClass,
2564         removeClass: removeClass,
2565         setClass: setClass,
2566         getClass: getClass,
2567         setOpacity: setOpacity,
2568         testProp: testProp,
2569         setTransform: setTransform,
2570         setPosition: setPosition,
2571         getPosition: getPosition,
2572         disableTextSelection: disableTextSelection,
2573         enableTextSelection: enableTextSelection,
2574         disableImageDrag: disableImageDrag,
2575         enableImageDrag: enableImageDrag,
2576         preventOutline: preventOutline,
2577         restoreOutline: restoreOutline,
2578         getSizedParentNode: getSizedParentNode,
2579         getScale: getScale
2580 });
2581
2582 /*
2583  * @namespace DomEvent
2584  * Utility functions to work with the [DOM events](https://developer.mozilla.org/docs/Web/API/Event), used by Leaflet internally.
2585  */
2586
2587 // Inspired by John Resig, Dean Edwards and YUI addEvent implementations.
2588
2589 // @function on(el: HTMLElement, types: String, fn: Function, context?: Object): this
2590 // Adds a listener function (`fn`) to a particular DOM event type of the
2591 // element `el`. You can optionally specify the context of the listener
2592 // (object the `this` keyword will point to). You can also pass several
2593 // space-separated types (e.g. `'click dblclick'`).
2594
2595 // @alternative
2596 // @function on(el: HTMLElement, eventMap: Object, context?: Object): this
2597 // Adds a set of type/listener pairs, e.g. `{click: onClick, mousemove: onMouseMove}`
2598 function on(obj, types, fn, context) {
2599
2600         if (typeof types === 'object') {
2601                 for (var type in types) {
2602                         addOne(obj, type, types[type], fn);
2603                 }
2604         } else {
2605                 types = splitWords(types);
2606
2607                 for (var i = 0, len = types.length; i < len; i++) {
2608                         addOne(obj, types[i], fn, context);
2609                 }
2610         }
2611
2612         return this;
2613 }
2614
2615 var eventsKey = '_leaflet_events';
2616
2617 // @function off(el: HTMLElement, types: String, fn: Function, context?: Object): this
2618 // Removes a previously added listener function.
2619 // Note that if you passed a custom context to on, you must pass the same
2620 // context to `off` in order to remove the listener.
2621
2622 // @alternative
2623 // @function off(el: HTMLElement, eventMap: Object, context?: Object): this
2624 // Removes a set of type/listener pairs, e.g. `{click: onClick, mousemove: onMouseMove}`
2625 function off(obj, types, fn, context) {
2626
2627         if (typeof types === 'object') {
2628                 for (var type in types) {
2629                         removeOne(obj, type, types[type], fn);
2630                 }
2631         } else if (types) {
2632                 types = splitWords(types);
2633
2634                 for (var i = 0, len = types.length; i < len; i++) {
2635                         removeOne(obj, types[i], fn, context);
2636                 }
2637         } else {
2638                 for (var j in obj[eventsKey]) {
2639                         removeOne(obj, j, obj[eventsKey][j]);
2640                 }
2641                 delete obj[eventsKey];
2642         }
2643
2644         return this;
2645 }
2646
2647 function addOne(obj, type, fn, context) {
2648         var id = type + stamp(fn) + (context ? '_' + stamp(context) : '');
2649
2650         if (obj[eventsKey] && obj[eventsKey][id]) { return this; }
2651
2652         var handler = function (e) {
2653                 return fn.call(context || obj, e || window.event);
2654         };
2655
2656         var originalHandler = handler;
2657
2658         if (pointer && type.indexOf('touch') === 0) {
2659                 // Needs DomEvent.Pointer.js
2660                 addPointerListener(obj, type, handler, id);
2661
2662         } else if (touch && (type === 'dblclick') && addDoubleTapListener &&
2663                    !(pointer && chrome)) {
2664                 // Chrome >55 does not need the synthetic dblclicks from addDoubleTapListener
2665                 // See #5180
2666                 addDoubleTapListener(obj, handler, id);
2667
2668         } else if ('addEventListener' in obj) {
2669
2670                 if (type === 'mousewheel') {
2671                         obj.addEventListener('onwheel' in obj ? 'wheel' : 'mousewheel', handler, false);
2672
2673                 } else if ((type === 'mouseenter') || (type === 'mouseleave')) {
2674                         handler = function (e) {
2675                                 e = e || window.event;
2676                                 if (isExternalTarget(obj, e)) {
2677                                         originalHandler(e);
2678                                 }
2679                         };
2680                         obj.addEventListener(type === 'mouseenter' ? 'mouseover' : 'mouseout', handler, false);
2681
2682                 } else {
2683                         if (type === 'click' && android) {
2684                                 handler = function (e) {
2685                                         filterClick(e, originalHandler);
2686                                 };
2687                         }
2688                         obj.addEventListener(type, handler, false);
2689                 }
2690
2691         } else if ('attachEvent' in obj) {
2692                 obj.attachEvent('on' + type, handler);
2693         }
2694
2695         obj[eventsKey] = obj[eventsKey] || {};
2696         obj[eventsKey][id] = handler;
2697 }
2698
2699 function removeOne(obj, type, fn, context) {
2700
2701         var id = type + stamp(fn) + (context ? '_' + stamp(context) : ''),
2702             handler = obj[eventsKey] && obj[eventsKey][id];
2703
2704         if (!handler) { return this; }
2705
2706         if (pointer && type.indexOf('touch') === 0) {
2707                 removePointerListener(obj, type, id);
2708
2709         } else if (touch && (type === 'dblclick') && removeDoubleTapListener &&
2710                    !(pointer && chrome)) {
2711                 removeDoubleTapListener(obj, id);
2712
2713         } else if ('removeEventListener' in obj) {
2714
2715                 if (type === 'mousewheel') {
2716                         obj.removeEventListener('onwheel' in obj ? 'wheel' : 'mousewheel', handler, false);
2717
2718                 } else {
2719                         obj.removeEventListener(
2720                                 type === 'mouseenter' ? 'mouseover' :
2721                                 type === 'mouseleave' ? 'mouseout' : type, handler, false);
2722                 }
2723
2724         } else if ('detachEvent' in obj) {
2725                 obj.detachEvent('on' + type, handler);
2726         }
2727
2728         obj[eventsKey][id] = null;
2729 }
2730
2731 // @function stopPropagation(ev: DOMEvent): this
2732 // Stop the given event from propagation to parent elements. Used inside the listener functions:
2733 // ```js
2734 // L.DomEvent.on(div, 'click', function (ev) {
2735 //      L.DomEvent.stopPropagation(ev);
2736 // });
2737 // ```
2738 function stopPropagation(e) {
2739
2740         if (e.stopPropagation) {
2741                 e.stopPropagation();
2742         } else if (e.originalEvent) {  // In case of Leaflet event.
2743                 e.originalEvent._stopped = true;
2744         } else {
2745                 e.cancelBubble = true;
2746         }
2747         skipped(e);
2748
2749         return this;
2750 }
2751
2752 // @function disableScrollPropagation(el: HTMLElement): this
2753 // Adds `stopPropagation` to the element's `'mousewheel'` events (plus browser variants).
2754 function disableScrollPropagation(el) {
2755         addOne(el, 'mousewheel', stopPropagation);
2756         return this;
2757 }
2758
2759 // @function disableClickPropagation(el: HTMLElement): this
2760 // Adds `stopPropagation` to the element's `'click'`, `'doubleclick'`,
2761 // `'mousedown'` and `'touchstart'` events (plus browser variants).
2762 function disableClickPropagation(el) {
2763         on(el, 'mousedown touchstart dblclick', stopPropagation);
2764         addOne(el, 'click', fakeStop);
2765         return this;
2766 }
2767
2768 // @function preventDefault(ev: DOMEvent): this
2769 // Prevents the default action of the DOM Event `ev` from happening (such as
2770 // following a link in the href of the a element, or doing a POST request
2771 // with page reload when a `<form>` is submitted).
2772 // Use it inside listener functions.
2773 function preventDefault(e) {
2774         if (e.preventDefault) {
2775                 e.preventDefault();
2776         } else {
2777                 e.returnValue = false;
2778         }
2779         return this;
2780 }
2781
2782 // @function stop(ev: DOMEvent): this
2783 // Does `stopPropagation` and `preventDefault` at the same time.
2784 function stop(e) {
2785         preventDefault(e);
2786         stopPropagation(e);
2787         return this;
2788 }
2789
2790 // @function getMousePosition(ev: DOMEvent, container?: HTMLElement): Point
2791 // Gets normalized mouse position from a DOM event relative to the
2792 // `container` (border excluded) or to the whole page if not specified.
2793 function getMousePosition(e, container) {
2794         if (!container) {
2795                 return new Point(e.clientX, e.clientY);
2796         }
2797
2798         var scale = getScale(container),
2799             offset = scale.boundingClientRect; // left and top  values are in page scale (like the event clientX/Y)
2800
2801         return new Point(
2802                 // offset.left/top values are in page scale (like clientX/Y),
2803                 // whereas clientLeft/Top (border width) values are the original values (before CSS scale applies).
2804                 (e.clientX - offset.left) / scale.x - container.clientLeft,
2805                 (e.clientY - offset.top) / scale.y - container.clientTop
2806         );
2807 }
2808
2809 // Chrome on Win scrolls double the pixels as in other platforms (see #4538),
2810 // and Firefox scrolls device pixels, not CSS pixels
2811 var wheelPxFactor =
2812         (win && chrome) ? 2 * window.devicePixelRatio :
2813         gecko ? window.devicePixelRatio : 1;
2814
2815 // @function getWheelDelta(ev: DOMEvent): Number
2816 // Gets normalized wheel delta from a mousewheel DOM event, in vertical
2817 // pixels scrolled (negative if scrolling down).
2818 // Events from pointing devices without precise scrolling are mapped to
2819 // a best guess of 60 pixels.
2820 function getWheelDelta(e) {
2821         return (edge) ? e.wheelDeltaY / 2 : // Don't trust window-geometry-based delta
2822                (e.deltaY && e.deltaMode === 0) ? -e.deltaY / wheelPxFactor : // Pixels
2823                (e.deltaY && e.deltaMode === 1) ? -e.deltaY * 20 : // Lines
2824                (e.deltaY && e.deltaMode === 2) ? -e.deltaY * 60 : // Pages
2825                (e.deltaX || e.deltaZ) ? 0 :     // Skip horizontal/depth wheel events
2826                e.wheelDelta ? (e.wheelDeltaY || e.wheelDelta) / 2 : // Legacy IE pixels
2827                (e.detail && Math.abs(e.detail) < 32765) ? -e.detail * 20 : // Legacy Moz lines
2828                e.detail ? e.detail / -32765 * 60 : // Legacy Moz pages
2829                0;
2830 }
2831
2832 var skipEvents = {};
2833
2834 function fakeStop(e) {
2835         // fakes stopPropagation by setting a special event flag, checked/reset with skipped(e)
2836         skipEvents[e.type] = true;
2837 }
2838
2839 function skipped(e) {
2840         var events = skipEvents[e.type];
2841         // reset when checking, as it's only used in map container and propagates outside of the map
2842         skipEvents[e.type] = false;
2843         return events;
2844 }
2845
2846 // check if element really left/entered the event target (for mouseenter/mouseleave)
2847 function isExternalTarget(el, e) {
2848
2849         var related = e.relatedTarget;
2850
2851         if (!related) { return true; }
2852
2853         try {
2854                 while (related && (related !== el)) {
2855                         related = related.parentNode;
2856                 }
2857         } catch (err) {
2858                 return false;
2859         }
2860         return (related !== el);
2861 }
2862
2863 var lastClick;
2864
2865 // this is a horrible workaround for a bug in Android where a single touch triggers two click events
2866 function filterClick(e, handler) {
2867         var timeStamp = (e.timeStamp || (e.originalEvent && e.originalEvent.timeStamp)),
2868             elapsed = lastClick && (timeStamp - lastClick);
2869
2870         // are they closer together than 500ms yet more than 100ms?
2871         // Android typically triggers them ~300ms apart while multiple listeners
2872         // on the same event should be triggered far faster;
2873         // or check if click is simulated on the element, and if it is, reject any non-simulated events
2874
2875         if ((elapsed && elapsed > 100 && elapsed < 500) || (e.target._simulatedClick && !e._simulated)) {
2876                 stop(e);
2877                 return;
2878         }
2879         lastClick = timeStamp;
2880
2881         handler(e);
2882 }
2883
2884
2885
2886
2887 var DomEvent = (Object.freeze || Object)({
2888         on: on,
2889         off: off,
2890         stopPropagation: stopPropagation,
2891         disableScrollPropagation: disableScrollPropagation,
2892         disableClickPropagation: disableClickPropagation,
2893         preventDefault: preventDefault,
2894         stop: stop,
2895         getMousePosition: getMousePosition,
2896         getWheelDelta: getWheelDelta,
2897         fakeStop: fakeStop,
2898         skipped: skipped,
2899         isExternalTarget: isExternalTarget,
2900         addListener: on,
2901         removeListener: off
2902 });
2903
2904 /*
2905  * @class PosAnimation
2906  * @aka L.PosAnimation
2907  * @inherits Evented
2908  * Used internally for panning animations, utilizing CSS3 Transitions for modern browsers and a timer fallback for IE6-9.
2909  *
2910  * @example
2911  * ```js
2912  * var fx = new L.PosAnimation();
2913  * fx.run(el, [300, 500], 0.5);
2914  * ```
2915  *
2916  * @constructor L.PosAnimation()
2917  * Creates a `PosAnimation` object.
2918  *
2919  */
2920
2921 var PosAnimation = Evented.extend({
2922
2923         // @method run(el: HTMLElement, newPos: Point, duration?: Number, easeLinearity?: Number)
2924         // Run an animation of a given element to a new position, optionally setting
2925         // duration in seconds (`0.25` by default) and easing linearity factor (3rd
2926         // argument of the [cubic bezier curve](http://cubic-bezier.com/#0,0,.5,1),
2927         // `0.5` by default).
2928         run: function (el, newPos, duration, easeLinearity) {
2929                 this.stop();
2930
2931                 this._el = el;
2932                 this._inProgress = true;
2933                 this._duration = duration || 0.25;
2934                 this._easeOutPower = 1 / Math.max(easeLinearity || 0.5, 0.2);
2935
2936                 this._startPos = getPosition(el);
2937                 this._offset = newPos.subtract(this._startPos);
2938                 this._startTime = +new Date();
2939
2940                 // @event start: Event
2941                 // Fired when the animation starts
2942                 this.fire('start');
2943
2944                 this._animate();
2945         },
2946
2947         // @method stop()
2948         // Stops the animation (if currently running).
2949         stop: function () {
2950                 if (!this._inProgress) { return; }
2951
2952                 this._step(true);
2953                 this._complete();
2954         },
2955
2956         _animate: function () {
2957                 // animation loop
2958                 this._animId = requestAnimFrame(this._animate, this);
2959                 this._step();
2960         },
2961
2962         _step: function (round) {
2963                 var elapsed = (+new Date()) - this._startTime,
2964                     duration = this._duration * 1000;
2965
2966                 if (elapsed < duration) {
2967                         this._runFrame(this._easeOut(elapsed / duration), round);
2968                 } else {
2969                         this._runFrame(1);
2970                         this._complete();
2971                 }
2972         },
2973
2974         _runFrame: function (progress, round) {
2975                 var pos = this._startPos.add(this._offset.multiplyBy(progress));
2976                 if (round) {
2977                         pos._round();
2978                 }
2979                 setPosition(this._el, pos);
2980
2981                 // @event step: Event
2982                 // Fired continuously during the animation.
2983                 this.fire('step');
2984         },
2985
2986         _complete: function () {
2987                 cancelAnimFrame(this._animId);
2988
2989                 this._inProgress = false;
2990                 // @event end: Event
2991                 // Fired when the animation ends.
2992                 this.fire('end');
2993         },
2994
2995         _easeOut: function (t) {
2996                 return 1 - Math.pow(1 - t, this._easeOutPower);
2997         }
2998 });
2999
3000 /*
3001  * @class Map
3002  * @aka L.Map
3003  * @inherits Evented
3004  *
3005  * The central class of the API — it is used to create a map on a page and manipulate it.
3006  *
3007  * @example
3008  *
3009  * ```js
3010  * // initialize the map on the "map" div with a given center and zoom
3011  * var map = L.map('map', {
3012  *      center: [51.505, -0.09],
3013  *      zoom: 13
3014  * });
3015  * ```
3016  *
3017  */
3018
3019 var Map = Evented.extend({
3020
3021         options: {
3022                 // @section Map State Options
3023                 // @option crs: CRS = L.CRS.EPSG3857
3024                 // The [Coordinate Reference System](#crs) to use. Don't change this if you're not
3025                 // sure what it means.
3026                 crs: EPSG3857,
3027
3028                 // @option center: LatLng = undefined
3029                 // Initial geographic center of the map
3030                 center: undefined,
3031
3032                 // @option zoom: Number = undefined
3033                 // Initial map zoom level
3034                 zoom: undefined,
3035
3036                 // @option minZoom: Number = *
3037                 // Minimum zoom level of the map.
3038                 // If not specified and at least one `GridLayer` or `TileLayer` is in the map,
3039                 // the lowest of their `minZoom` options will be used instead.
3040                 minZoom: undefined,
3041
3042                 // @option maxZoom: Number = *
3043                 // Maximum zoom level of the map.
3044                 // If not specified and at least one `GridLayer` or `TileLayer` is in the map,
3045                 // the highest of their `maxZoom` options will be used instead.
3046                 maxZoom: undefined,
3047
3048                 // @option layers: Layer[] = []
3049                 // Array of layers that will be added to the map initially
3050                 layers: [],
3051
3052                 // @option maxBounds: LatLngBounds = null
3053                 // When this option is set, the map restricts the view to the given
3054                 // geographical bounds, bouncing the user back if the user tries to pan
3055                 // outside the view. To set the restriction dynamically, use
3056                 // [`setMaxBounds`](#map-setmaxbounds) method.
3057                 maxBounds: undefined,
3058
3059                 // @option renderer: Renderer = *
3060                 // The default method for drawing vector layers on the map. `L.SVG`
3061                 // or `L.Canvas` by default depending on browser support.
3062                 renderer: undefined,
3063
3064
3065                 // @section Animation Options
3066                 // @option zoomAnimation: Boolean = true
3067                 // Whether the map zoom animation is enabled. By default it's enabled
3068                 // in all browsers that support CSS3 Transitions except Android.
3069                 zoomAnimation: true,
3070
3071                 // @option zoomAnimationThreshold: Number = 4
3072                 // Won't animate zoom if the zoom difference exceeds this value.
3073                 zoomAnimationThreshold: 4,
3074
3075                 // @option fadeAnimation: Boolean = true
3076                 // Whether the tile fade animation is enabled. By default it's enabled
3077                 // in all browsers that support CSS3 Transitions except Android.
3078                 fadeAnimation: true,
3079
3080                 // @option markerZoomAnimation: Boolean = true
3081                 // Whether markers animate their zoom with the zoom animation, if disabled
3082                 // they will disappear for the length of the animation. By default it's
3083                 // enabled in all browsers that support CSS3 Transitions except Android.
3084                 markerZoomAnimation: true,
3085
3086                 // @option transform3DLimit: Number = 2^23
3087                 // Defines the maximum size of a CSS translation transform. The default
3088                 // value should not be changed unless a web browser positions layers in
3089                 // the wrong place after doing a large `panBy`.
3090                 transform3DLimit: 8388608, // Precision limit of a 32-bit float
3091
3092                 // @section Interaction Options
3093                 // @option zoomSnap: Number = 1
3094                 // Forces the map's zoom level to always be a multiple of this, particularly
3095                 // right after a [`fitBounds()`](#map-fitbounds) or a pinch-zoom.
3096                 // By default, the zoom level snaps to the nearest integer; lower values
3097                 // (e.g. `0.5` or `0.1`) allow for greater granularity. A value of `0`
3098                 // means the zoom level will not be snapped after `fitBounds` or a pinch-zoom.
3099                 zoomSnap: 1,
3100
3101                 // @option zoomDelta: Number = 1
3102                 // Controls how much the map's zoom level will change after a
3103                 // [`zoomIn()`](#map-zoomin), [`zoomOut()`](#map-zoomout), pressing `+`
3104                 // or `-` on the keyboard, or using the [zoom controls](#control-zoom).
3105                 // Values smaller than `1` (e.g. `0.5`) allow for greater granularity.
3106                 zoomDelta: 1,
3107
3108                 // @option trackResize: Boolean = true
3109                 // Whether the map automatically handles browser window resize to update itself.
3110                 trackResize: true
3111         },
3112
3113         initialize: function (id, options) { // (HTMLElement or String, Object)
3114                 options = setOptions(this, options);
3115
3116                 this._initContainer(id);
3117                 this._initLayout();
3118
3119                 // hack for https://github.com/Leaflet/Leaflet/issues/1980
3120                 this._onResize = bind(this._onResize, this);
3121
3122                 this._initEvents();
3123
3124                 if (options.maxBounds) {
3125                         this.setMaxBounds(options.maxBounds);
3126                 }
3127
3128                 if (options.zoom !== undefined) {
3129                         this._zoom = this._limitZoom(options.zoom);
3130                 }
3131
3132                 if (options.center && options.zoom !== undefined) {
3133                         this.setView(toLatLng(options.center), options.zoom, {reset: true});
3134                 }
3135
3136                 this._handlers = [];
3137                 this._layers = {};
3138                 this._zoomBoundLayers = {};
3139                 this._sizeChanged = true;
3140
3141                 this.callInitHooks();
3142
3143                 // don't animate on browsers without hardware-accelerated transitions or old Android/Opera
3144                 this._zoomAnimated = TRANSITION && any3d && !mobileOpera &&
3145                                 this.options.zoomAnimation;
3146
3147                 // zoom transitions run with the same duration for all layers, so if one of transitionend events
3148                 // happens after starting zoom animation (propagating to the map pane), we know that it ended globally
3149                 if (this._zoomAnimated) {
3150                         this._createAnimProxy();
3151                         on(this._proxy, TRANSITION_END, this._catchTransitionEnd, this);
3152                 }
3153
3154                 this._addLayers(this.options.layers);
3155         },
3156
3157
3158         // @section Methods for modifying map state
3159
3160         // @method setView(center: LatLng, zoom: Number, options?: Zoom/pan options): this
3161         // Sets the view of the map (geographical center and zoom) with the given
3162         // animation options.
3163         setView: function (center, zoom, options) {
3164
3165                 zoom = zoom === undefined ? this._zoom : this._limitZoom(zoom);
3166                 center = this._limitCenter(toLatLng(center), zoom, this.options.maxBounds);
3167                 options = options || {};
3168
3169                 this._stop();
3170
3171                 if (this._loaded && !options.reset && options !== true) {
3172
3173                         if (options.animate !== undefined) {
3174                                 options.zoom = extend({animate: options.animate}, options.zoom);
3175                                 options.pan = extend({animate: options.animate, duration: options.duration}, options.pan);
3176                         }
3177
3178                         // try animating pan or zoom
3179                         var moved = (this._zoom !== zoom) ?
3180                                 this._tryAnimatedZoom && this._tryAnimatedZoom(center, zoom, options.zoom) :
3181                                 this._tryAnimatedPan(center, options.pan);
3182
3183                         if (moved) {
3184                                 // prevent resize handler call, the view will refresh after animation anyway
3185                                 clearTimeout(this._sizeTimer);
3186                                 return this;
3187                         }
3188                 }
3189
3190                 // animation didn't start, just reset the map view
3191                 this._resetView(center, zoom);
3192
3193                 return this;
3194         },
3195
3196         // @method setZoom(zoom: Number, options?: Zoom/pan options): this
3197         // Sets the zoom of the map.
3198         setZoom: function (zoom, options) {
3199                 if (!this._loaded) {
3200                         this._zoom = zoom;
3201                         return this;
3202                 }
3203                 return this.setView(this.getCenter(), zoom, {zoom: options});
3204         },
3205
3206         // @method zoomIn(delta?: Number, options?: Zoom options): this
3207         // Increases the zoom of the map by `delta` ([`zoomDelta`](#map-zoomdelta) by default).
3208         zoomIn: function (delta, options) {
3209                 delta = delta || (any3d ? this.options.zoomDelta : 1);
3210                 return this.setZoom(this._zoom + delta, options);
3211         },
3212
3213         // @method zoomOut(delta?: Number, options?: Zoom options): this
3214         // Decreases the zoom of the map by `delta` ([`zoomDelta`](#map-zoomdelta) by default).
3215         zoomOut: function (delta, options) {
3216                 delta = delta || (any3d ? this.options.zoomDelta : 1);
3217                 return this.setZoom(this._zoom - delta, options);
3218         },
3219
3220         // @method setZoomAround(latlng: LatLng, zoom: Number, options: Zoom options): this
3221         // Zooms the map while keeping a specified geographical point on the map
3222         // stationary (e.g. used internally for scroll zoom and double-click zoom).
3223         // @alternative
3224         // @method setZoomAround(offset: Point, zoom: Number, options: Zoom options): this
3225         // Zooms the map while keeping a specified pixel on the map (relative to the top-left corner) stationary.
3226         setZoomAround: function (latlng, zoom, options) {
3227                 var scale = this.getZoomScale(zoom),
3228                     viewHalf = this.getSize().divideBy(2),
3229                     containerPoint = latlng instanceof Point ? latlng : this.latLngToContainerPoint(latlng),
3230
3231                     centerOffset = containerPoint.subtract(viewHalf).multiplyBy(1 - 1 / scale),
3232                     newCenter = this.containerPointToLatLng(viewHalf.add(centerOffset));
3233
3234                 return this.setView(newCenter, zoom, {zoom: options});
3235         },
3236
3237         _getBoundsCenterZoom: function (bounds, options) {
3238
3239                 options = options || {};
3240                 bounds = bounds.getBounds ? bounds.getBounds() : toLatLngBounds(bounds);
3241
3242                 var paddingTL = toPoint(options.paddingTopLeft || options.padding || [0, 0]),
3243                     paddingBR = toPoint(options.paddingBottomRight || options.padding || [0, 0]),
3244
3245                     zoom = this.getBoundsZoom(bounds, false, paddingTL.add(paddingBR));
3246
3247                 zoom = (typeof options.maxZoom === 'number') ? Math.min(options.maxZoom, zoom) : zoom;
3248
3249                 if (zoom === Infinity) {
3250                         return {
3251                                 center: bounds.getCenter(),
3252                                 zoom: zoom
3253                         };
3254                 }
3255
3256                 var paddingOffset = paddingBR.subtract(paddingTL).divideBy(2),
3257
3258                     swPoint = this.project(bounds.getSouthWest(), zoom),
3259                     nePoint = this.project(bounds.getNorthEast(), zoom),
3260                     center = this.unproject(swPoint.add(nePoint).divideBy(2).add(paddingOffset), zoom);
3261
3262                 return {
3263                         center: center,
3264                         zoom: zoom
3265                 };
3266         },
3267
3268         // @method fitBounds(bounds: LatLngBounds, options?: fitBounds options): this
3269         // Sets a map view that contains the given geographical bounds with the
3270         // maximum zoom level possible.
3271         fitBounds: function (bounds, options) {
3272
3273                 bounds = toLatLngBounds(bounds);
3274
3275                 if (!bounds.isValid()) {
3276                         throw new Error('Bounds are not valid.');
3277                 }
3278
3279                 var target = this._getBoundsCenterZoom(bounds, options);
3280                 return this.setView(target.center, target.zoom, options);
3281         },
3282
3283         // @method fitWorld(options?: fitBounds options): this
3284         // Sets a map view that mostly contains the whole world with the maximum
3285         // zoom level possible.
3286         fitWorld: function (options) {
3287                 return this.fitBounds([[-90, -180], [90, 180]], options);
3288         },
3289
3290         // @method panTo(latlng: LatLng, options?: Pan options): this
3291         // Pans the map to a given center.
3292         panTo: function (center, options) { // (LatLng)
3293                 return this.setView(center, this._zoom, {pan: options});
3294         },
3295
3296         // @method panBy(offset: Point, options?: Pan options): this
3297         // Pans the map by a given number of pixels (animated).
3298         panBy: function (offset, options) {
3299                 offset = toPoint(offset).round();
3300                 options = options || {};
3301
3302                 if (!offset.x && !offset.y) {
3303                         return this.fire('moveend');
3304                 }
3305                 // If we pan too far, Chrome gets issues with tiles
3306                 // and makes them disappear or appear in the wrong place (slightly offset) #2602
3307                 if (options.animate !== true && !this.getSize().contains(offset)) {
3308                         this._resetView(this.unproject(this.project(this.getCenter()).add(offset)), this.getZoom());
3309                         return this;
3310                 }
3311
3312                 if (!this._panAnim) {
3313                         this._panAnim = new PosAnimation();
3314
3315                         this._panAnim.on({
3316                                 'step': this._onPanTransitionStep,
3317                                 'end': this._onPanTransitionEnd
3318                         }, this);
3319                 }
3320
3321                 // don't fire movestart if animating inertia
3322                 if (!options.noMoveStart) {
3323                         this.fire('movestart');
3324                 }
3325
3326                 // animate pan unless animate: false specified
3327                 if (options.animate !== false) {
3328                         addClass(this._mapPane, 'leaflet-pan-anim');
3329
3330                         var newPos = this._getMapPanePos().subtract(offset).round();
3331                         this._panAnim.run(this._mapPane, newPos, options.duration || 0.25, options.easeLinearity);
3332                 } else {
3333                         this._rawPanBy(offset);
3334                         this.fire('move').fire('moveend');
3335                 }
3336
3337                 return this;
3338         },
3339
3340         // @method flyTo(latlng: LatLng, zoom?: Number, options?: Zoom/pan options): this
3341         // Sets the view of the map (geographical center and zoom) performing a smooth
3342         // pan-zoom animation.
3343         flyTo: function (targetCenter, targetZoom, options) {
3344
3345                 options = options || {};
3346                 if (options.animate === false || !any3d) {
3347                         return this.setView(targetCenter, targetZoom, options);
3348                 }
3349
3350                 this._stop();
3351
3352                 var from = this.project(this.getCenter()),
3353                     to = this.project(targetCenter),
3354                     size = this.getSize(),
3355                     startZoom = this._zoom;
3356
3357                 targetCenter = toLatLng(targetCenter);
3358                 targetZoom = targetZoom === undefined ? startZoom : targetZoom;
3359
3360                 var w0 = Math.max(size.x, size.y),
3361                     w1 = w0 * this.getZoomScale(startZoom, targetZoom),
3362                     u1 = (to.distanceTo(from)) || 1,
3363                     rho = 1.42,
3364                     rho2 = rho * rho;
3365
3366                 function r(i) {
3367                         var s1 = i ? -1 : 1,
3368                             s2 = i ? w1 : w0,
3369                             t1 = w1 * w1 - w0 * w0 + s1 * rho2 * rho2 * u1 * u1,
3370                             b1 = 2 * s2 * rho2 * u1,
3371                             b = t1 / b1,
3372                             sq = Math.sqrt(b * b + 1) - b;
3373
3374                             // workaround for floating point precision bug when sq = 0, log = -Infinite,
3375                             // thus triggering an infinite loop in flyTo
3376                             var log = sq < 0.000000001 ? -18 : Math.log(sq);
3377
3378                         return log;
3379                 }
3380
3381                 function sinh(n) { return (Math.exp(n) - Math.exp(-n)) / 2; }
3382                 function cosh(n) { return (Math.exp(n) + Math.exp(-n)) / 2; }
3383                 function tanh(n) { return sinh(n) / cosh(n); }
3384
3385                 var r0 = r(0);
3386
3387                 function w(s) { return w0 * (cosh(r0) / cosh(r0 + rho * s)); }
3388                 function u(s) { return w0 * (cosh(r0) * tanh(r0 + rho * s) - sinh(r0)) / rho2; }
3389
3390                 function easeOut(t) { return 1 - Math.pow(1 - t, 1.5); }
3391
3392                 var start = Date.now(),
3393                     S = (r(1) - r0) / rho,
3394                     duration = options.duration ? 1000 * options.duration : 1000 * S * 0.8;
3395
3396                 function frame() {
3397                         var t = (Date.now() - start) / duration,
3398                             s = easeOut(t) * S;
3399
3400                         if (t <= 1) {
3401                                 this._flyToFrame = requestAnimFrame(frame, this);
3402
3403                                 this._move(
3404                                         this.unproject(from.add(to.subtract(from).multiplyBy(u(s) / u1)), startZoom),
3405                                         this.getScaleZoom(w0 / w(s), startZoom),
3406                                         {flyTo: true});
3407
3408                         } else {
3409                                 this
3410                                         ._move(targetCenter, targetZoom)
3411                                         ._moveEnd(true);
3412                         }
3413                 }
3414
3415                 this._moveStart(true, options.noMoveStart);
3416
3417                 frame.call(this);
3418                 return this;
3419         },
3420
3421         // @method flyToBounds(bounds: LatLngBounds, options?: fitBounds options): this
3422         // Sets the view of the map with a smooth animation like [`flyTo`](#map-flyto),
3423         // but takes a bounds parameter like [`fitBounds`](#map-fitbounds).
3424         flyToBounds: function (bounds, options) {
3425                 var target = this._getBoundsCenterZoom(bounds, options);
3426                 return this.flyTo(target.center, target.zoom, options);
3427         },
3428
3429         // @method setMaxBounds(bounds: Bounds): this
3430         // Restricts the map view to the given bounds (see the [maxBounds](#map-maxbounds) option).
3431         setMaxBounds: function (bounds) {
3432                 bounds = toLatLngBounds(bounds);
3433
3434                 if (!bounds.isValid()) {
3435                         this.options.maxBounds = null;
3436                         return this.off('moveend', this._panInsideMaxBounds);
3437                 } else if (this.options.maxBounds) {
3438                         this.off('moveend', this._panInsideMaxBounds);
3439                 }
3440
3441                 this.options.maxBounds = bounds;
3442
3443                 if (this._loaded) {
3444                         this._panInsideMaxBounds();
3445                 }
3446
3447                 return this.on('moveend', this._panInsideMaxBounds);
3448         },
3449
3450         // @method setMinZoom(zoom: Number): this
3451         // Sets the lower limit for the available zoom levels (see the [minZoom](#map-minzoom) option).
3452         setMinZoom: function (zoom) {
3453                 var oldZoom = this.options.minZoom;
3454                 this.options.minZoom = zoom;
3455
3456                 if (this._loaded && oldZoom !== zoom) {
3457                         this.fire('zoomlevelschange');
3458
3459                         if (this.getZoom() < this.options.minZoom) {
3460                                 return this.setZoom(zoom);
3461                         }
3462                 }
3463
3464                 return this;
3465         },
3466
3467         // @method setMaxZoom(zoom: Number): this
3468         // Sets the upper limit for the available zoom levels (see the [maxZoom](#map-maxzoom) option).
3469         setMaxZoom: function (zoom) {
3470                 var oldZoom = this.options.maxZoom;
3471                 this.options.maxZoom = zoom;
3472
3473                 if (this._loaded && oldZoom !== zoom) {
3474                         this.fire('zoomlevelschange');
3475
3476                         if (this.getZoom() > this.options.maxZoom) {
3477                                 return this.setZoom(zoom);
3478                         }
3479                 }
3480
3481                 return this;
3482         },
3483
3484         // @method panInsideBounds(bounds: LatLngBounds, options?: Pan options): this
3485         // Pans the map to the closest view that would lie inside the given bounds (if it's not already), controlling the animation using the options specific, if any.
3486         panInsideBounds: function (bounds, options) {
3487                 this._enforcingBounds = true;
3488                 var center = this.getCenter(),
3489                     newCenter = this._limitCenter(center, this._zoom, toLatLngBounds(bounds));
3490
3491                 if (!center.equals(newCenter)) {
3492                         this.panTo(newCenter, options);
3493                 }
3494
3495                 this._enforcingBounds = false;
3496                 return this;
3497         },
3498
3499         // @method invalidateSize(options: Zoom/pan options): this
3500         // Checks if the map container size changed and updates the map if so —
3501         // call it after you've changed the map size dynamically, also animating
3502         // pan by default. If `options.pan` is `false`, panning will not occur.
3503         // If `options.debounceMoveend` is `true`, it will delay `moveend` event so
3504         // that it doesn't happen often even if the method is called many
3505         // times in a row.
3506
3507         // @alternative
3508         // @method invalidateSize(animate: Boolean): this
3509         // Checks if the map container size changed and updates the map if so —
3510         // call it after you've changed the map size dynamically, also animating
3511         // pan by default.
3512         invalidateSize: function (options) {
3513                 if (!this._loaded) { return this; }
3514
3515                 options = extend({
3516                         animate: false,
3517                         pan: true
3518                 }, options === true ? {animate: true} : options);
3519
3520                 var oldSize = this.getSize();
3521                 this._sizeChanged = true;
3522                 this._lastCenter = null;
3523
3524                 var newSize = this.getSize(),
3525                     oldCenter = oldSize.divideBy(2).round(),
3526                     newCenter = newSize.divideBy(2).round(),
3527                     offset = oldCenter.subtract(newCenter);
3528
3529                 if (!offset.x && !offset.y) { return this; }
3530
3531                 if (options.animate && options.pan) {
3532                         this.panBy(offset);
3533
3534                 } else {
3535                         if (options.pan) {
3536                                 this._rawPanBy(offset);
3537                         }
3538
3539                         this.fire('move');
3540
3541                         if (options.debounceMoveend) {
3542                                 clearTimeout(this._sizeTimer);
3543                                 this._sizeTimer = setTimeout(bind(this.fire, this, 'moveend'), 200);
3544                         } else {
3545                                 this.fire('moveend');
3546                         }
3547                 }
3548
3549                 // @section Map state change events
3550                 // @event resize: ResizeEvent
3551                 // Fired when the map is resized.
3552                 return this.fire('resize', {
3553                         oldSize: oldSize,
3554                         newSize: newSize
3555                 });
3556         },
3557
3558         // @section Methods for modifying map state
3559         // @method stop(): this
3560         // Stops the currently running `panTo` or `flyTo` animation, if any.
3561         stop: function () {
3562                 this.setZoom(this._limitZoom(this._zoom));
3563                 if (!this.options.zoomSnap) {
3564                         this.fire('viewreset');
3565                 }
3566                 return this._stop();
3567         },
3568
3569         // @section Geolocation methods
3570         // @method locate(options?: Locate options): this
3571         // Tries to locate the user using the Geolocation API, firing a [`locationfound`](#map-locationfound)
3572         // event with location data on success or a [`locationerror`](#map-locationerror) event on failure,
3573         // and optionally sets the map view to the user's location with respect to
3574         // detection accuracy (or to the world view if geolocation failed).
3575         // Note that, if your page doesn't use HTTPS, this method will fail in
3576         // modern browsers ([Chrome 50 and newer](https://sites.google.com/a/chromium.org/dev/Home/chromium-security/deprecating-powerful-features-on-insecure-origins))
3577         // See `Locate options` for more details.
3578         locate: function (options) {
3579
3580                 options = this._locateOptions = extend({
3581                         timeout: 10000,
3582                         watch: false
3583                         // setView: false
3584                         // maxZoom: <Number>
3585                         // maximumAge: 0
3586                         // enableHighAccuracy: false
3587                 }, options);
3588
3589                 if (!('geolocation' in navigator)) {
3590                         this._handleGeolocationError({
3591                                 code: 0,
3592                                 message: 'Geolocation not supported.'
3593                         });
3594                         return this;
3595                 }
3596
3597                 var onResponse = bind(this._handleGeolocationResponse, this),
3598                     onError = bind(this._handleGeolocationError, this);
3599
3600                 if (options.watch) {
3601                         this._locationWatchId =
3602                                 navigator.geolocation.watchPosition(onResponse, onError, options);
3603                 } else {
3604                         navigator.geolocation.getCurrentPosition(onResponse, onError, options);
3605                 }
3606                 return this;
3607         },
3608
3609         // @method stopLocate(): this
3610         // Stops watching location previously initiated by `map.locate({watch: true})`
3611         // and aborts resetting the map view if map.locate was called with
3612         // `{setView: true}`.
3613         stopLocate: function () {
3614                 if (navigator.geolocation && navigator.geolocation.clearWatch) {
3615                         navigator.geolocation.clearWatch(this._locationWatchId);
3616                 }
3617                 if (this._locateOptions) {
3618                         this._locateOptions.setView = false;
3619                 }
3620                 return this;
3621         },
3622
3623         _handleGeolocationError: function (error) {
3624                 var c = error.code,
3625                     message = error.message ||
3626                             (c === 1 ? 'permission denied' :
3627                             (c === 2 ? 'position unavailable' : 'timeout'));
3628
3629                 if (this._locateOptions.setView && !this._loaded) {
3630                         this.fitWorld();
3631                 }
3632
3633                 // @section Location events
3634                 // @event locationerror: ErrorEvent
3635                 // Fired when geolocation (using the [`locate`](#map-locate) method) failed.
3636                 this.fire('locationerror', {
3637                         code: c,
3638                         message: 'Geolocation error: ' + message + '.'
3639                 });
3640         },
3641
3642         _handleGeolocationResponse: function (pos) {
3643                 var lat = pos.coords.latitude,
3644                     lng = pos.coords.longitude,
3645                     latlng = new LatLng(lat, lng),
3646                     bounds = latlng.toBounds(pos.coords.accuracy * 2),
3647                     options = this._locateOptions;
3648
3649                 if (options.setView) {
3650                         var zoom = this.getBoundsZoom(bounds);
3651                         this.setView(latlng, options.maxZoom ? Math.min(zoom, options.maxZoom) : zoom);
3652                 }
3653
3654                 var data = {
3655                         latlng: latlng,
3656                         bounds: bounds,
3657                         timestamp: pos.timestamp
3658                 };
3659
3660                 for (var i in pos.coords) {
3661                         if (typeof pos.coords[i] === 'number') {
3662                                 data[i] = pos.coords[i];
3663                         }
3664                 }
3665
3666                 // @event locationfound: LocationEvent
3667                 // Fired when geolocation (using the [`locate`](#map-locate) method)
3668                 // went successfully.
3669                 this.fire('locationfound', data);
3670         },
3671
3672         // TODO Appropriate docs section?
3673         // @section Other Methods
3674         // @method addHandler(name: String, HandlerClass: Function): this
3675         // Adds a new `Handler` to the map, given its name and constructor function.
3676         addHandler: function (name, HandlerClass) {
3677                 if (!HandlerClass) { return this; }
3678
3679                 var handler = this[name] = new HandlerClass(this);
3680
3681                 this._handlers.push(handler);
3682
3683                 if (this.options[name]) {
3684                         handler.enable();
3685                 }
3686
3687                 return this;
3688         },
3689
3690         // @method remove(): this
3691         // Destroys the map and clears all related event listeners.
3692         remove: function () {
3693
3694                 this._initEvents(true);
3695
3696                 if (this._containerId !== this._container._leaflet_id) {
3697                         throw new Error('Map container is being reused by another instance');
3698                 }
3699
3700                 try {
3701                         // throws error in IE6-8
3702                         delete this._container._leaflet_id;
3703                         delete this._containerId;
3704                 } catch (e) {
3705                         /*eslint-disable */
3706                         this._container._leaflet_id = undefined;
3707                         /* eslint-enable */
3708                         this._containerId = undefined;
3709                 }
3710
3711                 if (this._locationWatchId !== undefined) {
3712                         this.stopLocate();
3713                 }
3714
3715                 this._stop();
3716
3717                 remove(this._mapPane);
3718
3719                 if (this._clearControlPos) {
3720                         this._clearControlPos();
3721                 }
3722                 if (this._resizeRequest) {
3723                         cancelAnimFrame(this._resizeRequest);
3724                         this._resizeRequest = null;
3725                 }
3726
3727                 this._clearHandlers();
3728
3729                 if (this._loaded) {
3730                         // @section Map state change events
3731                         // @event unload: Event
3732                         // Fired when the map is destroyed with [remove](#map-remove) method.
3733                         this.fire('unload');
3734                 }
3735
3736                 var i;
3737                 for (i in this._layers) {
3738                         this._layers[i].remove();
3739                 }
3740                 for (i in this._panes) {
3741                         remove(this._panes[i]);
3742                 }
3743
3744                 this._layers = [];
3745                 this._panes = [];
3746                 delete this._mapPane;
3747                 delete this._renderer;
3748
3749                 return this;
3750         },
3751
3752         // @section Other Methods
3753         // @method createPane(name: String, container?: HTMLElement): HTMLElement
3754         // Creates a new [map pane](#map-pane) with the given name if it doesn't exist already,
3755         // then returns it. The pane is created as a child of `container`, or
3756         // as a child of the main map pane if not set.
3757         createPane: function (name, container) {
3758                 var className = 'leaflet-pane' + (name ? ' leaflet-' + name.replace('Pane', '') + '-pane' : ''),
3759                     pane = create$1('div', className, container || this._mapPane);
3760
3761                 if (name) {
3762                         this._panes[name] = pane;
3763                 }
3764                 return pane;
3765         },
3766
3767         // @section Methods for Getting Map State
3768
3769         // @method getCenter(): LatLng
3770         // Returns the geographical center of the map view
3771         getCenter: function () {
3772                 this._checkIfLoaded();
3773
3774                 if (this._lastCenter && !this._moved()) {
3775                         return this._lastCenter;
3776                 }
3777                 return this.layerPointToLatLng(this._getCenterLayerPoint());
3778         },
3779
3780         // @method getZoom(): Number
3781         // Returns the current zoom level of the map view
3782         getZoom: function () {
3783                 return this._zoom;
3784         },
3785
3786         // @method getBounds(): LatLngBounds
3787         // Returns the geographical bounds visible in the current map view
3788         getBounds: function () {
3789                 var bounds = this.getPixelBounds(),
3790                     sw = this.unproject(bounds.getBottomLeft()),
3791                     ne = this.unproject(bounds.getTopRight());
3792
3793                 return new LatLngBounds(sw, ne);
3794         },
3795
3796         // @method getMinZoom(): Number
3797         // Returns the minimum zoom level of the map (if set in the `minZoom` option of the map or of any layers), or `0` by default.
3798         getMinZoom: function () {
3799                 return this.options.minZoom === undefined ? this._layersMinZoom || 0 : this.options.minZoom;
3800         },
3801
3802         // @method getMaxZoom(): Number
3803         // Returns the maximum zoom level of the map (if set in the `maxZoom` option of the map or of any layers).
3804         getMaxZoom: function () {
3805                 return this.options.maxZoom === undefined ?
3806                         (this._layersMaxZoom === undefined ? Infinity : this._layersMaxZoom) :
3807                         this.options.maxZoom;
3808         },
3809
3810         // @method getBoundsZoom(bounds: LatLngBounds, inside?: Boolean, padding?: Point): Number
3811         // Returns the maximum zoom level on which the given bounds fit to the map
3812         // view in its entirety. If `inside` (optional) is set to `true`, the method
3813         // instead returns the minimum zoom level on which the map view fits into
3814         // the given bounds in its entirety.
3815         getBoundsZoom: function (bounds, inside, padding) { // (LatLngBounds[, Boolean, Point]) -> Number
3816                 bounds = toLatLngBounds(bounds);
3817                 padding = toPoint(padding || [0, 0]);
3818
3819                 var zoom = this.getZoom() || 0,
3820                     min = this.getMinZoom(),
3821                     max = this.getMaxZoom(),
3822                     nw = bounds.getNorthWest(),
3823                     se = bounds.getSouthEast(),
3824                     size = this.getSize().subtract(padding),
3825                     boundsSize = toBounds(this.project(se, zoom), this.project(nw, zoom)).getSize(),
3826                     snap = any3d ? this.options.zoomSnap : 1,
3827                     scalex = size.x / boundsSize.x,
3828                     scaley = size.y / boundsSize.y,
3829                     scale = inside ? Math.max(scalex, scaley) : Math.min(scalex, scaley);
3830
3831                 zoom = this.getScaleZoom(scale, zoom);
3832
3833                 if (snap) {
3834                         zoom = Math.round(zoom / (snap / 100)) * (snap / 100); // don't jump if within 1% of a snap level
3835                         zoom = inside ? Math.ceil(zoom / snap) * snap : Math.floor(zoom / snap) * snap;
3836                 }
3837
3838                 return Math.max(min, Math.min(max, zoom));
3839         },
3840
3841         // @method getSize(): Point
3842         // Returns the current size of the map container (in pixels).
3843         getSize: function () {
3844                 if (!this._size || this._sizeChanged) {
3845                         this._size = new Point(
3846                                 this._container.clientWidth || 0,
3847                                 this._container.clientHeight || 0);
3848
3849                         this._sizeChanged = false;
3850                 }
3851                 return this._size.clone();
3852         },
3853
3854         // @method getPixelBounds(): Bounds
3855         // Returns the bounds of the current map view in projected pixel
3856         // coordinates (sometimes useful in layer and overlay implementations).
3857         getPixelBounds: function (center, zoom) {
3858                 var topLeftPoint = this._getTopLeftPoint(center, zoom);
3859                 return new Bounds(topLeftPoint, topLeftPoint.add(this.getSize()));
3860         },
3861
3862         // TODO: Check semantics - isn't the pixel origin the 0,0 coord relative to
3863         // the map pane? "left point of the map layer" can be confusing, specially
3864         // since there can be negative offsets.
3865         // @method getPixelOrigin(): Point
3866         // Returns the projected pixel coordinates of the top left point of
3867         // the map layer (useful in custom layer and overlay implementations).
3868         getPixelOrigin: function () {
3869                 this._checkIfLoaded();
3870                 return this._pixelOrigin;
3871         },
3872
3873         // @method getPixelWorldBounds(zoom?: Number): Bounds
3874         // Returns the world's bounds in pixel coordinates for zoom level `zoom`.
3875         // If `zoom` is omitted, the map's current zoom level is used.
3876         getPixelWorldBounds: function (zoom) {
3877                 return this.options.crs.getProjectedBounds(zoom === undefined ? this.getZoom() : zoom);
3878         },
3879
3880         // @section Other Methods
3881
3882         // @method getPane(pane: String|HTMLElement): HTMLElement
3883         // Returns a [map pane](#map-pane), given its name or its HTML element (its identity).
3884         getPane: function (pane) {
3885                 return typeof pane === 'string' ? this._panes[pane] : pane;
3886         },
3887
3888         // @method getPanes(): Object
3889         // Returns a plain object containing the names of all [panes](#map-pane) as keys and
3890         // the panes as values.
3891         getPanes: function () {
3892                 return this._panes;
3893         },
3894
3895         // @method getContainer: HTMLElement
3896         // Returns the HTML element that contains the map.
3897         getContainer: function () {
3898                 return this._container;
3899         },
3900
3901
3902         // @section Conversion Methods
3903
3904         // @method getZoomScale(toZoom: Number, fromZoom: Number): Number
3905         // Returns the scale factor to be applied to a map transition from zoom level
3906         // `fromZoom` to `toZoom`. Used internally to help with zoom animations.
3907         getZoomScale: function (toZoom, fromZoom) {
3908                 // TODO replace with universal implementation after refactoring projections
3909                 var crs = this.options.crs;
3910                 fromZoom = fromZoom === undefined ? this._zoom : fromZoom;
3911                 return crs.scale(toZoom) / crs.scale(fromZoom);
3912         },
3913
3914         // @method getScaleZoom(scale: Number, fromZoom: Number): Number
3915         // Returns the zoom level that the map would end up at, if it is at `fromZoom`
3916         // level and everything is scaled by a factor of `scale`. Inverse of
3917         // [`getZoomScale`](#map-getZoomScale).
3918         getScaleZoom: function (scale, fromZoom) {
3919                 var crs = this.options.crs;
3920                 fromZoom = fromZoom === undefined ? this._zoom : fromZoom;
3921                 var zoom = crs.zoom(scale * crs.scale(fromZoom));
3922                 return isNaN(zoom) ? Infinity : zoom;
3923         },
3924
3925         // @method project(latlng: LatLng, zoom: Number): Point
3926         // Projects a geographical coordinate `LatLng` according to the projection
3927         // of the map's CRS, then scales it according to `zoom` and the CRS's
3928         // `Transformation`. The result is pixel coordinate relative to
3929         // the CRS origin.
3930         project: function (latlng, zoom) {
3931                 zoom = zoom === undefined ? this._zoom : zoom;
3932                 return this.options.crs.latLngToPoint(toLatLng(latlng), zoom);
3933         },
3934
3935         // @method unproject(point: Point, zoom: Number): LatLng
3936         // Inverse of [`project`](#map-project).
3937         unproject: function (point, zoom) {
3938                 zoom = zoom === undefined ? this._zoom : zoom;
3939                 return this.options.crs.pointToLatLng(toPoint(point), zoom);
3940         },
3941
3942         // @method layerPointToLatLng(point: Point): LatLng
3943         // Given a pixel coordinate relative to the [origin pixel](#map-getpixelorigin),
3944         // returns the corresponding geographical coordinate (for the current zoom level).
3945         layerPointToLatLng: function (point) {
3946                 var projectedPoint = toPoint(point).add(this.getPixelOrigin());
3947                 return this.unproject(projectedPoint);
3948         },
3949
3950         // @method latLngToLayerPoint(latlng: LatLng): Point
3951         // Given a geographical coordinate, returns the corresponding pixel coordinate
3952         // relative to the [origin pixel](#map-getpixelorigin).
3953         latLngToLayerPoint: function (latlng) {
3954                 var projectedPoint = this.project(toLatLng(latlng))._round();
3955                 return projectedPoint._subtract(this.getPixelOrigin());
3956         },
3957
3958         // @method wrapLatLng(latlng: LatLng): LatLng
3959         // Returns a `LatLng` where `lat` and `lng` has been wrapped according to the
3960         // map's CRS's `wrapLat` and `wrapLng` properties, if they are outside the
3961         // CRS's bounds.
3962         // By default this means longitude is wrapped around the dateline so its
3963         // value is between -180 and +180 degrees.
3964         wrapLatLng: function (latlng) {
3965                 return this.options.crs.wrapLatLng(toLatLng(latlng));