PageSourceSearch

https://khushiipropertiies.com/wp-content/plugins/wpdirectorykit/public/js/openstreetmap/leaflet.js?ver=1.7.1

js khushiipropertiies.com collected 2026-10-09 23:36:12 UTC 523,003 bytes, 14,124 lines download raw bytes

1/* @preserve
2 * Leaflet 1.8.0-beta.0+main.0ea4073, a JS library for interactive maps. https://leafletjs.com
3 * (c) 2010-2022 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    (global = typeof globalThis !== 'undefined' ? globalThis : global || self, factory(global.leaflet = {}));
10  })(this, (function (exports) { 'use strict';
11  
12    var version = "1.8.0-beta.0+main.32783c97";
13  
14    /*
15     * @namespace Util
16     *
17     * Various utility functions, used by Leaflet internally.
18     */
19  
20    // @function extend(dest: Object, src?: Object): Object
21    // Merges the properties of the `src` object (or multiple objects) into `dest` object and returns the latter. Has an `L.extend` shortcut.
22    function extend(dest) {
23        var i, j, len, src;
24  
25        for (j = 1, len = arguments.length; j < len; j++) {
26            src = arguments[j];
27            for (i in src) {
28                dest[i] = src[i];
vendor: 14,357 bytes, lines 29-406
29            }
30        }
31        return dest;
32    }
33  
34    // @function create(proto: Object, properties?: Object): Object
35    // Compatibility polyfill for [Object.create](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Object/create)
36    var create$2 = Object.create || (function () {
37        function F() {}
38        return function (proto) {
39            F.prototype = proto;
40            return new F();
41        };
42    })();
43  
44    // @function bind(fn: Function, …): Function
45    // 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).
46    // Has a `L.bind()` shortcut.
47    function bind(fn, obj) {
48        var slice = Array.prototype.slice;
49  
50        if (fn.bind) {
51            return fn.bind.apply(fn, slice.call(arguments, 1));
52        }
53  
54        var args = slice.call(arguments, 2);
55  
56        return function () {
57            return fn.apply(obj, args.length ? args.concat(slice.call(arguments)) : arguments);
58        };
59    }
60  
61    // @property lastId: Number
62    // Last unique ID used by [`stamp()`](#util-stamp)
63    var lastId = 0;
64  
65    // @function stamp(obj: Object): Number
66    // Returns the unique ID of an object, assigning it one if it doesn't have it.
67    function stamp(obj) {
68        if (!('_leaflet_id' in obj)) {
69            obj['_leaflet_id'] = ++lastId;
70        }
71        return obj._leaflet_id;
72    }
73  
74    // @function throttle(fn: Function, time: Number, context: Object): Function
75    // Returns a function which executes function `fn` with the given scope `context`
76    // (so that the `this` keyword refers to `context` inside `fn`'s code). The function
77    // `fn` will be called no more than one time per given amount of `time`. The arguments
78    // received by the bound function will be any arguments passed when binding the
79    // function, followed by any arguments passed when invoking the bound function.
80    // Has an `L.throttle` shortcut.
81    function throttle(fn, time, context) {
82        var lock, args, wrapperFn, later;
83  
84        later = function () {
85            // reset lock and call if queued
86            lock = false;
87            if (args) {
88                wrapperFn.apply(context, args);
89                args = false;
90            }
91        };
92  
93        wrapperFn = function () {
94            if (lock) {
95                // called too soon, queue to call later
96                args = arguments;
97  
98            } else {
99                // call and lock until later
100                fn.apply(context, arguments);
101                setTimeout(later, time);
102                lock = true;
103            }
104        };
105  
106        return wrapperFn;
107    }
108  
109    // @function wrapNum(num: Number, range: Number[], includeMax?: Boolean): Number
110    // Returns the number `num` modulo `range` in such a way so it lies within
111    // `range[0]` and `range[1]`. The returned value will be always smaller than
112    // `range[1]` unless `includeMax` is set to `true`.
113    function wrapNum(x, range, includeMax) {
114        var max = range[1],
115            min = range[0],
116            d = max - min;
117        return x === max && includeMax ? x : ((x - min) % d + d) % d + min;
118    }
119  
120    // @function falseFn(): Function
121    // Returns a function which always returns `false`.
122    function falseFn() { return false; }
123  
124    // @function formatNum(num: Number, precision?: Number|false): Number
125    // Returns the number `num` rounded with specified `precision`.
126    // The default `precision` value is 6 decimal places.
127    // `false` can be passed to skip any processing (can be useful to avoid round-off errors).
128    function formatNum(num, precision) {
129        if (precision === false) { return num; }
130        var pow = Math.pow(10, precision === undefined ? 6 : precision);
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 (!Object.prototype.hasOwnProperty.call(obj, 'options')) {
150            obj.options = obj.options ? create$2(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 https://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    var Util = {
257      __proto__: null,
258      extend: extend,
259      create: create$2,
260      bind: bind,
261      get lastId () { return lastId; },
262      stamp: stamp,
263      throttle: throttle,
264      wrapNum: wrapNum,
265      falseFn: falseFn,
266      formatNum: formatNum,
267      trim: trim,
268      splitWords: splitWords,
269      setOptions: setOptions,
270      getParamString: getParamString,
271      template: template,
272      isArray: isArray,
273      indexOf: indexOf,
274      emptyImageUrl: emptyImageUrl,
275      requestFn: requestFn,
276      cancelFn: cancelFn,
277      requestAnimFrame: requestAnimFrame,
278      cancelAnimFrame: cancelAnimFrame
279    };
280  
281    // @class Class
282    // @aka L.Class
283  
284    // @section
285    // @uninheritable
286  
287    // Thanks to John Resig and Dean Edwards for inspiration!
288  
289    function Class() {}
290  
291    Class.extend = function (props) {
292  
293        // @function extend(props: Object): Function
294        // [Extends the current class](#class-inheritance) given the properties to be included.
295        // Returns a Javascript function that is a class constructor (to be called with `new`).
296        var NewClass = function () {
297  
298            setOptions(this);
299  
300            // call the constructor
301            if (this.initialize) {
302                this.initialize.apply(this, arguments);
303            }
304  
305            // call all constructor hooks
306            this.callInitHooks();
307        };
308  
309        var parentProto = NewClass.__super__ = this.prototype;
310  
311        var proto = create$2(parentProto);
312        proto.constructor = NewClass;
313  
314        NewClass.prototype = proto;
315  
316        // inherit parent's statics
317        for (var i in this) {
318            if (Object.prototype.hasOwnProperty.call(this, i) && i !== 'prototype' && i !== '__super__') {
319                NewClass[i] = this[i];
320            }
321        }
322  
323        // mix static properties into the class
324        if (props.statics) {
325            extend(NewClass, 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        }
333  
334        // mix given properties into the prototype
335        extend(proto, props);
336        delete proto.statics;
337        delete proto.includes;
338  
339        // merge options
340        if (proto.options) {
341            proto.options = parentProto.options ? create$2(parentProto.options) : {};
342            extend(proto.options, props.options);
343        }
344  
345        proto._initHooks = [];
346  
347        // add method for calling all hooks
348        proto.callInitHooks = function () {
349  
350            if (this._initHooksCalled) { return; }
351  
352            if (parentProto.callInitHooks) {
353                parentProto.callInitHooks.call(this);
354            }
355  
356            this._initHooksCalled = true;
357  
358            for (var i = 0, len = proto._initHooks.length; i < len; i++) {
359                proto._initHooks[i].call(this);
360            }
361        };
362  
363        return NewClass;
364    };
365  
366  
367    // @function include(properties: Object): this
368    // [Includes a mixin](#class-includes) into the current class.
369    Class.include = function (props) {
370        var parentOptions = this.prototype.options;
371        extend(this.prototype, props);
372        if (props.options) {
373            this.prototype.options = parentOptions;
374            this.mergeOptions(props.options);
375        }
376        return this;
377    };
378  
379    // @function mergeOptions(options: Object): this
380    // [Merges `options`](#class-options) into the defaults of the class.
381    Class.mergeOptions = function (options) {
382        extend(this.prototype.options, options);
383        return this;
384    };
385  
386    // @function addInitHook(fn: Function): this
387    // Adds a [constructor hook](#class-constructor-hooks) to the class.
388    Class.addInitHook = function (fn) { // (Function) || (String, args...)
389        var args = Array.prototype.slice.call(arguments, 1);
390  
391        var init = typeof fn === 'function' ? fn : function () {
392            this[fn].apply(this, args);
393        };
394  
395        this.prototype._initHooks = this.prototype._initHooks || [];
396        this.prototype._initHooks.push(init);
397        return this;
398    };
399  
400    function checkDeprecatedMixinEvents(includes) {
401        if (typeof L === 'undefined' || !L || !L.Mixin) { return; }
402  
403        includes = isArray(includes) ? includes : [includes];
404  
405        for (var i = 0; i < includes.length; i++) {
406            if (includes[i] === L.Mixin.Events) {
407                console.warn('Deprecated include of L.Mixin.Events: ' +
408                    'this property will be removed in future releases, ' +
409                    'please inherit from L.Evented instead.', new Error().stack);
410            }
411        }
412    }
413  
414    /*
415     * @class Evented
416     * @aka L.Evented
417     * @inherits Class
418     *
419     * 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).
420     *
421     * @example
422     *
423     * ```js
424     * map.on('click', function(e) {
425     * 	alert(e.latlng);
426     * } );
427     * ```
428     *
429     * Leaflet deals with event listeners by reference, so if you want to add a listener and then remove it, define it as a function:
430     *
431     * ```js
432     * function onClick(e) { ... }
433     *
434     * map.on('click', onClick);
435     * map.off('click', onClick);
436     * ```
437     */
438  
439    var Events = {
440        /* @method on(type: String, fn: Function, context?: Object): this
441         * 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'`).
442         *
443         * @alternative
444         * @method on(eventMap: Object): this
445         * Adds a set of type/listener pairs, e.g. `{click: onClick, mousemove: onMouseMove}`
446         */
447        on: function (types, fn, context) {
448  
449            // types can be a map of types/handlers
450            if (typeof types === 'object') {
451                for (var type in types) {
452                    // we don't process space-separated events here for performance;
453                    // it's a hot path since Layer uses the on(obj) syntax
454                    this._on(type, types[type], fn);
455                }
456  
457            } else {
458                // types can be a string of space-separated words
459                types = splitWords(types);
460  
461                for (var i = 0, len = types.length; i < len; i++) {
462                    this._on(types[i], fn, context);
463                }
464            }
465  
466            return this;
467        },
468  
469        /* @method off(type: String, fn?: Function, context?: Object): this
470         * 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.
471         *
472         * @alternative
473         * @method off(eventMap: Object): this
474         * Removes a set of type/listener pairs.
475         *
476         * @alternative
477         * @method off: this
478         * Removes all listeners to all events on the object. This includes implicitly attached events.
479         */
480        off: function (types, fn, context) {
481  
482            if (!arguments.length) {
483                // clear all listeners if called without arguments
484                delete this._events;
485  
486            } else if (typeof types === 'object') {
487                for (var type in types) {
488                    this._off(type, types[type], fn);
489                }
490  
491            } else {
492                types = splitWords(types);
493  
494                var removeAll = arguments.length === 1;
495                for (var i = 0, len = types.length; i < len; i++) {
496                    if (removeAll) {
497                        this._off(types[i]);
498                    } else {
499                        this._off(types[i], fn, context);
500                    }
501                }
502            }
503  
504            return this;
505        },
506  
507        // attach listener (without syntactic sugar now)
508        _on: function (type, fn, context) {
509            if (typeof fn !== 'function') {
510                console.warn('wrong listener type: ' + typeof fn);
511                return;
512            }
513            this._events = this._events || {};
514  
515            /* get/init listeners for type */
516            var typeListeners = this._events[type];
517            if (!typeListeners) {
518                typeListeners = [];
519                this._events[type] = typeListeners;
520            }
521  
522            if (context === this) {
523                // Less memory footprint.
524                context = undefined;
525            }
526            var newListener = {fn: fn, ctx: context},
527                listeners = typeListeners;
528  
529            // check if fn already there
530            for (var i = 0, len = listeners.length; i < len; i++) {
531                if (listeners[i].fn === fn && listeners[i].ctx === context) {
532                    return;
533                }
534            }
535  
536            listeners.push(newListener);
537        },
538  
539        _off: function (type, fn, context) {
540            var listeners,
541                i,
542                len;
543  
544            if (!this._events) { return; }
545  
546            listeners = this._events[type];
547  
548            if (!listeners) {
549                return;
550            }
551  
552            if (arguments.length === 1) { // remove all
553                if (this._firingCount) {
554                    // Set all removed listeners to noop
555                    // so they are not called if remove happens in fire
556                    for (i = 0, len = listeners.length; i < len; i++) {
557                        listeners[i].fn = falseFn;
558                    }
559                }
560                // clear all listeners for a type if function isn't specified
561                delete this._events[type];
562                return;
563            }
564  
565            if (context === this) {
566                context = undefined;
567            }
568  
569            if (typeof fn !== 'function') {
570                console.warn('wrong listener type: ' + typeof fn);
571                return;
572            }
573            // find fn and remove it
574            for (i = 0, len = listeners.length; i < len; i++) {
575                var l = listeners[i];
576                if (l.ctx !== context) { continue; }
577                if (l.fn === fn) {
578                    if (this._firingCount) {
579                        // set the removed listener to noop so that's not called if remove happens in fire
580                        l.fn = falseFn;
581  
582                        /* copy array in case events are being fired */
583                        this._events[type] = listeners = listeners.slice();
584                    }
585                    listeners.splice(i, 1);
586  
587                    return;
588                }
589            }
590            console.warn('listener not found');
591        },
592  
593        // @method fire(type: String, data?: Object, propagate?: Boolean): this
594        // Fires an event of the specified type. You can optionally provide a data
595        // object — the first argument of the listener function will contain its
596        // properties. The event can optionally be propagated to event parents.
597        fire: function (type, data, propagate) {
598            if (!this.listens(type, propagate)) { return this; }
599  
600            var event = extend({}, data, {
601                type: type,
602                target: this,
603                sourceTarget: data && data.sourceTarget || this
604            });
605  
606            if (this._events) {
607                var listeners = this._events[type];
608  
609                if (listeners) {
610                    this._firingCount = (this._firingCount + 1) || 1;
611                    for (var i = 0, len = listeners.length; i < len; i++) {
612                        var l = listeners[i];
613                        l.fn.call(l.ctx || this, event);
614                    }
615  
616                    this._firingCount--;
617                }
618            }
619  
620            if (propagate) {
621                // propagate the event to parents (set with addEventParent)
622                this._propagateEvent(event);
623            }
624  
625            return this;
626        },
627  
628        // @method listens(type: String, propagate?: Boolean): Boolean
629        // Returns `true` if a particular event type has any listeners attached to it.
630        // The verification can optionally be propagated, it will return `true` if parents have the listener attached to it.
631        listens: function (type, propagate) {
632            if (typeof type !== 'string') {
633                console.warn('"string" type argument expected');
634            }
635            var listeners = this._events && this._events[type];
636            if (listeners && listeners.length) { return true; }
637  
638            if (propagate) {
639                // also check parents for listeners if event propagates
640                for (var id in this._eventParents) {
641                    if (this._eventParents[id].listens(type, propagate)) { return true; }
642                }
643            }
644            return false;
645        },
646  
647        // @method once(…): this
648        // Behaves as [`on(…)`](#evented-on), except the listener will only get fired once and then removed.
649        once: function (types, fn, context) {
650  
651            if (typeof types === 'object') {
652                for (var type in types) {
653                    this.once(type, types[type], fn);
654                }
655                return this;
656            }
657  
658            var handler = bind(function () {
659                this
660                    .off(types, fn, context)
661                    .off(types, handler, context);
662            }, this);
663  
664            // add a listener that's executed once and removed after that
665            return this
666                .on(types, fn, context)
667                .on(types, handler, context);
668        },
669  
670        // @method addEventParent(obj: Evented): this
671        // Adds an event parent - an `Evented` that will receive propagated events
672        addEventParent: function (obj) {
673            this._eventParents = this._eventParents || {};
674            this._eventParents[stamp(obj)] = obj;
675            return this;
676        },
677  
678        // @method removeEventParent(obj: Evented): this
679        // Removes an event parent, so it will stop receiving propagated events
680        removeEventParent: function (obj) {
681            if (this._eventParents) {
682                delete this._eventParents[stamp(obj)];
683            }
684            return this;
685        },
686  
687        _propagateEvent: function (e) {
688            for (var id in this._eventParents) {
689                this._eventParents[id].fire(e.type, extend({
690                    layer: e.target,
691                    propagatedFrom: e.target
692                }, e), true);
693            }
694        }
695    };
696  
697    // aliases; we should ditch those eventually
698  
699    // @method addEventListener(…): this
700    // Alias to [`on(…)`](#evented-on)
701    Events.addEventListener = Events.on;
702  
703    // @method removeEventListener(…): this
704    // Alias to [`off(…)`](#evented-off)
705  
706    // @method clearAllEventListeners(…): this
707    // Alias to [`off()`](#evented-off)
708    Events.removeEventListener = Events.clearAllEventListeners = Events.off;
709  
710    // @method addOneTimeEventListener(…): this
711    // Alias to [`once(…)`](#evented-once)
712    Events.addOneTimeEventListener = Events.once;
713  
714    // @method fireEvent(…): this
715    // Alias to [`fire(…)`](#evented-fire)
716    Events.fireEvent = Events.fire;
717  
718    // @method hasEventListeners(…): Boolean
719    // Alias to [`listens(…)`](#evented-listens)
720    Events.hasEventListeners = Events.listens;
721  
722    var Evented = Class.extend(Events);
723  
724    /*
725     * @class Point
726     * @aka L.Point
727     *
728     * Represents a point with `x` and `y` coordinates in pixels.
729     *
730     * @example
731     *
732     * ```js
733     * var point = L.point(200, 300);
734     * ```
735     *
736     * 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:
737     *
738     * ```js
739     * map.panBy([200, 300]);
740     * map.panBy(L.point(200, 300));
741     * ```
742     *
743     * Note that `Point` does not inherit from Leaflet's `Class` object,
744     * which means new classes can't inherit from it, and new methods
745     * can't be added to it with the `include` function.
746     */
747  
748    function Point(x, y, round) {
749        // @property x: Number; The `x` coordinate of the point
750        this.x = (round ? Math.round(x) : x);
751        // @property y: Number; The `y` coordinate of the point
752        this.y = (round ? Math.round(y) : y);
753    }
754  
755    var trunc = Math.trunc || function (v) {
756        return v > 0 ? Math.floor(v) : Math.ceil(v);
757    };
758  
759    Point.prototype = {
760  
761        // @method clone(): Point
762        // Returns a copy of the current point.
763        clone: function () {
764            return new Point(this.x, this.y);
765        },
766  
767        // @method add(otherPoint: Point): Point
768        // Returns the result of addition of the current and the given points.
769        add: function (point) {
770            // non-destructive, returns a new point
771            return this.clone()._add(toPoint(point));
772        },
773  
774        _add: function (point) {
775            // destructive, used directly for performance in situations where it's s
vendor: 26,462 bytes, lines 775-1505
775afe to modify existing point
776            this.x += point.x;
777            this.y += point.y;
778            return this;
779        },
780  
781        // @method subtract(otherPoint: Point): Point
782        // Returns the result of subtraction of the given point from the current.
783        subtract: function (point) {
784            return this.clone()._subtract(toPoint(point));
785        },
786  
787        _subtract: function (point) {
788            this.x -= point.x;
789            this.y -= point.y;
790            return this;
791        },
792  
793        // @method divideBy(num: Number): Point
794        // Returns the result of division of the current point by the given number.
795        divideBy: function (num) {
796            return this.clone()._divideBy(num);
797        },
798  
799        _divideBy: function (num) {
800            this.x /= num;
801            this.y /= num;
802            return this;
803        },
804  
805        // @method multiplyBy(num: Number): Point
806        // Returns the result of multiplication of the current point by the given number.
807        multiplyBy: function (num) {
808            return this.clone()._multiplyBy(num);
809        },
810  
811        _multiplyBy: function (num) {
812            this.x *= num;
813            this.y *= num;
814            return this;
815        },
816  
817        // @method scaleBy(scale: Point): Point
818        // Multiply each coordinate of the current point by each coordinate of
819        // `scale`. In linear algebra terms, multiply the point by the
820        // [scaling matrix](https://en.wikipedia.org/wiki/Scaling_%28geometry%29#Matrix_representation)
821        // defined by `scale`.
822        scaleBy: function (point) {
823            return new Point(this.x * point.x, this.y * point.y);
824        },
825  
826        // @method unscaleBy(scale: Point): Point
827        // Inverse of `scaleBy`. Divide each coordinate of the current point by
828        // each coordinate of `scale`.
829        unscaleBy: function (point) {
830            return new Point(this.x / point.x, this.y / point.y);
831        },
832  
833        // @method round(): Point
834        // Returns a copy of the current point with rounded coordinates.
835        round: function () {
836            return this.clone()._round();
837        },
838  
839        _round: function () {
840            this.x = Math.round(this.x);
841            this.y = Math.round(this.y);
842            return this;
843        },
844  
845        // @method floor(): Point
846        // Returns a copy of the current point with floored coordinates (rounded down).
847        floor: function () {
848            return this.clone()._floor();
849        },
850  
851        _floor: function () {
852            this.x = Math.floor(this.x);
853            this.y = Math.floor(this.y);
854            return this;
855        },
856  
857        // @method ceil(): Point
858        // Returns a copy of the current point with ceiled coordinates (rounded up).
859        ceil: function () {
860            return this.clone()._ceil();
861        },
862  
863        _ceil: function () {
864            this.x = Math.ceil(this.x);
865            this.y = Math.ceil(this.y);
866            return this;
867        },
868  
869        // @method trunc(): Point
870        // Returns a copy of the current point with truncated coordinates (rounded towards zero).
871        trunc: function () {
872            return this.clone()._trunc();
873        },
874  
875        _trunc: function () {
876            this.x = trunc(this.x);
877            this.y = trunc(this.y);
878            return this;
879        },
880  
881        // @method distanceTo(otherPoint: Point): Number
882        // Returns the cartesian distance between the current and the given points.
883        distanceTo: function (point) {
884            point = toPoint(point);
885  
886            var x = point.x - this.x,
887                y = point.y - this.y;
888  
889            return Math.sqrt(x * x + y * y);
890        },
891  
892        // @method equals(otherPoint: Point): Boolean
893        // Returns `true` if the given point has the same coordinates.
894        equals: function (point) {
895            point = toPoint(point);
896  
897            return point.x === this.x &&
898                   point.y === this.y;
899        },
900  
901        // @method contains(otherPoint: Point): Boolean
902        // Returns `true` if both coordinates of the given point are less than the corresponding current point coordinates (in absolute values).
903        contains: function (point) {
904            point = toPoint(point);
905  
906            return Math.abs(point.x) <= Math.abs(this.x) &&
907                   Math.abs(point.y) <= Math.abs(this.y);
908        },
909  
910        // @method toString(): String
911        // Returns a string representation of the point for debugging purposes.
912        toString: function () {
913            return 'Point(' +
914                    formatNum(this.x) + ', ' +
915                    formatNum(this.y) + ')';
916        }
917    };
918  
919    // @factory L.point(x: Number, y: Number, round?: Boolean)
920    // Creates a Point object with the given `x` and `y` coordinates. If optional `round` is set to true, rounds the `x` and `y` values.
921  
922    // @alternative
923    // @factory L.point(coords: Number[])
924    // Expects an array of the form `[x, y]` instead.
925  
926    // @alternative
927    // @factory L.point(coords: Object)
928    // Expects a plain object of the form `{x: Number, y: Number}` instead.
929    function toPoint(x, y, round) {
930        if (x instanceof Point) {
931            return x;
932        }
933        if (isArray(x)) {
934            return new Point(x[0], x[1]);
935        }
936        if (x === undefined || x === null) {
937            return x;
938        }
939        if (typeof x === 'object' && 'x' in x && 'y' in x) {
940            return new Point(x.x, x.y);
941        }
942        return new Point(x, y, round);
943    }
944  
945    /*
946     * @class Bounds
947     * @aka L.Bounds
948     *
949     * Represents a rectangular area in pixel coordinates.
950     *
951     * @example
952     *
953     * ```js
954     * var p1 = L.point(10, 10),
955     * p2 = L.point(40, 60),
956     * bounds = L.bounds(p1, p2);
957     * ```
958     *
959     * 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:
960     *
961     * ```js
962     * otherBounds.intersects([[10, 10], [40, 60]]);
963     * ```
964     *
965     * Note that `Bounds` does not inherit from Leaflet's `Class` object,
966     * which means new classes can't inherit from it, and new methods
967     * can't be added to it with the `include` function.
968     */
969  
970    function Bounds(a, b) {
971        if (!a) { return; }
972  
973        var points = b ? [a, b] : a;
974  
975        for (var i = 0, len = points.length; i < len; i++) {
976            this.extend(points[i]);
977        }
978    }
979  
980    Bounds.prototype = {
981        // @method extend(point: Point): this
982        // Extends the bounds to contain the given point.
983        extend: function (point) { // (Point)
984            point = toPoint(point);
985  
986            // @property min: Point
987            // The top left corner of the rectangle.
988            // @property max: Point
989            // The bottom right corner of the rectangle.
990            if (!this.min && !this.max) {
991                this.min = point.clone();
992                this.max = point.clone();
993            } else {
994                this.min.x = Math.min(point.x, this.min.x);
995                this.max.x = Math.max(point.x, this.max.x);
996                this.min.y = Math.min(point.y, this.min.y);
997                this.max.y = Math.max(point.y, this.max.y);
998            }
999            return this;
1000        },
1001  
1002        // @method getCenter(round?: Boolean): Point
1003        // Returns the center point of the bounds.
1004        getCenter: function (round) {
1005            return new Point(
1006                    (this.min.x + this.max.x) / 2,
1007                    (this.min.y + this.max.y) / 2, round);
1008        },
1009  
1010        // @method getBottomLeft(): Point
1011        // Returns the bottom-left point of the bounds.
1012        getBottomLeft: function () {
1013            return new Point(this.min.x, this.max.y);
1014        },
1015  
1016        // @method getTopRight(): Point
1017        // Returns the top-right point of the bounds.
1018        getTopRight: function () { // -> Point
1019            return new Point(this.max.x, this.min.y);
1020        },
1021  
1022        // @method getTopLeft(): Point
1023        // Returns the top-left point of the bounds (i.e. [`this.min`](#bounds-min)).
1024        getTopLeft: function () {
1025            return this.min; // left, top
1026        },
1027  
1028        // @method getBottomRight(): Point
1029        // Returns the bottom-right point of the bounds (i.e. [`this.max`](#bounds-max)).
1030        getBottomRight: function () {
1031            return this.max; // right, bottom
1032        },
1033  
1034        // @method getSize(): Point
1035        // Returns the size of the given bounds
1036        getSize: function () {
1037            return this.max.subtract(this.min);
1038        },
1039  
1040        // @method contains(otherBounds: Bounds): Boolean
1041        // Returns `true` if the rectangle contains the given one.
1042        // @alternative
1043        // @method contains(point: Point): Boolean
1044        // Returns `true` if the rectangle contains the given point.
1045        contains: function (obj) {
1046            var min, max;
1047  
1048            if (typeof obj[0] === 'number' || obj instanceof Point) {
1049                obj = toPoint(obj);
1050            } else {
1051                obj = toBounds(obj);
1052            }
1053  
1054            if (obj instanceof Bounds) {
1055                min = obj.min;
1056                max = obj.max;
1057            } else {
1058                min = max = obj;
1059            }
1060  
1061            return (min.x >= this.min.x) &&
1062                   (max.x <= this.max.x) &&
1063                   (min.y >= this.min.y) &&
1064                   (max.y <= this.max.y);
1065        },
1066  
1067        // @method intersects(otherBounds: Bounds): Boolean
1068        // Returns `true` if the rectangle intersects the given bounds. Two bounds
1069        // intersect if they have at least one point in common.
1070        intersects: function (bounds) { // (Bounds) -> Boolean
1071            bounds = toBounds(bounds);
1072  
1073            var min = this.min,
1074                max = this.max,
1075                min2 = bounds.min,
1076                max2 = bounds.max,
1077                xIntersects = (max2.x >= min.x) && (min2.x <= max.x),
1078                yIntersects = (max2.y >= min.y) && (min2.y <= max.y);
1079  
1080            return xIntersects && yIntersects;
1081        },
1082  
1083        // @method overlaps(otherBounds: Bounds): Boolean
1084        // Returns `true` if the rectangle overlaps the given bounds. Two bounds
1085        // overlap if their intersection is an area.
1086        overlaps: function (bounds) { // (Bounds) -> Boolean
1087            bounds = toBounds(bounds);
1088  
1089            var min = this.min,
1090                max = this.max,
1091                min2 = bounds.min,
1092                max2 = bounds.max,
1093                xOverlaps = (max2.x > min.x) && (min2.x < max.x),
1094                yOverlaps = (max2.y > min.y) && (min2.y < max.y);
1095  
1096            return xOverlaps && yOverlaps;
1097        },
1098  
1099        isValid: function () {
1100            return !!(this.min && this.max);
1101        }
1102    };
1103  
1104  
1105    // @factory L.bounds(corner1: Point, corner2: Point)
1106    // Creates a Bounds object from two corners coordinate pairs.
1107    // @alternative
1108    // @factory L.bounds(points: Point[])
1109    // Creates a Bounds object from the given array of points.
1110    function toBounds(a, b) {
1111        if (!a || a instanceof Bounds) {
1112            return a;
1113        }
1114        return new Bounds(a, b);
1115    }
1116  
1117    /*
1118     * @class LatLngBounds
1119     * @aka L.LatLngBounds
1120     *
1121     * Represents a rectangular geographical area on a map.
1122     *
1123     * @example
1124     *
1125     * ```js
1126     * var corner1 = L.latLng(40.712, -74.227),
1127     * corner2 = L.latLng(40.774, -74.125),
1128     * bounds = L.latLngBounds(corner1, corner2);
1129     * ```
1130     *
1131     * 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:
1132     *
1133     * ```js
1134     * map.fitBounds([
1135     * 	[40.712, -74.227],
1136     * 	[40.774, -74.125]
1137     * ]);
1138     * ```
1139     *
1140     * 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.
1141     *
1142     * Note that `LatLngBounds` does not inherit from Leaflet's `Class` object,
1143     * which means new classes can't inherit from it, and new methods
1144     * can't be added to it with the `include` function.
1145     */
1146  
1147    function LatLngBounds(corner1, corner2) { // (LatLng, LatLng) or (LatLng[])
1148        if (!corner1) { return; }
1149  
1150        var latlngs = corner2 ? [corner1, corner2] : corner1;
1151  
1152        for (var i = 0, len = latlngs.length; i < len; i++) {
1153            this.extend(latlngs[i]);
1154        }
1155    }
1156  
1157    LatLngBounds.prototype = {
1158  
1159        // @method extend(latlng: LatLng): this
1160        // Extend the bounds to contain the given point
1161  
1162        // @alternative
1163        // @method extend(otherBounds: LatLngBounds): this
1164        // Extend the bounds to contain the given bounds
1165        extend: function (obj) {
1166            var sw = this._southWest,
1167                ne = this._northEast,
1168                sw2, ne2;
1169  
1170            if (obj instanceof LatLng) {
1171                sw2 = obj;
1172                ne2 = obj;
1173  
1174            } else if (obj instanceof LatLngBounds) {
1175                sw2 = obj._southWest;
1176                ne2 = obj._northEast;
1177  
1178                if (!sw2 || !ne2) { return this; }
1179  
1180            } else {
1181                return obj ? this.extend(toLatLng(obj) || toLatLngBounds(obj)) : this;
1182            }
1183  
1184            if (!sw && !ne) {
1185                this._southWest = new LatLng(sw2.lat, sw2.lng);
1186                this._northEast = new LatLng(ne2.lat, ne2.lng);
1187            } else {
1188                sw.lat = Math.min(sw2.lat, sw.lat);
1189                sw.lng = Math.min(sw2.lng, sw.lng);
1190                ne.lat = Math.max(ne2.lat, ne.lat);
1191                ne.lng = Math.max(ne2.lng, ne.lng);
1192            }
1193  
1194            return this;
1195        },
1196  
1197        // @method pad(bufferRatio: Number): LatLngBounds
1198        // Returns bounds created by extending or retracting the current bounds by a given ratio in each direction.
1199        // For example, a ratio of 0.5 extends the bounds by 50% in each direction.
1200        // Negative values will retract the bounds.
1201        pad: function (bufferRatio) {
1202            var sw = this._southWest,
1203                ne = this._northEast,
1204                heightBuffer = Math.abs(sw.lat - ne.lat) * bufferRatio,
1205                widthBuffer = Math.abs(sw.lng - ne.lng) * bufferRatio;
1206  
1207            return new LatLngBounds(
1208                    new LatLng(sw.lat - heightBuffer, sw.lng - widthBuffer),
1209                    new LatLng(ne.lat + heightBuffer, ne.lng + widthBuffer));
1210        },
1211  
1212        // @method getCenter(): LatLng
1213        // Returns the center point of the bounds.
1214        getCenter: function () {
1215            return new LatLng(
1216                    (this._southWest.lat + this._northEast.lat) / 2,
1217                    (this._southWest.lng + this._northEast.lng) / 2);
1218        },
1219  
1220        // @method getSouthWest(): LatLng
1221        // Returns the south-west point of the bounds.
1222        getSouthWest: function () {
1223            return this._southWest;
1224        },
1225  
1226        // @method getNorthEast(): LatLng
1227        // Returns the north-east point of the bounds.
1228        getNorthEast: function () {
1229            return this._northEast;
1230        },
1231  
1232        // @method getNorthWest(): LatLng
1233        // Returns the north-west point of the bounds.
1234        getNorthWest: function () {
1235            return new LatLng(this.getNorth(), this.getWest());
1236        },
1237  
1238        // @method getSouthEast(): LatLng
1239        // Returns the south-east point of the bounds.
1240        getSouthEast: function () {
1241            return new LatLng(this.getSouth(), this.getEast());
1242        },
1243  
1244        // @method getWest(): Number
1245        // Returns the west longitude of the bounds
1246        getWest: function () {
1247            return this._southWest.lng;
1248        },
1249  
1250        // @method getSouth(): Number
1251        // Returns the south latitude of the bounds
1252        getSouth: function () {
1253            return this._southWest.lat;
1254        },
1255  
1256        // @method getEast(): Number
1257        // Returns the east longitude of the bounds
1258        getEast: function () {
1259            return this._northEast.lng;
1260        },
1261  
1262        // @method getNorth(): Number
1263        // Returns the north latitude of the bounds
1264        getNorth: function () {
1265            return this._northEast.lat;
1266        },
1267  
1268        // @method contains(otherBounds: LatLngBounds): Boolean
1269        // Returns `true` if the rectangle contains the given one.
1270  
1271        // @alternative
1272        // @method contains (latlng: LatLng): Boolean
1273        // Returns `true` if the rectangle contains the given point.
1274        contains: function (obj) { // (LatLngBounds) or (LatLng) -> Boolean
1275            if (typeof obj[0] === 'number' || obj instanceof LatLng || 'lat' in obj) {
1276                obj = toLatLng(obj);
1277            } else {
1278                obj = toLatLngBounds(obj);
1279            }
1280  
1281            var sw = this._southWest,
1282                ne = this._northEast,
1283                sw2, ne2;
1284  
1285            if (obj instanceof LatLngBounds) {
1286                sw2 = obj.getSouthWest();
1287                ne2 = obj.getNorthEast();
1288            } else {
1289                sw2 = ne2 = obj;
1290            }
1291  
1292            return (sw2.lat >= sw.lat) && (ne2.lat <= ne.lat) &&
1293                   (sw2.lng >= sw.lng) && (ne2.lng <= ne.lng);
1294        },
1295  
1296        // @method intersects(otherBounds: LatLngBounds): Boolean
1297        // Returns `true` if the rectangle intersects the given bounds. Two bounds intersect if they have at least one point in common.
1298        intersects: function (bounds) {
1299            bounds = toLatLngBounds(bounds);
1300  
1301            var sw = this._southWest,
1302                ne = this._northEast,
1303                sw2 = bounds.getSouthWest(),
1304                ne2 = bounds.getNorthEast(),
1305  
1306                latIntersects = (ne2.lat >= sw.lat) && (sw2.lat <= ne.lat),
1307                lngIntersects = (ne2.lng >= sw.lng) && (sw2.lng <= ne.lng);
1308  
1309            return latIntersects && lngIntersects;
1310        },
1311  
1312        // @method overlaps(otherBounds: LatLngBounds): Boolean
1313        // Returns `true` if the rectangle overlaps the given bounds. Two bounds overlap if their intersection is an area.
1314        overlaps: function (bounds) {
1315            bounds = toLatLngBounds(bounds);
1316  
1317            var sw = this._southWest,
1318                ne = this._northEast,
1319                sw2 = bounds.getSouthWest(),
1320                ne2 = bounds.getNorthEast(),
1321  
1322                latOverlaps = (ne2.lat > sw.lat) && (sw2.lat < ne.lat),
1323                lngOverlaps = (ne2.lng > sw.lng) && (sw2.lng < ne.lng);
1324  
1325            return latOverlaps && lngOverlaps;
1326        },
1327  
1328        // @method toBBoxString(): String
1329        // 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.
1330        toBBoxString: function () {
1331            return [this.getWest(), this.getSouth(), this.getEast(), this.getNorth()].join(',');
1332        },
1333  
1334        // @method equals(otherBounds: LatLngBounds, maxMargin?: Number): Boolean
1335        // 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.
1336        equals: function (bounds, maxMargin) {
1337            if (!bounds) { return false; }
1338  
1339            bounds = toLatLngBounds(bounds);
1340  
1341            return this._southWest.equals(bounds.getSouthWest(), maxMargin) &&
1342                   this._northEast.equals(bounds.getNorthEast(), maxMargin);
1343        },
1344  
1345        // @method isValid(): Boolean
1346        // Returns `true` if the bounds are properly initialized.
1347        isValid: function () {
1348            return !!(this._southWest && this._northEast);
1349        }
1350    };
1351  
1352    // TODO International date line?
1353  
1354    // @factory L.latLngBounds(corner1: LatLng, corner2: LatLng)
1355    // Creates a `LatLngBounds` object by defining two diagonally opposite corners of the rectangle.
1356  
1357    // @alternative
1358    // @factory L.latLngBounds(latlngs: LatLng[])
1359    // 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).
1360    function toLatLngBounds(a, b) {
1361        if (a instanceof LatLngBounds) {
1362            return a;
1363        }
1364        return new LatLngBounds(a, b);
1365    }
1366  
1367    /* @class LatLng
1368     * @aka L.LatLng
1369     *
1370     * Represents a geographical point with a certain latitude and longitude.
1371     *
1372     * @example
1373     *
1374     * ```
1375     * var latlng = L.latLng(50.5, 30.5);
1376     * ```
1377     *
1378     * 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:
1379     *
1380     * ```
1381     * map.panTo([50, 30]);
1382     * map.panTo({lon: 30, lat: 50});
1383     * map.panTo({lat: 50, lng: 30});
1384     * map.panTo(L.latLng(50, 30));
1385     * ```
1386     *
1387     * Note that `LatLng` does not inherit from Leaflet's `Class` object,
1388     * which means new classes can't inherit from it, and new methods
1389     * can't be added to it with the `include` function.
1390     */
1391  
1392    function LatLng(lat, lng, alt) {
1393        if (isNaN(lat) || isNaN(lng)) {
1394            throw new Error('Invalid LatLng object: (' + lat + ', ' + lng + ')');
1395        }
1396  
1397        // @property lat: Number
1398        // Latitude in degrees
1399        this.lat = +lat;
1400  
1401        // @property lng: Number
1402        // Longitude in degrees
1403        this.lng = +lng;
1404  
1405        // @property alt: Number
1406        // Altitude in meters (optional)
1407        if (alt !== undefined) {
1408            this.alt = +alt;
1409        }
1410    }
1411  
1412    LatLng.prototype = {
1413        // @method equals(otherLatLng: LatLng, maxMargin?: Number): Boolean
1414        // 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.
1415        equals: function (obj, maxMargin) {
1416            if (!obj) { return false; }
1417  
1418            obj = toLatLng(obj);
1419  
1420            var margin = Math.max(
1421                    Math.abs(this.lat - obj.lat),
1422                    Math.abs(this.lng - obj.lng));
1423  
1424            return margin <= (maxMargin === undefined ? 1.0E-9 : maxMargin);
1425        },
1426  
1427        // @method toString(): String
1428        // Returns a string representation of the point (for debugging purposes).
1429        toString: function (precision) {
1430            return 'LatLng(' +
1431                    formatNum(this.lat, precision) + ', ' +
1432                    formatNum(this.lng, precision) + ')';
1433        },
1434  
1435        // @method distanceTo(otherLatLng: LatLng): Number
1436        // 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).
1437        distanceTo: function (other) {
1438            return Earth.distance(this, toLatLng(other));
1439        },
1440  
1441        // @method wrap(): LatLng
1442        // Returns a new `LatLng` object with the longitude wrapped so it's always between -180 and +180 degrees.
1443        wrap: function () {
1444            return Earth.wrapLatLng(this);
1445        },
1446  
1447        // @method toBounds(sizeInMeters: Number): LatLngBounds
1448        // Returns a new `LatLngBounds` object in which each boundary is `sizeInMeters/2` meters apart from the `LatLng`.
1449        toBounds: function (sizeInMeters) {
1450            var latAccuracy = 180 * sizeInMeters / 40075017,
1451                lngAccuracy = latAccuracy / Math.cos((Math.PI / 180) * this.lat);
1452  
1453            return toLatLngBounds(
1454                    [this.lat - latAccuracy, this.lng - lngAccuracy],
1455                    [this.lat + latAccuracy, this.lng + lngAccuracy]);
1456        },
1457  
1458        clone: function () {
1459            return new LatLng(this.lat, this.lng, this.alt);
1460        }
1461    };
1462  
1463  
1464  
1465    // @factory L.latLng(latitude: Number, longitude: Number, altitude?: Number): LatLng
1466    // Creates an object representing a geographical point with the given latitude and longitude (and optionally altitude).
1467  
1468    // @alternative
1469    // @factory L.latLng(coords: Array): LatLng
1470    // Expects an array of the form `[Number, Number]` or `[Number, Number, Number]` instead.
1471  
1472    // @alternative
1473    // @factory L.latLng(coords: Object): LatLng
1474    // Expects an plain object of the form `{lat: Number, lng: Number}` or `{lat: Number, lng: Number, alt: Number}` instead.
1475  
1476    function toLatLng(a, b, c) {
1477        if (a instanceof LatLng) {
1478            return a;
1479        }
1480        if (isArray(a) && typeof a[0] !== 'object') {
1481            if (a.length === 3) {
1482                return new LatLng(a[0], a[1], a[2]);
1483            }
1484            if (a.length === 2) {
1485                return new LatLng(a[0], a[1]);
1486            }
1487            return null;
1488        }
1489        if (a === undefined || a === null) {
1490            return a;
1491        }
1492        if (typeof a === 'object' && 'lat' in a) {
1493            return new LatLng(a.lat, 'lng' in a ? a.lng : a.lon, a.alt);
1494        }
1495        if (b === undefined) {
1496            return null;
1497        }
1498        return new LatLng(a, b, c);
1499    }
1500  
1501    /*
1502     * @namespace CRS
1503     * @crs L.CRS.Base
1504     * Object that defines coordinate reference systems for projecting
1505     * geographical points into pixel (screen) coordinates and back (and to
1506     * coordinates in other units for [WMS](https://en.wikipedia.org/wiki/Web_Map_Service) services). See
1507     * [spatial reference system](https://en.wikipedia.org/wiki/Spatial_reference_system).
1508     *
1509     * Leaflet defines the most usual CRSs by default. If you want to use a
1510     * CRS not defined by default, take a look at the
1511     * [Proj4Leaflet](https://github.com/kartena/Proj4Leaflet) plugin.
1512     *
1513     * Note that the CRS instances do not inherit from Leaflet's `Class` object,
1514     * and can't be instantiated. Also, new classes can't inherit from them,
1515     * and methods can't be added to them with the `include` function.
1516     */
1517  
1518    var CRS = {
1519        // @method latLngToPoint(latlng: LatLng, zoom: Number): Point
1520        // Projects geographical coordinates into pixel coordinates for a given zoom.
1521        latLngToPoint: function (latlng, zoom) {
1522            var projectedPoint = this.projection.project(latlng),
1523                scale = this.scale(zoom);
1524  
1525            return this.transformation._transform(projectedPoint, scale);
1526        },
1527  
1528        // @method pointToLatLng(point: Point, zoom: Number): LatLng
1529        // The inverse of `latLngToPoint`. Projects pixel coordinates on a given
1530        // zoom into geographical coordinates.
1531        pointToLatLng: function (point, zoom) {
1532            var scale = this.scale(zoom),
1533                untransformedPoint = this.transformation.untransform(point, scale);
1534  
1535            return this.projection.unproject(untransformedPoint);
1536        },
1537  
1538        // @method project(latlng: LatLng): Point
1539        // Projects geographical coordinates into coordinates in units accepted for
1540        // this CRS (e.g. meters for EPSG:3857, for passing it to WMS services).
1541        project: function (latlng) {
1542            return this.projection.project(latlng);
1543        },
1544  
1545        // @method unproject(point: Point): LatLng
1546        // Given a projected coordinate returns the corresponding LatLng.
1547        // The inverse of `project`.
1548        unproject: function (point) {
1549            return this.projection.unproject(point);
1550        },
1551  
1552        // @method scale(zoom: Number): Number
1553        // Returns the scale used when transforming projected coordinates into
1554        // pixel coordinates for a particular zoom. For example, it returns
1555        // `256 * 2^zoom` for Mercator-based CRS.
1556        scale: function (zoom) {
1557            return 256 * Math.pow(2, zoom);
1558        },
1559  
1560        // @method zoom(scale: Number): Number
1561        // Inverse of `scale()`, returns the zoom level corresponding to a scale
1562        // factor of `scale`.
1563        zoom: function (scale) {
1564            return Math.log(scale / 256) / Math.LN2;
1565        },
1566  
1567        // @method getProjectedBounds(zoom: Number): Bounds
1568        // Returns the projection's bounds scaled and transformed for the provided `zoom`.
1569        getProjectedBounds: function (zoom) {
1570            if (this.infinite) { return null; }
1571  
1572            var b = this.projection.bounds,
1573                s = this.scale(zoom),
1574                min = this.transformation.transform(b.min, s),
1575                max = this.transformation.transform(b.max, s);
1576  
1577            return new Bounds(min, max);
1578        },
1579  
1580        // @method distance(latlng1: LatLng, latlng2: LatLng): Number
1581        // Returns the distance between two geographical coordinates.
1582  
1583        // @property code: String
1584        // Standard code name of the CRS passed into WMS services (e.g. `'EPSG:3857'`)
1585        //
1586        // @property wrapLng: Number[]
1587        // An array of two numbers defining whether the longitude (horizontal) coordinate
1588        // axis wraps around a given range and how. Defaults to `[-180, 180]` in most
1589        // geographical CRSs. If `undefined`, the longitude axis does not wrap around.
1590        //
1591        // @property wrapLat: Number[]
1592        // Like `wrapLng`, but for the latitude (vertical) axis.
1593  
1594        // wrapLng: [min, max],
1595        // wrapLat: [min, max],
1596  
1597        // @property infinite: Boolean
1598        // If true, the coordinate space will be unbounded (infinite in both axes)
1599        infinite: false,
1600  
1601        // @method wrapLatLng(latlng: LatLng): LatLng
1602        // Returns a `LatLng` where lat and lng has been wrapped according to the
1603        // CRS's `wrapLat` and `wrapLng` properties, if they are outside the CRS's bounds.
1604        wrapLatLng: function (latlng) {
1605            var lng = this.wrapLng ? wrapNum(latlng.lng, this.wrapLng, true) : latlng.lng,
1606                lat = this.wrapLat ? wrapNum(latlng.lat, this.wrapLat, true) : latlng.lat,
1607                alt = latlng.alt;
1608  
vendor: 115,451 bytes, lines 1609-4500
1609            return new LatLng(lat, lng, alt);
1610        },
1611  
1612        // @method wrapLatLngBounds(bounds: LatLngBounds): LatLngBounds
1613        // Returns a `LatLngBounds` with the same size as the given one, ensuring
1614        // that its center is within the CRS's bounds.
1615        // Only accepts actual `L.LatLngBounds` instances, not arrays.
1616        wrapLatLngBounds: function (bounds) {
1617            var center = bounds.getCenter(),
1618                newCenter = this.wrapLatLng(center),
1619                latShift = center.lat - newCenter.lat,
1620                lngShift = center.lng - newCenter.lng;
1621  
1622            if (latShift === 0 && lngShift === 0) {
1623                return bounds;
1624            }
1625  
1626            var sw = bounds.getSouthWest(),
1627                ne = bounds.getNorthEast(),
1628                newSw = new LatLng(sw.lat - latShift, sw.lng - lngShift),
1629                newNe = new LatLng(ne.lat - latShift, ne.lng - lngShift);
1630  
1631            return new LatLngBounds(newSw, newNe);
1632        }
1633    };
1634  
1635    /*
1636     * @namespace CRS
1637     * @crs L.CRS.Earth
1638     *
1639     * Serves as the base for CRS that are global such that they cover the earth.
1640     * Can only be used as the base for other CRS and cannot be used directly,
1641     * since it does not have a `code`, `projection` or `transformation`. `distance()` returns
1642     * meters.
1643     */
1644  
1645    var Earth = extend({}, CRS, {
1646        wrapLng: [-180, 180],
1647  
1648        // Mean Earth Radius, as recommended for use by
1649        // the International Union of Geodesy and Geophysics,
1650        // see https://rosettacode.org/wiki/Haversine_formula
1651        R: 6371000,
1652  
1653        // distance between two geographical points using spherical law of cosines approximation
1654        distance: function (latlng1, latlng2) {
1655            var rad = Math.PI / 180,
1656                lat1 = latlng1.lat * rad,
1657                lat2 = latlng2.lat * rad,
1658                sinDLat = Math.sin((latlng2.lat - latlng1.lat) * rad / 2),
1659                sinDLon = Math.sin((latlng2.lng - latlng1.lng) * rad / 2),
1660                a = sinDLat * sinDLat + Math.cos(lat1) * Math.cos(lat2) * sinDLon * sinDLon,
1661                c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
1662            return this.R * c;
1663        }
1664    });
1665  
1666    /*
1667     * @namespace Projection
1668     * @projection L.Projection.SphericalMercator
1669     *
1670     * Spherical Mercator projection — the most common projection for online maps,
1671     * used by almost all free and commercial tile providers. Assumes that Earth is
1672     * a sphere. Used by the `EPSG:3857` CRS.
1673     */
1674  
1675    var earthRadius = 6378137;
1676  
1677    var SphericalMercator = {
1678  
1679        R: earthRadius,
1680        MAX_LATITUDE: 85.0511287798,
1681  
1682        project: function (latlng) {
1683            var d = Math.PI / 180,
1684                max = this.MAX_LATITUDE,
1685                lat = Math.max(Math.min(max, latlng.lat), -max),
1686                sin = Math.sin(lat * d);
1687  
1688            return new Point(
1689                this.R * latlng.lng * d,
1690                this.R * Math.log((1 + sin) / (1 - sin)) / 2);
1691        },
1692  
1693        unproject: function (point) {
1694            var d = 180 / Math.PI;
1695  
1696            return new LatLng(
1697                (2 * Math.atan(Math.exp(point.y / this.R)) - (Math.PI / 2)) * d,
1698                point.x * d / this.R);
1699        },
1700  
1701        bounds: (function () {
1702            var d = earthRadius * Math.PI;
1703            return new Bounds([-d, -d], [d, d]);
1704        })()
1705    };
1706  
1707    /*
1708     * @class Transformation
1709     * @aka L.Transformation
1710     *
1711     * Represents an affine transformation: a set of coefficients `a`, `b`, `c`, `d`
1712     * for transforming a point of a form `(x, y)` into `(a*x + b, c*y + d)` and doing
1713     * the reverse. Used by Leaflet in its projections code.
1714     *
1715     * @example
1716     *
1717     * ```js
1718     * var transformation = L.transformation(2, 5, -1, 10),
1719     * 	p = L.point(1, 2),
1720     * 	p2 = transformation.transform(p), //  L.point(7, 8)
1721     * 	p3 = transformation.untransform(p2); //  L.point(1, 2)
1722     * ```
1723     */
1724  
1725  
1726    // factory new L.Transformation(a: Number, b: Number, c: Number, d: Number)
1727    // Creates a `Transformation` object with the given coefficients.
1728    function Transformation(a, b, c, d) {
1729        if (isArray(a)) {
1730            // use array properties
1731            this._a = a[0];
1732            this._b = a[1];
1733            this._c = a[2];
1734            this._d = a[3];
1735            return;
1736        }
1737        this._a = a;
1738        this._b = b;
1739        this._c = c;
1740        this._d = d;
1741    }
1742  
1743    Transformation.prototype = {
1744        // @method transform(point: Point, scale?: Number): Point
1745        // Returns a transformed point, optionally multiplied by the given scale.
1746        // Only accepts actual `L.Point` instances, not arrays.
1747        transform: function (point, scale) { // (Point, Number) -> Point
1748            return this._transform(point.clone(), scale);
1749        },
1750  
1751        // destructive transform (faster)
1752        _transform: function (point, scale) {
1753            scale = scale || 1;
1754            point.x = scale * (this._a * point.x + this._b);
1755            point.y = scale * (this._c * point.y + this._d);
1756            return point;
1757        },
1758  
1759        // @method untransform(point: Point, scale?: Number): Point
1760        // Returns the reverse transformation of the given point, optionally divided
1761        // by the given scale. Only accepts actual `L.Point` instances, not arrays.
1762        untransform: function (point, scale) {
1763            scale = scale || 1;
1764            return new Point(
1765                    (point.x / scale - this._b) / this._a,
1766                    (point.y / scale - this._d) / this._c);
1767        }
1768    };
1769  
1770    // factory L.transformation(a: Number, b: Number, c: Number, d: Number)
1771  
1772    // @factory L.transformation(a: Number, b: Number, c: Number, d: Number)
1773    // Instantiates a Transformation object with the given coefficients.
1774  
1775    // @alternative
1776    // @factory L.transformation(coefficients: Array): Transformation
1777    // Expects an coefficients array of the form
1778    // `[a: Number, b: Number, c: Number, d: Number]`.
1779  
1780    function toTransformation(a, b, c, d) {
1781        return new Transformation(a, b, c, d);
1782    }
1783  
1784    /*
1785     * @namespace CRS
1786     * @crs L.CRS.EPSG3857
1787     *
1788     * The most common CRS for online maps, used by almost all free and commercial
1789     * tile providers. Uses Spherical Mercator projection. Set in by default in
1790     * Map's `crs` option.
1791     */
1792  
1793    var EPSG3857 = extend({}, Earth, {
1794        code: 'EPSG:3857',
1795        projection: SphericalMercator,
1796  
1797        transformation: (function () {
1798            var scale = 0.5 / (Math.PI * SphericalMercator.R);
1799            return toTransformation(scale, 0.5, -scale, 0.5);
1800        }())
1801    });
1802  
1803    var EPSG900913 = extend({}, EPSG3857, {
1804        code: 'EPSG:900913'
1805    });
1806  
1807    // @namespace SVG; @section
1808    // There are several static functions which can be called without instantiating L.SVG:
1809  
1810    // @function create(name: String): SVGElement
1811    // Returns a instance of [SVGElement](https://developer.mozilla.org/docs/Web/API/SVGElement),
1812    // corresponding to the class name passed. For example, using 'line' will return
1813    // an instance of [SVGLineElement](https://developer.mozilla.org/docs/Web/API/SVGLineElement).
1814    function svgCreate(name) {
1815        return document.createElementNS('http://www.w3.org/2000/svg', name);
1816    }
1817  
1818    // @function pointsToPath(rings: Point[], closed: Boolean): String
1819    // Generates a SVG path string for multiple rings, with each ring turning
1820    // into "M..L..L.." instructions
1821    function pointsToPath(rings, closed) {
1822        var str = '',
1823        i, j, len, len2, points, p;
1824  
1825        for (i = 0, len = rings.length; i < len; i++) {
1826            points = rings[i];
1827  
1828            for (j = 0, len2 = points.length; j < len2; j++) {
1829                p = points[j];
1830                str += (j ? 'L' : 'M') + p.x + ' ' + p.y;
1831            }
1832  
1833            // closes the ring for polygons; "x" is VML syntax
1834            str += closed ? (Browser.svg ? 'z' : 'x') : '';
1835        }
1836  
1837        // SVG complains about empty path strings
1838        return str || 'M0 0';
1839    }
1840  
1841    /*
1842     * @namespace Browser
1843     * @aka L.Browser
1844     *
1845     * A namespace with static properties for browser/feature detection used by Leaflet internally.
1846     *
1847     * @example
1848     *
1849     * ```js
1850     * if (L.Browser.ielt9) {
1851     *   alert('Upgrade your browser, dude!');
1852     * }
1853     * ```
1854     */
1855  
1856    var style = document.documentElement.style;
1857  
1858    // @property ie: Boolean; `true` for all Internet Explorer versions (not Edge).
1859    var ie = 'ActiveXObject' in window;
1860  
1861    // @property ielt9: Boolean; `true` for Internet Explorer versions less than 9.
1862    var ielt9 = ie && !document.addEventListener;
1863  
1864    // @property edge: Boolean; `true` for the Edge web browser.
1865    var edge = 'msLaunchUri' in navigator && !('documentMode' in document);
1866  
1867    // @property webkit: Boolean;
1868    // `true` for webkit-based browsers like Chrome and Safari (including mobile versions).
1869    var webkit = userAgentContains('webkit');
1870  
1871    // @property android: Boolean
1872    // **Deprecated.** `true` for any browser running on an Android platform.
1873    var android = userAgentContains('android');
1874  
1875    // @property android23: Boolean; **Deprecated.** `true` for browsers running on Android 2 or Android 3.
1876    var android23 = userAgentContains('android 2') || userAgentContains('android 3');
1877  
1878    /* See https://stackoverflow.com/a/17961266 for details on detecting stock Android */
1879    var webkitVer = parseInt(/WebKit\/([0-9]+)|$/.exec(navigator.userAgent)[1], 10); // also matches AppleWebKit
1880    // @property androidStock: Boolean; **Deprecated.** `true` for the Android stock browser (i.e. not Chrome)
1881    var androidStock = android && userAgentContains('Google') && webkitVer < 537 && !('AudioNode' in window);
1882  
1883    // @property opera: Boolean; `true` for the Opera browser
1884    var opera = !!window.opera;
1885  
1886    // @property chrome: Boolean; `true` for the Chrome browser.
1887    var chrome = !edge && userAgentContains('chrome');
1888  
1889    // @property gecko: Boolean; `true` for gecko-based browsers like Firefox.
1890    var gecko = userAgentContains('gecko') && !webkit && !opera && !ie;
1891  
1892    // @property safari: Boolean; `true` for the Safari browser.
1893    var safari = !chrome && userAgentContains('safari');
1894  
1895    var phantom = userAgentContains('phantom');
1896  
1897    // @property opera12: Boolean
1898    // `true` for the Opera browser supporting CSS transforms (version 12 or later).
1899    var opera12 = 'OTransition' in style;
1900  
1901    // @property win: Boolean; `true` when the browser is running in a Windows platform
1902    var win = navigator.platform.indexOf('Win') === 0;
1903  
1904    // @property ie3d: Boolean; `true` for all Internet Explorer versions supporting CSS transforms.
1905    var ie3d = ie && ('transition' in style);
1906  
1907    // @property webkit3d: Boolean; `true` for webkit-based browsers supporting CSS transforms.
1908    var webkit3d = ('WebKitCSSMatrix' in window) && ('m11' in new window.WebKitCSSMatrix()) && !android23;
1909  
1910    // @property gecko3d: Boolean; `true` for gecko-based browsers supporting CSS transforms.
1911    var gecko3d = 'MozPerspective' in style;
1912  
1913    // @property any3d: Boolean
1914    // `true` for all browsers supporting CSS transforms.
1915    var any3d = !window.L_DISABLE_3D && (ie3d || webkit3d || gecko3d) && !opera12 && !phantom;
1916  
1917    // @property mobile: Boolean; `true` for all browsers running in a mobile device.
1918    var mobile = typeof orientation !== 'undefined' || userAgentContains('mobile');
1919  
1920    // @property mobileWebkit: Boolean; `true` for all webkit-based browsers in a mobile device.
1921    var mobileWebkit = mobile && webkit;
1922  
1923    // @property mobileWebkit3d: Boolean
1924    // `true` for all webkit-based browsers in a mobile device supporting CSS transforms.
1925    var mobileWebkit3d = mobile && webkit3d;
1926  
1927    // @property msPointer: Boolean
1928    // `true` for browsers implementing the Microsoft touch events model (notably IE10).
1929    var msPointer = !window.PointerEvent && window.MSPointerEvent;
1930  
1931    // @property pointer: Boolean
1932    // `true` for all browsers supporting [pointer events](https://msdn.microsoft.com/en-us/library/dn433244%28v=vs.85%29.aspx).
1933    var pointer = !!(window.PointerEvent || msPointer);
1934  
1935    // @property touchNative: Boolean
1936    // `true` for all browsers supporting [touch events](https://developer.mozilla.org/docs/Web/API/Touch_events).
1937    // **This does not necessarily mean** that the browser is running in a computer with
1938    // a touchscreen, it only means that the browser is capable of understanding
1939    // touch events.
1940    var touchNative = 'ontouchstart' in window || !!window.TouchEvent;
1941  
1942    // @property touch: Boolean
1943    // `true` for all browsers supporting either [touch](#browser-touch) or [pointer](#browser-pointer) events.
1944    // Note: pointer events will be preferred (if available), and processed for all `touch*` listeners.
1945    var touch = !window.L_NO_TOUCH && (touchNative || pointer);
1946  
1947    // @property mobileOpera: Boolean; `true` for the Opera browser in a mobile device.
1948    var mobileOpera = mobile && opera;
1949  
1950    // @property mobileGecko: Boolean
1951    // `true` for gecko-based browsers running in a mobile device.
1952    var mobileGecko = mobile && gecko;
1953  
1954    // @property retina: Boolean
1955    // `true` for browsers on a high-resolution "retina" screen or on any screen when browser's display zoom is more than 100%.
1956    var retina = (window.devicePixelRatio || (window.screen.deviceXDPI / window.screen.logicalXDPI)) > 1;
1957  
1958    // @property passiveEvents: Boolean
1959    // `true` for browsers that support passive events.
1960    var passiveEvents = (function () {
1961        var supportsPassiveOption = false;
1962        try {
1963            var opts = Object.defineProperty({}, 'passive', {
1964                get: function () { // eslint-disable-line getter-return
1965                    supportsPassiveOption = true;
1966                }
1967            });
1968            window.addEventListener('testPassiveEventSupport', falseFn, opts);
1969            window.removeEventListener('testPassiveEventSupport', falseFn, opts);
1970        } catch (e) {
1971            // Errors can safely be ignored since this is only a browser support test.
1972        }
1973        return supportsPassiveOption;
1974    }());
1975  
1976    // @property canvas: Boolean
1977    // `true` when the browser supports [`<canvas>`](https://developer.mozilla.org/docs/Web/API/Canvas_API).
1978    var canvas$1 = (function () {
1979        return !!document.createElement('canvas').getContext;
1980    }());
1981  
1982    // @property svg: Boolean
1983    // `true` when the browser supports [SVG](https://developer.mozilla.org/docs/Web/SVG).
1984    var svg$1 = !!(document.createElementNS && svgCreate('svg').createSVGRect);
1985  
1986    // @property vml: Boolean
1987    // `true` if the browser supports [VML](https://en.wikipedia.org/wiki/Vector_Markup_Language).
1988    var vml = !svg$1 && (function () {
1989        try {
1990            var div = document.createElement('div');
1991            div.innerHTML = '<v:shape adj="1"/>';
1992  
1993            var shape = div.firstChild;
1994            shape.style.behavior = 'url(#default#VML)';
1995  
1996            return shape && (typeof shape.adj === 'object');
1997  
1998        } catch (e) {
1999            return false;
2000        }
2001    }());
2002  
2003  
2004    function userAgentContains(str) {
2005        return navigator.userAgent.toLowerCase().indexOf(str) >= 0;
2006    }
2007  
2008  
2009    var Browser = {
2010        ie: ie,
2011        ielt9: ielt9,
2012        edge: edge,
2013        webkit: webkit,
2014        android: android,
2015        android23: android23,
2016        androidStock: androidStock,
2017        opera: opera,
2018        chrome: chrome,
2019        gecko: gecko,
2020        safari: safari,
2021        phantom: phantom,
2022        opera12: opera12,
2023        win: win,
2024        ie3d: ie3d,
2025        webkit3d: webkit3d,
2026        gecko3d: gecko3d,
2027        any3d: any3d,
2028        mobile: mobile,
2029        mobileWebkit: mobileWebkit,
2030        mobileWebkit3d: mobileWebkit3d,
2031        msPointer: msPointer,
2032        pointer: pointer,
2033        touch: touch,
2034        touchNative: touchNative,
2035        mobileOpera: mobileOpera,
2036        mobileGecko: mobileGecko,
2037        retina: retina,
2038        passiveEvents: passiveEvents,
2039        canvas: canvas$1,
2040        svg: svg$1,
2041        vml: vml,
2042    };
2043  
2044    /*
2045     * Extends L.DomEvent to provide touch support for Internet Explorer and Windows-based devices.
2046     */
2047  
2048    var POINTER_DOWN =   Browser.msPointer ? 'MSPointerDown'   : 'pointerdown';
2049    var POINTER_MOVE =   Browser.msPointer ? 'MSPointerMove'   : 'pointermove';
2050    var POINTER_UP =     Browser.msPointer ? 'MSPointerUp'     : 'pointerup';
2051    var POINTER_CANCEL = Browser.msPointer ? 'MSPointerCancel' : 'pointercancel';
2052    var pEvent = {
2053        touchstart  : POINTER_DOWN,
2054        touchmove   : POINTER_MOVE,
2055        touchend    : POINTER_UP,
2056        touchcancel : POINTER_CANCEL
2057    };
2058    var handle = {
2059        touchstart  : _onPointerStart,
2060        touchmove   : _handlePointer,
2061        touchend    : _handlePointer,
2062        touchcancel : _handlePointer
2063    };
2064    var _pointers = {};
2065    var _pointerDocListener = false;
2066  
2067    // Provides a touch events wrapper for (ms)pointer events.
2068    // ref https://www.w3.org/TR/pointerevents/ https://www.w3.org/Bugs/Public/show_bug.cgi?id=22890
2069  
2070    function addPointerListener(obj, type, handler) {
2071        if (type === 'touchstart') {
2072            _addPointerDocListener();
2073        }
2074        if (!handle[type]) {
2075            console.warn('wrong event specified:', type);
2076            return L.Util.falseFn;
2077        }
2078        handler = handle[type].bind(this, handler);
2079        obj.addEventListener(pEvent[type], handler, false);
2080        return handler;
2081    }
2082  
2083    function removePointerListener(obj, type, handler) {
2084        if (!pEvent[type]) {
2085            console.warn('wrong event specified:', type);
2086            return;
2087        }
2088        obj.removeEventListener(pEvent[type], handler, false);
2089    }
2090  
2091    function _globalPointerDown(e) {
2092        _pointers[e.pointerId] = e;
2093    }
2094  
2095    function _globalPointerMove(e) {
2096        if (_pointers[e.pointerId]) {
2097            _pointers[e.pointerId] = e;
2098        }
2099    }
2100  
2101    function _globalPointerUp(e) {
2102        delete _pointers[e.pointerId];
2103    }
2104  
2105    function _addPointerDocListener() {
2106        // need to keep track of what pointers and how many are active to provide e.touches emulation
2107        if (!_pointerDocListener) {
2108            // we listen document as any drags that end by moving the touch off the screen get fired there
2109            document.addEventListener(POINTER_DOWN, _globalPointerDown, true);
2110            document.addEventListener(POINTER_MOVE, _globalPointerMove, true);
2111            document.addEventListener(POINTER_UP, _globalPointerUp, true);
2112            document.addEventListener(POINTER_CANCEL, _globalPointerUp, true);
2113  
2114            _pointerDocListener = true;
2115        }
2116    }
2117  
2118    function _handlePointer(handler, e) {
2119        if (e.pointerType === (e.MSPOINTER_TYPE_MOUSE || 'mouse')) { return; }
2120  
2121        e.touches = [];
2122        for (var i in _pointers) {
2123            e.touches.push(_pointers[i]);
2124        }
2125        e.changedTouches = [e];
2126  
2127        handler(e);
2128    }
2129  
2130    function _onPointerStart(handler, e) {
2131        // IE10 specific: MsTouch needs preventDefault. See #2000
2132        if (e.MSPOINTER_TYPE_TOUCH && e.pointerType === e.MSPOINTER_TYPE_TOUCH) {
2133            preventDefault(e);
2134        }
2135        _handlePointer(handler, e);
2136    }
2137  
2138    /*
2139     * Extends the event handling code with double tap support for mobile browsers.
2140     *
2141     * Note: currently most browsers fire native dblclick, with only a few exceptions
2142     * (see https://github.com/Leaflet/Leaflet/issues/7012#issuecomment-595087386)
2143     */
2144  
2145    function makeDblclick(event) {
2146        // in modern browsers `type` cannot be just overridden:
2147        // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Errors/Getter_only
2148        var newEvent = {},
2149            prop, i;
2150        for (i in event) {
2151            prop = event[i];
2152            newEvent[i] = prop && prop.bind ? prop.bind(event) : prop;
2153        }
2154        event = newEvent;
2155        newEvent.type = 'dblclick';
2156        newEvent.detail = 2;
2157        newEvent.isTrusted = false;
2158        newEvent._simulated = true; // for debug purposes
2159        return newEvent;
2160    }
2161  
2162    var delay = 200;
2163    function addDoubleTapListener(obj, handler) {
2164        // Most browsers handle double tap natively
2165        obj.addEventListener('dblclick', handler);
2166  
2167        // On some platforms the browser doesn't fire native dblclicks for touch events.
2168        // It seems that in all such cases `detail` property of `click` event is always `1`.
2169        // So here we rely on that fact to avoid excessive 'dblclick' simulation when not needed.
2170        var last = 0,
2171            detail;
2172        function simDblclick(e) {
2173            if (e.detail !== 1) {
2174                detail = e.detail; // keep in sync to avoid false dblclick in some cases
2175                return;
2176            }
2177  
2178            if (e.pointerType === 'mouse' ||
2179                (e.sourceCapabilities && !e.sourceCapabilities.firesTouchEvents)) {
2180  
2181                return;
2182            }
2183  
2184            var now = Date.now();
2185            if (now - last <= delay) {
2186                detail++;
2187                if (detail === 2) {
2188                    handler(makeDblclick(e));
2189                }
2190            } else {
2191                detail = 1;
2192            }
2193            last = now;
2194        }
2195  
2196        obj.addEventListener('click', simDblclick);
2197  
2198        return {
2199            dblclick: handler,
2200            simDblclick: simDblclick
2201        };
2202    }
2203  
2204    function removeDoubleTapListener(obj, handlers) {
2205        obj.removeEventListener('dblclick', handlers.dblclick);
2206        obj.removeEventListener('click', handlers.simDblclick);
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 && 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 && 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        // Check if the element is an SVGElementInstance and use the correspondingElement instead
2356        // (Required for linked SVG elements in IE11.)
2357        if (el.correspondingElement) {
2358            el = el.correspondingElement;
2359        }
2360        return el.className.baseVal === undefined ? el.className : el.className.baseVal;
2361    }
2362  
2363    // @function setOpacity(el: HTMLElement, opacity: Number)
2364    // Set the opacity of an element (including old IE support).
2365    // `opacity` must be a number from `0` to `1`.
2366    function setOpacity(el, value) {
2367        if ('opacity' in el.style) {
2368            el.style.opacity = value;
2369        } else if ('filter' in el.style) {
2370            _setOpacityIE(el, value);
2371        }
2372    }
2373  
2374    function _setOpacityIE(el, value) {
2375        var filter = false,
2376            filterName = 'DXImageTransform.Microsoft.Alpha';
2377  
2378        // filters collection throws an error if we try to retrieve a filter that doesn't exist
2379        try {
2380            filter = el.filters.item(filterName);
2381        } catch (e) {
2382            // don't set opacity to 1 if we haven't already set an opacity,
2383            // it isn't needed and breaks transparent pngs.
2384            if (value === 1) { return; }
2385        }
2386  
2387        value = Math.round(value * 100);
2388  
2389        if (filter) {
2390            filter.Enabled = (value !== 100);
2391            filter.Opacity = value;
2392        } else {
2393            el.style.filter += ' progid:' + filterName + '(opacity=' + value + ')';
2394        }
2395    }
2396  
2397    // @function testProp(props: String[]): String|false
2398    // Goes through the array of style names and returns the first name
2399    // that is a valid style name for an element. If no such name is found,
2400    // it returns false. Useful for vendor-prefixed styles like `transform`.
2401    function testProp(props) {
2402        var style = document.documentElement.style;
2403  
2404        for (var i = 0; i < props.length; i++) {
2405            if (props[i] in style) {
2406                return props[i];
2407            }
2408        }
2409        return false;
2410    }
2411  
2412    // @function setTransform(el: HTMLElement, offset: Point, scale?: Number)
2413    // Resets the 3D CSS transform of `el` so it is translated by `offset` pixels
2414    // and optionally scaled by `scale`. Does not have an effect if the
2415    // browser doesn't support 3D CSS transforms.
2416    function setTransform(el, offset, scale) {
2417        var pos = offset || new Point(0, 0);
2418  
2419        el.style[TRANSFORM] =
2420            (Browser.ie3d ?
2421                'translate(' + pos.x + 'px,' + pos.y + 'px)' :
2422                'translate3d(' + pos.x + 'px,' + pos.y + 'px,0)') +
2423            (scale ? ' scale(' + scale + ')' : '');
2424    }
2425  
2426    // @function setPosition(el: HTMLElement, position: Point)
2427    // Sets the position of `el` to coordinates specified by `position`,
2428    // using CSS translate or top/left positioning depending on the browser
2429    // (used by Leaflet internally to position its layers).
2430    function setPosition(el, point) {
2431  
2432        /*eslint-disable */
2433        el._leaflet_pos = point;
2434        /* eslint-enable */
2435  
2436        if (Browser.any3d) {
2437            setTransform(el, point);
2438        } else {
2439            el.style.left = point.x + 'px';
2440            el.style.top = point.y + 'px';
2441        }
2442    }
2443  
2444    // @function getPosition(el: HTMLElement): Point
2445    // Returns the coordinates of an element previously positioned with setPosition.
2446    function getPosition(el) {
2447        // this method is only used for elements previously positioned using setPosition,
2448        // so it's safe to cache the position for performance
2449  
2450        return el._leaflet_pos || new Point(0, 0);
2451    }
2452  
2453    // @function disableTextSelection()
2454    // Prevents the user from generating `selectstart` DOM events, usually generated
2455    // when the user drags the mouse through a page with text. Used internally
2456    // by Leaflet to override the behaviour of any click-and-drag interaction on
2457    // the map. Affects drag interactions on the whole document.
2458  
2459    // @function enableTextSelection()
2460    // Cancels the effects of a previous [`L.DomUtil.disableTextSelection`](#domutil-disabletextselection).
2461    var disableTextSelection;
2462    var enableTextSelection;
2463    var _userSelect;
2464    if ('onselectstart' in document) {
2465        disableTextSelection = function () {
2466            on(window, 'selectstart', preventDefault);
2467        };
2468        enableTextSelection = function () {
2469            off(window, 'selectstart', preventDefault);
2470        };
2471    } else {
2472        var userSelectProperty = testProp(
2473            ['userSelect', 'WebkitUserSelect', 'OUserSelect', 'MozUserSelect', 'msUserSelect']);
2474  
2475        disableTextSelection = function () {
2476            if (userSelectProperty) {
2477                var style = document.documentElement.style;
2478                _userSelect = style[userSelectProperty];
2479                style[userSelectProperty] = 'none';
2480            }
2481        };
2482        enableTextSelection = function () {
2483            if (userSelectProperty) {
2484                document.documentElement.style[userSelectProperty] = _userSelect;
2485                _userSelect = undefined;
2486            }
2487        };
2488    }
2489  
2490    // @function disableImageDrag()
2491    // As [`L.DomUtil.disableTextSelection`](#domutil-disabletextselection), but
2492    // for `dragstart` DOM events, usually generated when the user drags an image.
2493    function disableImageDrag() {
2494        on(window, 'dragstart', preventDefault);
2495    }
2496  
2497    // @function enableImageDrag()
2498    // Cancels the effects of a previous [`L.DomUtil.disableImageDrag`](#domutil-disabletextselection).
2499    function enableImageDrag() {
2500        off(window, 'dragstart', preventDefault);
2501    }
2502  
2503    var _outlineElement, _outlineStyle;
2504    // @function preventOutline(el: HTMLElement)
2505    // Makes the [outline](https://developer.mozilla.org/docs/Web/CSS/outline)
2506    // of the element `el` invisible. Used internally by Leaflet to prevent
2507    // focusable elements from displaying an outline when the user performs a
2508    // drag interaction on them.
2509    function preventOutline(element) {
2510        while (element.tabIndex === -1) {
2511            element = element.parentNode;
2512        }
2513        if (!element.style) { return; }
2514        restoreOutline();
2515        _outlineElement = element;
2516        _outlineStyle = element.style.outline;
2517        element.style.outline = 'none';
2518        on(window, 'keydown', restoreOutline);
2519    }
2520  
2521    // @function restoreOutline()
2522    // Cancels the effects of a previous [`L.DomUtil.preventOutline`]().
2523    function restoreOutline() {
2524        if (!_outlineElement) { return; }
2525        _outlineElement.style.outline = _outlineStyle;
2526        _outlineElement = undefined;
2527        _outlineStyle = undefined;
2528        off(window, 'keydown', restoreOutline);
2529    }
2530  
2531    // @function getSizedParentNode(el: HTMLElement): HTMLElement
2532    // Finds the closest parent node which size (width and height) is not null.
2533    function getSizedParentNode(element) {
2534        do {
2535            element = element.parentNode;
2536        } while ((!element.offsetWidth || !element.offsetHeight) && element !== document.body);
2537        return element;
2538    }
2539  
2540    // @function getScale(el: HTMLElement): Object
2541    // Computes the CSS scale currently applied on the element.
2542    // Returns an object with `x` and `y` members as horizontal and vertical scales respectively,
2543    // and `boundingClientRect` as the result of [`getBoundingClientRect()`](https://developer.mozilla.org/en-US/docs/Web/API/Element/getBoundingClientRect).
2544    function getScale(element) {
2545        var rect = element.getBoundingClientRect(); // Read-only in old browsers.
2546  
2547        return {
2548            x: rect.width / element.offsetWidth || 1,
2549            y: rect.height / element.offsetHeight || 1,
2550            boundingClientRect: rect
2551        };
2552    }
2553  
2554    var DomUtil = {
2555      __proto__: null,
2556      TRANSFORM: TRANSFORM,
2557      TRANSITION: TRANSITION,
2558      TRANSITION_END: TRANSITION_END,
2559      get: get,
2560      getStyle: getStyle,
2561      create: create$1,
2562      remove: remove,
2563      empty: empty,
2564      toFront: toFront,
2565      toBack: toBack,
2566      hasClass: hasClass,
2567      addClass: addClass,
2568      removeClass: removeClass,
2569      setClass: setClass,
2570      getClass: getClass,
2571      setOpacity: setOpacity,
2572      testProp: testProp,
2573      setTransform: setTransform,
2574      setPosition: setPosition,
2575      getPosition: getPosition,
2576      get disableTextSelection () { return disableTextSelection; },
2577      get enableTextSelection () { return enableTextSelection; },
2578      disableImageDrag: disableImageDrag,
2579      enableImageDrag: enableImageDrag,
2580      preventOutline: preventOutline,
2581      restoreOutline: restoreOutline,
2582      getSizedParentNode: getSizedParentNode,
2583      getScale: getScale
2584    };
2585  
2586    /*
2587     * @namespace DomEvent
2588     * Utility functions to work with the [DOM events](https://developer.mozilla.org/docs/Web/API/Event), used by Leaflet internally.
2589     */
2590  
2591    // Inspired by John Resig, Dean Edwards and YUI addEvent implementations.
2592  
2593    // @function on(el: HTMLElement, types: String, fn: Function, context?: Object): this
2594    // Adds a listener function (`fn`) to a particular DOM event type of the
2595    // element `el`. You can optionally specify the context of the listener
2596    // (object the `this` keyword will point to). You can also pass several
2597    // space-separated types (e.g. `'click dblclick'`).
2598  
2599    // @alternative
2600    // @function on(el: HTMLElement, eventMap: Object, context?: Object): this
2601    // Adds a set of type/listener pairs, e.g. `{click: onClick, mousemove: onMouseMove}`
2602    function on(obj, types, fn, context) {
2603  
2604        if (types && typeof types === 'object') {
2605            for (var type in types) {
2606                addOne(obj, type, types[type], fn);
2607            }
2608        } else {
2609            types = splitWords(types);
2610  
2611            for (var i = 0, len = types.length; i < len; i++) {
2612                addOne(obj, types[i], fn, context);
2613            }
2614        }
2615  
2616        return this;
2617    }
2618  
2619    var eventsKey = '_leaflet_events';
2620  
2621    // @function off(el: HTMLElement, types: String, fn: Function, context?: Object): this
2622    // Removes a previously added listener function.
2623    // Note that if you passed a custom context to on, you must pass the same
2624    // context to `off` in order to remove the listener.
2625  
2626    // @alternative
2627    // @function off(el: HTMLElement, eventMap: Object, context?: Object): this
2628    // Removes a set of type/listener pairs, e.g. `{click: onClick, mousemove: onMouseMove}`
2629  
2630    // @alternative
2631    // @function off(el: HTMLElement, types: String): this
2632    // Removes all previously added listeners of given types.
2633  
2634    // @alternative
2635    // @function off(el: HTMLElement): this
2636    // Removes all previously added listeners from given HTMLElement
2637    function off(obj, types, fn, context) {
2638  
2639        if (arguments.length === 1) {
2640            batchRemove(obj);
2641            delete obj[eventsKey];
2642  
2643        } else if (types && typeof types === 'object') {
2644            for (var type in types) {
2645                removeOne(obj, type, types[type], fn);
2646            }
2647  
2648        } else {
2649            types = splitWords(types);
2650  
2651            if (arguments.length === 2) {
2652                batchRemove(obj, function (type) {
2653                    return indexOf(types, type) !== -1;
2654                });
2655            } else {
2656                for (var i = 0, len = types.length; i < len; i++) {
2657                    removeOne(obj, types[i], fn, context);
2658                }
2659            }
2660        }
2661  
2662        return this;
2663    }
2664  
2665    function batchRemove(obj, filterFn) {
2666        for (var id in obj[eventsKey]) {
2667            var type = id.split(/\d/)[0];
2668            if (!filterFn || filterFn(type)) {
2669                removeOne(obj, type, null, null, id);
2670            }
2671        }
2672    }
2673  
2674    var mouseSubst = {
2675        mouseenter: 'mouseover',
2676        mouseleave: 'mouseout',
2677        wheel: !('onwheel' in window) && 'mousewheel'
2678    };
2679  
2680    function addOne(obj, type, fn, context) {
2681        var id = type + stamp(fn) + (context ? '_' + stamp(context) : '');
2682  
2683        if (obj[eventsKey] && obj[eventsKey][id]) { return this; }
2684  
2685        var handler = function (e) {
2686            return fn.call(context || obj, e || window.event);
2687        };
2688  
2689        var originalHandler = handler;
2690  
2691        if (!Browser.touchNative && Browser.pointer && type.indexOf('touch') === 0) {
2692            // Needs DomEvent.Pointer.js
2693            handler = addPointerListener(obj, type, handler);
2694  
2695        } else if (Browser.touch && (type === 'dblclick')) {
2696            handler = addDoubleTapListener(obj, handler);
2697  
2698        } else if ('addEventListener' in obj) {
2699  
2700            if (type === 'touchstart' || type === 'touchmove' || type === 'wheel' ||  type === 'mousewheel') {
2701                obj.addEventListener(mouseSubst[type] || type, handler, Browser.passiveEvents ? {passive: false} : false);
2702  
2703            } else if (type === 'mouseenter' || type === 'mouseleave') {
2704                handler = function (e) {
2705                    e = e || window.event;
2706                    if (isExternalTarget(obj, e)) {
2707                        originalHandler(e);
2708                    }
2709                };
2710                obj.addEventListener(mouseSubst[type], handler, false);
2711  
2712            } else {
2713                obj.addEventListener(type, originalHandler, false);
2714            }
2715  
2716        } else {
2717            obj.attachEvent('on' + type, handler);
2718        }
2719  
2720        obj[eventsKey] = obj[eventsKey] || {};
2721        obj[eventsKey][id] = handler;
2722    }
2723  
2724    function removeOne(obj, type, fn, context, id) {
2725        id = id || type + stamp(fn) + (context ? '_' + stamp(context) : '');
2726        var handler = obj[eventsKey] && obj[eventsKey][id];
2727  
2728        if (!handler) { return this; }
2729  
2730        if (!Browser.touchNative && Browser.pointer && type.indexOf('touch') === 0) {
2731            removePointerListener(obj, type, handler);
2732  
2733        } else if (Browser.touch && (type === 'dblclick')) {
2734            removeDoubleTapListener(obj, handler);
2735  
2736        } else if ('removeEventListener' in obj) {
2737  
2738            obj.removeEventListener(mouseSubst[type] || type, handler, false);
2739  
2740        } else {
2741            obj.detachEvent('on' + type, handler);
2742        }
2743  
2744        obj[eventsKey][id] = null;
2745    }
2746  
2747    // @function stopPropagation(ev: DOMEvent): this
2748    // Stop the given event from propagation to parent elements. Used inside the listener functions:
2749    // ```js
2750    // L.DomEvent.on(div, 'click', function (ev) {
2751    // 	L.DomEvent.stopPropagation(ev);
2752    // });
2753    // ```
2754    function stopPropagation(e) {
2755  
2756        if (e.stopPropagation) {
2757            e.stopPropagation();
2758        } else if (e.originalEvent) {  // In case of Leaflet event.
2759            e.originalEvent._stopped = true;
2760        } else {
2761            e.cancelBubble = true;
2762        }
2763  
2764        return this;
2765    }
2766  
2767    // @function disableScrollPropagation(el: HTMLElement): this
2768    // Adds `stopPropagation` to the element's `'wheel'` events (plus browser variants).
2769    function disableScrollPropagation(el) {
2770        addOne(el, 'wheel', stopPropagation);
2771        return this;
2772    }
2773  
2774    // @function disableClickPropagation(el: HTMLElement): this
2775    // Adds `stopPropagation` to the element's `'click'`, `'dblclick'`, `'contextmenu'`,
2776    // `'mousedown'` and `'touchstart'` events (plus browser variants).
2777    function disableClickPropagation(el) {
2778        on(el, 'mousedown touchstart dblclick contextmenu', stopPropagation);
2779        el['_leaflet_disable_click'] = true;
2780        return this;
2781    }
2782  
2783    // @function preventDefault(ev: DOMEvent): this
2784    // Prevents the default action of the DOM Event `ev` from happening (such as
2785    // following a link in the href of the a element, or doing a POST request
2786    // with page reload when a `<form>` is submitted).
2787    // Use it inside listener functions.
2788    function preventDefault(e) {
2789        if (e.preventDefault) {
2790            e.preventDefault();
2791        } else {
2792            e.returnValue = false;
2793        }
2794        return this;
2795    }
2796  
2797    // @function stop(ev: DOMEvent): this
2798    // Does `stopPropagation` and `preventDefault` at the same time.
2799    function stop(e) {
2800        preventDefault(e);
2801        stopPropagation(e);
2802        return this;
2803    }
2804  
2805    // @function getMousePosition(ev: DOMEvent, container?: HTMLElement): Point
2806    // Gets normalized mouse position from a DOM event relative to the
2807    // `container` (border excluded) or to the whole page if not specified.
2808    function getMousePosition(e, container) {
2809        if (!container) {
2810            return new Point(e.clientX, e.clientY);
2811        }
2812  
2813        var scale = getScale(container),
2814            offset = scale.boundingClientRect; // left and top  values are in page scale (like the event clientX/Y)
2815  
2816        return new Point(
2817            // offset.left/top values are in page scale (like clientX/Y),
2818            // whereas clientLeft/Top (border width) values are the original values (before CSS scale applies).
2819            (e.clientX - offset.left) / scale.x - container.clientLeft,
2820            (e.clientY - offset.top) / scale.y - container.clientTop
2821        );
2822    }
2823  
2824    // Chrome on Win scrolls double the pixels as in other platforms (see #4538),
2825    // and Firefox scrolls device pixels, not CSS pixels
2826    var wheelPxFactor =
2827        (Browser.win && Browser.chrome) ? 2 * window.devicePixelRatio :
2828        Browser.gecko ? window.devicePixelRatio : 1;
2829  
2830    // @function getWheelDelta(ev: DOMEvent): Number
2831    // Gets normalized wheel delta from a wheel DOM event, in vertical
2832    // pixels scrolled (negative if scrolling down).
2833    // Events from pointing devices without precise scrolling are mapped to
2834    // a best guess of 60 pixels.
2835    function getWheelDelta(e) {
2836        return (Browser.edge) ? e.wheelDeltaY / 2 : // Don't trust window-geometry-based delta
2837               (e.deltaY && e.deltaMode === 0) ? -e.deltaY / wheelPxFactor : // Pixels
2838               (e.deltaY && e.deltaMode === 1) ? -e.deltaY * 20 : // Lines
2839               (e.deltaY && e.deltaMode === 2) ? -e.deltaY * 60 : // Pages
2840               (e.deltaX || e.deltaZ) ? 0 :	// Skip horizontal/depth wheel events
2841               e.wheelDelta ? (e.wheelDeltaY || e.wheelDelta) / 2 : // Legacy IE pixels
2842               (e.detail && Math.abs(e.detail) < 32765) ? -e.detail * 20 : // Legacy Moz lines
2843               e.detail ? e.detail / -32765 * 60 : // Legacy Moz pages
2844               0;
2845    }
2846  
2847    // check if element really left/entered the event target (for mouseenter/mouseleave)
2848    function isExternalTarget(el, e) {
2849  
2850        var related = e.relatedTarget;
2851  
2852        if (!related) { return true; }
2853  
2854        try {
2855            while (related && (related !== el)) {
2856                related = related.parentNode;
2857            }
2858        } catch (err) {
2859            return false;
2860        }
2861        return (related !== el);
2862    }
2863  
2864    var DomEvent = {
2865      __proto__: null,
2866      on: on,
2867      off: off,
2868      stopPropagation: stopPropagation,
2869      disableScrollPropagation: disableScrollPropagation,
2870      disableClickPropagation: disableClickPropagation,
2871      preventDefault: preventDefault,
2872      stop: stop,
2873      getMousePosition: getMousePosition,
2874      getWheelDelta: getWheelDelta,
2875      isExternalTarget: isExternalTarget,
2876      addListener: on,
2877      removeListener: off
2878    };
2879  
2880    /*
2881     * @class PosAnimation
2882     * @aka L.PosAnimation
2883     * @inherits Evented
2884     * Used internally for panning animations, utilizing CSS3 Transitions for modern browsers and a timer fallback for IE6-9.
2885     *
2886     * @example
2887     * ```js
2888     * var fx = new L.PosAnimation();
2889     * fx.run(el, [300, 500], 0.5);
2890     * ```
2891     *
2892     * @constructor L.PosAnimation()
2893     * Creates a `PosAnimation` object.
2894     *
2895     */
2896  
2897    var PosAnimation = Evented.extend({
2898  
2899        // @method run(el: HTMLElement, newPos: Point, duration?: Number, easeLinearity?: Number)
2900        // Run an animation of a given element to a new position, optionally setting
2901        // duration in seconds (`0.25` by default) and easing linearity factor (3rd
2902        // argument of the [cubic bezier curve](https://cubic-bezier.com/#0,0,.5,1),
2903        // `0.5` by default).
2904        run: function (el, newPos, duration, easeLinearity) {
2905            this.stop();
2906  
2907            this._el = el;
2908            this._inProgress = true;
2909            this._duration = duration || 0.25;
2910            this._easeOutPower = 1 / Math.max(easeLinearity || 0.5, 0.2);
2911  
2912            this._startPos = getPosition(el);
2913            this._offset = newPos.subtract(this._startPos);
2914            this._startTime = +new Date();
2915  
2916            // @event start: Event
2917            // Fired when the animation starts
2918            this.fire('start');
2919  
2920            this._animate();
2921        },
2922  
2923        // @method stop()
2924        // Stops the animation (if currently running).
2925        stop: function () {
2926            if (!this._inProgress) { return; }
2927  
2928            this._step(true);
2929            this._complete();
2930        },
2931  
2932        _animate: function () {
2933            // animation loop
2934            this._animId = requestAnimFrame(this._animate, this);
2935            this._step();
2936        },
2937  
2938        _step: function (round) {
2939            var elapsed = (+new Date()) - this._startTime,
2940                duration = this._duration * 1000;
2941  
2942            if (elapsed < duration) {
2943                this._runFrame(this._easeOut(elapsed / duration), round);
2944            } else {
2945                this._runFrame(1);
2946                this._complete();
2947            }
2948        },
2949  
2950        _runFrame: function (progress, round) {
2951            var pos = this._startPos.add(this._offset.multiplyBy(progress));
2952            if (round) {
2953                pos._round();
2954            }
2955            setPosition(this._el, pos);
2956  
2957            // @event step: Event
2958            // Fired continuously during the animation.
2959            this.fire('step');
2960        },
2961  
2962        _complete: function () {
2963            cancelAnimFrame(this._animId);
2964  
2965            this._inProgress = false;
2966            // @event end: Event
2967            // Fired when the animation ends.
2968            this.fire('end');
2969        },
2970  
2971        _easeOut: function (t) {
2972            return 1 - Math.pow(1 - t, this._easeOutPower);
2973        }
2974    });
2975  
2976    /*
2977     * @class Map
2978     * @aka L.Map
2979     * @inherits Evented
2980     *
2981     * The central class of the API — it is used to create a map on a page and manipulate it.
2982     *
2983     * @example
2984     *
2985     * ```js
2986     * // initialize the map on the "map" div with a given center and zoom
2987     * var map = L.map('map', {
2988     * 	center: [51.505, -0.09],
2989     * 	zoom: 13
2990     * });
2991     * ```
2992     *
2993     */
2994  
2995    var Map = Evented.extend({
2996  
2997        options: {
2998            // @section Map State Options
2999            // @option crs: CRS = L.CRS.EPSG3857
3000            // The [Coordinate Reference System](#crs) to use. Don't change this if you're not
3001            // sure what it means.
3002            crs: EPSG3857,
3003  
3004            // @option center: LatLng = undefined
3005            // Initial geographic center of the map
3006            center: undefined,
3007  
3008            // @option zoom: Number = undefined
3009            // Initial map zoom level
3010            zoom: undefined,
3011  
3012            // @option minZoom: Number = *
3013            // Minimum zoom level of the map.
3014            // If not specified and at least one `GridLayer` or `TileLayer` is in the map,
3015            // the lowest of their `minZoom` options will be used instead.
3016            minZoom: undefined,
3017  
3018            // @option maxZoom: Number = *
3019            // Maximum zoom level of the map.
3020            // If not specified and at least one `GridLayer` or `TileLayer` is in the map,
3021            // the highest of their `maxZoom` options will be used instead.
3022            maxZoom: undefined,
3023  
3024            // @option layers: Layer[] = []
3025            // Array of layers that will be added to the map initially
3026            layers: [],
3027  
3028            // @option maxBounds: LatLngBounds = null
3029            // When this option is set, the map restricts the view to the given
3030            // geographical bounds, bouncing the user back if the user tries to pan
3031            // outside the view. To set the restriction dynamically, use
3032            // [`setMaxBounds`](#map-setmaxbounds) method.
3033            maxBounds: undefined,
3034  
3035            // @option renderer: Renderer = *
3036            // The default method for drawing vector layers on the map. `L.SVG`
3037            // or `L.Canvas` by default depending on browser support.
3038            renderer: undefined,
3039  
3040  
3041            // @section Animation Options
3042            // @option zoomAnimation: Boolean = true
3043            // Whether the map zoom animation is enabled. By default it's enabled
3044            // in all browsers that support CSS3 Transitions except Android.
3045            zoomAnimation: true,
3046  
3047            // @option zoomAnimationThreshold: Number = 4
3048            // Won't animate zoom if the zoom difference exceeds this value.
3049            zoomAnimationThreshold: 4,
3050  
3051            // @option fadeAnimation: Boolean = true
3052            // Whether the tile fade animation is enabled. By default it's enabled
3053            // in all browsers that support CSS3 Transitions except Android.
3054            fadeAnimation: true,
3055  
3056            // @option markerZoomAnimation: Boolean = true
3057            // Whether markers animate their zoom with the zoom animation, if disabled
3058            // they will disappear for the length of the animation. By default it's
3059            // enabled in all browsers that support CSS3 Transitions except Android.
3060            markerZoomAnimation: true,
3061  
3062            // @option transform3DLimit: Number = 2^23
3063            // Defines the maximum size of a CSS translation transform. The default
3064            // value should not be changed unless a web browser positions layers in
3065            // the wrong place after doing a large `panBy`.
3066            transform3DLimit: 8388608, // Precision limit of a 32-bit float
3067  
3068            // @section Interaction Options
3069            // @option zoomSnap: Number = 1
3070            // Forces the map's zoom level to always be a multiple of this, particularly
3071            // right after a [`fitBounds()`](#map-fitbounds) or a pinch-zoom.
3072            // By default, the zoom level snaps to the nearest integer; lower values
3073            // (e.g. `0.5` or `0.1`) allow for greater granularity. A value of `0`
3074            // means the zoom level will not be snapped after `fitBounds` or a pinch-zoom.
3075            zoomSnap: 1,
3076  
3077            // @option zoomDelta: Number = 1
3078            // Controls how much the map's zoom level will change after a
3079            // [`zoomIn()`](#map-zoomin), [`zoomOut()`](#map-zoomout), pressing `+`
3080            // or `-` on the keyboard, or using the [zoom controls](#control-zoom).
3081            // Values smaller than `1` (e.g. `0.5`) allow for greater granularity.
3082            zoomDelta: 1,
3083  
3084            // @option trackResize: Boolean = true
3085            // Whether the map automatically handles browser window resize to update itself.
3086            trackResize: true
3087        },
3088  
3089        initialize: function (id, options) { // (HTMLElement or String, Object)
3090            options = setOptions(this, options);
3091  
3092            // Make sure to assign internal flags at the beginning,
3093            // to avoid inconsistent state in some edge cases.
3094            this._handlers = [];
3095            this._layers = {};
3096            this._zoomBoundLayers = {};
3097            this._sizeChanged = true;
3098  
3099            this._initContainer(id);
3100            this._initLayout();
3101  
3102            // hack for https://github.com/Leaflet/Leaflet/issues/1980
3103            this._onResize = bind(this._onResize, this);
3104  
3105            this._initEvents();
3106  
3107            if (options.maxBounds) {
3108                this.setMaxBounds(options.maxBounds);
3109            }
3110  
3111            if (options.zoom !== undefined) {
3112                this._zoom = this._limitZoom(options.zoom);
3113            }
3114  
3115            if (options.center && options.zoom !== undefined) {
3116                this.setView(toLatLng(options.center), options.zoom, {reset: true});
3117            }
3118  
3119            this.callInitHooks();
3120  
3121            // don't animate on browsers without hardware-accelerated transitions or old Android/Opera
3122            this._zoomAnimated = TRANSITION && Browser.any3d && !Browser.mobileOpera &&
3123                    this.options.zoomAnimation;
3124  
3125            // zoom transitions run with the same duration for all layers, so if one of transitionend events
3126            // happens after starting zoom animation (propagating to the map pane), we know that it ended globally
3127            if (this._zoomAnimated) {
3128                this._createAnimProxy();
3129                on(this._proxy, TRANSITION_END, this._catchTransitionEnd, this);
3130            }
3131  
3132            this._addLayers(this.options.layers);
3133        },
3134  
3135  
3136        // @section Methods for modifying map state
3137  
3138        // @method setView(center: LatLng, zoom: Number, options?: Zoom/pan options): this
3139        // Sets the view of the map (geographical center and zoom) with the given
3140        // animation options.
3141        setView: function (center, zoom, options) {
3142  
3143            zoom = zoom === undefined ? this._zoom : this._limitZoom(zoom);
3144            center = this._limitCenter(toLatLng(center), zoom, this.options.maxBounds);
3145            options = options || {};
3146  
3147            this._stop();
3148  
3149            if (this._loaded && !options.reset && options !== true) {
3150  
3151                if (options.animate !== undefined) {
3152                    options.zoom = extend({animate: options.animate}, options.zoom);
3153                    options.pan = extend({animate: options.animate, duration: options.duration}, options.pan);
3154                }
3155  
3156                // try animating pan or zoom
3157                var moved = (this._zoom !== zoom) ?
3158                    this._tryAnimatedZoom && this._tryAnimatedZoom(center, zoom, options.zoom) :
3159                    this._tryAnimatedPan(center, options.pan);
3160  
3161                if (moved) {
3162                    // prevent resize handler call, the view will refresh after animation anyway
3163                    clearTimeout(this._sizeTimer);
3164                    return this;
3165                }
3166            }
3167  
3168            // animation didn't start, just reset the map view
3169            this._resetView(center, zoom);
3170  
3171            return this;
3172        },
3173  
3174        // @method setZoom(zoom: Number, options?: Zoom/pan options): this
3175        // Sets the zoom of the map.
3176        setZoom: function (zoom, options) {
3177            if (!this._loaded) {
3178                this._zoom = zoom;
3179                return this;
3180            }
3181            return this.setView(this.getCenter(), zoom, {zoom: options});
3182        },
3183  
3184        // @method zoomIn(delta?: Number, options?: Zoom options): this
3185        // Increases the zoom of the map by `delta` ([`zoomDelta`](#map-zoomdelta) by default).
3186        zoomIn: function (delta, options) {
3187            delta = delta || (Browser.any3d ? this.options.zoomDelta : 1);
3188            return this.setZoom(this._zoom + delta, options);
3189        },
3190  
3191        // @method zoomOut(delta?: Number, options?: Zoom options): this
3192        // Decreases the zoom of the map by `delta` ([`zoomDelta`](#map-zoomdelta) by default).
3193        zoomOut: function (delta, options) {
3194            delta = delta || (Browser.any3d ? this.options.zoomDelta : 1);
3195            return this.setZoom(this._zoom - delta, options);
3196        },
3197  
3198        // @method setZoomAround(latlng: LatLng, zoom: Number, options: Zoom options): this
3199        // Zooms the map while keeping a specified geographical point on the map
3200        // stationary (e.g. used internally for scroll zoom and double-click zoom).
3201        // @alternative
3202        // @method setZoomAround(offset: Point, zoom: Number, options: Zoom options): this
3203        // Zooms the map while keeping a specified pixel on the map (relative to the top-left corner) stationary.
3204        setZoomAround: function (latlng, zoom, options) {
3205            var scale = this.getZoomScale(zoom),
3206                viewHalf = this.getSize().divideBy(2),
3207                containerPoint = latlng instanceof Point ? latlng : this.latLngToContainerPoint(latlng),
3208  
3209                centerOffset = containerPoint.subtract(viewHalf).multiplyBy(1 - 1 / scale),
3210                newCenter = this.containerPointToLatLng(viewHalf.add(centerOffset));
3211  
3212            return this.setView(newCenter, zoom, {zoom: options});
3213        },
3214  
3215        _getBoundsCenterZoom: function (bounds, options) {
3216  
3217            options = options || {};
3218            bounds = bounds.getBounds ? bounds.getBounds() : toLatLngBounds(bounds);
3219  
3220            var paddingTL = toPoint(options.paddingTopLeft || options.padding || [0, 0]),
3221                paddingBR = toPoint(options.paddingBottomRight || options.padding || [0, 0]),
3222  
3223                zoom = this.getBoundsZoom(bounds, false, paddingTL.add(paddingBR));
3224  
3225            zoom = (typeof options.maxZoom === 'number') ? Math.min(options.maxZoom, zoom) : zoom;
3226  
3227            if (zoom === Infinity) {
3228                return {
3229                    center: bounds.getCenter(),
3230                    zoom: zoom
3231                };
3232            }
3233  
3234            var paddingOffset = paddingBR.subtract(paddingTL).divideBy(2),
3235  
3236                swPoint = this.project(bounds.getSouthWest(), zoom),
3237                nePoint = this.project(bounds.getNorthEast(), zoom),
3238                center = this.unproject(swPoint.add(nePoint).divideBy(2).add(paddingOffset), zoom);
3239  
3240            return {
3241                center: center,
3242                zoom: zoom
3243            };
3244        },
3245  
3246        // @method fitBounds(bounds: LatLngBounds, options?: fitBounds options): this
3247        // Sets a map view that contains the given geographical bounds with the
3248        // maximum zoom level possible.
3249        fitBounds: function (bounds, options) {
3250  
3251            bounds = toLatLngBounds(bounds);
3252  
3253            if (!bounds.isValid()) {
3254                throw new Error('Bounds are not valid.');
3255            }
3256  
3257            var target = this._getBoundsCenterZoom(bounds, options);
3258            return this.setView(target.center, target.zoom, options);
3259        },
3260  
3261        // @method fitWorld(options?: fitBounds options): this
3262        // Sets a map view that mostly contains the whole world with the maximum
3263        // zoom level possible.
3264        fitWorld: function (options) {
3265            return this.fitBounds([[-90, -180], [90, 180]], options);
3266        },
3267  
3268        // @method panTo(latlng: LatLng, options?: Pan options): this
3269        // Pans the map to a given center.
3270        panTo: function (center, options) { // (LatLng)
3271            return this.setView(center, this._zoom, {pan: options});
3272        },
3273  
3274        // @method panBy(offset: Point, options?: Pan options): this
3275        // Pans the map by a given number of pixels (animated).
3276        panBy: function (offset, options) {
3277            offset = toPoint(offset).round();
3278            options = options || {};
3279  
3280            if (!offset.x && !offset.y) {
3281                return this.fire('moveend');
3282            }
3283            // If we pan too far, Chrome gets issues with tiles
3284            // and makes them disappear or appear in the wrong place (slightly offset) #2602
3285            if (options.animate !== true && !this.getSize().contains(offset)) {
3286                this._resetView(this.unproject(this.project(this.getCenter()).add(offset)), this.getZoom());
3287                return this;
3288            }
3289  
3290            if (!this._panAnim) {
3291                this._panAnim = new PosAnimation();
3292  
3293                this._panAnim.on({
3294                    'step': this._onPanTransitionStep,
3295                    'end': this._onPanTransitionEnd
3296                }, this);
3297            }
3298  
3299            // don't fire movestart if animating inertia
3300            if (!options.noMoveStart) {
3301                this.fire('movestart');
3302            }
3303  
3304            // animate pan unless animate: false specified
3305            if (options.animate !== false) {
3306                addClass(this._mapPane, 'leaflet-pan-anim');
3307  
3308                var newPos = this._getMapPanePos().subtract(offset).round();
3309                this._panAnim.run(this._mapPane, newPos, options.duration || 0.25, options.easeLinearity);
3310            } else {
3311                this._rawPanBy(offset);
3312                this.fire('move').fire('moveend');
3313            }
3314  
3315            return this;
3316        },
3317  
3318        // @method flyTo(latlng: LatLng, zoom?: Number, options?: Zoom/pan options): this
3319        // Sets the view of the map (geographical center and zoom) performing a smooth
3320        // pan-zoom animation.
3321        flyTo: function (targetCenter, targetZoom, options) {
3322  
3323            options = options || {};
3324            if (options.animate === false || !Browser.any3d) {
3325                return this.setView(targetCenter, targetZoom, options);
3326            }
3327  
3328            this._stop();
3329  
3330            var from = this.project(this.getCenter()),
3331                to = this.project(targetCenter),
3332                size = this.getSize(),
3333                startZoom = this._zoom;
3334  
3335            targetCenter = toLatLng(targetCenter);
3336            targetZoom = targetZoom === undefined ? startZoom : targetZoom;
3337  
3338            var w0 = Math.max(size.x, size.y),
3339                w1 = w0 * this.getZoomScale(startZoom, targetZoom),
3340                u1 = (to.distanceTo(from)) || 1,
3341                rho = 1.42,
3342                rho2 = rho * rho;
3343  
3344            function r(i) {
3345                var s1 = i ? -1 : 1,
3346                    s2 = i ? w1 : w0,
3347                    t1 = w1 * w1 - w0 * w0 + s1 * rho2 * rho2 * u1 * u1,
3348                    b1 = 2 * s2 * rho2 * u1,
3349                    b = t1 / b1,
3350                    sq = Math.sqrt(b * b + 1) - b;
3351  
3352                    // workaround for floating point precision bug when sq = 0, log = -Infinite,
3353                    // thus triggering an infinite loop in flyTo
3354                    var log = sq < 0.000000001 ? -18 : Math.log(sq);
3355  
3356                return log;
3357            }
3358  
3359            function sinh(n) { return (Math.exp(n) - Math.exp(-n)) / 2; }
3360            function cosh(n) { return (Math.exp(n) + Math.exp(-n)) / 2; }
3361            function tanh(n) { return sinh(n) / cosh(n); }
3362  
3363            var r0 = r(0);
3364  
3365            function w(s) { return w0 * (cosh(r0) / cosh(r0 + rho * s)); }
3366            function u(s) { return w0 * (cosh(r0) * tanh(r0 + rho * s) - sinh(r0)) / rho2; }
3367  
3368            function easeOut(t) { return 1 - Math.pow(1 - t, 1.5); }
3369  
3370            var start = Date.now(),
3371                S = (r(1) - r0) / rho,
3372                duration = options.duration ? 1000 * options.duration : 1000 * S * 0.8;
3373  
3374            function frame() {
3375                var t = (Date.now() - start) / duration,
3376                    s = easeOut(t) * S;
3377  
3378                if (t <= 1) {
3379                    this._flyToFrame = requestAnimFrame(frame, this);
3380  
3381                    this._move(
3382                        this.unproject(from.add(to.subtract(from).multiplyBy(u(s) / u1)), startZoom),
3383                        this.getScaleZoom(w0 / w(s), startZoom),
3384                        {flyTo: true});
3385  
3386                } else {
3387                    this
3388                        ._move(targetCenter, targetZoom)
3389                        ._moveEnd(true);
3390                }
3391            }
3392  
3393            this._moveStart(true, options.noMoveStart);
3394  
3395            frame.call(this);
3396            return this;
3397        },
3398  
3399        // @method flyToBounds(bounds: LatLngBounds, options?: fitBounds options): this
3400        // Sets the view of the map with a smooth animation like [`flyTo`](#map-flyto),
3401        // but takes a bounds parameter like [`fitBounds`](#map-fitbounds).
3402        flyToBounds: function (bounds, options) {
3403            var target = this._getBoundsCenterZoom(bounds, options);
3404            return this.flyTo(target.center, target.zoom, options);
3405        },
3406  
3407        // @method setMaxBounds(bounds: LatLngBounds): this
3408        // Restricts the map view to the given bounds (see the [maxBounds](#map-maxbounds) option).
3409        setMaxBounds: function (bounds) {
3410            bounds = toLatLngBounds(bounds);
3411  
3412            if (!bounds.isValid()) {
3413                this.options.maxBounds = null;
3414                return this.off('moveend', this._panInsideMaxBounds);
3415            } else if (this.options.maxBounds) {
3416                this.off('moveend', this._panInsideMaxBounds);
3417            }
3418  
3419            this.options.maxBounds = bounds;
3420  
3421            if (this._loaded) {
3422                this._panInsideMaxBounds();
3423            }
3424  
3425            return this.on('moveend', this._panInsideMaxBounds);
3426        },
3427  
3428        // @method setMinZoom(zoom: Number): this
3429        // Sets the lower limit for the available zoom levels (see the [minZoom](#map-minzoom) option).
3430        setMinZoom: function (zoom) {
3431            var oldZoom = this.options.minZoom;
3432            this.options.minZoom = zoom;
3433  
3434            if (this._loaded && oldZoom !== zoom) {
3435                this.fire('zoomlevelschange');
3436  
3437                if (this.getZoom() < this.options.minZoom) {
3438                    return this.setZoom(zoom);
3439                }
3440            }
3441  
3442            return this;
3443        },
3444  
3445        // @method setMaxZoom(zoom: Number): this
3446        // Sets the upper limit for the available zoom levels (see the [maxZoom](#map-maxzoom) option).
3447        setMaxZoom: function (zoom) {
3448            var oldZoom = this.options.maxZoom;
3449            this.options.maxZoom = zoom;
3450  
3451            if (this._loaded && oldZoom !== zoom) {
3452                this.fire('zoomlevelschange');
3453  
3454                if (this.getZoom() > this.options.maxZoom) {
3455                    return this.setZoom(zoom);
3456                }
3457            }
3458  
3459            return this;
3460        },
3461  
3462        // @method panInsideBounds(bounds: LatLngBounds, options?: Pan options): this
3463        // 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.
3464        panInsideBounds: function (bounds, options) {
3465            this._enforcingBounds = true;
3466            var center = this.getCenter(),
3467                newCenter = this._limitCenter(center, this._zoom, toLatLngBounds(bounds));
3468  
3469            if (!center.equals(newCenter)) {
3470                this.panTo(newCenter, options);
3471            }
3472  
3473            this._enforcingBounds = false;
3474            return this;
3475        },
3476  
3477        // @method panInside(latlng: LatLng, options?: padding options): this
3478        // Pans the map the minimum amount to make the `latlng` visible. Use
3479        // padding options to fit the display to more restricted bounds.
3480        // If `latlng` is already within the (optionally padded) display bounds,
3481        // the map will not be panned.
3482        panInside: function (latlng, options) {
3483            options = options || {};
3484  
3485            var paddingTL = toPoint(options.paddingTopLeft || options.padding || [0, 0]),
3486                paddingBR = toPoint(options.paddingBottomRight || options.padding || [0, 0]),
3487                pixelCenter = this.project(this.getCenter()),
3488                pixelPoint = this.project(latlng),
3489                pixelBounds = this.getPixelBounds(),
3490                paddedBounds = toBounds([pixelBounds.min.add(paddingTL), pixelBounds.max.subtract(paddingBR)]),
3491                paddedSize = paddedBounds.getSize();
3492  
3493            if (!paddedBounds.contains(pixelPoint)) {
3494                this._enforcingBounds = true;
3495                var centerOffset = pixelPoint.subtract(paddedBounds.getCenter());
3496                var offset = paddedBounds.extend(pixelPoint).getSize().subtract(paddedSize);
3497                pixelCenter.x += centerOffset.x < 0 ? -offset.x : offset.x;
3498                pixelCenter.y += centerOffset.y < 0 ? -offset.y : offset.y;
3499                this.panTo(this.unproject(pixelCenter), options);
3500                this._enforcingBounds = false;
3501            }
3502            return this;
3503        },
3504  
3505        // @method invalidateSize(options: Zoom/pan options): this
3506        // Checks if the map container size changed and updates the map if so —
3507        // call it after you've changed the map size dynamically, also animating
3508        // pan by default. If `options.pan` is `false`, panning will not occur.
3509        // If `options.debounceMoveend` is `true`, it will delay `moveend` event so
3510        // that it doesn't happen often even if the method is called many
3511        // times in a row.
3512  
3513        // @alternative
3514        // @method invalidateSize(animate: Boolean): this
3515        // Checks if the map container size changed and updates the map if so —
3516        // call it after you've changed the map size dynamically, also animating
3517        // pan by default.
3518        invalidateSize: function (options) {
3519            if (!this._loaded) { return this; }
3520  
3521            options = extend({
3522                animate: false,
3523                pan: true
3524            }, options === true ? {animate: true} : options);
3525  
3526            var oldSize = this.getSize();
3527            this._sizeChanged = true;
3528            this._lastCenter = null;
3529  
3530            var newSize = this.getSize(),
3531                oldCenter = oldSize.divideBy(2).round(),
3532                newCenter = newSize.divideBy(2).round(),
3533                offset = oldCenter.subtract(newCenter);
3534  
3535            if (!offset.x && !offset.y) { return this; }
3536  
3537            if (options.animate && options.pan) {
3538                this.panBy(offset);
3539  
3540            } else {
3541                if (options.pan) {
3542                    this._rawPanBy(offset);
3543                }
3544  
3545                this.fire('move');
3546  
3547                if (options.debounceMoveend) {
3548                    clearTimeout(this._sizeTimer);
3549                    this._sizeTimer = setTimeout(bind(this.fire, this, 'moveend'), 200);
3550                } else {
3551                    this.fire('moveend');
3552                }
3553            }
3554  
3555            // @section Map state change events
3556            // @event resize: ResizeEvent
3557            // Fired when the map is resized.
3558            return this.fire('resize', {
3559                oldSize: oldSize,
3560                newSize: newSize
3561            });
3562        },
3563  
3564        // @section Methods for modifying map state
3565        // @method stop(): this
3566        // Stops the currently running `panTo` or `flyTo` animation, if any.
3567        stop: function () {
3568            this.setZoom(this._limitZoom(this._zoom));
3569            if (!this.options.zoomSnap) {
3570                this.fire('viewreset');
3571            }
3572            return this._stop();
3573        },
3574  
3575        // @section Geolocation methods
3576        // @method locate(options?: Locate options): this
3577        // Tries to locate the user using the Geolocation API, firing a [`locationfound`](#map-locationfound)
3578        // event with location data on success or a [`locationerror`](#map-locationerror) event on failure,
3579        // and optionally sets the map view to the user's location with respect to
3580        // detection accuracy (or to the world view if geolocation failed).
3581        // Note that, if your page doesn't use HTTPS, this method will fail in
3582        // modern browsers ([Chrome 50 and newer](https://sites.google.com/a/chromium.org/dev/Home/chromium-security/deprecating-powerful-features-on-insecure-origins))
3583        // See `Locate options` for more details.
3584        locate: function (options) {
3585  
3586            options = this._locateOptions = extend({
3587                timeout: 10000,
3588                watch: false
3589                // setView: false
3590                // maxZoom: <Number>
3591                // maximumAge: 0
3592                // enableHighAccuracy: false
3593            }, options);
3594  
3595            if (!('geolocation' in navigator)) {
3596                this._handleGeolocationError({
3597                    code: 0,
3598                    message: 'Geolocation not supported.'
3599                });
3600                return this;
3601            }
3602  
3603            var onResponse = bind(this._handleGeolocationResponse, this),
3604                onError = bind(this._handleGeolocationError, this);
3605  
3606            if (options.watch) {
3607                this._locationWatchId =
3608                        navigator.geolocation.watchPosition(onResponse, onError, options);
3609            } else {
3610                navigator.geolocation.getCurrentPosition(onResponse, onError, options);
3611            }
3612            return this;
3613        },
3614  
3615        // @method stopLocate(): this
3616        // Stops watching location previously initiated by `map.locate({watch: true})`
3617        // and aborts resetting the map view if map.locate was called with
3618        // `{setView: true}`.
3619        stopLocate: function () {
3620            if (navigator.geolocation && navigator.geolocation.clearWatch) {
3621                navigator.geolocation.clearWatch(this._locationWatchId);
3622            }
3623            if (this._locateOptions) {
3624                this._locateOptions.setView = false;
3625            }
3626            return this;
3627        },
3628  
3629        _handleGeolocationError: function (error) {
3630            if (!this._container._leaflet_id) { return; }
3631  
3632            var c = error.code,
3633                message = error.message ||
3634                        (c === 1 ? 'permission denied' :
3635                        (c === 2 ? 'position unavailable' : 'timeout'));
3636  
3637            if (this._locateOptions.setView && !this._loaded) {
3638                this.fitWorld();
3639            }
3640  
3641            // @section Location events
3642            // @event locationerror: ErrorEvent
3643            // Fired when geolocation (using the [`locate`](#map-locate) method) failed.
3644            this.fire('locationerror', {
3645                code: c,
3646                message: 'Geolocation error: ' + message + '.'
3647            });
3648        },
3649  
3650        _handleGeolocationResponse: function (pos) {
3651            if (!this._container._leaflet_id) { return; }
3652  
3653            var lat = pos.coords.latitude,
3654                lng = pos.coords.longitude,
3655                latlng = new LatLng(lat, lng),
3656                bounds = latlng.toBounds(pos.coords.accuracy * 2),
3657                options = this._locateOptions;
3658  
3659            if (options.setView) {
3660                var zoom = this.getBoundsZoom(bounds);
3661                this.setView(latlng, options.maxZoom ? Math.min(zoom, options.maxZoom) : zoom);
3662            }
3663  
3664            var data = {
3665                latlng: latlng,
3666                bounds: bounds,
3667                timestamp: pos.timestamp
3668            };
3669  
3670            for (var i in pos.coords) {
3671                if (typeof pos.coords[i] === 'number') {
3672                    data[i] = pos.coords[i];
3673                }
3674            }
3675  
3676            // @event locationfound: LocationEvent
3677            // Fired when geolocation (using the [`locate`](#map-locate) method)
3678            // went successfully.
3679            this.fire('locationfound', data);
3680        },
3681  
3682        // TODO Appropriate docs section?
3683        // @section Other Methods
3684        // @method addHandler(name: String, HandlerClass: Function): this
3685        // Adds a new `Handler` to the map, given its name and constructor function.
3686        addHandler: function (name, HandlerClass) {
3687            if (!HandlerClass) { return this; }
3688  
3689            var handler = this[name] = new HandlerClass(this);
3690  
3691            this._handlers.push(handler);
3692  
3693            if (this.options[name]) {
3694                handler.enable();
3695            }
3696  
3697            return this;
3698        },
3699  
3700        // @method remove(): this
3701        // Destroys the map and clears all related event listeners.
3702        remove: function () {
3703  
3704            this._initEvents(true);
3705            if (this.options.maxBounds) { this.off('moveend', this._panInsideMaxBounds); }
3706  
3707            if (this._containerId !== this._container._leaflet_id) {
3708                throw new Error('Map container is being reused by another instance');
3709            }
3710  
3711            try {
3712                // throws error in IE6-8
3713                delete this._container._leaflet_id;
3714                delete this._containerId;
3715            } catch (e) {
3716                /*eslint-disable */
3717                this._container._leaflet_id = undefined;
3718                /* eslint-enable */
3719                this._containerId = undefined;
3720            }
3721  
3722            if (this._locationWatchId !== undefined) {
3723                this.stopLocate();
3724            }
3725  
3726            this._stop();
3727  
3728            remove(this._mapPane);
3729  
3730            if (this._clearControlPos) {
3731                this._clearControlPos();
3732            }
3733            if (this._resizeRequest) {
3734                cancelAnimFrame(this._resizeRequest);
3735                this._resizeRequest = null;
3736            }
3737  
3738            this._clearHandlers();
3739  
3740            if (this._loaded) {
3741                // @section Map state change events
3742                // @event unload: Event
3743                // Fired when the map is destroyed with [remove](#map-remove) method.
3744                this.fire('unload');
3745            }
3746  
3747            var i;
3748            for (i in this._layers) {
3749                this._layers[i].remove();
3750            }
3751            for (i in this._panes) {
3752                remove(this._panes[i]);
3753            }
3754  
3755            this._layers = [];
3756            this._panes = [];
3757            delete this._mapPane;
3758            delete this._renderer;
3759  
3760            return this;
3761        },
3762  
3763        // @section Other Methods
3764        // @method createPane(name: String, container?: HTMLElement): HTMLElement
3765        // Creates a new [map pane](#map-pane) with the given name if it doesn't exist already,
3766        // then returns it. The pane is created as a child of `container`, or
3767        // as a child of the main map pane if not set.
3768        createPane: function (name, container) {
3769            var className = 'leaflet-pane' + (name ? ' leaflet-' + name.replace('Pane', '') + '-pane' : ''),
3770                pane = create$1('div', className, container || this._mapPane);
3771  
3772            if (name) {
3773                this._panes[name] = pane;
3774            }
3775            return pane;
3776        },
3777  
3778        // @section Methods for Getting Map State
3779  
3780        // @method getCenter(): LatLng
3781        // Returns the geographical center of the map view
3782        getCenter: function () {
3783            this._checkIfLoaded();
3784  
3785            if (this._lastCenter && !this._moved()) {
3786                return this._lastCenter;
3787            }
3788            return this.layerPointToLatLng(this._getCenterLayerPoint());
3789        },
3790  
3791        // @method getZoom(): Number
3792        // Returns the current zoom level of the map view
3793        getZoom: function () {
3794            return this._zoom;
3795        },
3796  
3797        // @method getBounds(): LatLngBounds
3798        // Returns the geographical bounds visible in the current map view
3799        getBounds: function () {
3800            var bounds = this.getPixelBounds(),
3801                sw = this.unproject(bounds.getBottomLeft()),
3802                ne = this.unproject(bounds.getTopRight());
3803  
3804            return new LatLngBounds(sw, ne);
3805        },
3806  
3807        // @method getMinZoom(): Number
3808        // 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.
3809        getMinZoom: function () {
3810            return this.options.minZoom === undefined ? this._layersMinZoom || 0 : this.options.minZoom;
3811        },
3812  
3813        // @method getMaxZoom(): Number
3814        // Returns the maximum zoom level of the map (if set in the `maxZoom` option of the map or of any layers).
3815        getMaxZoom: function () {
3816            return this.options.maxZoom === undefined ?
3817                (this._layersMaxZoom === undefined ? Infinity : this._layersMaxZoom) :
3818                this.options.maxZoom;
3819        },
3820  
3821        // @method getBoundsZoom(bounds: LatLngBounds, inside?: Boolean, padding?: Point): Number
3822        // Returns the maximum zoom level on which the given bounds fit to the map
3823        // view in its entirety. If `inside` (optional) is set to `true`, the method
3824        // instead returns the minimum zoom level on which the map view fits into
3825        // the given bounds in its entirety.
3826        getBoundsZoom: function (bounds, inside, padding) { // (LatLngBounds[, Boolean, Point]) -> Number
3827            bounds = toLatLngBounds(bounds);
3828            padding = toPoint(padding || [0, 0]);
3829  
3830            var zoom = this.getZoom() || 0,
3831                min = this.getMinZoom(),
3832                max = this.getMaxZoom(),
3833                nw = bounds.getNorthWest(),
3834                se = bounds.getSouthEast(),
3835                size = this.getSize().subtract(padding),
3836                boundsSize = toBounds(this.project(se, zoom), this.project(nw, zoom)).getSize(),
3837                snap = Browser.any3d ? this.options.zoomSnap : 1,
3838                scalex = size.x / boundsSize.x,
3839                scaley = size.y / boundsSize.y,
3840                scale = inside ? Math.max(scalex, scaley) : Math.min(scalex, scaley);
3841  
3842            zoom = this.getScaleZoom(scale, zoom);
3843  
3844            if (snap) {
3845                zoom = Math.round(zoom / (snap / 100)) * (snap / 100); // don't jump if within 1% of a snap level
3846                zoom = inside ? Math.ceil(zoom / snap) * snap : Math.floor(zoom / snap) * snap;
3847            }
3848  
3849            return Math.max(min, Math.min(max, zoom));
3850        },
3851  
3852        // @method getSize(): Point
3853        // Returns the current size of the map container (in pixels).
3854        getSize: function () {
3855            if (!this._size || this._sizeChanged) {
3856                this._size = new Point(
3857                    this._container.clientWidth || 0,
3858                    this._container.clientHeight || 0);
3859  
3860                this._sizeChanged = false;
3861            }
3862            return this._size.clone();
3863        },
3864  
3865        // @method getPixelBounds(): Bounds
3866        // Returns the bounds of the current map view in projected pixel
3867        // coordinates (sometimes useful in layer and overlay implementations).
3868        getPixelBounds: function (center, zoom) {
3869            var topLeftPoint = this._getTopLeftPoint(center, zoom);
3870            return new Bounds(topLeftPoint, topLeftPoint.add(this.getSize()));
3871        },
3872  
3873        // TODO: Check semantics - isn't the pixel origin the 0,0 coord relative to
3874        // the map pane? "left point of the map layer" can be confusing, specially
3875        // since there can be negative offsets.
3876        // @method getPixelOrigin(): Point
3877        // Returns the projected pixel coordinates of the top left point of
3878        // the map layer (useful in custom layer and overlay implementations).
3879        getPixelOrigin: function () {
3880            this._checkIfLoaded();
3881            return this._pixelOrigin;
3882        },
3883  
3884        // @method getPixelWorldBounds(zoom?: Number): Bounds
3885        // Returns the world's bounds in pixel coordinates for zoom level `zoom`.
3886        // If `zoom` is omitted, the map's current zoom level is used.
3887        getPixelWorldBounds: function (zoom) {
3888            return this.options.crs.getProjectedBounds(zoom === undefined ? this.getZoom() : zoom);
3889        },
3890  
3891        // @section Other Methods
3892  
3893        // @method getPane(pane: String|HTMLElement): HTMLElement
3894        // Returns a [map pane](#map-pane), given its name or its HTML element (its identity).
3895        getPane: function (pane) {
3896            return typeof pane === 'string' ? this._panes[pane] : pane;
3897        },
3898  
3899        // @method getPanes(): Object
3900        // Returns a plain object containing the names of all [panes](#map-pane) as keys and
3901        // the panes as values.
3902        getPanes: function () {
3903            return this._panes;
3904        },
3905  
3906        // @method getContainer: HTMLElement
3907        // Returns the HTML element that contains the map.
3908        getContainer: function () {
3909            return this._container;
3910        },
3911  
3912  
3913        // @section Conversion Methods
3914  
3915        // @method getZoomScale(toZoom: Number, fromZoom: Number): Number
3916        // Returns the scale factor to be applied to a map transition from zoom level
3917        // `fromZoom` to `toZoom`. Used internally to help with zoom animations.
3918        getZoomScale: function (toZoom, fromZoom) {
3919            // TODO replace with universal implementation after refactoring projections
3920            var crs = this.options.crs;
3921            fromZoom = fromZoom === undefined ? this._zoom : fromZoom;
3922            return crs.scale(toZoom) / crs.scale(fromZoom);
3923        },
3924  
3925        // @method getScaleZoom(scale: Number, fromZoom: Number): Number
3926        // Returns the zoom level that the map would end up at, if it is at `fromZoom`
3927        // level and everything is scaled by a factor of `scale`. Inverse of
3928        // [`getZoomScale`](#map-getZoomScale).
3929        getScaleZoom: function (scale, fromZoom) {
3930            var crs = this.options.crs;
3931            fromZoom = fromZoom === undefined ? this._zoom : fromZoom;
3932            var zoom = crs.zoom(scale * crs.scale(fromZoom));
3933            return isNaN(zoom) ? Infinity : zoom;
3934        },
3935  
3936        // @method project(latlng: LatLng, zoom: Number): Point
3937        // Projects a geographical coordinate `LatLng` according to the projection
3938        // of the map's CRS, then scales it according to `zoom` and the CRS's
3939        // `Transformation`. The result is pixel coordinate relative to
3940        // the CRS origin.
3941        project: function (latlng, zoom) {
3942            zoom = zoom === undefined ? this._zoom : zoom;
3943            return this.options.crs.latLngToPoint(toLatLng(latlng), zoom);
3944        },
3945  
3946        // @method unproject(point: Point, zoom: Number): LatLng
3947        // Inverse of [`project`](#map-project).
3948        unproject: function (point, zoom) {
3949            zoom = zoom === undefined ? this._zoom : zoom;
3950            return this.options.crs.pointToLatLng(toPoint(point), zoom);
3951        },
3952  
3953        // @method layerPointToLatLng(point: Point): LatLng
3954        // Given a pixel coordinate relative to the [origin pixel](#map-getpixelorigin),
3955        // returns the corresponding geographical coordinate (for the current zoom level).
3956        layerPointToLatLng: function (point) {
3957            var projectedPoint = toPoint(point).add(this.getPixelOrigin());
3958            return this.unproject(projectedPoint);
3959        },
3960  
3961        // @method latLngToLayerPoint(latlng: LatLng): Point
3962        // Given a geographical coordinate, returns the corresponding pixel coordinate
3963        // relative to the [origin pixel](#map-getpixelorigin).
3964        latLngToLayerPoint: function (latlng) {
3965            var projectedPoint = this.project(toLatLng(latlng))._round();
3966            return projectedPoint._subtract(this.getPixelOrigin());
3967        },
3968  
3969        // @method wrapLatLng(latlng: LatLng): LatLng
3970        // Returns a `LatLng` where `lat` and `lng` has been wrapped according to the
3971        // map's CRS's `wrapLat` and `wrapLng` properties, if they are outside the
3972        // CRS's bounds.
3973        // By default this means longitude is wrapped around the dateline so its
3974        // value is between -180 and +180 degrees.
3975        wrapLatLng: function (latlng) {
3976            return this.options.crs.wrapLatLng(toLatLng(latlng));
3977        },
3978  
3979        // @method wrapLatLngBounds(bounds: LatLngBounds): LatLngBounds
3980        // Returns a `LatLngBounds` with the same size as the given one, ensuring that
3981        // its center is within the CRS's bounds.
3982        // By default this means the center longitude is wrapped around the dateline so its
3983        // value is between -180 and +180 degrees, and the majority of the bounds
3984        // overlaps the CRS's bounds.
3985        wrapLatLngBounds: function (latlng) {
3986            return this.options.crs.wrapLatLngBounds(toLatLngBounds(latlng));
3987        },
3988  
3989        // @method distance(latlng1: LatLng, latlng2: LatLng): Number
3990        // Returns the distance between two geographical coordinates according to
3991        // the map's CRS. By default this measures distance in meters.
3992        distance: function (latlng1, latlng2) {
3993            return this.options.crs.distance(toLatLng(latlng1), toLatLng(latlng2));
3994        },
3995  
3996        // @method containerPointToLayerPoint(point: Point): Point
3997        // Given a pixel coordinate relative to the map container, returns the corresponding
3998        // pixel coordinate relative to the [origin pixel](#map-getpixelorigin).
3999        containerPointToLayerPoint: function (point) { // (Point)
4000            return toPoint(point).subtract(this._getMapPanePos());
4001        },
4002  
4003        // @method layerPointToContainerPoint(point: Point): Point
4004        // Given a pixel coordinate relative to the [origin pixel](#map-getpixelorigin),
4005        // returns the corresponding pixel coordinate relative to the map container.
4006        layerPointToContainerPoint: function (point) { // (Point)
4007            return toPoint(point).add(this._getMapPanePos());
4008        },
4009  
4010        // @method containerPointToLatLng(point: Point): LatLng
4011        // Given a pixel coordinate relative to the map container, returns
4012        // the corresponding geographical coordinate (for the current zoom level).
4013        containerPointToLatLng: function (point) {
4014            var layerPoint = this.containerPointToLayerPoint(toPoint(point));
4015            return this.layerPointToLatLng(layerPoint);
4016        },
4017  
4018        // @method latLngToContainerPoint(latlng: LatLng): Point
4019        // Given a geographical coordinate, returns the corresponding pixel coordinate
4020        // relative to the map container.
4021        latLngToContainerPoint: function (latlng) {
4022            return this.layerPointToContainerPoint(this.latLngToLayerPoint(toLatLng(latlng)));
4023        },
4024  
4025        // @method mouseEventToContainerPoint(ev: MouseEvent): Point
4026        // Given a MouseEvent object, returns the pixel coordinate relative to the
4027        // map container where the event took place.
4028        mouseEventToContainerPoint: function (e) {
4029            return getMousePosition(e, this._container);
4030        },
4031  
4032        // @method mouseEventToLayerPoint(ev: MouseEvent): Point
4033        // Given a MouseEvent object, returns the pixel coordinate relative to
4034        // the [origin pixel](#map-getpixelorigin) where the event took place.
4035        mouseEventToLayerPoint: function (e) {
4036            return this.containerPointToLayerPoint(this.mouseEventToContainerPoint(e));
4037        },
4038  
4039        // @method mouseEventToLatLng(ev: MouseEvent): LatLng
4040        // Given a MouseEvent object, returns geographical coordinate where the
4041        // event took place.
4042        mouseEventToLatLng: function (e) { // (MouseEvent)
4043            return this.layerPointToLatLng(this.mouseEventToLayerPoint(e));
4044        },
4045  
4046  
4047        // map initialization methods
4048  
4049        _initContainer: function (id) {
4050            var container = this._container = get(id);
4051  
4052            if (!container) {
4053                throw new Error('Map container not found.');
4054            } else if (container._leaflet_id) {
4055                throw new Error('Map container is already initialized.');
4056            }
4057  
4058            on(container, 'scroll', this._onScroll, this);
4059            this._containerId = stamp(container);
4060        },
4061  
4062        _initLayout: function () {
4063            var container = this._container;
4064  
4065            this._fadeAnimated = this.options.fadeAnimation && Browser.any3d;
4066  
4067            addClass(container, 'leaflet-container' +
4068                (Browser.touch ? ' leaflet-touch' : '') +
4069                (Browser.retina ? ' leaflet-retina' : '') +
4070                (Browser.ielt9 ? ' leaflet-oldie' : '') +
4071                (Browser.safari ? ' leaflet-safari' : '') +
4072                (this._fadeAnimated ? ' leaflet-fade-anim' : ''));
4073  
4074            var position = getStyle(container, 'position');
4075  
4076            if (position !== 'absolute' && position !== 'relative' && position !== 'fixed') {
4077                container.style.position = 'relative';
4078            }
4079  
4080            this._initPanes();
4081  
4082            if (this._initControlPos) {
4083                this._initControlPos();
4084            }
4085        },
4086  
4087        _initPanes: function () {
4088            var panes = this._panes = {};
4089            this._paneRenderers = {};
4090  
4091            // @section
4092            //
4093            // Panes are DOM elements used to control the ordering of layers on the map. You
4094            // can access panes with [`map.getPane`](#map-getpane) or
4095            // [`map.getPanes`](#map-getpanes) methods. New panes can be created with the
4096            // [`map.createPane`](#map-createpane) method.
4097            //
4098            // Every map has the following default panes that differ only in zIndex.
4099            //
4100            // @pane mapPane: HTMLElement = 'auto'
4101            // Pane that contains all other map panes
4102  
4103            this._mapPane = this.createPane('mapPane', this._container);
4104            setPosition(this._mapPane, new Point(0, 0));
4105  
4106            // @pane tilePane: HTMLElement = 200
4107            // Pane for `GridLayer`s and `TileLayer`s
4108            this.createPane('tilePane');
4109            // @pane overlayPane: HTMLElement = 400
4110            // Pane for vectors (`Path`s, like `Polyline`s and `Polygon`s), `ImageOverlay`s and `VideoOverlay`s
4111            this.createPane('overlayPane');
4112            // @pane shadowPane: HTMLElement = 500
4113            // Pane for overlay shadows (e.g. `Marker` shadows)
4114            this.createPane('shadowPane');
4115            // @pane markerPane: HTMLElement = 600
4116            // Pane for `Icon`s of `Marker`s
4117            this.createPane('markerPane');
4118            // @pane tooltipPane: HTMLElement = 650
4119            // Pane for `Tooltip`s.
4120            this.createPane('tooltipPane');
4121            // @pane popupPane: HTMLElement = 700
4122            // Pane for `Popup`s.
4123            this.createPane('popupPane');
4124  
4125            if (!this.options.markerZoomAnimation) {
4126                addClass(panes.markerPane, 'leaflet-zoom-hide');
4127                addClass(panes.shadowPane, 'leaflet-zoom-hide');
4128            }
4129        },
4130  
4131  
4132        // private methods that modify map state
4133  
4134        // @section Map state change events
4135        _resetView: function (center, zoom) {
4136            setPosition(this._mapPane, new Point(0, 0));
4137  
4138            var loading = !this._loaded;
4139            this._loaded = true;
4140            zoom = this._limitZoom(zoom);
4141  
4142            this.fire('viewprereset');
4143  
4144            var zoomChanged = this._zoom !== zoom;
4145            this
4146                ._moveStart(zoomChanged, false)
4147                ._move(center, zoom)
4148                ._moveEnd(zoomChanged);
4149  
4150            // @event viewreset: Event
4151            // Fired when the map needs to redraw its content (this usually happens
4152            // on map zoom or load). Very useful for creating custom overlays.
4153            this.fire('viewreset');
4154  
4155            // @event load: Event
4156            // Fired when the map is initialized (when its center and zoom are set
4157            // for the first time).
4158            if (loading) {
4159                this.fire('load');
4160            }
4161        },
4162  
4163        _moveStart: function (zoomChanged, noMoveStart) {
4164            // @event zoomstart: Event
4165            // Fired when the map zoom is about to change (e.g. before zoom animation).
4166            // @event movestart: Event
4167            // Fired when the view of the map starts changing (e.g. user starts dragging the map).
4168            if (zoomChanged) {
4169                this.fire('zoomstart');
4170            }
4171            if (!noMoveStart) {
4172                this.fire('movestart');
4173            }
4174            return this;
4175        },
4176  
4177        _move: function (center, zoom, data, supressEvent) {
4178            if (zoom === undefined) {
4179                zoom = this._zoom;
4180            }
4181            var zoomChanged = this._zoom !== zoom;
4182  
4183            this._zoom = zoom;
4184            this._lastCenter = center;
4185            this._pixelOrigin = this._getNewPixelOrigin(center);
4186  
4187            if (!supressEvent) {
4188                // @event zoom: Event
4189                // Fired repeatedly during any change in zoom level,
4190                // including zoom and fly animations.
4191                if (zoomChanged || (data && data.pinch)) {	// Always fire 'zoom' if pinching because #3530
4192                    this.fire('zoom', data);
4193                }
4194  
4195                // @event move: Event
4196                // Fired repeatedly during any movement of the map,
4197                // including pan and fly animations.
4198                this.fire('move', data);
4199            } else if (data && data.pinch) {	// Always fire 'zoom' if pinching because #3530
4200                this.fire('zoom', data);
4201            }
4202            return this;
4203        },
4204  
4205        _moveEnd: function (zoomChanged) {
4206            // @event zoomend: Event
4207            // Fired when the map zoom changed, after any animations.
4208            if (zoomChanged) {
4209                this.fire('zoomend');
4210            }
4211  
4212            // @event moveend: Event
4213            // Fired when the center of the map stops changing
4214            // (e.g. user stopped dragging the map or after non-centered zoom).
4215            return this.fire('moveend');
4216        },
4217  
4218        _stop: function () {
4219            cancelAnimFrame(this._flyToFrame);
4220            if (this._panAnim) {
4221                this._panAnim.stop();
4222            }
4223            return this;
4224        },
4225  
4226        _rawPanBy: function (offset) {
4227            setPosition(this._mapPane, this._getMapPanePos().subtract(offset));
4228        },
4229  
4230        _getZoomSpan: function () {
4231            return this.getMaxZoom() - this.getMinZoom();
4232        },
4233  
4234        _panInsideMaxBounds: function () {
4235            if (!this._enforcingBounds) {
4236                this.panInsideBounds(this.options.maxBounds);
4237            }
4238        },
4239  
4240        _checkIfLoaded: function () {
4241            if (!this._loaded) {
4242                throw new Error('Set map center and zoom first.');
4243            }
4244        },
4245  
4246        // DOM event handling
4247  
4248        // @section Interaction events
4249        _initEvents: function (remove) {
4250            this._targets = {};
4251            this._targets[stamp(this._container)] = this;
4252  
4253            var onOff = remove ? off : on;
4254  
4255            // @event click: MouseEvent
4256            // Fired when the user clicks (or taps) the map.
4257            // @event dblclick: MouseEvent
4258            // Fired when the user double-clicks (or double-taps) the map.
4259            // @event mousedown: MouseEvent
4260            // Fired when the user pushes the mouse button on the map.
4261            // @event mouseup: MouseEvent
4262            // Fired when the user releases the mouse button on the map.
4263            // @event mouseover: MouseEvent
4264            // Fired when the mouse enters the map.
4265            // @event mouseout: MouseEvent
4266            // Fired when the mouse leaves the map.
4267            // @event mousemove: MouseEvent
4268            // Fired while the mouse moves over the map.
4269            // @event contextmenu: MouseEvent
4270            // Fired when the user pushes the right mouse button on the map, prevents
4271            // default browser context menu from showing if there are listeners on
4272            // this event. Also fired on mobile when the user holds a single touch
4273            // for a second (also called long press).
4274            // @event keypress: KeyboardEvent
4275            // Fired when the user presses a key from the keyboard that produces a character value while the map is focused.
4276            // @event keydown: KeyboardEvent
4277            // Fired when the user presses a key from the keyboard while the map is focused. Unlike the `keypress` event,
4278            // the `keydown` event is fired for keys that produce a character value and for keys
4279            // that do not produce a character value.
4280            // @event keyup: KeyboardEvent
4281            // Fired when the user releases a key from the keyboard while the map is focused.
4282            onOff(this._container, 'click dblclick mousedown mouseup ' +
4283                'mouseover mouseout mousemove contextmenu keypress keydown keyup', this._handleDOMEvent, this);
4284  
4285            if (this.options.trackResize) {
4286                onOff(window, 'resize', this._onResize, this);
4287            }
4288  
4289            if (Browser.any3d && this.options.transform3DLimit) {
4290                (remove ? this.off : this.on).call(this, 'moveend', this._onMoveEnd);
4291            }
4292        },
4293  
4294        _onResize: function () {
4295            cancelAnimFrame(this._resizeRequest);
4296            this._resizeRequest = requestAnimFrame(
4297                    function () { this.invalidateSize({debounceMoveend: true}); }, this);
4298        },
4299  
4300        _onScroll: function () {
4301            this._container.scrollTop  = 0;
4302            this._container.scrollLeft = 0;
4303        },
4304  
4305        _onMoveEnd: function () {
4306            var pos = this._getMapPanePos();
4307            if (Math.max(Math.abs(pos.x), Math.abs(pos.y)) >= this.options.transform3DLimit) {
4308                // https://bugzilla.mozilla.org/show_bug.cgi?id=1203873 but Webkit also have
4309                // a pixel offset on very high values, see: https://jsfiddle.net/dg6r5hhb/
4310                this._resetView(this.getCenter(), this.getZoom());
4311            }
4312        },
4313  
4314        _findEventTargets: function (e, type) {
4315            var targets = [],
4316                target,
4317                isHover = type === 'mouseout' || type === 'mouseover',
4318                src = e.target || e.srcElement,
4319                dragging = false;
4320  
4321            while (src) {
4322                target = this._targets[stamp(src)];
4323                if (target && (type === 'click' || type === 'preclick') && this._draggableMoved(target)) {
4324                    // Prevent firing click after you just dragged an object.
4325                    dragging = true;
4326                    break;
4327                }
4328                if (target && target.listens(type, true)) {
4329                    if (isHover && !isExternalTarget(src, e)) { break; }
4330                    targets.push(target);
4331                    if (isHover) { break; }
4332                }
4333                if (src === this._container) { break; }
4334                src = src.parentNode;
4335            }
4336            if (!targets.length && !dragging && !isHover && this.listens(type, true)) {
4337                targets = [this];
4338            }
4339            return targets;
4340        },
4341  
4342        _isClickDisabled: function (el) {
4343            while (el !== this._container) {
4344                if (el['_leaflet_disable_click']) { return true; }
4345                el = el.parentNode;
4346            }
4347        },
4348  
4349        _handleDOMEvent: function (e) {
4350            var el = (e.target || e.srcElement);
4351            if (!this._loaded || el['_leaflet_disable_events'] || e.type === 'click' && this._isClickDisabled(el)) {
4352                return;
4353            }
4354  
4355            var type = e.type;
4356  
4357            if (type === 'mousedown') {
4358                // prevents outline when clicking on keyboard-focusable element
4359                preventOutline(el);
4360            }
4361  
4362            this._fireDOMEvent(e, type);
4363        },
4364  
4365        _mouseEvents: ['click', 'dblclick', 'mouseover', 'mouseout', 'contextmenu'],
4366  
4367        _fireDOMEvent: function (e, type, canvasTargets) {
4368  
4369            if (e.type === 'click') {
4370                // Fire a synthetic 'preclick' event which propagates up (mainly for closing popups).
4371                // @event preclick: MouseEvent
4372                // Fired before mouse click on the map (sometimes useful when you
4373                // want something to happen on click before any existing click
4374                // handlers start running).
4375                var synth = extend({}, e);
4376                synth.type = 'preclick';
4377                this._fireDOMEvent(synth, synth.type, canvasTargets);
4378            }
4379  
4380            // Find the layer the event is propagating from and its parents.
4381            var targets = this._findEventTargets(e, type);
4382  
4383            if (canvasTargets) {
4384                var filtered = []; // pick only targets with listeners
4385                for (var i = 0; i < canvasTargets.length; i++) {
4386                    if (canvasTargets[i].listens(type, true)) {
4387                        filtered.push(canvasTargets[i]);
4388                    }
4389                }
4390                targets = filtered.concat(targets);
4391            }
4392  
4393            if (!targets.length) { return; }
4394  
4395            if (type === 'contextmenu') {
4396                preventDefault(e);
4397            }
4398  
4399            var target = targets[0];
4400            var data = {
4401                originalEvent: e
4402            };
4403  
4404            if (e.type !== 'keypress' && e.type !== 'keydown' && e.type !== 'keyup') {
4405                var isMarker = target.getLatLng && (!target._radius || target._radius <= 10);
4406                data.containerPoint = isMarker ?
4407                    this.latLngToContainerPoint(target.getLatLng()) : this.mouseEventToContainerPoint(e);
4408                data.layerPoint = this.containerPointToLayerPoint(data.containerPoint);
4409                data.latlng = isMarker ? target.getLatLng() : this.layerPointToLatLng(data.layerPoint);
4410            }
4411  
4412            for (i = 0; i < targets.length; i++) {
4413                targets[i].fire(type, data, true);
4414                if (data.originalEvent._stopped ||
4415                    (targets[i].options.bubblingMouseEvents === false && indexOf(this._mouseEvents, type) !== -1)) { return; }
4416            }
4417        },
4418  
4419        _draggableMoved: function (obj) {
4420            obj = obj.dragging && obj.dragging.enabled() ? obj : this;
4421            return (obj.dragging && obj.dragging.moved()) || (this.boxZoom && this.boxZoom.moved());
4422        },
4423  
4424        _clearHandlers: function () {
4425            for (var i = 0, len = this._handlers.length; i < len; i++) {
4426                this._handlers[i].disable();
4427            }
4428        },
4429  
4430        // @section Other Methods
4431  
4432        // @method whenReady(fn: Function, context?: Object): this
4433        // Runs the given function `fn` when the map gets initialized with
4434        // a view (center and zoom) and at least one layer, or immediately
4435        // if it's already initialized, optionally passing a function context.
4436        whenReady: function (callback, context) {
4437            if (this._loaded) {
4438                callback.call(context || this, {target: this});
4439            } else {
4440                this.on('load', callback, context);
4441            }
4442            return this;
4443        },
4444  
4445  
4446        // private methods for getting map state
4447  
4448        _getMapPanePos: function () {
4449            return getPosition(this._mapPane) || new Point(0, 0);
4450        },
4451  
4452        _moved: function () {
4453            var pos = this._getMapPanePos();
4454            return pos && !pos.equals([0, 0]);
4455        },
4456  
4457        _getTopLeftPoint: function (center, zoom) {
4458            var pixelOrigin = center && zoom !== undefined ?
4459                this._getNewPixelOrigin(center, zoom) :
4460                this.getPixelOrigin();
4461            return pixelOrigin.subtract(this._getMapPanePos());
4462        },
4463  
4464        _getNewPixelOrigin: function (center, zoom) {
4465            var viewHalf = this.getSize()._divideBy(2);
4466            return this.project(center, zoom)._subtract(viewHalf)._add(this._getMapPanePos())._round();
4467        },
4468  
4469        _latLngToNewLayerPoint: function (latlng, zoom, center) {
4470            var topLeft = this._getNewPixelOrigin(center, zoom);
4471            return this.project(latlng, zoom)._subtract(topLeft);
4472        },
4473  
4474        _latLngBoundsToNewLayerBounds: function (latLngBounds, zoom, center) {
4475            var topLeft = this._getNewPixelOrigin(center, zoom);
4476            return toBounds([
4477                this.project(latLngBounds.getSouthWest(), zoom)._subtract(topLeft),
4478                this.project(latLngBounds.getNorthWest(), zoom)._subtract(topLeft),
4479                this.project(latLngBounds.getSouthEast(), zoom)._subtract(topLeft),
4480                this.project(latLngBounds.getNorthEast(), zoom)._subtract(topLeft)
4481            ]);
4482        },
4483  
4484        // layer point of the current center
4485        _getCenterLayerPoint: function () {
4486            return this.containerPointToLayerPoint(this.getSize()._divideBy(2));
4487        },
4488  
4489        // offset of the specified place to the current center in pixels
4490        _getCenterOffset: function (latlng) {
4491            return this.latLngToLayerPoint(latlng).subtract(this._getCenterLayerPoint());
4492        },
4493  
4494        // adjust center for view to get inside bounds
4495        _limitCenter: function (center, zoom, bounds) {
4496  
4497            if (!bounds) { return center; }
4498  
4499            var centerPoint = this.project(center, zoom),
4500                viewHalf = this.getSize().divideBy(2),
4501                viewBounds = new Bounds(centerPoint.subtract(viewHalf), centerPoint.add(viewHalf)),
4502                offset = this._getBoundsOffset(viewBounds, bounds, zoom);
4503  
4504            // If offset is less than a pixel, ignore.
4505            // This prevents unstable projections from getting into
4506            // an infinite loop of tiny offsets.
4507            if (offset.round().equals([0, 0])) {
4508                return center;
4509            }
4510  
4511            return this.unproject(centerPoint.add(offset), zoom);
4512        },
4513  
4514        // adjust offset for view to get inside bounds
4515        _limitOffset: function (offset, bounds) {
4516            if (!bounds) { return offset; }
4517  
4518            var viewBounds = this.getPixelBounds(),
4519                newBounds = new Bounds(viewBounds.min.add(offset), viewBounds.max.add(offset));
4520  
4521            return offset.add(this._getBoundsOffset(newBounds, bounds));
4522        },
4523  
4524        // returns offset needed for pxBounds to get inside maxBounds at a specified zoom
4525        _getBoundsOffset: function (pxBounds, maxBounds, zoom) {
4526            var projectedMaxBounds = toBounds(
4527                    this.project(maxBounds.getNorthEast(), zoom),
vendor: 13,538 bytes, lines 4528-4921
4528                    this.project(maxBounds.getSouthWest(), zoom)
4529                ),
4530                minOffset = projectedMaxBounds.min.subtract(pxBounds.min),
4531                maxOffset = projectedMaxBounds.max.subtract(pxBounds.max),
4532  
4533                dx = this._rebound(minOffset.x, -maxOffset.x),
4534                dy = this._rebound(minOffset.y, -maxOffset.y);
4535  
4536            return new Point(dx, dy);
4537        },
4538  
4539        _rebound: function (left, right) {
4540            return left + right > 0 ?
4541                Math.round(left - right) / 2 :
4542                Math.max(0, Math.ceil(left)) - Math.max(0, Math.floor(right));
4543        },
4544  
4545        _limitZoom: function (zoom) {
4546            var min = this.getMinZoom(),
4547                max = this.getMaxZoom(),
4548                snap = Browser.any3d ? this.options.zoomSnap : 1;
4549            if (snap) {
4550                zoom = Math.round(zoom / snap) * snap;
4551            }
4552            return Math.max(min, Math.min(max, zoom));
4553        },
4554  
4555        _onPanTransitionStep: function () {
4556            this.fire('move');
4557        },
4558  
4559        _onPanTransitionEnd: function () {
4560            removeClass(this._mapPane, 'leaflet-pan-anim');
4561            this.fire('moveend');
4562        },
4563  
4564        _tryAnimatedPan: function (center, options) {
4565            // difference between the new and current centers in pixels
4566            var offset = this._getCenterOffset(center)._trunc();
4567  
4568            // don't animate too far unless animate: true specified in options
4569            if ((options && options.animate) !== true && !this.getSize().contains(offset)) { return false; }
4570  
4571            this.panBy(offset, options);
4572  
4573            return true;
4574        },
4575  
4576        _createAnimProxy: function () {
4577  
4578            var proxy = this._proxy = create$1('div', 'leaflet-proxy leaflet-zoom-animated');
4579            this._panes.mapPane.appendChild(proxy);
4580  
4581            this.on('zoomanim', function (e) {
4582                var prop = TRANSFORM,
4583                    transform = this._proxy.style[prop];
4584  
4585                setTransform(this._proxy, this.project(e.center, e.zoom), this.getZoomScale(e.zoom, 1));
4586  
4587                // workaround for case when transform is the same and so transitionend event is not fired
4588                if (transform === this._proxy.style[prop] && this._animatingZoom) {
4589                    this._onZoomTransitionEnd();
4590                }
4591            }, this);
4592  
4593            this.on('load moveend', this._animMoveEnd, this);
4594  
4595            this._on('unload', this._destroyAnimProxy, this);
4596        },
4597  
4598        _destroyAnimProxy: function () {
4599            remove(this._proxy);
4600            this.off('load moveend', this._animMoveEnd, this);
4601            delete this._proxy;
4602        },
4603  
4604        _animMoveEnd: function () {
4605            var c = this.getCenter(),
4606                z = this.getZoom();
4607            setTransform(this._proxy, this.project(c, z), this.getZoomScale(z, 1));
4608        },
4609  
4610        _catchTransitionEnd: function (e) {
4611            if (this._animatingZoom && e.propertyName.indexOf('transform') >= 0) {
4612                this._onZoomTransitionEnd();
4613            }
4614        },
4615  
4616        _nothingToAnimate: function () {
4617            return !this._container.getElementsByClassName('leaflet-zoom-animated').length;
4618        },
4619  
4620        _tryAnimatedZoom: function (center, zoom, options) {
4621  
4622            if (this._animatingZoom) { return true; }
4623  
4624            options = options || {};
4625  
4626            // don't animate if disabled, not supported or zoom difference is too large
4627            if (!this._zoomAnimated || options.animate === false || this._nothingToAnimate() ||
4628                    Math.abs(zoom - this._zoom) > this.options.zoomAnimationThreshold) { return false; }
4629  
4630            // offset is the pixel coords of the zoom origin relative to the current center
4631            var scale = this.getZoomScale(zoom),
4632                offset = this._getCenterOffset(center)._divideBy(1 - 1 / scale);
4633  
4634            // don't animate if the zoom origin isn't within one screen from the current center, unless forced
4635            if (options.animate !== true && !this.getSize().contains(offset)) { return false; }
4636  
4637            requestAnimFrame(function () {
4638                this
4639                    ._moveStart(true, false)
4640                    ._animateZoom(center, zoom, true);
4641            }, this);
4642  
4643            return true;
4644        },
4645  
4646        _animateZoom: function (center, zoom, startAnim, noUpdate) {
4647            if (!this._mapPane) { return; }
4648  
4649            if (startAnim) {
4650                this._animatingZoom = true;
4651  
4652                // remember what center/zoom to set after animation
4653                this._animateToCenter = center;
4654                this._animateToZoom = zoom;
4655  
4656                addClass(this._mapPane, 'leaflet-zoom-anim');
4657            }
4658  
4659            // @section Other Events
4660            // @event zoomanim: ZoomAnimEvent
4661            // Fired at least once per zoom animation. For continuous zoom, like pinch zooming, fired once per frame during zoom.
4662            this.fire('zoomanim', {
4663                center: center,
4664                zoom: zoom,
4665                noUpdate: noUpdate
4666            });
4667  
4668            if (!this._tempFireZoomEvent) {
4669                this._tempFireZoomEvent = this._zoom !== this._animateToZoom;
4670            }
4671  
4672            this._move(this._animateToCenter, this._animateToZoom, undefined, true);
4673  
4674            // Work around webkit not firing 'transitionend', see https://github.com/Leaflet/Leaflet/issues/3689, 2693
4675            setTimeout(bind(this._onZoomTransitionEnd, this), 250);
4676        },
4677  
4678        _onZoomTransitionEnd: function () {
4679            if (!this._animatingZoom) { return; }
4680  
4681            if (this._mapPane) {
4682                removeClass(this._mapPane, 'leaflet-zoom-anim');
4683            }
4684  
4685            this._animatingZoom = false;
4686  
4687            this._move(this._animateToCenter, this._animateToZoom, undefined, true);
4688  
4689            if (this._tempFireZoomEvent) {
4690                this.fire('zoom');
4691            }
4692            delete this._tempFireZoomEvent;
4693  
4694            this.fire('move');
4695  
4696            // This anim frame should prevent an obscure iOS webkit tile loading race condition.
4697            requestAnimFrame(function () {
4698                this._moveEnd(true);
4699            }, this);
4700        }
4701    });
4702  
4703    // @section
4704  
4705    // @factory L.map(id: String, options?: Map options)
4706    // Instantiates a map object given the DOM ID of a `<div>` element
4707    // and optionally an object literal with `Map options`.
4708    //
4709    // @alternative
4710    // @factory L.map(el: HTMLElement, options?: Map options)
4711    // Instantiates a map object given an instance of a `<div>` HTML element
4712    // and optionally an object literal with `Map options`.
4713    function createMap(id, options) {
4714        return new Map(id, options);
4715    }
4716  
4717    /*
4718     * @class Control
4719     * @aka L.Control
4720     * @inherits Class
4721     *
4722     * L.Control is a base class for implementing map controls. Handles positioning.
4723     * All other controls extend from this class.
4724     */
4725  
4726    var Control = Class.extend({
4727        // @section
4728        // @aka Control Options
4729        options: {
4730            // @option position: String = 'topright'
4731            // The position of the control (one of the map corners). Possible values are `'topleft'`,
4732            // `'topright'`, `'bottomleft'` or `'bottomright'`
4733            position: 'topright'
4734        },
4735  
4736        initialize: function (options) {
4737            setOptions(this, options);
4738        },
4739  
4740        /* @section
4741         * Classes extending L.Control will inherit the following methods:
4742         *
4743         * @method getPosition: string
4744         * Returns the position of the control.
4745         */
4746        getPosition: function () {
4747            return this.options.position;
4748        },
4749  
4750        // @method setPosition(position: string): this
4751        // Sets the position of the control.
4752        setPosition: function (position) {
4753            var map = this._map;
4754  
4755            if (map) {
4756                map.removeControl(this);
4757            }
4758  
4759            this.options.position = position;
4760  
4761            if (map) {
4762                map.addControl(this);
4763            }
4764  
4765            return this;
4766        },
4767  
4768        // @method getContainer: HTMLElement
4769        // Returns the HTMLElement that contains the control.
4770        getContainer: function () {
4771            return this._container;
4772        },
4773  
4774        // @method addTo(map: Map): this
4775        // Adds the control to the given map.
4776        addTo: function (map) {
4777            this.remove();
4778            this._map = map;
4779  
4780            var container = this._container = this.onAdd(map),
4781                pos = this.getPosition(),
4782                corner = map._controlCorners[pos];
4783  
4784            addClass(container, 'leaflet-control');
4785  
4786            if (pos.indexOf('bottom') !== -1) {
4787                corner.insertBefore(container, corner.firstChild);
4788            } else {
4789                corner.appendChild(container);
4790            }
4791  
4792            this._map.on('unload', this.remove, this);
4793  
4794            return this;
4795        },
4796  
4797        // @method remove: this
4798        // Removes the control from the map it is currently active on.
4799        remove: function () {
4800            if (!this._map) {
4801                return this;
4802            }
4803  
4804            remove(this._container);
4805  
4806            if (this.onRemove) {
4807                this.onRemove(this._map);
4808            }
4809  
4810            this._map.off('unload', this.remove, this);
4811            this._map = null;
4812  
4813            return this;
4814        },
4815  
4816        _refocusOnMap: function (e) {
4817            // if map exists and event is not a keyboard event
4818            if (this._map && e && e.screenX > 0 && e.screenY > 0) {
4819                this._map.getContainer().focus();
4820            }
4821        }
4822    });
4823  
4824    var control = function (options) {
4825        return new Control(options);
4826    };
4827  
4828    /* @section Extension methods
4829     * @uninheritable
4830     *
4831     * Every control should extend from `L.Control` and (re-)implement the following methods.
4832     *
4833     * @method onAdd(map: Map): HTMLElement
4834     * Should return the container DOM element for the control and add listeners on relevant map events. Called on [`control.addTo(map)`](#control-addTo).
4835     *
4836     * @method onRemove(map: Map)
4837     * Optional method. Should contain all clean up code that removes the listeners previously added in [`onAdd`](#control-onadd). Called on [`control.remove()`](#control-remove).
4838     */
4839  
4840    /* @namespace Map
4841     * @section Methods for Layers and Controls
4842     */
4843    Map.include({
4844        // @method addControl(control: Control): this
4845        // Adds the given control to the map
4846        addControl: function (control) {
4847            control.addTo(this);
4848            return this;
4849        },
4850  
4851        // @method removeControl(control: Control): this
4852        // Removes the given control from the map
4853        removeControl: function (control) {
4854            control.remove();
4855            return this;
4856        },
4857  
4858        _initControlPos: function () {
4859            var corners = this._controlCorners = {},
4860                l = 'leaflet-',
4861                container = this._controlContainer =
4862                        create$1('div', l + 'control-container', this._container);
4863  
4864            function createCorner(vSide, hSide) {
4865                var className = l + vSide + ' ' + l + hSide;
4866  
4867                corners[vSide + hSide] = create$1('div', className, container);
4868            }
4869  
4870            createCorner('top', 'left');
4871            createCorner('top', 'right');
4872            createCorner('bottom', 'left');
4873            createCorner('bottom', 'right');
4874        },
4875  
4876        _clearControlPos: function () {
4877            for (var i in this._controlCorners) {
4878                remove(this._controlCorners[i]);
4879            }
4880            remove(this._controlContainer);
4881            delete this._controlCorners;
4882            delete this._controlContainer;
4883        }
4884    });
4885  
4886    /*
4887     * @class Control.Layers
4888     * @aka L.Control.Layers
4889     * @inherits Control
4890     *
4891     * The layers control gives users the ability to switch between different base layers and switch overlays on/off (check out the [detailed example](https://leafletjs.com/examples/layers-control/)). Extends `Control`.
4892     *
4893     * @example
4894     *
4895     * ```js
4896     * var baseLayers = {
4897     * 	"Mapbox": mapbox,
4898     * 	"OpenStreetMap": osm
4899     * };
4900     *
4901     * var overlays = {
4902     * 	"Marker": marker,
4903     * 	"Roads": roadsLayer
4904     * };
4905     *
4906     * L.control.layers(baseLayers, overlays).addTo(map);
4907     * ```
4908     *
4909     * The `baseLayers` and `overlays` parameters are object literals with layer names as keys and `Layer` objects as values:
4910     *
4911     * ```js
4912     * {
4913     *     "<someName1>": layer1,
4914     *     "<someName2>": layer2
4915     * }
4916     * ```
4917     *
4918     * The layer names can contain HTML, which allows you to add additional styling to the items:
4919     *
4920     * ```js
4921     * {"<img src='my-layer-icon' /> <span class='my-layer-item'>
4921My Layer</span>": myLayer}
4922     * ```
4923     */
4924  
4925    var Layers = Control.extend({
4926        // @section
4927        // @aka Control.Layers options
4928        options: {
4929            // @option collapsed: Boolean = true
4930            // If `true`, the control will be collapsed into an icon and expanded on mouse hover, touch, or keyboard activation.
4931            collapsed: true,
4932            position: 'topright',
4933  
4934            // @option autoZIndex: Boolean = true
4935            // If `true`, the control will assign zIndexes in increasing order to all of its layers so that the order is preserved when switching them on/off.
4936            autoZIndex: true,
4937  
4938            // @option hideSingleBase: Boolean = false
4939            // If `true`, the base layers in the control will be hidden when there is only one.
4940            hideSingleBase: false,
4941  
4942            // @option sortLayers: Boolean = false
4943            // Whether to sort the layers. When `false`, layers will keep the order
4944            // in which they were added to the control.
4945            sortLayers: false,
4946  
4947            // @option sortFunction: Function = *
4948            // A [compare function](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Array/sort)
4949            // that will be used for sorting the layers, when `sortLayers` is `true`.
4950            // The function receives both the `L.Layer` instances and their names, as in
4951            // `sortFunction(layerA, layerB, nameA, nameB)`.
4952            // By default, it sorts layers alphabetically by their name.
4953            sortFunction: function (layerA, layerB, nameA, nameB) {
4954                return nameA < nameB ? -1 : (nameB < nameA ? 1 : 0);
4955            }
4956        },
4957  
4958        initialize: function (baseLayers, overlays, options) {
4959            setOptions(this, options);
4960  
4961            this._layerControlInputs = [];
4962            this._layers = [];
4963            this._lastZIndex = 0;
4964            this._handlingClick = false;
4965  
4966            for (var i in baseLayers) {
4967                this._addLayer(baseLayers[i], i);
4968            }
4969  
4970            for (i in overlays) {
4971                this._addLayer(overlays[i], i, true);
4972            }
4973        },
4974  
4975        onAdd: function (map) {
4976            this._initLayout();
4977            this._update();
4978  
4979            this._map = map;
4980            map.on('zoomend', this._checkDisabledLayers, this);
4981  
4982            for (var i = 0; i < this._layers.length; i++) {
4983                this._layers[i].layer.on('add remove', this._onLayerChange, this);
4984            }
4985  
4986            return this._container;
4987        },
4988  
4989        addTo: function (map) {
4990            Control.prototype.addTo.call(this, map);
4991            // Trigger expand after Layers Control has been inserted into DOM so that is now has an actual height.
4992            return this._expandIfNotCollapsed();
4993        },
4994  
4995        onRemove: function () {
4996            this._map.off('zoomend', this._checkDisabledLayers, this);
4997  
4998            for (var i = 0; i < this._layers.length; i++) {
4999                this._layers[i].layer.off('add remove', this._onLayerChange, this);
5000            }
5001        },
5002  
5003        // @method addBaseLayer(layer: Layer, name: String): this
5004        // Adds a base layer (radio button entry) with the given name to the control.
5005        addBaseLayer: function (layer, name) {
5006            this._addLayer(layer, name);
5007            return (this._map) ? this._update() : this;
5008        },
5009  
5010        // @method addOverlay(layer: Layer, name: String): this
5011        // Adds an overlay (checkbox entry) with the given name to the control.
5012        addOverlay: function (layer, name) {
5013            this._addLayer(layer, name, true);
5014            return (this._map) ? this._update() : this;
5015        },
5016  
5017        // @method removeLayer(layer: Layer): this
5018        // Remove the given layer from the control.
5019        removeLayer: function (layer) {
5020            layer.off('add remove', this._onLayerChange, this);
5021  
5022            var obj = this._getLayer(stamp(layer));
vendor: 129,416 bytes, lines 5023-8680
5023            if (obj) {
5024                this._layers.splice(this._layers.indexOf(obj), 1);
5025            }
5026            return (this._map) ? this._update() : this;
5027        },
5028  
5029        // @method expand(): this
5030        // Expand the control container if collapsed.
5031        expand: function () {
5032            addClass(this._container, 'leaflet-control-layers-expanded');
5033            this._section.style.height = null;
5034            var acceptableHeight = this._map.getSize().y - (this._container.offsetTop + 50);
5035            if (acceptableHeight < this._section.clientHeight) {
5036                addClass(this._section, 'leaflet-control-layers-scrollbar');
5037                this._section.style.height = acceptableHeight + 'px';
5038            } else {
5039                removeClass(this._section, 'leaflet-control-layers-scrollbar');
5040            }
5041            this._checkDisabledLayers();
5042            return this;
5043        },
5044  
5045        // @method collapse(): this
5046        // Collapse the control container if expanded.
5047        collapse: function () {
5048            removeClass(this._container, 'leaflet-control-layers-expanded');
5049            return this;
5050        },
5051  
5052        _initLayout: function () {
5053            var className = 'leaflet-control-layers',
5054                container = this._container = create$1('div', className),
5055                collapsed = this.options.collapsed;
5056  
5057            // makes this work on IE touch devices by stopping it from firing a mouseout event when the touch is released
5058            container.setAttribute('aria-haspopup', true);
5059  
5060            disableClickPropagation(container);
5061            disableScrollPropagation(container);
5062  
5063            var section = this._section = create$1('section', className + '-list');
5064  
5065            if (collapsed) {
5066                this._map.on('click', this.collapse, this);
5067  
5068                on(container, {
5069                    mouseenter: function () {
5070                        on(section, 'click', preventDefault);
5071                        this.expand();
5072                        setTimeout(function () {
5073                            off(section, 'click', preventDefault);
5074                        });
5075                    },
5076                    mouseleave: this.collapse
5077                }, this);
5078            }
5079  
5080            var link = this._layersLink = create$1('a', className + '-toggle', container);
5081            link.href = '#';
5082            link.title = 'Layers';
5083            link.setAttribute('role', 'button');
5084  
5085            on(link, 'click', preventDefault); // prevent link function
5086            on(link, 'focus', this.expand, this);
5087  
5088            if (!collapsed) {
5089                this.expand();
5090            }
5091  
5092            this._baseLayersList = create$1('div', className + '-base', section);
5093            this._separator = create$1('div', className + '-separator', section);
5094            this._overlaysList = create$1('div', className + '-overlays', section);
5095  
5096            container.appendChild(section);
5097        },
5098  
5099        _getLayer: function (id) {
5100            for (var i = 0; i < this._layers.length; i++) {
5101  
5102                if (this._layers[i] && stamp(this._layers[i].layer) === id) {
5103                    return this._layers[i];
5104                }
5105            }
5106        },
5107  
5108        _addLayer: function (layer, name, overlay) {
5109            if (this._map) {
5110                layer.on('add remove', this._onLayerChange, this);
5111            }
5112  
5113            this._layers.push({
5114                layer: layer,
5115                name: name,
5116                overlay: overlay
5117            });
5118  
5119            if (this.options.sortLayers) {
5120                this._layers.sort(bind(function (a, b) {
5121                    return this.options.sortFunction(a.layer, b.layer, a.name, b.name);
5122                }, this));
5123            }
5124  
5125            if (this.options.autoZIndex && layer.setZIndex) {
5126                this._lastZIndex++;
5127                layer.setZIndex(this._lastZIndex);
5128            }
5129  
5130            this._expandIfNotCollapsed();
5131        },
5132  
5133        _update: function () {
5134            if (!this._container) { return this; }
5135  
5136            empty(this._baseLayersList);
5137            empty(this._overlaysList);
5138  
5139            this._layerControlInputs = [];
5140            var baseLayersPresent, overlaysPresent, i, obj, baseLayersCount = 0;
5141  
5142            for (i = 0; i < this._layers.length; i++) {
5143                obj = this._layers[i];
5144                this._addItem(obj);
5145                overlaysPresent = overlaysPresent || obj.overlay;
5146                baseLayersPresent = baseLayersPresent || !obj.overlay;
5147                baseLayersCount += !obj.overlay ? 1 : 0;
5148            }
5149  
5150            // Hide base layers section if there's only one layer.
5151            if (this.options.hideSingleBase) {
5152                baseLayersPresent = baseLayersPresent && baseLayersCount > 1;
5153                this._baseLayersList.style.display = baseLayersPresent ? '' : 'none';
5154            }
5155  
5156            this._separator.style.display = overlaysPresent && baseLayersPresent ? '' : 'none';
5157  
5158            return this;
5159        },
5160  
5161        _onLayerChange: function (e) {
5162            if (!this._handlingClick) {
5163                this._update();
5164            }
5165  
5166            var obj = this._getLayer(stamp(e.target));
5167  
5168            // @namespace Map
5169            // @section Layer events
5170            // @event baselayerchange: LayersControlEvent
5171            // Fired when the base layer is changed through the [layers control](#control-layers).
5172            // @event overlayadd: LayersControlEvent
5173            // Fired when an overlay is selected through the [layers control](#control-layers).
5174            // @event overlayremove: LayersControlEvent
5175            // Fired when an overlay is deselected through the [layers control](#control-layers).
5176            // @namespace Control.Layers
5177            var type = obj.overlay ?
5178                (e.type === 'add' ? 'overlayadd' : 'overlayremove') :
5179                (e.type === 'add' ? 'baselayerchange' : null);
5180  
5181            if (type) {
5182                this._map.fire(type, obj);
5183            }
5184        },
5185  
5186        // IE7 bugs out if you create a radio dynamically, so you have to do it this hacky way (see https://stackoverflow.com/a/119079)
5187        _createRadioElement: function (name, checked) {
5188  
5189            var radioHtml = '<input type="radio" class="leaflet-control-layers-selector" name="' +
5190                    name + '"' + (checked ? ' checked="checked"' : '') + '/>';
5191  
5192            var radioFragment = document.createElement('div');
5193            radioFragment.innerHTML = radioHtml;
5194  
5195            return radioFragment.firstChild;
5196        },
5197  
5198        _addItem: function (obj) {
5199            var label = document.createElement('label'),
5200                checked = this._map.hasLayer(obj.layer),
5201                input;
5202  
5203            if (obj.overlay) {
5204                input = document.createElement('input');
5205                input.type = 'checkbox';
5206                input.className = 'leaflet-control-layers-selector';
5207                input.defaultChecked = checked;
5208            } else {
5209                input = this._createRadioElement('leaflet-base-layers_' + stamp(this), checked);
5210            }
5211  
5212            this._layerControlInputs.push(input);
5213            input.layerId = stamp(obj.layer);
5214  
5215            on(input, 'click', this._onInputClick, this);
5216  
5217            var name = document.createElement('span');
5218            name.innerHTML = ' ' + obj.name;
5219  
5220            // Helps from preventing layer control flicker when checkboxes are disabled
5221            // https://github.com/Leaflet/Leaflet/issues/2771
5222            var holder = document.createElement('span');
5223  
5224            label.appendChild(holder);
5225            holder.appendChild(input);
5226            holder.appendChild(name);
5227  
5228            var container = obj.overlay ? this._overlaysList : this._baseLayersList;
5229            container.appendChild(label);
5230  
5231            this._checkDisabledLayers();
5232            return label;
5233        },
5234  
5235        _onInputClick: function () {
5236            var inputs = this._layerControlInputs,
5237                input, layer;
5238            var addedLayers = [],
5239                removedLayers = [];
5240  
5241            this._handlingClick = true;
5242  
5243            for (var i = inputs.length - 1; i >= 0; i--) {
5244                input = inputs[i];
5245                layer = this._getLayer(input.layerId).layer;
5246  
5247                if (input.checked) {
5248                    addedLayers.push(layer);
5249                } else if (!input.checked) {
5250                    removedLayers.push(layer);
5251                }
5252            }
5253  
5254            // Bugfix issue 2318: Should remove all old layers before readding new ones
5255            for (i = 0; i < removedLayers.length; i++) {
5256                if (this._map.hasLayer(removedLayers[i])) {
5257                    this._map.removeLayer(removedLayers[i]);
5258                }
5259            }
5260            for (i = 0; i < addedLayers.length; i++) {
5261                if (!this._map.hasLayer(addedLayers[i])) {
5262                    this._map.addLayer(addedLayers[i]);
5263                }
5264            }
5265  
5266            this._handlingClick = false;
5267  
5268            this._refocusOnMap();
5269        },
5270  
5271        _checkDisabledLayers: function () {
5272            var inputs = this._layerControlInputs,
5273                input,
5274                layer,
5275                zoom = this._map.getZoom();
5276  
5277            for (var i = inputs.length - 1; i >= 0; i--) {
5278                input = inputs[i];
5279                layer = this._getLayer(input.layerId).layer;
5280                input.disabled = (layer.options.minZoom !== undefined && zoom < layer.options.minZoom) ||
5281                                 (layer.options.maxZoom !== undefined && zoom > layer.options.maxZoom);
5282  
5283            }
5284        },
5285  
5286        _expandIfNotCollapsed: function () {
5287            if (this._map && !this.options.collapsed) {
5288                this.expand();
5289            }
5290            return this;
5291        }
5292  
5293    });
5294  
5295  
5296    // @factory L.control.layers(baselayers?: Object, overlays?: Object, options?: Control.Layers options)
5297    // Creates a layers control with the given layers. Base layers will be switched with radio buttons, while overlays will be switched with checkboxes. Note that all base layers should be passed in the base layers object, but only one should be added to the map during map instantiation.
5298    var layers = function (baseLayers, overlays, options) {
5299        return new Layers(baseLayers, overlays, options);
5300    };
5301  
5302    /*
5303     * @class Control.Zoom
5304     * @aka L.Control.Zoom
5305     * @inherits Control
5306     *
5307     * A basic zoom control with two buttons (zoom in and zoom out). It is put on the map by default unless you set its [`zoomControl` option](#map-zoomcontrol) to `false`. Extends `Control`.
5308     */
5309  
5310    var Zoom = Control.extend({
5311        // @section
5312        // @aka Control.Zoom options
5313        options: {
5314            position: 'topleft',
5315  
5316            // @option zoomInText: String = '<span aria-hidden="true">+</span>'
5317            // The text set on the 'zoom in' button.
5318            zoomInText: '<span aria-hidden="true">+</span>',
5319  
5320            // @option zoomInTitle: String = 'Zoom in'
5321            // The title set on the 'zoom in' button.
5322            zoomInTitle: 'Zoom in',
5323  
5324            // @option zoomOutText: String = '<span aria-hidden="true">&#x2212;</span>'
5325            // The text set on the 'zoom out' button.
5326            zoomOutText: '<span aria-hidden="true">&#x2212;</span>',
5327  
5328            // @option zoomOutTitle: String = 'Zoom out'
5329            // The title set on the 'zoom out' button.
5330            zoomOutTitle: 'Zoom out'
5331        },
5332  
5333        onAdd: function (map) {
5334            var zoomName = 'leaflet-control-zoom',
5335                container = create$1('div', zoomName + ' leaflet-bar'),
5336                options = this.options;
5337  
5338            this._zoomInButton  = this._createButton(options.zoomInText, options.zoomInTitle,
5339                    zoomName + '-in',  container, this._zoomIn);
5340            this._zoomOutButton = this._createButton(options.zoomOutText, options.zoomOutTitle,
5341                    zoomName + '-out', container, this._zoomOut);
5342  
5343            this._updateDisabled();
5344            map.on('zoomend zoomlevelschange', this._updateDisabled, this);
5345  
5346            return container;
5347        },
5348  
5349        onRemove: function (map) {
5350            map.off('zoomend zoomlevelschange', this._updateDisabled, this);
5351        },
5352  
5353        disable: function () {
5354            this._disabled = true;
5355            this._updateDisabled();
5356            return this;
5357        },
5358  
5359        enable: function () {
5360            this._disabled = false;
5361            this._updateDisabled();
5362            return this;
5363        },
5364  
5365        _zoomIn: function (e) {
5366            if (!this._disabled && this._map._zoom < this._map.getMaxZoom()) {
5367                this._map.zoomIn(this._map.options.zoomDelta * (e.shiftKey ? 3 : 1));
5368            }
5369        },
5370  
5371        _zoomOut: function (e) {
5372            if (!this._disabled && this._map._zoom > this._map.getMinZoom()) {
5373                this._map.zoomOut(this._map.options.zoomDelta * (e.shiftKey ? 3 : 1));
5374            }
5375        },
5376  
5377        _createButton: function (html, title, className, container, fn) {
5378            var link = create$1('a', className, container);
5379            link.innerHTML = html;
5380            link.href = '#';
5381            link.title = title;
5382  
5383            /*
5384             * Will force screen readers like VoiceOver to read this as "Zoom in - button"
5385             */
5386            link.setAttribute('role', 'button');
5387            link.setAttribute('aria-label', title);
5388  
5389            disableClickPropagation(link);
5390            on(link, 'click', stop);
5391            on(link, 'click', fn, this);
5392            on(link, 'click', this._refocusOnMap, this);
5393  
5394            return link;
5395        },
5396  
5397        _updateDisabled: function () {
5398            var map = this._map,
5399                className = 'leaflet-disabled';
5400  
5401            removeClass(this._zoomInButton, className);
5402            removeClass(this._zoomOutButton, className);
5403            this._zoomInButton.setAttribute('aria-disabled', 'false');
5404            this._zoomOutButton.setAttribute('aria-disabled', 'false');
5405  
5406            if (this._disabled || map._zoom === map.getMinZoom()) {
5407                addClass(this._zoomOutButton, className);
5408                this._zoomOutButton.setAttribute('aria-disabled', 'true');
5409            }
5410            if (this._disabled || map._zoom === map.getMaxZoom()) {
5411                addClass(this._zoomInButton, className);
5412                this._zoomInButton.setAttribute('aria-disabled', 'true');
5413            }
5414        }
5415    });
5416  
5417    // @namespace Map
5418    // @section Control options
5419    // @option zoomControl: Boolean = true
5420    // Whether a [zoom control](#control-zoom) is added to the map by default.
5421    Map.mergeOptions({
5422        zoomControl: true
5423    });
5424  
5425    Map.addInitHook(function () {
5426        if (this.options.zoomControl) {
5427            // @section Controls
5428            // @property zoomControl: Control.Zoom
5429            // The default zoom control (only available if the
5430            // [`zoomControl` option](#map-zoomcontrol) was `true` when creating the map).
5431            this.zoomControl = new Zoom();
5432            this.addControl(this.zoomControl);
5433        }
5434    });
5435  
5436    // @namespace Control.Zoom
5437    // @factory L.control.zoom(options: Control.Zoom options)
5438    // Creates a zoom control
5439    var zoom = function (options) {
5440        return new Zoom(options);
5441    };
5442  
5443    /*
5444     * @class Control.Scale
5445     * @aka L.Control.Scale
5446     * @inherits Control
5447     *
5448     * A simple scale control that shows the scale of the current center of screen in metric (m/km) and imperial (mi/ft) systems. Extends `Control`.
5449     *
5450     * @example
5451     *
5452     * ```js
5453     * L.control.scale().addTo(map);
5454     * ```
5455     */
5456  
5457    var Scale = Control.extend({
5458        // @section
5459        // @aka Control.Scale options
5460        options: {
5461            position: 'bottomleft',
5462  
5463            // @option maxWidth: Number = 100
5464            // Maximum width of the control in pixels. The width is set dynamically to show round values (e.g. 100, 200, 500).
5465            maxWidth: 100,
5466  
5467            // @option metric: Boolean = True
5468            // Whether to show the metric scale line (m/km).
5469            metric: true,
5470  
5471            // @option imperial: Boolean = True
5472            // Whether to show the imperial scale line (mi/ft).
5473            imperial: true
5474  
5475            // @option updateWhenIdle: Boolean = false
5476            // If `true`, the control is updated on [`moveend`](#map-moveend), otherwise it's always up-to-date (updated on [`move`](#map-move)).
5477        },
5478  
5479        onAdd: function (map) {
5480            var className = 'leaflet-control-scale',
5481                container = create$1('div', className),
5482                options = this.options;
5483  
5484            this._addScales(options, className + '-line', container);
5485  
5486            map.on(options.updateWhenIdle ? 'moveend' : 'move', this._update, this);
5487            map.whenReady(this._update, this);
5488  
5489            return container;
5490        },
5491  
5492        onRemove: function (map) {
5493            map.off(this.options.updateWhenIdle ? 'moveend' : 'move', this._update, this);
5494        },
5495  
5496        _addScales: function (options, className, container) {
5497            if (options.metric) {
5498                this._mScale = create$1('div', className, container);
5499            }
5500            if (options.imperial) {
5501                this._iScale = create$1('div', className, container);
5502            }
5503        },
5504  
5505        _update: function () {
5506            var map = this._map,
5507                y = map.getSize().y / 2;
5508  
5509            var maxMeters = map.distance(
5510                map.containerPointToLatLng([0, y]),
5511                map.containerPointToLatLng([this.options.maxWidth, y]));
5512  
5513            this._updateScales(maxMeters);
5514        },
5515  
5516        _updateScales: function (maxMeters) {
5517            if (this.options.metric && maxMeters) {
5518                this._updateMetric(maxMeters);
5519            }
5520            if (this.options.imperial && maxMeters) {
5521                this._updateImperial(maxMeters);
5522            }
5523        },
5524  
5525        _updateMetric: function (maxMeters) {
5526            var meters = this._getRoundNum(maxMeters),
5527                label = meters < 1000 ? meters + ' m' : (meters / 1000) + ' km';
5528  
5529            this._updateScale(this._mScale, label, meters / maxMeters);
5530        },
5531  
5532        _updateImperial: function (maxMeters) {
5533            var maxFeet = maxMeters * 3.2808399,
5534                maxMiles, miles, feet;
5535  
5536            if (maxFeet > 5280) {
5537                maxMiles = maxFeet / 5280;
5538                miles = this._getRoundNum(maxMiles);
5539                this._updateScale(this._iScale, miles + ' mi', miles / maxMiles);
5540  
5541            } else {
5542                feet = this._getRoundNum(maxFeet);
5543                this._updateScale(this._iScale, feet + ' ft', feet / maxFeet);
5544            }
5545        },
5546  
5547        _updateScale: function (scale, text, ratio) {
5548            scale.style.width = Math.round(this.options.maxWidth * ratio) + 'px';
5549            scale.innerHTML = text;
5550        },
5551  
5552        _getRoundNum: function (num) {
5553            var pow10 = Math.pow(10, (Math.floor(num) + '').length - 1),
5554                d = num / pow10;
5555  
5556            d = d >= 10 ? 10 :
5557                d >= 5 ? 5 :
5558                d >= 3 ? 3 :
5559                d >= 2 ? 2 : 1;
5560  
5561            return pow10 * d;
5562        }
5563    });
5564  
5565  
5566    // @factory L.control.scale(options?: Control.Scale options)
5567    // Creates an scale control with the given options.
5568    var scale = function (options) {
5569        return new Scale(options);
5570    };
5571  
5572    /*
5573     * @class Control.Attribution
5574     * @aka L.Control.Attribution
5575     * @inherits Control
5576     *
5577     * The attribution control allows you to display attribution data in a small text box on a map. It is put on the map by default unless you set its [`attributionControl` option](#map-attributioncontrol) to `false`, and it fetches attribution texts from layers with the [`getAttribution` method](#layer-getattribution) automatically. Extends Control.
5578     */
5579  
5580    var Attribution = Control.extend({
5581        // @section
5582        // @aka Control.Attribution options
5583        options: {
5584            position: 'bottomright',
5585  
5586            // @option prefix: String|false = 'Leaflet'
5587            // The HTML text shown before the attributions. Pass `false` to disable.
5588            prefix: '<a href="https://leafletjs.com" title="A JavaScript library for interactive maps">Leaflet</a>'
5589  
5590        },
5591  
5592        initialize: function (options) {
5593            setOptions(this, options);
5594  
5595            this._attributions = {};
5596        },
5597  
5598        onAdd: function (map) {
5599            map.attributionControl = this;
5600            this._container = create$1('div', 'leaflet-control-attribution');
5601            disableClickPropagation(this._container);
5602  
5603            // TODO ugly, refactor
5604            for (var i in map._layers) {
5605                if (map._layers[i].getAttribution) {
5606                    this.addAttribution(map._layers[i].getAttribution());
5607                }
5608            }
5609  
5610            this._update();
5611  
5612            map.on('layeradd', this._addAttribution, this);
5613  
5614            return this._container;
5615        },
5616  
5617        onRemove: function (map) {
5618            map.off('layeradd', this._addAttribution, this);
5619        },
5620  
5621        _addAttribution: function (ev) {
5622            if (ev.layer.getAttribution) {
5623                this.addAttribution(ev.layer.getAttribution());
5624                ev.layer.once('remove', function () {
5625                    this.removeAttribution(ev.layer.getAttribution());
5626                }, this);
5627            }
5628        },
5629  
5630        // @method setPrefix(prefix: String|false): this
5631        // The HTML text shown before the attributions. Pass `false` to disable.
5632        setPrefix: function (prefix) {
5633            this.options.prefix = prefix;
5634            this._update();
5635            return this;
5636        },
5637  
5638        // @method addAttribution(text: String): this
5639        // Adds an attribution text (e.g. `'Vector data &copy; Mapbox'`).
5640        addAttribution: function (text) {
5641            if (!text) { return this; }
5642  
5643            if (!this._attributions[text]) {
5644                this._attributions[text] = 0;
5645            }
5646            this._attributions[text]++;
5647  
5648            this._update();
5649  
5650            return this;
5651        },
5652  
5653        // @method removeAttribution(text: String): this
5654        // Removes an attribution text.
5655        removeAttribution: function (text) {
5656            if (!text) { return this; }
5657  
5658            if (this._attributions[text]) {
5659                this._attributions[text]--;
5660                this._update();
5661            }
5662  
5663            return this;
5664        },
5665  
5666        _update: function () {
5667            if (!this._map) { return; }
5668  
5669            var attribs = [];
5670  
5671            for (var i in this._attributions) {
5672                if (this._attributions[i]) {
5673                    attribs.push(i);
5674                }
5675            }
5676  
5677            var prefixAndAttribs = [];
5678  
5679            if (this.options.prefix) {
5680                prefixAndAttribs.push(this.options.prefix);
5681            }
5682            if (attribs.length) {
5683                prefixAndAttribs.push(attribs.join(', '));
5684            }
5685  
5686            this._container.innerHTML = prefixAndAttribs.join(' <span aria-hidden="true">|</span> ');
5687        }
5688    });
5689  
5690    // @namespace Map
5691    // @section Control options
5692    // @option attributionControl: Boolean = true
5693    // Whether a [attribution control](#control-attribution) is added to the map by default.
5694    Map.mergeOptions({
5695        attributionControl: true
5696    });
5697  
5698    Map.addInitHook(function () {
5699        if (this.options.attributionControl) {
5700            new Attribution().addTo(this);
5701        }
5702    });
5703  
5704    // @namespace Control.Attribution
5705    // @factory L.control.attribution(options: Control.Attribution options)
5706    // Creates an attribution control.
5707    var attribution = function (options) {
5708        return new Attribution(options);
5709    };
5710  
5711    Control.Layers = Layers;
5712    Control.Zoom = Zoom;
5713    Control.Scale = Scale;
5714    Control.Attribution = Attribution;
5715  
5716    control.layers = layers;
5717    control.zoom = zoom;
5718    control.scale = scale;
5719    control.attribution = attribution;
5720  
5721    /*
5722        L.Handler is a base class for handler classes that are used internally to inject
5723        interaction features like dragging to classes like Map and Marker.
5724    */
5725  
5726    // @class Handler
5727    // @aka L.Handler
5728    // Abstract class for map interaction handlers
5729  
5730    var Handler = Class.extend({
5731        initialize: function (map) {
5732            this._map = map;
5733        },
5734  
5735        // @method enable(): this
5736        // Enables the handler
5737        enable: function () {
5738            if (this._enabled) { return this; }
5739  
5740            this._enabled = true;
5741            this.addHooks();
5742            return this;
5743        },
5744  
5745        // @method disable(): this
5746        // Disables the handler
5747        disable: function () {
5748            if (!this._enabled) { return this; }
5749  
5750            this._enabled = false;
5751            this.removeHooks();
5752            return this;
5753        },
5754  
5755        // @method enabled(): Boolean
5756        // Returns `true` if the handler is enabled
5757        enabled: function () {
5758            return !!this._enabled;
5759        }
5760  
5761        // @section Extension methods
5762        // Classes inheriting from `Handler` must implement the two following methods:
5763        // @method addHooks()
5764        // Called when the handler is enabled, should add event hooks.
5765        // @method removeHooks()
5766        // Called when the handler is disabled, should remove the event hooks added previously.
5767    });
5768  
5769    // @section There is static function which can be called without instantiating L.Handler:
5770    // @function addTo(map: Map, name: String): this
5771    // Adds a new Handler to the given map with the given name.
5772    Handler.addTo = function (map, name) {
5773        map.addHandler(name, this);
5774        return this;
5775    };
5776  
5777    var Mixin = {Events: Events};
5778  
5779    /*
5780     * @class Draggable
5781     * @aka L.Draggable
5782     * @inherits Evented
5783     *
5784     * A class for making DOM elements draggable (including touch support).
5785     * Used internally for map and marker dragging. Only works for elements
5786     * that were positioned with [`L.DomUtil.setPosition`](#domutil-setposition).
5787     *
5788     * @example
5789     * ```js
5790     * var draggable = new L.Draggable(elementToDrag);
5791     * draggable.enable();
5792     * ```
5793     */
5794  
5795    var START = Browser.touch ? 'touchstart mousedown' : 'mousedown';
5796  
5797    var Draggable = Evented.extend({
5798  
5799        options: {
5800            // @section
5801            // @aka Draggable options
5802            // @option clickTolerance: Number = 3
5803            // The max number of pixels a user can shift the mouse pointer during a click
5804            // for it to be considered a valid click (as opposed to a mouse drag).
5805            clickTolerance: 3
5806        },
5807  
5808        // @constructor L.Draggable(el: HTMLElement, dragHandle?: HTMLElement, preventOutline?: Boolean, options?: Draggable options)
5809        // Creates a `Draggable` object for moving `el` when you start dragging the `dragHandle` element (equals `el` itself by default).
5810        initialize: function (element, dragStartTarget, preventOutline, options) {
5811            setOptions(this, options);
5812  
5813            this._element = element;
5814            this._dragStartTarget = dragStartTarget || element;
5815            this._preventOutline = preventOutline;
5816        },
5817  
5818        // @method enable()
5819        // Enables the dragging ability
5820        enable: function () {
5821            if (this._enabled) { return; }
5822  
5823            on(this._dragStartTarget, START, this._onDown, this);
5824  
5825            this._enabled = true;
5826        },
5827  
5828        // @method disable()
5829        // Disables the dragging ability
5830        disable: function () {
5831            if (!this._enabled) { return; }
5832  
5833            // If we're currently dragging this draggable,
5834            // disabling it counts as first ending the drag.
5835            if (Draggable._dragging === this) {
5836                this.finishDrag();
5837            }
5838  
5839            off(this._dragStartTarget, START, this._onDown, this);
5840  
5841            this._enabled = false;
5842            this._moved = false;
5843        },
5844  
5845        _onDown: function (e) {
5846            // Ignore the event if disabled; this happens in IE11
5847            // under some circumstances, see #3666.
5848            if (!this._enabled) { return; }
5849  
5850            this._moved = false;
5851  
5852            if (hasClass(this._element, 'leaflet-zoom-anim')) { return; }
5853  
5854            if (Draggable._dragging || e.shiftKey || ((e.which !== 1) && (e.button !== 1) && !e.touches)) { return; }
5855            Draggable._dragging = this;  // Prevent dragging multiple objects at once.
5856  
5857            if (this._preventOutline) {
5858                preventOutline(this._element);
5859            }
5860  
5861            disableImageDrag();
5862            disableTextSelection();
5863  
5864            if (this._moving) { return; }
5865  
5866            // @event down: Event
5867            // Fired when a drag is about to start.
5868            this.fire('down');
5869  
5870            var first = e.touches ? e.touches[0] : e,
5871                sizedParent = getSizedParentNode(this._element);
5872  
5873            this._startPoint = new Point(first.clientX, first.clientY);
5874            this._startPos = getPosition(this._element);
5875  
5876            // Cache the scale, so that we can continuously compensate for it during drag (_onMove).
5877            this._parentScale = getScale(sizedParent);
5878  
5879            var mouseevent = e.type === 'mousedown';
5880            on(document, mouseevent ? 'mousemove' : 'touchmove', this._onMove, this);
5881            on(document, mouseevent ? 'mouseup' : 'touchend touchcancel', this._onUp, this);
5882        },
5883  
5884        _onMove: function (e) {
5885            // Ignore the event if disabled; this happens in IE11
5886            // under some circumstances, see #3666.
5887            if (!this._enabled) { return; }
5888  
5889            if (e.touches && e.touches.length > 1) {
5890                this._moved = true;
5891                return;
5892            }
5893  
5894            var first = (e.touches && e.touches.length === 1 ? e.touches[0] : e),
5895                offset = new Point(first.clientX, first.clientY)._subtract(this._startPoint);
5896  
5897            if (!offset.x && !offset.y) { return; }
5898            if (Math.abs(offset.x) + Math.abs(offset.y) < this.options.clickTolerance) { return; }
5899  
5900            // We assume that the parent container's position, border and scale do not change for the duration of the drag.
5901            // Therefore there is no need to account for the position and border (they are eliminated by the subtraction)
5902            // and we can use the cached value for the scale.
5903            offset.x /= this._parentScale.x;
5904            offset.y /= this._parentScale.y;
5905  
5906            preventDefault(e);
5907  
5908            if (!this._moved) {
5909                // @event dragstart: Event
5910                // Fired when a drag starts
5911                this.fire('dragstart');
5912  
5913                this._moved = true;
5914  
5915                addClass(document.body, 'leaflet-dragging');
5916  
5917                this._lastTarget = e.target || e.srcElement;
5918                // IE and Edge do not give the <use> element, so fetch it
5919                // if necessary
5920                if (window.SVGElementInstance && this._lastTarget instanceof window.SVGElementInstance) {
5921                    this._lastTarget = this._lastTarget.correspondingUseElement;
5922                }
5923                addClass(this._lastTarget, 'leaflet-drag-target');
5924            }
5925  
5926            this._newPos = this._startPos.add(offset);
5927            this._moving = true;
5928  
5929            this._lastEvent = e;
5930            this._updatePosition();
5931        },
5932  
5933        _updatePosition: function () {
5934            var e = {originalEvent: this._lastEvent};
5935  
5936            // @event predrag: Event
5937            // Fired continuously during dragging *before* each corresponding
5938            // update of the element's position.
5939            this.fire('predrag', e);
5940            setPosition(this._element, this._newPos);
5941  
5942            // @event drag: Event
5943            // Fired continuously during dragging.
5944            this.fire('drag', e);
5945        },
5946  
5947        _onUp: function () {
5948            // Ignore the event if disabled; this happens in IE11
5949            // under some circumstances, see #3666.
5950            if (!this._enabled) { return; }
5951            this.finishDrag();
5952        },
5953  
5954        finishDrag: function () {
5955            removeClass(document.body, 'leaflet-dragging');
5956  
5957            if (this._lastTarget) {
5958                removeClass(this._lastTarget, 'leaflet-drag-target');
5959                this._lastTarget = null;
5960            }
5961  
5962            off(document, 'mousemove touchmove', this._onMove, this);
5963            off(document, 'mouseup touchend touchcancel', this._onUp, this);
5964  
5965            enableImageDrag();
5966            enableTextSelection();
5967  
5968            if (this._moved && this._moving) {
5969  
5970                // @event dragend: DragEndEvent
5971                // Fired when the drag ends.
5972                this.fire('dragend', {
5973                    distance: this._newPos.distanceTo(this._startPos)
5974                });
5975            }
5976  
5977            this._moving = false;
5978            Draggable._dragging = false;
5979        }
5980  
5981    });
5982  
5983    /*
5984     * @namespace LineUtil
5985     *
5986     * Various utility functions for polyline points processing, used by Leaflet internally to make polylines lightning-fast.
5987     */
5988  
5989    // Simplify polyline with vertex reduction and Douglas-Peucker simplification.
5990    // Improves rendering performance dramatically by lessening the number of points to draw.
5991  
5992    // @function simplify(points: Point[], tolerance: Number): Point[]
5993    // Dramatically reduces the number of points in a polyline while retaining
5994    // its shape and returns a new array of simplified points, using the
5995    // [Ramer-Douglas-Peucker algorithm](https://en.wikipedia.org/wiki/Ramer-Douglas-Peucker_algorithm).
5996    // Used for a huge performance boost when processing/displaying Leaflet polylines for
5997    // each zoom level and also reducing visual noise. tolerance affects the amount of
5998    // simplification (lesser value means higher quality but slower and with more points).
5999    // Also released as a separated micro-library [Simplify.js](https://mourner.github.io/simplify-js/).
6000    function simplify(points, tolerance) {
6001        if (!tolerance || !points.length) {
6002            return points.slice();
6003        }
6004  
6005        var sqTolerance = tolerance * tolerance;
6006  
6007            // stage 1: vertex reduction
6008            points = _reducePoints(points, sqTolerance);
6009  
6010            // stage 2: Douglas-Peucker simplification
6011            points = _simplifyDP(points, sqTolerance);
6012  
6013        return points;
6014    }
6015  
6016    // @function pointToSegmentDistance(p: Point, p1: Point, p2: Point): Number
6017    // Returns the distance between point `p` and segment `p1` to `p2`.
6018    function pointToSegmentDistance(p, p1, p2) {
6019        return Math.sqrt(_sqClosestPointOnSegment(p, p1, p2, true));
6020    }
6021  
6022    // @function closestPointOnSegment(p: Point, p1: Point, p2: Point): Number
6023    // Returns the closest point from a point `p` on a segment `p1` to `p2`.
6024    function closestPointOnSegment(p, p1, p2) {
6025        return _sqClosestPointOnSegment(p, p1, p2);
6026    }
6027  
6028    // Ramer-Douglas-Peucker simplification, see https://en.wikipedia.org/wiki/Ramer-Douglas-Peucker_algorithm
6029    function _simplifyDP(points, sqTolerance) {
6030  
6031        var len = points.length,
6032            ArrayConstructor = typeof Uint8Array !== undefined + '' ? Uint8Array : Array,
6033            markers = new ArrayConstructor(len);
6034  
6035            markers[0] = markers[len - 1] = 1;
6036  
6037        _simplifyDPStep(points, markers, sqTolerance, 0, len - 1);
6038  
6039        var i,
6040            newPoints = [];
6041  
6042        for (i = 0; i < len; i++) {
6043            if (markers[i]) {
6044                newPoints.push(points[i]);
6045            }
6046        }
6047  
6048        return newPoints;
6049    }
6050  
6051    function _simplifyDPStep(points, markers, sqTolerance, first, last) {
6052  
6053        var maxSqDist = 0,
6054        index, i, sqDist;
6055  
6056        for (i = first + 1; i <= last - 1; i++) {
6057            sqDist = _sqClosestPointOnSegment(points[i], points[first], points[last], true);
6058  
6059            if (sqDist > maxSqDist) {
6060                index = i;
6061                maxSqDist = sqDist;
6062            }
6063        }
6064  
6065        if (maxSqDist > sqTolerance) {
6066            markers[index] = 1;
6067  
6068            _simplifyDPStep(points, markers, sqTolerance, first, index);
6069            _simplifyDPStep(points, markers, sqTolerance, index, last);
6070        }
6071    }
6072  
6073    // reduce points that are too close to each other to a single point
6074    function _reducePoints(points, sqTolerance) {
6075        var reducedPoints = [points[0]];
6076  
6077        for (var i = 1, prev = 0, len = points.length; i < len; i++) {
6078            if (_sqDist(points[i], points[prev]) > sqTolerance) {
6079                reducedPoints.push(points[i]);
6080                prev = i;
6081            }
6082        }
6083        if (prev < len - 1) {
6084            reducedPoints.push(points[len - 1]);
6085        }
6086        return reducedPoints;
6087    }
6088  
6089    var _lastCode;
6090  
6091    // @function clipSegment(a: Point, b: Point, bounds: Bounds, useLastCode?: Boolean, round?: Boolean): Point[]|Boolean
6092    // Clips the segment a to b by rectangular bounds with the
6093    // [Cohen-Sutherland algorithm](https://en.wikipedia.org/wiki/Cohen%E2%80%93Sutherland_algorithm)
6094    // (modifying the segment points directly!). Used by Leaflet to only show polyline
6095    // points that are on the screen or near, increasing performance.
6096    function clipSegment(a, b, bounds, useLastCode, round) {
6097        var codeA = useLastCode ? _lastCode : _getBitCode(a, bounds),
6098            codeB = _getBitCode(b, bounds),
6099  
6100            codeOut, p, newCode;
6101  
6102            // save 2nd code to avoid calculating it on the next segment
6103            _lastCode = codeB;
6104  
6105        while (true) {
6106            // if a,b is inside the clip window (trivial accept)
6107            if (!(codeA | codeB)) {
6108                return [a, b];
6109            }
6110  
6111            // if a,b is outside the clip window (trivial reject)
6112            if (codeA & codeB) {
6113                return false;
6114            }
6115  
6116            // other cases
6117            codeOut = codeA || codeB;
6118            p = _getEdgeIntersection(a, b, codeOut, bounds, round);
6119            newCode = _getBitCode(p, bounds);
6120  
6121            if (codeOut === codeA) {
6122                a = p;
6123                codeA = newCode;
6124            } else {
6125                b = p;
6126                codeB = newCode;
6127            }
6128        }
6129    }
6130  
6131    function _getEdgeIntersection(a, b, code, bounds, round) {
6132        var dx = b.x - a.x,
6133            dy = b.y - a.y,
6134            min = bounds.min,
6135            max = bounds.max,
6136            x, y;
6137  
6138        if (code & 8) { // top
6139            x = a.x + dx * (max.y - a.y) / dy;
6140            y = max.y;
6141  
6142        } else if (code & 4) { // bottom
6143            x = a.x + dx * (min.y - a.y) / dy;
6144            y = min.y;
6145  
6146        } else if (code & 2) { // right
6147            x = max.x;
6148            y = a.y + dy * (max.x - a.x) / dx;
6149  
6150        } else if (code & 1) { // left
6151            x = min.x;
6152            y = a.y + dy * (min.x - a.x) / dx;
6153        }
6154  
6155        return new Point(x, y, round);
6156    }
6157  
6158    function _getBitCode(p, bounds) {
6159        var code = 0;
6160  
6161        if (p.x < bounds.min.x) { // left
6162            code |= 1;
6163        } else if (p.x > bounds.max.x) { // right
6164            code |= 2;
6165        }
6166  
6167        if (p.y < bounds.min.y) { // bottom
6168            code |= 4;
6169        } else if (p.y > bounds.max.y) { // top
6170            code |= 8;
6171        }
6172  
6173        return code;
6174    }
6175  
6176    // square distance (to avoid unnecessary Math.sqrt calls)
6177    function _sqDist(p1, p2) {
6178        var dx = p2.x - p1.x,
6179            dy = p2.y - p1.y;
6180        return dx * dx + dy * dy;
6181    }
6182  
6183    // return closest point on segment or distance to that point
6184    function _sqClosestPointOnSegment(p, p1, p2, sqDist) {
6185        var x = p1.x,
6186            y = p1.y,
6187            dx = p2.x - x,
6188            dy = p2.y - y,
6189            dot = dx * dx + dy * dy,
6190            t;
6191  
6192        if (dot > 0) {
6193            t = ((p.x - x) * dx + (p.y - y) * dy) / dot;
6194  
6195            if (t > 1) {
6196                x = p2.x;
6197                y = p2.y;
6198            } else if (t > 0) {
6199                x += dx * t;
6200                y += dy * t;
6201            }
6202        }
6203  
6204        dx = p.x - x;
6205        dy = p.y - y;
6206  
6207        return sqDist ? dx * dx + dy * dy : new Point(x, y);
6208    }
6209  
6210  
6211    // @function isFlat(latlngs: LatLng[]): Boolean
6212    // Returns true if `latlngs` is a flat array, false is nested.
6213    function isFlat(latlngs) {
6214        return !isArray(latlngs[0]) || (typeof latlngs[0][0] !== 'object' && typeof latlngs[0][0] !== 'undefined');
6215    }
6216  
6217    function _flat(latlngs) {
6218        console.warn('Deprecated use of _flat, please use L.LineUtil.isFlat instead.');
6219        return isFlat(latlngs);
6220    }
6221  
6222    var LineUtil = {
6223      __proto__: null,
6224      simplify: simplify,
6225      pointToSegmentDistance: pointToSegmentDistance,
6226      closestPointOnSegment: closestPointOnSegment,
6227      clipSegment: clipSegment,
6228      _getEdgeIntersection: _getEdgeIntersection,
6229      _getBitCode: _getBitCode,
6230      _sqClosestPointOnSegment: _sqClosestPointOnSegment,
6231      isFlat: isFlat,
6232      _flat: _flat
6233    };
6234  
6235    /*
6236     * @namespace PolyUtil
6237     * Various utility functions for polygon geometries.
6238     */
6239  
6240    /* @function clipPolygon(points: Point[], bounds: Bounds, round?: Boolean): Point[]
6241     * Clips the polygon geometry defined by the given `points` by the given bounds (using the [Sutherland-Hodgman algorithm](https://en.wikipedia.org/wiki/Sutherland%E2%80%93Hodgman_algorithm)).
6242     * Used by Leaflet to only show polygon points that are on the screen or near, increasing
6243     * performance. Note that polygon points needs different algorithm for clipping
6244     * than polyline, so there's a separate method for it.
6245     */
6246    function clipPolygon(points, bounds, round) {
6247        var clippedPoints,
6248            edges = [1, 4, 2, 8],
6249            i, j, k,
6250            a, b,
6251            len, edge, p;
6252  
6253        for (i = 0, len = points.length; i < len; i++) {
6254            points[i]._code = _getBitCode(points[i], bounds);
6255        }
6256  
6257        // for each edge (left, bottom, right, top)
6258        for (k = 0; k < 4; k++) {
6259            edge = edges[k];
6260            clippedPoints = [];
6261  
6262            for (i = 0, len = points.length, j = len - 1; i < len; j = i++) {
6263                a = points[i];
6264                b = points[j];
6265  
6266                // if a is inside the clip window
6267                if (!(a._code & edge)) {
6268                    // if b is outside the clip window (a->b goes out of screen)
6269                    if (b._code & edge) {
6270                        p = _getEdgeIntersection(b, a, edge, bounds, round);
6271                        p._code = _getBitCode(p, bounds);
6272                        clippedPoints.push(p);
6273                    }
6274                    clippedPoints.push(a);
6275  
6276                // else if b is inside the clip window (a->b enters the screen)
6277                } else if (!(b._code & edge)) {
6278                    p = _getEdgeIntersection(b, a, edge, bounds, round);
6279                    p._code = _getBitCode(p, bounds);
6280                    clippedPoints.push(p);
6281                }
6282            }
6283            points = clippedPoints;
6284        }
6285  
6286        return points;
6287    }
6288  
6289    var PolyUtil = {
6290      __proto__: null,
6291      clipPolygon: clipPolygon
6292    };
6293  
6294    /*
6295     * @namespace Projection
6296     * @section
6297     * Leaflet comes with a set of already defined Projections out of the box:
6298     *
6299     * @projection L.Projection.LonLat
6300     *
6301     * Equirectangular, or Plate Carree projection — the most simple projection,
6302     * mostly used by GIS enthusiasts. Directly maps `x` as longitude, and `y` as
6303     * latitude. Also suitable for flat worlds, e.g. game maps. Used by the
6304     * `EPSG:4326` and `Simple` CRS.
6305     */
6306  
6307    var LonLat = {
6308        project: function (latlng) {
6309            return new Point(latlng.lng, latlng.lat);
6310        },
6311  
6312        unproject: function (point) {
6313            return new LatLng(point.y, point.x);
6314        },
6315  
6316        bounds: new Bounds([-180, -90], [180, 90])
6317    };
6318  
6319    /*
6320     * @namespace Projection
6321     * @projection L.Projection.Mercator
6322     *
6323     * Elliptical Mercator projection — more complex than Spherical Mercator. Assumes that Earth is an ellipsoid. Used by the EPSG:3395 CRS.
6324     */
6325  
6326    var Mercator = {
6327        R: 6378137,
6328        R_MINOR: 6356752.314245179,
6329  
6330        bounds: new Bounds([-20037508.34279, -15496570.73972], [20037508.34279, 18764656.23138]),
6331  
6332        project: function (latlng) {
6333            var d = Math.PI / 180,
6334                r = this.R,
6335                y = latlng.lat * d,
6336                tmp = this.R_MINOR / r,
6337                e = Math.sqrt(1 - tmp * tmp),
6338                con = e * Math.sin(y);
6339  
6340            var ts = Math.tan(Math.PI / 4 - y / 2) / Math.pow((1 - con) / (1 + con), e / 2);
6341            y = -r * Math.log(Math.max(ts, 1E-10));
6342  
6343            return new Point(latlng.lng * d * r, y);
6344        },
6345  
6346        unproject: function (point) {
6347            var d = 180 / Math.PI,
6348                r = this.R,
6349                tmp = this.R_MINOR / r,
6350                e = Math.sqrt(1 - tmp * tmp),
6351                ts = Math.exp(-point.y / r),
6352                phi = Math.PI / 2 - 2 * Math.atan(ts);
6353  
6354            for (var i = 0, dphi = 0.1, con; i < 15 && Math.abs(dphi) > 1e-7; i++) {
6355                con = e * Math.sin(phi);
6356                con = Math.pow((1 - con) / (1 + con), e / 2);
6357                dphi = Math.PI / 2 - 2 * Math.atan(ts * con) - phi;
6358                phi += dphi;
6359            }
6360  
6361            return new LatLng(phi * d, point.x * d / r);
6362        }
6363    };
6364  
6365    /*
6366     * @class Projection
6367  
6368     * An object with methods for projecting geographical coordinates of the world onto
6369     * a flat surface (and back). See [Map projection](https://en.wikipedia.org/wiki/Map_projection).
6370  
6371     * @property bounds: Bounds
6372     * The bounds (specified in CRS units) where the projection is valid
6373  
6374     * @method project(latlng: LatLng): Point
6375     * Projects geographical coordinates into a 2D point.
6376     * Only accepts actual `L.LatLng` instances, not arrays.
6377  
6378     * @method unproject(point: Point): LatLng
6379     * The inverse of `project`. Projects a 2D point into a geographical location.
6380     * Only accepts actual `L.Point` instances, not arrays.
6381  
6382     * Note that the projection instances do not inherit from Leaflet's `Class` object,
6383     * and can't be instantiated. Also, new classes can't inherit from them,
6384     * and methods can't be added to them with the `include` function.
6385  
6386     */
6387  
6388    var index = {
6389      __proto__: null,
6390      LonLat: LonLat,
6391      Mercator: Mercator,
6392      SphericalMercator: SphericalMercator
6393    };
6394  
6395    /*
6396     * @namespace CRS
6397     * @crs L.CRS.EPSG3395
6398     *
6399     * Rarely used by some commercial tile providers. Uses Elliptical Mercator projection.
6400     */
6401    var EPSG3395 = extend({}, Earth, {
6402        code: 'EPSG:3395',
6403        projection: Mercator,
6404  
6405        transformation: (function () {
6406            var scale = 0.5 / (Math.PI * Mercator.R);
6407            return toTransformation(scale, 0.5, -scale, 0.5);
6408        }())
6409    });
6410  
6411    /*
6412     * @namespace CRS
6413     * @crs L.CRS.EPSG4326
6414     *
6415     * A common CRS among GIS enthusiasts. Uses simple Equirectangular projection.
6416     *
6417     * Leaflet 1.0.x complies with the [TMS coordinate scheme for EPSG:4326](https://wiki.osgeo.org/wiki/Tile_Map_Service_Specification#global-geodetic),
6418     * which is a breaking change from 0.7.x behaviour.  If you are using a `TileLayer`
6419     * with this CRS, ensure that there are two 256x256 pixel tiles covering the
6420     * whole earth at zoom level zero, and that the tile coordinate origin is (-180,+90),
6421     * or (-180,-90) for `TileLayer`s with [the `tms` option](#tilelayer-tms) set.
6422     */
6423  
6424    var EPSG4326 = extend({}, Earth, {
6425        code: 'EPSG:4326',
6426        projection: LonLat,
6427        transformation: toTransformation(1 / 180, 1, -1 / 180, 0.5)
6428    });
6429  
6430    /*
6431     * @namespace CRS
6432     * @crs L.CRS.Simple
6433     *
6434     * A simple CRS that maps longitude and latitude into `x` and `y` directly.
6435     * May be used for maps of flat surfaces (e.g. game maps). Note that the `y`
6436     * axis should still be inverted (going from bottom to top). `distance()` returns
6437     * simple euclidean distance.
6438     */
6439  
6440    var Simple = extend({}, CRS, {
6441        projection: LonLat,
6442        transformation: toTransformation(1, 0, -1, 0),
6443  
6444        scale: function (zoom) {
6445            return Math.pow(2, zoom);
6446        },
6447  
6448        zoom: function (scale) {
6449            return Math.log(scale) / Math.LN2;
6450        },
6451  
6452        distance: function (latlng1, latlng2) {
6453            var dx = latlng2.lng - latlng1.lng,
6454                dy = latlng2.lat - latlng1.lat;
6455  
6456            return Math.sqrt(dx * dx + dy * dy);
6457        },
6458  
6459        infinite: true
6460    });
6461  
6462    CRS.Earth = Earth;
6463    CRS.EPSG3395 = EPSG3395;
6464    CRS.EPSG3857 = EPSG3857;
6465    CRS.EPSG900913 = EPSG900913;
6466    CRS.EPSG4326 = EPSG4326;
6467    CRS.Simple = Simple;
6468  
6469    /*
6470     * @class Layer
6471     * @inherits Evented
6472     * @aka L.Layer
6473     * @aka ILayer
6474     *
6475     * A set of methods from the Layer base class that all Leaflet layers use.
6476     * Inherits all methods, options and events from `L.Evented`.
6477     *
6478     * @example
6479     *
6480     * ```js
6481     * var layer = L.marker(latlng).addTo(map);
6482     * layer.addTo(map);
6483     * layer.remove();
6484     * ```
6485     *
6486     * @event add: Event
6487     * Fired after the layer is added to a map
6488     *
6489     * @event remove: Event
6490     * Fired after the layer is removed from a map
6491     */
6492  
6493  
6494    var Layer = Evented.extend({
6495  
6496        // Classes extending `L.Layer` will inherit the following options:
6497        options: {
6498            // @option pane: String = 'overlayPane'
6499            // By default the layer will be added to the map's [overlay pane](#map-overlaypane). Overriding this option will cause the layer to be placed on another pane by default.
6500            pane: 'overlayPane',
6501  
6502            // @option attribution: String = null
6503            // String to be shown in the attribution control, e.g. "© OpenStreetMap contributors". It describes the layer data and is often a legal obligation towards copyright holders and tile providers.
6504            attribution: null,
6505  
6506            bubblingMouseEvents: true
6507        },
6508  
6509        /* @section
6510         * Classes extending `L.Layer` will inherit the following methods:
6511         *
6512         * @method addTo(map: Map|LayerGroup): this
6513         * Adds the layer to the given map or layer group.
6514         */
6515        addTo: function (map) {
6516            map.addLayer(this);
6517            return this;
6518        },
6519  
6520        // @method remove: this
6521        // Removes the layer from the map it is currently active on.
6522        remove: function () {
6523            return this.removeFrom(this._map || this._mapToAdd);
6524        },
6525  
6526        // @method removeFrom(map: Map): this
6527        // Removes the layer from the given map
6528        //
6529        // @alternative
6530        // @method removeFrom(group: LayerGroup): this
6531        // Removes the layer from the given `LayerGroup`
6532        removeFrom: function (obj) {
6533            if (obj) {
6534                obj.removeLayer(this);
6535            }
6536            return this;
6537        },
6538  
6539        // @method getPane(name? : String): HTMLElement
6540        // Returns the `HTMLElement` representing the named pane on the map. If `name` is omitted, returns the pane for this layer.
6541        getPane: function (name) {
6542            return this._map.getPane(name ? (this.options[name] || name) : this.options.pane);
6543        },
6544  
6545        addInteractiveTarget: function (targetEl) {
6546            this._map._targets[stamp(targetEl)] = this;
6547            return this;
6548        },
6549  
6550        removeInteractiveTarget: function (targetEl) {
6551            delete this._map._targets[stamp(targetEl)];
6552            return this;
6553        },
6554  
6555        // @method getAttribution: String
6556        // Used by the `attribution control`, returns the [attribution option](#gridlayer-attribution).
6557        getAttribution: function () {
6558            return this.options.attribution;
6559        },
6560  
6561        _layerAdd: function (e) {
6562            var map = e.target;
6563  
6564            // check in case layer gets added and then removed before the map is ready
6565            if (!map.hasLayer(this)) { return; }
6566  
6567            this._map = map;
6568            this._zoomAnimated = map._zoomAnimated;
6569  
6570            if (this.getEvents) {
6571                var events = this.getEvents();
6572                map.on(events, this);
6573                this.once('remove', function () {
6574                    map.off(events, this);
6575                }, this);
6576            }
6577  
6578            this.onAdd(map);
6579  
6580            this.fire('add');
6581            map.fire('layeradd', {layer: this});
6582        }
6583    });
6584  
6585    /* @section Extension methods
6586     * @uninheritable
6587     *
6588     * Every layer should extend from `L.Layer` and (re-)implement the following methods.
6589     *
6590     * @method onAdd(map: Map): this
6591     * Should contain code that creates DOM elements for the layer, adds them to `map panes` where they should belong and puts listeners on relevant map events. Called on [`map.addLayer(layer)`](#map-addlayer).
6592     *
6593     * @method onRemove(map: Map): this
6594     * Should contain all clean up code that removes the layer's elements from the DOM and removes listeners previously added in [`onAdd`](#layer-onadd). Called on [`map.removeLayer(layer)`](#map-removelayer).
6595     *
6596     * @method getEvents(): Object
6597     * This optional method should return an object like `{ viewreset: this._reset }` for [`addEventListener`](#evented-addeventlistener). The event handlers in this object will be automatically added and removed from the map with your layer.
6598     *
6599     * @method getAttribution(): String
6600     * This optional method should return a string containing HTML to be shown on the `Attribution control` whenever the layer is visible.
6601     *
6602     * @method beforeAdd(map: Map): this
6603     * Optional method. Called on [`map.addLayer(layer)`](#map-addlayer), before the layer is added to the map, before events are initialized, without waiting until the map is in a usable state. Use for early initialization only.
6604     */
6605  
6606  
6607    /* @namespace Map
6608     * @section Layer events
6609     *
6610     * @event layeradd: LayerEvent
6611     * Fired when a new layer is added to the map.
6612     *
6613     * @event layerremove: LayerEvent
6614     * Fired when some layer is removed from the map
6615     *
6616     * @section Methods for Layers and Controls
6617     */
6618    Map.include({
6619        // @method addLayer(layer: Layer): this
6620        // Adds the given layer to the map
6621        addLayer: function (layer) {
6622            if (!layer._layerAdd) {
6623                throw new Error('The provided object is not a Layer.');
6624            }
6625  
6626            var id = stamp(layer);
6627            if (this._layers[id]) { return this; }
6628            this._layers[id] = layer;
6629  
6630            layer._mapToAdd = this;
6631  
6632            if (layer.beforeAdd) {
6633                layer.beforeAdd(this);
6634            }
6635  
6636            this.whenReady(layer._layerAdd, layer);
6637  
6638            return this;
6639        },
6640  
6641        // @method removeLayer(layer: Layer): this
6642        // Removes the given layer from the map.
6643        removeLayer: function (layer) {
6644            var id = stamp(layer);
6645  
6646            if (!this._layers[id]) { return this; }
6647  
6648            if (this._loaded) {
6649                layer.onRemove(this);
6650            }
6651  
6652            delete this._layers[id];
6653  
6654            if (this._loaded) {
6655                this.fire('layerremove', {layer: layer});
6656                layer.fire('remove');
6657            }
6658  
6659            layer._map = layer._mapToAdd = null;
6660  
6661            return this;
6662        },
6663  
6664        // @method hasLayer(layer: Layer): Boolean
6665        // Returns `true` if the given layer is currently added to the map
6666        hasLayer: function (layer) {
6667            return stamp(layer) in this._layers;
6668        },
6669  
6670        /* @method eachLayer(fn: Function, context?: Object): this
6671         * Iterates over the layers of the map, optionally specifying context of the iterator function.
6672         * ```
6673         * map.eachLayer(function(layer){
6674         *     layer.bindPopup('Hello');
6675         * });
6676         * ```
6677         */
6678        eachLayer: function (method, context) {
6679            for (var i in this._layers) {
6680                method.call(context, this._layers[i]);
6681            }
6682            return this;
6683        },
6684  
6685        _addLayers: function (layers) {
6686            layers = layers ? (isArray(layers) ? layers : [layers]) : [];
6687  
6688            for (var i = 0, len = layers.length; i < len; i++) {
6689                this.addLayer(layers[i]);
6690            }
6691        },
6692  
6693        _addZoomLimit: function (layer) {
6694            if (!isNaN(layer.options.maxZoom) || !isNaN(layer.options.minZoom)) {
6695                this._zoomBoundLayers[stamp(layer)] = layer;
6696                this._updateZoomLevels();
6697            }
6698        },
6699  
6700        _removeZoomLimit: function (layer) {
6701            var id = stamp(layer);
6702  
6703            if (this._zoomBoundLayers[id]) {
6704                delete this._zoomBoundLayers[id];
6705                this._updateZoomLevels();
6706            }
6707        },
6708  
6709        _updateZoomLevels: function () {
6710            var minZoom = Infinity,
6711                maxZoom = -Infinity,
6712                oldZoomSpan = this._getZoomSpan();
6713  
6714            for (var i in this._zoomBoundLayers) {
6715                var options = this._zoomBoundLayers[i].options;
6716  
6717                minZoom = options.minZoom === undefined ? minZoom : Math.min(minZoom, options.minZoom);
6718                maxZoom = options.maxZoom === undefined ? maxZoom : Math.max(maxZoom, options.maxZoom);
6719            }
6720  
6721            this._layersMaxZoom = maxZoom === -Infinity ? undefined : maxZoom;
6722            this._layersMinZoom = minZoom === Infinity ? undefined : minZoom;
6723  
6724            // @section Map state change events
6725            // @event zoomlevelschange: Event
6726            // Fired when the number of zoomlevels on the map is changed due
6727            // to adding or removing a layer.
6728            if (oldZoomSpan !== this._getZoomSpan()) {
6729                this.fire('zoomlevelschange');
6730            }
6731  
6732            if (this.options.maxZoom === undefined && this._layersMaxZoom && this.getZoom() > this._layersMaxZoom) {
6733                this.setZoom(this._layersMaxZoom);
6734            }
6735            if (this.options.minZoom === undefined && this._layersMinZoom && this.getZoom() < this._layersMinZoom) {
6736                this.setZoom(this._layersMinZoom);
6737            }
6738        }
6739    });
6740  
6741    /*
6742     * @class LayerGroup
6743     * @aka L.LayerGroup
6744     * @inherits Interactive layer
6745     *
6746     * Used to group several layers and handle them as one. If you add it to the map,
6747     * any layers added or removed from the group will be added/removed on the map as
6748     * well. Extends `Layer`.
6749     *
6750     * @example
6751     *
6752     * ```js
6753     * L.layerGroup([marker1, marker2])
6754     * 	.addLayer(polyline)
6755     * 	.addTo(map);
6756     * ```
6757     */
6758  
6759    var LayerGroup = Layer.extend({
6760  
6761        initialize: function (layers, options) {
6762            setOptions(this, options);
6763  
6764            this._layers = {};
6765  
6766            var i, len;
6767  
6768            if (layers) {
6769                for (i = 0, len = layers.length; i < len; i++) {
6770                    this.addLayer(layers[i]);
6771                }
6772            }
6773        },
6774  
6775        // @method addLayer(layer: Layer): this
6776        // Adds the given layer to the group.
6777        addLayer: function (layer) {
6778            var id = this.getLayerId(layer);
6779  
6780            this._layers[id] = layer;
6781  
6782            if (this._map) {
6783                this._map.addLayer(layer);
6784            }
6785  
6786            return this;
6787        },
6788  
6789        // @method removeLayer(layer: Layer): this
6790        // Removes the given layer from the group.
6791        // @alternative
6792        // @method removeLayer(id: Number): this
6793        // Removes the layer with the given internal ID from the group.
6794        removeLayer: function (layer) {
6795            var id = layer in this._layers ? layer : this.getLayerId(layer);
6796  
6797            if (this._map && this._layers[id]) {
6798                this._map.removeLayer(this._layers[id]);
6799            }
6800  
6801            delete this._layers[id];
6802  
6803            return this;
6804        },
6805  
6806        // @method hasLayer(layer: Layer): Boolean
6807        // Returns `true` if the given layer is currently added to the group.
6808        // @alternative
6809        // @method hasLayer(id: Number): Boolean
6810        // Returns `true` if the given internal ID is currently added to the group.
6811        hasLayer: function (layer) {
6812            var layerId = typeof layer === 'number' ? layer : this.getLayerId(layer);
6813            return layerId in this._layers;
6814        },
6815  
6816        // @method clearLayers(): this
6817        // Removes all the layers from the group.
6818        clearLayers: function () {
6819            return this.eachLayer(this.removeLayer, this);
6820        },
6821  
6822        // @method invoke(methodName: String, …): this
6823        // Calls `methodName` on every layer contained in this group, passing any
6824        // additional parameters. Has no effect if the layers contained do not
6825        // implement `methodName`.
6826        invoke: function (methodName) {
6827            var args = Array.prototype.slice.call(arguments, 1),
6828                i, layer;
6829  
6830            for (i in this._layers) {
6831                layer = this._layers[i];
6832  
6833                if (layer[methodName]) {
6834                    layer[methodName].apply(layer, args);
6835                }
6836            }
6837  
6838            return this;
6839        },
6840  
6841        onAdd: function (map) {
6842            this.eachLayer(map.addLayer, map);
6843        },
6844  
6845        onRemove: function (map) {
6846            this.eachLayer(map.removeLayer, map);
6847        },
6848  
6849        // @method eachLayer(fn: Function, context?: Object): this
6850        // Iterates over the layers of the group, optionally specifying context of the iterator function.
6851        // ```js
6852        // group.eachLayer(function (layer) {
6853        // 	layer.bindPopup('Hello');
6854        // });
6855        // ```
6856        eachLayer: function (method, context) {
6857            for (var i in this._layers) {
6858                method.call(context, this._layers[i]);
6859            }
6860            return this;
6861        },
6862  
6863        // @method getLayer(id: Number): Layer
6864        // Returns the layer with the given internal ID.
6865        getLayer: function (id) {
6866            return this._layers[id];
6867        },
6868  
6869        // @method getLayers(): Layer[]
6870        // Returns an array of all the layers added to the group.
6871        getLayers: function () {
6872            var layers = [];
6873            this.eachLayer(layers.push, layers);
6874            return layers;
6875        },
6876  
6877        // @method setZIndex(zIndex: Number): this
6878        // Calls `setZIndex` on every layer contained in this group, passing the z-index.
6879        setZIndex: function (zIndex) {
6880            return this.invoke('setZIndex', zIndex);
6881        },
6882  
6883        // @method getLayerId(layer: Layer): Number
6884        // Returns the internal ID for a layer
6885        getLayerId: function (layer) {
6886            return stamp(layer);
6887        }
6888    });
6889  
6890  
6891    // @factory L.layerGroup(layers?: Layer[], options?: Object)
6892    // Create a layer group, optionally given an initial set of layers and an `options` object.
6893    var layerGroup = function (layers, options) {
6894        return new LayerGroup(layers, options);
6895    };
6896  
6897    /*
6898     * @class FeatureGroup
6899     * @aka L.FeatureGroup
6900     * @inherits LayerGroup
6901     *
6902     * Extended `LayerGroup` that makes it easier to do the same thing to all its member layers:
6903     *  * [`bindPopup`](#layer-bindpopup) binds a popup to all of the layers at once (likewise with [`bindTooltip`](#layer-bindtooltip))
6904     *  * Events are propagated to the `FeatureGroup`, so if the group has an event
6905     * handler, it will handle events from any of the layers. This includes mouse events
6906     * and custom events.
6907     *  * Has `layeradd` and `layerremove` events
6908     *
6909     * @example
6910     *
6911     * ```js
6912     * L.featureGroup([marker1, marker2, polyline])
6913     * 	.bindPopup('Hello world!')
6914     * 	.on('click', function() { alert('Clicked on a member of the group!'); })
6915     * 	.addTo(map);
6916     * ```
6917     */
6918  
6919    var FeatureGroup = LayerGroup.extend({
6920  
6921        addLayer: function (layer) {
6922            if (this.hasLayer(layer)) {
6923                return this;
6924            }
6925  
6926            layer.addEventParent(this);
6927  
6928            LayerGroup.prototype.addLayer.call(this, layer);
6929  
6930            // @event layeradd: LayerEvent
6931            // Fired when a layer is added to this `FeatureGroup`
6932            return this.fire('layeradd', {layer: layer});
6933        },
6934  
6935        removeLayer: function (layer) {
6936            if (!this.hasLayer(layer)) {
6937                return this;
6938            }
6939            if (layer in this._layers) {
6940                layer = this._layers[layer];
6941            }
6942  
6943            layer.removeEventParent(this);
6944  
6945            LayerGroup.prototype.removeLayer.call(this, layer);
6946  
6947            // @event layerremove: LayerEvent
6948            // Fired when a layer is removed from this `FeatureGroup`
6949            return this.fire('layerremove', {layer: layer});
6950        },
6951  
6952        // @method setStyle(style: Path options): this
6953        // Sets the given path options to each layer of the group that has a `setStyle` method.
6954        setStyle: function (style) {
6955            return this.invoke('setStyle', style);
6956        },
6957  
6958        // @method bringToFront(): this
6959        // Brings the layer group to the top of all other layers
6960        bringToFront: function () {
6961            return this.invoke('bringToFront');
6962        },
6963  
6964        // @method bringToBack(): this
6965        // Brings the layer group to the back of all other layers
6966        bringToBack: function () {
6967            return this.invoke('bringToBack');
6968        },
6969  
6970        // @method getBounds(): LatLngBounds
6971        // Returns the LatLngBounds of the Feature Group (created from bounds and coordinates of its children).
6972        getBounds: function () {
6973            var bounds = new LatLngBounds();
6974  
6975            for (var id in this._layers) {
6976                var layer = this._layers[id];
6977                bounds.extend(layer.getBounds ? layer.getBounds() : layer.getLatLng());
6978            }
6979            return bounds;
6980        }
6981    });
6982  
6983    // @factory L.featureGroup(layers?: Layer[], options?: Object)
6984    // Create a feature group, optionally given an initial set of layers and an `options` object.
6985    var featureGroup = function (layers, options) {
6986        return new FeatureGroup(layers, options);
6987    };
6988  
6989    /*
6990     * @class Icon
6991     * @aka L.Icon
6992     *
6993     * Represents an icon to provide when creating a marker.
6994     *
6995     * @example
6996     *
6997     * ```js
6998     * var myIcon = L.icon({
6999     *     iconUrl: 'my-icon.png',
7000     *     iconRetinaUrl: '[email protected]',
7001     *     iconSize: [38, 95],
7002     *     iconAnchor: [22, 94],
7003     *     popupAnchor: [-3, -76],
7004     *     shadowUrl: 'my-icon-shadow.png',
7005     *     shadowRetinaUrl: '[email protected]',
7006     *     shadowSize: [68, 95],
7007     *     shadowAnchor: [22, 94]
7008     * });
7009     *
7010     * L.marker([50.505, 30.57], {icon: myIcon}).addTo(map);
7011     * ```
7012     *
7013     * `L.Icon.Default` extends `L.Icon` and is the blue icon Leaflet uses for markers by default.
7014     *
7015     */
7016  
7017    var Icon = Class.extend({
7018  
7019        /* @section
7020         * @aka Icon options
7021         *
7022         * @option iconUrl: String = null
7023         * **(required)** The URL to the icon image (absolute or relative to your script path).
7024         *
7025         * @option iconRetinaUrl: String = null
7026         * The URL to a retina sized version of the icon image (absolute or relative to your
7027         * script path). Used for Retina screen devices.
7028         *
7029         * @option iconSize: Point = null
7030         * Size of the icon image in pixels.
7031         *
7032         * @option iconAnchor: Point = null
7033         * The coordinates of the "tip" of the icon (relative to its top left corner). The icon
7034         * will be aligned so that this point is at the marker's geographical location. Centered
7035         * by default if size is specified, also can be set in CSS with negative margins.
7036         *
7037         * @option popupAnchor: Point = [0, 0]
7038         * The coordinates of the point from which popups will "open", relative to the icon anchor.
7039         *
7040         * @option tooltipAnchor: Point = [0, 0]
7041         * The coordinates of the point from which tooltips will "open", relative to the icon anchor.
7042         *
7043         * @option shadowUrl: String = null
7044         * The URL to the icon shadow image. If not specified, no shadow image will be created.
7045         *
7046         * @option shadowRetinaUrl: String = null
7047         *
7048         * @option shadowSize: Point = null
7049         * Size of the shadow image in pixels.
7050         *
7051         * @option shadowAnchor: Point = null
7052         * The coordinates of the "tip" of the shadow (relative to its top left corner) (the same
7053         * as iconAnchor if not specified).
7054         *
7055         * @option className: String = ''
7056         * A custom class name to assign to both icon and shadow images. Empty by default.
7057         */
7058  
7059        options: {
7060            popupAnchor: [0, 0],
7061            tooltipAnchor: [0, 0],
7062  
7063            // @option crossOrigin: Boolean|String = false
7064            // Whether the crossOrigin attribute will be added to the tiles.
7065            // If a String is provided, all tiles will have their crossOrigin attribute set to the String provided. This is needed if you want to access tile pixel data.
7066            // Refer to [CORS Settings](https://developer.mozilla.org/en-US/docs/Web/HTML/CORS_settings_attributes) for valid String values.
7067            crossOrigin: false
7068        },
7069  
7070        initialize: function (options) {
7071            setOptions(this, options);
7072        },
7073  
7074        // @method createIcon(oldIcon?: HTMLElement): HTMLElement
7075        // Called internally when the icon has to be shown, returns a `<img>` HTML element
7076        // styled according to the options.
7077        createIcon: function (oldIcon) {
7078            return this._createIcon('icon', oldIcon);
7079        },
7080  
7081        // @method createShadow(oldIcon?: HTMLElement): HTMLElement
7082        // As `createIcon`, but for the shadow beneath it.
7083        createShadow: function (oldIcon) {
7084            return this._createIcon('shadow', oldIcon);
7085        },
7086  
7087        _createIcon: function (name, oldIcon) {
7088            var src = this._getIconUrl(name);
7089  
7090            if (!src) {
7091                if (name === 'icon') {
7092                    throw new Error('iconUrl not set in Icon options (see the docs).');
7093                }
7094                return null;
7095            }
7096  
7097            var img = this._createImg(src, oldIcon && oldIcon.tagName === 'IMG' ? oldIcon : null);
7098            this._setIconStyles(img, name);
7099  
7100            if (this.options.crossOrigin || this.options.crossOrigin === '') {
7101                img.crossOrigin = this.options.crossOrigin === true ? '' : this.options.crossOrigin;
7102            }
7103  
7104            return img;
7105        },
7106  
7107        _setIconStyles: function (img, name) {
7108            var options = this.options;
7109            var sizeOption = options[name + 'Size'];
7110  
7111            if (typeof sizeOption === 'number') {
7112                sizeOption = [sizeOption, sizeOption];
7113            }
7114  
7115            var size = toPoint(sizeOption),
7116                anchor = toPoint(name === 'shadow' && options.shadowAnchor || options.iconAnchor ||
7117                        size && size.divideBy(2, true));
7118  
7119            img.className = 'leaflet-marker-' + name + ' ' + (options.className || '');
7120  
7121            if (anchor) {
7122                img.style.marginLeft = (-anchor.x) + 'px';
7123                img.style.marginTop  = (-anchor.y) + 'px';
7124            }
7125  
7126            if (size) {
7127                img.style.width  = size.x + 'px';
7128                img.style.height = size.y + 'px';
7129            }
7130        },
7131  
7132        _createImg: function (src, el) {
7133            el = el || document.createElement('img');
7134            el.src = src;
7135            return el;
7136        },
7137  
7138        _getIconUrl: function (name) {
7139            return Browser.retina && this.options[name + 'RetinaUrl'] || this.options[name + 'Url'];
7140        }
7141    });
7142  
7143  
7144    // @factory L.icon(options: Icon options)
7145    // Creates an icon instance with the given options.
7146    function icon(options) {
7147        return new Icon(options);
7148    }
7149  
7150    /*
7151     * @miniclass Icon.Default (Icon)
7152     * @aka L.Icon.Default
7153     * @section
7154     *
7155     * A trivial subclass of `Icon`, represents the icon to use in `Marker`s when
7156     * no icon is specified. Points to the blue marker image distributed with Leaflet
7157     * releases.
7158     *
7159     * In order to customize the default icon, just change the properties of `L.Icon.Default.prototype.options`
7160     * (which is a set of `Icon options`).
7161     *
7162     * If you want to _completely_ replace the default icon, override the
7163     * `L.Marker.prototype.options.icon` with your own icon instead.
7164     */
7165  
7166    var IconDefault = Icon.extend({
7167  
7168        options: {
7169            iconUrl:       'marker-icon.png',
7170            iconRetinaUrl: 'marker-icon-2x.png',
7171            shadowUrl:     'marker-shadow.png',
7172            iconSize:    [25, 41],
7173            iconAnchor:  [12, 41],
7174            popupAnchor: [1, -34],
7175            tooltipAnchor: [16, -28],
7176            shadowSize:  [41, 41]
7177        },
7178  
7179        _getIconUrl: function (name) {
7180            if (typeof IconDefault.imagePath !== 'string') {	// Deprecated, backwards-compatibility only
7181                IconDefault.imagePath = this._detectIconPath();
7182            }
7183  
7184            // @option imagePath: String
7185            // `Icon.Default` will try to auto-detect the location of the
7186            // blue icon images. If you are placing these images in a non-standard
7187            // way, set this option to point to the right path.
7188            return (this.options.imagePath || IconDefault.imagePath) + Icon.prototype._getIconUrl.call(this, name);
7189        },
7190  
7191        _stripUrl: function (path) {	// separate function to use in tests
7192            var strip = function (str, re, idx) {
7193                var match = re.exec(str);
7194                return match && match[idx];
7195            };
7196            path = strip(path, /^url\((['"])?(.+)\1\)$/, 2);
7197            return path && strip(path, /^(.*)marker-icon\.png$/, 1);
7198        },
7199  
7200        _detectIconPath: function () {
7201            var el = create$1('div',  'leaflet-default-icon-path', document.body);
7202            var path = getStyle(el, 'background-image') ||
7203                       getStyle(el, 'backgroundImage');	// IE8
7204  
7205            document.body.removeChild(el);
7206            path = this._stripUrl(path);
7207            if (path) { return path; }
7208            var link = document.querySelector('link[href$="leaflet.css"]');
7209            if (!link) { return ''; }
7210            return link.href.substring(0, link.href.length - 'leaflet.css'.length - 1);
7211        }
7212    });
7213  
7214    /*
7215     * L.Handler.MarkerDrag is used internally by L.Marker to make the markers draggable.
7216     */
7217  
7218  
7219    /* @namespace Marker
7220     * @section Interaction handlers
7221     *
7222     * Interaction handlers are properties of a marker instance that allow you to control interaction behavior in runtime, enabling or disabling certain features such as dragging (see `Handler` methods). Example:
7223     *
7224     * ```js
7225     * marker.dragging.disable();
7226     * ```
7227     *
7228     * @property dragging: Handler
7229     * Marker dragging handler (by both mouse and touch). Only valid when the marker is on the map (Otherwise set [`marker.options.draggable`](#marker-draggable)).
7230     */
7231  
7232    var MarkerDrag = Handler.extend({
7233        initialize: function (marker) {
7234            this._marker = marker;
7235        },
7236  
7237        addHooks: function () {
7238            var icon = this._marker._icon;
7239  
7240            if (!this._draggable) {
7241                this._draggable = new Draggable(icon, icon, true);
7242            }
7243  
7244            this._draggable.on({
7245                dragstart: this._onDragStart,
7246                predrag: this._onPreDrag,
7247                drag: this._onDrag,
7248                dragend: this._onDragEnd
7249            }, this).enable();
7250  
7251            addClass(icon, 'leaflet-marker-draggable');
7252        },
7253  
7254        removeHooks: function () {
7255            this._draggable.off({
7256                dragstart: this._onDragStart,
7257                predrag: this._onPreDrag,
7258                drag: this._onDrag,
7259                dragend: this._onDragEnd
7260            }, this).disable();
7261  
7262            if (this._marker._icon) {
7263                removeClass(this._marker._icon, 'leaflet-marker-draggable');
7264            }
7265        },
7266  
7267        moved: function () {
7268            return this._draggable && this._draggable._moved;
7269        },
7270  
7271        _adjustPan: function (e) {
7272            var marker = this._marker,
7273                map = marker._map,
7274                speed = this._marker.options.autoPanSpeed,
7275                padding = this._marker.options.autoPanPadding,
7276                iconPos = getPosition(marker._icon),
7277                bounds = map.getPixelBounds(),
7278                origin = map.getPixelOrigin();
7279  
7280            var panBounds = toBounds(
7281                bounds.min._subtract(origin).add(padding),
7282                bounds.max._subtract(origin).subtract(padding)
7283            );
7284  
7285            if (!panBounds.contains(iconPos)) {
7286                // Compute incremental movement
7287                var movement = toPoint(
7288                    (Math.max(panBounds.max.x, iconPos.x) - panBounds.max.x) / (bounds.max.x - panBounds.max.x) -
7289                    (Math.min(panBounds.min.x, iconPos.x) - panBounds.min.x) / (bounds.min.x - panBounds.min.x),
7290  
7291                    (Math.max(panBounds.max.y, iconPos.y) - panBounds.max.y) / (bounds.max.y - panBounds.max.y) -
7292                    (Math.min(panBounds.min.y, iconPos.y) - panBounds.min.y) / (bounds.min.y - panBounds.min.y)
7293                ).multiplyBy(speed);
7294  
7295                map.panBy(movement, {animate: false});
7296  
7297                this._draggable._newPos._add(movement);
7298                this._draggable._startPos._add(movement);
7299  
7300                setPosition(marker._icon, this._draggable._newPos);
7301                this._onDrag(e);
7302  
7303                this._panRequest = requestAnimFrame(this._adjustPan.bind(this, e));
7304            }
7305        },
7306  
7307        _onDragStart: function () {
7308            // @section Dragging events
7309            // @event dragstart: Event
7310            // Fired when the user starts dragging the marker.
7311  
7312            // @event movestart: Event
7313            // Fired when the marker starts moving (because of dragging).
7314  
7315            this._oldLatLng = this._marker.getLatLng();
7316  
7317            // When using ES6 imports it could not be set when `Popup` was not imported as well
7318            this._marker.closePopup && this._marker.closePopup();
7319  
7320            this._marker
7321                .fire('movestart')
7322                .fire('dragstart');
7323        },
7324  
7325        _onPreDrag: function (e) {
7326            if (this._marker.options.autoPan) {
7327                cancelAnimFrame(this._panRequest);
7328                this._panRequest = requestAnimFrame(this._adjustPan.bind(this, e));
7329            }
7330        },
7331  
7332        _onDrag: function (e) {
7333            var marker = this._marker,
7334                shadow = marker._shadow,
7335                iconPos = getPosition(marker._icon),
7336                latlng = marker._map.layerPointToLatLng(iconPos);
7337  
7338            // update shadow position
7339            if (shadow) {
7340                setPosition(shadow, iconPos);
7341            }
7342  
7343            marker._latlng = latlng;
7344            e.latlng = latlng;
7345            e.oldLatLng = this._oldLatLng;
7346  
7347            // @event drag: Event
7348            // Fired repeatedly while the user drags the marker.
7349            marker
7350                .fire('move', e)
7351                .fire('drag', e);
7352        },
7353  
7354        _onDragEnd: function (e) {
7355            // @event dragend: DragEndEvent
7356            // Fired when the user stops dragging the marker.
7357  
7358             cancelAnimFrame(this._panRequest);
7359  
7360            // @event moveend: Event
7361            // Fired when the marker stops moving (because of dragging).
7362            delete this._oldLatLng;
7363            this._marker
7364                .fire('moveend')
7365                .fire('dragend', e);
7366        }
7367    });
7368  
7369    /*
7370     * @class Marker
7371     * @inherits Interactive layer
7372     * @aka L.Marker
7373     * L.Marker is used to display clickable/draggable icons on the map. Extends `Layer`.
7374     *
7375     * @example
7376     *
7377     * ```js
7378     * L.marker([50.5, 30.5]).addTo(map);
7379     * ```
7380     */
7381  
7382    var Marker = Layer.extend({
7383  
7384        // @section
7385        // @aka Marker options
7386        options: {
7387            // @option icon: Icon = *
7388            // Icon instance to use for rendering the marker.
7389            // See [Icon documentation](#L.Icon) for details on how to customize the marker icon.
7390            // If not specified, a common instance of `L.Icon.Default` is used.
7391            icon: new IconDefault(),
7392  
7393            // Option inherited from "Interactive layer" abstract class
7394            interactive: true,
7395  
7396            // @option keyboard: Boolean = true
7397            // Whether the marker can be tabbed to with a keyboard and clicked by pressing enter.
7398            keyboard: true,
7399  
7400            // @option title: String = ''
7401            // Text for the browser tooltip that appear on marker hover (no tooltip by default).
7402            title: '',
7403  
7404            // @option alt: String = 'Marker'
7405            // Text for the `alt` attribute of the icon image (useful for accessibility).
7406            alt: 'Marker',
7407  
7408            // @option zIndexOffset: Number = 0
7409            // By default, marker images zIndex is set automatically based on its latitude. Use this option if you want to put the marker on top of all others (or below), specifying a high value like `1000` (or high negative value, respectively).
7410            zIndexOffset: 0,
7411  
7412            // @option opacity: Number = 1.0
7413            // The opacity of the marker.
7414            opacity: 1,
7415  
7416            // @option riseOnHover: Boolean = false
7417            // If `true`, the marker will get on top of others when you hover the mouse over it.
7418            riseOnHover: false,
7419  
7420            // @option riseOffset: Number = 250
7421            // The z-index offset used for the `riseOnHover` feature.
7422            riseOffset: 250,
7423  
7424            // @option pane: String = 'markerPane'
7425            // `Map pane` where the markers icon will be added.
7426            pane: 'markerPane',
7427  
7428            // @option shadowPane: String = 'shadowPane'
7429            // `Map pane` where the markers shadow will be added.
7430            shadowPane: 'shadowPane',
7431  
7432            // @option bubblingMouseEvents: Boolean = false
7433            // When `true`, a mouse event on this marker will trigger the same event on the map
7434            // (unless [`L.DomEvent.stopPropagation`](#domevent-stoppropagation) is used).
7435            bubblingMouseEvents: false,
7436  
7437            // @option autoPanOnFocus: Boolean = true
7438            // When `true`, the map will pan whenever the marker is focused (via
7439            // e.g. pressing `tab` on the keyboard) to ensure the marker is
7440            // visible within the map's bounds
7441            autoPanOnFocus: true,
7442  
7443            // @section Draggable marker options
7444            // @option draggable: Boolean = false
7445            // Whether the marker is draggable with mouse/touch or not.
7446            draggable: false,
7447  
7448            // @option autoPan: Boolean = false
7449            // Whether to pan the map when dragging this marker near its edge or not.
7450            autoPan: false,
7451  
7452            // @option autoPanPadding: Point = Point(50, 50)
7453            // Distance (in pixels to the left/right and to the top/bottom) of the
7454            // map edge to start panning the map.
7455            autoPanPadding: [50, 50],
7456  
7457            // @option autoPanSpeed: Number = 10
7458            // Number of pixels the map should pan by.
7459            autoPanSpeed: 10
7460        },
7461  
7462        /* @section
7463         *
7464         * In addition to [shared layer methods](#Layer) like `addTo()` and `remove()` and [popup methods](#Popup) like bindPopup() you can also use the following methods:
7465         */
7466  
7467        initialize: function (latlng, options) {
7468            setOptions(this, options);
7469            this._latlng = toLatLng(latlng);
7470        },
7471  
7472        onAdd: function (map) {
7473            this._zoomAnimated = this._zoomAnimated && map.options.markerZoomAnimation;
7474  
7475            if (this._zoomAnimated) {
7476                map.on('zoomanim', this._animateZoom, this);
7477            }
7478  
7479            this._initIcon();
7480            this.update();
7481        },
7482  
7483        onRemove: function (map) {
7484            if (this.dragging && this.dragging.enabled()) {
7485                this.options.draggable = true;
7486                this.dragging.removeHooks();
7487            }
7488            delete this.dragging;
7489  
7490            if (this._zoomAnimated) {
7491                map.off('zoomanim', this._animateZoom, this);
7492            }
7493  
7494            this._removeIcon();
7495            this._removeShadow();
7496        },
7497  
7498        getEvents: function () {
7499            return {
7500                zoom: this.update,
7501                viewreset: this.update
7502            };
7503        },
7504  
7505        // @method getLatLng: LatLng
7506        // Returns the current geographical position of the marker.
7507        getLatLng: function () {
7508            return this._latlng;
7509        },
7510  
7511        // @method setLatLng(latlng: LatLng): this
7512        // Changes the marker position to the given point.
7513        setLatLng: function (latlng) {
7514            var oldLatLng = this._latlng;
7515            this._latlng = toLatLng(latlng);
7516            this.update();
7517  
7518            // @event move: Event
7519            // Fired when the marker is moved via [`setLatLng`](#marker-setlatlng) or by [dragging](#marker-dragging). Old and new coordinates are included in event arguments as `oldLatLng`, `latlng`.
7520            return this.fire('move', {oldLatLng: oldLatLng, latlng: this._latlng});
7521        },
7522  
7523        // @method setZIndexOffset(offset: Number): this
7524        // Changes the [zIndex offset](#marker-zindexoffset) of the marker.
7525        setZIndexOffset: function (offset) {
7526            this.options.zIndexOffset = offset;
7527            return this.update();
7528        },
7529  
7530        // @method getIcon: Icon
7531        // Returns the current icon used by the marker
7532        getIcon: function () {
7533            return this.options.icon;
7534        },
7535  
7536        // @method setIcon(icon: Icon): this
7537        // Changes the marker icon.
7538        setIcon: function (icon) {
7539  
7540            this.options.icon = icon;
7541  
7542            if (this._map) {
7543                this._initIcon();
7544                this.update();
7545            }
7546  
7547            if (this._popup) {
7548                this.bindPopup(this._popup, this._popup.options);
7549            }
7550  
7551            return this;
7552        },
7553  
7554        getElement: function () {
7555            return this._icon;
7556        },
7557  
7558        update: function () {
7559  
7560            if (this._icon && this._map) {
7561                var pos = this._map.latLngToLayerPoint(this._latlng).round();
7562                this._setPos(pos);
7563            }
7564  
7565            return this;
7566        },
7567  
7568        _initIcon: function () {
7569            var options = this.options,
7570                classToAdd = 'leaflet-zoom-' + (this._zoomAnimated ? 'animated' : 'hide');
7571  
7572            var icon = options.icon.createIcon(this._icon),
7573                addIcon = false;
7574  
7575            // if we're not reusing the icon, remove the old one and init new one
7576            if (icon !== this._icon) {
7577                if (this._icon) {
7578                    this._removeIcon();
7579                }
7580                addIcon = true;
7581  
7582                if (options.title) {
7583                    icon.title = options.title;
7584                }
7585  
7586                if (icon.tagName === 'IMG') {
7587                    icon.alt = options.alt || '';
7588                }
7589            }
7590  
7591            addClass(icon, classToAdd);
7592  
7593            if (options.keyboard) {
7594                icon.tabIndex = '0';
7595                icon.setAttribute('role', 'button');
7596            }
7597  
7598            this._icon = icon;
7599  
7600            if (options.riseOnHover) {
7601                this.on({
7602                    mouseover: this._bringToFront,
7603                    mouseout: this._resetZIndex
7604                });
7605            }
7606  
7607            if (this.options.autoPanOnFocus) {
7608                on(icon, 'focus', this._panOnFocus, this);
7609            }
7610  
7611            var newShadow = options.icon.createShadow(this._shadow),
7612                addShadow = false;
7613  
7614            if (newShadow !== this._shadow) {
7615                this._removeShadow();
7616                addShadow = true;
7617            }
7618  
7619            if (newShadow) {
7620                addClass(newShadow, classToAdd);
7621                newShadow.alt = '';
7622            }
7623            this._shadow = newShadow;
7624  
7625  
7626            if (options.opacity < 1) {
7627                this._updateOpacity();
7628            }
7629  
7630  
7631            if (addIcon) {
7632                this.getPane().appendChild(this._icon);
7633            }
7634            this._initInteraction();
7635            if (newShadow && addShadow) {
7636                this.getPane(options.shadowPane).appendChild(this._shadow);
7637            }
7638        },
7639  
7640        _removeIcon: function () {
7641            if (this.options.riseOnHover) {
7642                this.off({
7643                    mouseover: this._bringToFront,
7644                    mouseout: this._resetZIndex
7645                });
7646            }
7647  
7648            if (this.options.autoPanOnFocus) {
7649                off(this._icon, 'focus', this._panOnFocus, this);
7650            }
7651  
7652            remove(this._icon);
7653            this.removeInteractiveTarget(this._icon);
7654  
7655            this._icon = null;
7656        },
7657  
7658        _removeShadow: function () {
7659            if (this._shadow) {
7660                remove(this._shadow);
7661            }
7662            this._shadow = null;
7663        },
7664  
7665        _setPos: function (pos) {
7666  
7667            if (this._icon) {
7668                setPosition(this._icon, pos);
7669            }
7670  
7671            if (this._shadow) {
7672                setPosition(this._shadow, pos);
7673            }
7674  
7675            this._zIndex = pos.y + this.options.zIndexOffset;
7676  
7677            this._resetZIndex();
7678        },
7679  
7680        _updateZIndex: function (offset) {
7681            if (this._icon) {
7682                this._icon.style.zIndex = this._zIndex + offset;
7683            }
7684        },
7685  
7686        _animateZoom: function (opt) {
7687            var pos = this._map._latLngToNewLayerPoint(this._latlng, opt.zoom, opt.center).round();
7688  
7689            this._setPos(pos);
7690        },
7691  
7692        _initInteraction: function () {
7693  
7694            if (!this.options.interactive) { return; }
7695  
7696            addClass(this._icon, 'leaflet-interactive');
7697  
7698            this.addInteractiveTarget(this._icon);
7699  
7700            if (MarkerDrag) {
7701                var draggable = this.options.draggable;
7702                if (this.dragging) {
7703                    draggable = this.dragging.enabled();
7704                    this.dragging.disable();
7705                }
7706  
7707                this.dragging = new MarkerDrag(this);
7708  
7709                if (draggable) {
7710                    this.dragging.enable();
7711                }
7712            }
7713        },
7714  
7715        // @method setOpacity(opacity: Number): this
7716        // Changes the opacity of the marker.
7717        setOpacity: function (opacity) {
7718            this.options.opacity = opacity;
7719            if (this._map) {
7720                this._updateOpacity();
7721            }
7722  
7723            return this;
7724        },
7725  
7726        _updateOpacity: function () {
7727            var opacity = this.options.opacity;
7728  
7729            if (this._icon) {
7730                setOpacity(this._icon, opacity);
7731            }
7732  
7733            if (this._shadow) {
7734                setOpacity(this._shadow, opacity);
7735            }
7736        },
7737  
7738        _bringToFront: function () {
7739            this._updateZIndex(this.options.riseOffset);
7740        },
7741  
7742        _resetZIndex: function () {
7743            this._updateZIndex(0);
7744        },
7745  
7746        _panOnFocus: function () {
7747            var map = this._map;
7748            if (!map) { return; }
7749  
7750            var iconOpts = this.options.icon.options;
7751            var size = toPoint(iconOpts.iconSize);
7752            var anchor = toPoint(iconOpts.iconAnchor);
7753  
7754            map.panInside(this._latlng, {
7755                paddingTopLeft: anchor,
7756                paddingBottomRight: size.subtract(anchor)
7757            });
7758        },
7759  
7760        _getPopupAnchor: function () {
7761            return this.options.icon.options.popupAnchor;
7762        },
7763  
7764        _getTooltipAnchor: function () {
7765            return this.options.icon.options.tooltipAnchor;
7766        }
7767    });
7768  
7769  
7770    // factory L.marker(latlng: LatLng, options? : Marker options)
7771  
7772    // @factory L.marker(latlng: LatLng, options? : Marker options)
7773    // Instantiates a Marker object given a geographical point and optionally an options object.
7774    function marker(latlng, options) {
7775        return new Marker(latlng, options);
7776    }
7777  
7778    /*
7779     * @class Path
7780     * @aka L.Path
7781     * @inherits Interactive layer
7782     *
7783     * An abstract class that contains options and constants shared between vector
7784     * overlays (Polygon, Polyline, Circle). Do not use it directly. Extends `Layer`.
7785     */
7786  
7787    var Path = Layer.extend({
7788  
7789        // @section
7790        // @aka Path options
7791        options: {
7792            // @option stroke: Boolean = true
7793            // Whether to draw stroke along the path. Set it to `false` to disable borders on polygons or circles.
7794            stroke: true,
7795  
7796            // @option color: String = '#3388ff'
7797            // Stroke color
7798            color: '#3388ff',
7799  
7800            // @option weight: Number = 3
7801            // Stroke width in pixels
7802            weight: 3,
7803  
7804            // @option opacity: Number = 1.0
7805            // Stroke opacity
7806            opacity: 1,
7807  
7808            // @option lineCap: String= 'round'
7809            // A string that defines [shape to be used at the end](https://developer.mozilla.org/docs/Web/SVG/Attribute/stroke-linecap) of the stroke.
7810            lineCap: 'round',
7811  
7812            // @option lineJoin: String = 'round'
7813            // A string that defines [shape to be used at the corners](https://developer.mozilla.org/docs/Web/SVG/Attribute/stroke-linejoin) of the stroke.
7814            lineJoin: 'round',
7815  
7816            // @option dashArray: String = null
7817            // A string that defines the stroke [dash pattern](https://developer.mozilla.org/docs/Web/SVG/Attribute/stroke-dasharray). Doesn't work on `Canvas`-powered layers in [some old browsers](https://developer.mozilla.org/docs/Web/API/CanvasRenderingContext2D/setLineDash#Browser_compatibility).
7818            dashArray: null,
7819  
7820            // @option dashOffset: String = null
7821            // A string that defines the [distance into the dash pattern to start the dash](https://developer.mozilla.org/docs/Web/SVG/Attribute/stroke-dashoffset). Doesn't work on `Canvas`-powered layers in [some old browsers](https://developer.mozilla.org/docs/Web/API/CanvasRenderingContext2D/setLineDash#Browser_compatibility).
7822            dashOffset: null,
7823  
7824            // @option fill: Boolean = depends
7825            // Whether to fill the path with color. Set it to `false` to disable filling on polygons or circles.
7826            fill: false,
7827  
7828            // @option fillColor: String = *
7829            // Fill color. Defaults to the value of the [`color`](#path-color) option
7830            fillColor: null,
7831  
7832            // @option fillOpacity: Number = 0.2
7833            // Fill opacity.
7834            fillOpacity: 0.2,
7835  
7836            // @option fillRule: String = 'evenodd'
7837            // A string that defines [how the inside of a shape](https://developer.mozilla.org/docs/Web/SVG/Attribute/fill-rule) is determined.
7838            fillRule: 'evenodd',
7839  
7840            // className: '',
7841  
7842            // Option inherited from "Interactive layer" abstract class
7843            interactive: true,
7844  
7845            // @option bubblingMouseEvents: Boolean = true
7846            // When `true`, a mouse event on this path will trigger the same event on the map
7847            // (unless [`L.DomEvent.stopPropagation`](#domevent-stoppropagation) is used).
7848            bubblingMouseEvents: true
7849        },
7850  
7851        beforeAdd: function (map) {
7852            // Renderer is set here because we need to call renderer.getEvents
7853            // before this.getEvents.
7854            this._renderer = map.getRenderer(this);
7855        },
7856  
7857        onAdd: function () {
7858            this._renderer._initPath(this);
7859            this._reset();
7860            this._renderer._addPath(this);
7861        },
7862  
7863        onRemove: function () {
7864            this._renderer._removePath(this);
7865        },
7866  
7867        // @method redraw(): this
7868        // Redraws the layer. Sometimes useful after you changed the coordinates that the path uses.
7869        redraw: function () {
7870            if (this._map) {
7871                this._renderer._updatePath(this);
7872            }
7873            return this;
7874        },
7875  
7876        // @method setStyle(style: Path options): this
7877        // Changes the appearance of a Path based on the options in the `Path options` object.
7878        setStyle: function (style) {
7879            setOptions(this, style);
7880            if (this._renderer) {
7881                this._renderer._updateStyle(this);
7882                if (this.options.stroke && style && Object.prototype.hasOwnProperty.call(style, 'weight')) {
7883                    this._updateBounds();
7884                }
7885            }
7886            return this;
7887        },
7888  
7889        // @method bringToFront(): this
7890        // Brings the layer to the top of all path layers.
7891        bringToFront: function () {
7892            if (this._renderer) {
7893                this._renderer._bringToFront(this);
7894            }
7895            return this;
7896        },
7897  
7898        // @method bringToBack(): this
7899        // Brings the layer to the bottom of all path layers.
7900        bringToBack: function () {
7901            if (this._renderer) {
7902                this._renderer._bringToBack(this);
7903            }
7904            return this;
7905        },
7906  
7907        getElement: function () {
7908            return this._path;
7909        },
7910  
7911        _reset: function () {
7912            // defined in child classes
7913            this._project();
7914            this._update();
7915        },
7916  
7917        _clickTolerance: function () {
7918            // used when doing hit detection for Canvas layers
7919            return (this.options.stroke ? this.options.weight / 2 : 0) +
7920              (this._renderer.options.tolerance || 0);
7921        }
7922    });
7923  
7924    /*
7925     * @class CircleMarker
7926     * @aka L.CircleMarker
7927     * @inherits Path
7928     *
7929     * A circle of a fixed size with radius specified in pixels. Extends `Path`.
7930     */
7931  
7932    var CircleMarker = Path.extend({
7933  
7934        // @section
7935        // @aka CircleMarker options
7936        options: {
7937            fill: true,
7938  
7939            // @option radius: Number = 10
7940            // Radius of the circle marker, in pixels
7941            radius: 10
7942        },
7943  
7944        initialize: function (latlng, options) {
7945            setOptions(this, options);
7946            this._latlng = toLatLng(latlng);
7947            this._radius = this.options.radius;
7948        },
7949  
7950        // @method setLatLng(latLng: LatLng): this
7951        // Sets the position of a circle marker to a new location.
7952        setLatLng: function (latlng) {
7953            var oldLatLng = this._latlng;
7954            this._latlng = toLatLng(latlng);
7955            this.redraw();
7956  
7957            // @event move: Event
7958            // Fired when the marker is moved via [`setLatLng`](#circlemarker-setlatlng). Old and new coordinates are included in event arguments as `oldLatLng`, `latlng`.
7959            return this.fire('move', {oldLatLng: oldLatLng, latlng: this._latlng});
7960        },
7961  
7962        // @method getLatLng(): LatLng
7963        // Returns the current geographical position of the circle marker
7964        getLatLng: function () {
7965            return this._latlng;
7966        },
7967  
7968        // @method setRadius(radius: Number): this
7969        // Sets the radius of a circle marker. Units are in pixels.
7970        setRadius: function (radius) {
7971            this.options.radius = this._radius = radius;
7972            return this.redraw();
7973        },
7974  
7975        // @method getRadius(): Number
7976        // Returns the current radius of the circle
7977        getRadius: function () {
7978            return this._radius;
7979        },
7980  
7981        setStyle : function (options) {
7982            var radius = options && options.radius || this._radius;
7983            Path.prototype.setStyle.call(this, options);
7984            this.setRadius(radius);
7985            return this;
7986        },
7987  
7988        _project: function () {
7989            this._point = this._map.latLngToLayerPoint(this._latlng);
7990            this._updateBounds();
7991        },
7992  
7993        _updateBounds: function () {
7994            var r = this._radius,
7995                r2 = this._radiusY || r,
7996                w = this._clickTolerance(),
7997                p = [r + w, r2 + w];
7998            this._pxBounds = new Bounds(this._point.subtract(p), this._point.add(p));
7999        },
8000  
8001        _update: function () {
8002            if (this._map) {
8003                this._updatePath();
8004            }
8005        },
8006  
8007        _updatePath: function () {
8008            this._renderer._updateCircle(this);
8009        },
8010  
8011        _empty: function () {
8012            return this._radius && !this._renderer._bounds.intersects(this._pxBounds);
8013        },
8014  
8015        // Needed by the `Canvas` renderer for interactivity
8016        _containsPoint: function (p) {
8017            return p.distanceTo(this._point) <= this._radius + this._clickTolerance();
8018        }
8019    });
8020  
8021  
8022    // @factory L.circleMarker(latlng: LatLng, options?: CircleMarker options)
8023    // Instantiates a circle marker object given a geographical point, and an optional options object.
8024    function circleMarker(latlng, options) {
8025        return new CircleMarker(latlng, options);
8026    }
8027  
8028    /*
8029     * @class Circle
8030     * @aka L.Circle
8031     * @inherits CircleMarker
8032     *
8033     * A class for drawing circle overlays on a map. Extends `CircleMarker`.
8034     *
8035     * It's an approximation and starts to diverge from a real circle closer to poles (due to projection distortion).
8036     *
8037     * @example
8038     *
8039     * ```js
8040     * L.circle([50.5, 30.5], {radius: 200}).addTo(map);
8041     * ```
8042     */
8043  
8044    var Circle = CircleMarker.extend({
8045  
8046        initialize: function (latlng, options, legacyOptions) {
8047            if (typeof options === 'number') {
8048                // Backwards compatibility with 0.7.x factory (latlng, radius, options?)
8049                options = extend({}, legacyOptions, {radius: options});
8050            }
8051            setOptions(this, options);
8052            this._latlng = toLatLng(latlng);
8053  
8054            if (isNaN(this.options.radius)) { throw new Error('Circle radius cannot be NaN'); }
8055  
8056            // @section
8057            // @aka Circle options
8058            // @option radius: Number; Radius of the circle, in meters.
8059            this._mRadius = this.options.radius;
8060        },
8061  
8062        // @method setRadius(radius: Number): this
8063        // Sets the radius of a circle. Units are in meters.
8064        setRadius: function (radius) {
8065            this._mRadius = radius;
8066            return this.redraw();
8067        },
8068  
8069        // @method getRadius(): Number
8070        // Returns the current radius of a circle. Units are in meters.
8071        getRadius: function () {
8072            return this._mRadius;
8073        },
8074  
8075        // @method getBounds(): LatLngBounds
8076        // Returns the `LatLngBounds` of the path.
8077        getBounds: function () {
8078            var half = [this._radius, this._radiusY || this._radius];
8079  
8080            return new LatLngBounds(
8081                this._map.layerPointToLatLng(this._point.subtract(half)),
8082                this._map.layerPointToLatLng(this._point.add(half)));
8083        },
8084  
8085        setStyle: Path.prototype.setStyle,
8086  
8087        _project: function () {
8088  
8089            var lng = this._latlng.lng,
8090                lat = this._latlng.lat,
8091                map = this._map,
8092                crs = map.options.crs;
8093  
8094            if (crs.distance === Earth.distance) {
8095                var d = Math.PI / 180,
8096                    latR = (this._mRadius / Earth.R) / d,
8097                    top = map.project([lat + latR, lng]),
8098                    bottom = map.project([lat - latR, lng]),
8099                    p = top.add(bottom).divideBy(2),
8100                    lat2 = map.unproject(p).lat,
8101                    lngR = Math.acos((Math.cos(latR * d) - Math.sin(lat * d) * Math.sin(lat2 * d)) /
8102                            (Math.cos(lat * d) * Math.cos(lat2 * d))) / d;
8103  
8104                if (isNaN(lngR) || lngR === 0) {
8105                    lngR = latR / Math.cos(Math.PI / 180 * lat); // Fallback for edge case, #2425
8106                }
8107  
8108                this._point = p.subtract(map.getPixelOrigin());
8109                this._radius = isNaN(lngR) ? 0 : p.x - map.project([lat2, lng - lngR]).x;
8110                this._radiusY = p.y - top.y;
8111  
8112            } else {
8113                var latlng2 = crs.unproject(crs.project(this._latlng).subtract([this._mRadius, 0]));
8114  
8115                this._point = map.latLngToLayerPoint(this._latlng);
8116                this._radius = this._point.x - map.latLngToLayerPoint(latlng2).x;
8117            }
8118  
8119            this._updateBounds();
8120        }
8121    });
8122  
8123    // @factory L.circle(latlng: LatLng, options?: Circle options)
8124    // Instantiates a circle object given a geographical point, and an options object
8125    // which contains the circle radius.
8126    // @alternative
8127    // @factory L.circle(latlng: LatLng, radius: Number, options?: Circle options)
8128    // Obsolete way of instantiating a circle, for compatibility with 0.7.x code.
8129    // Do not use in new applications or plugins.
8130    function circle(latlng, options, legacyOptions) {
8131        return new Circle(latlng, options, legacyOptions);
8132    }
8133  
8134    /*
8135     * @class Polyline
8136     * @aka L.Polyline
8137     * @inherits Path
8138     *
8139     * A class for drawing polyline overlays on a map. Extends `Path`.
8140     *
8141     * @example
8142     *
8143     * ```js
8144     * // create a red polyline from an array of LatLng points
8145     * var latlngs = [
8146     * 	[45.51, -122.68],
8147     * 	[37.77, -122.43],
8148     * 	[34.04, -118.2]
8149     * ];
8150     *
8151     * var polyline = L.polyline(latlngs, {color: 'red'}).addTo(map);
8152     *
8153     * // zoom the map to the polyline
8154     * map.fitBounds(polyline.getBounds());
8155     * ```
8156     *
8157     * You can also pass a multi-dimensional array to represent a `MultiPolyline` shape:
8158     *
8159     * ```js
8160     * // create a red polyline from an array of arrays of LatLng points
8161     * var latlngs = [
8162     * 	[[45.51, -122.68],
8163     * 	 [37.77, -122.43],
8164     * 	 [34.04, -118.2]],
8165     * 	[[40.78, -73.91],
8166     * 	 [41.83, -87.62],
8167     * 	 [32.76, -96.72]]
8168     * ];
8169     * ```
8170     */
8171  
8172  
8173    var Polyline = Path.extend({
8174  
8175        // @section
8176        // @aka Polyline options
8177        options: {
8178            // @option smoothFactor: Number = 1.0
8179            // How much to simplify the polyline on each zoom level. More means
8180            // better performance and smoother look, and less means more accurate representation.
8181            smoothFactor: 1.0,
8182  
8183            // @option noClip: Boolean = false
8184            // Disable polyline clipping.
8185            noClip: false
8186        },
8187  
8188        initialize: function (latlngs, options) {
8189            setOptions(this, options);
8190            this._setLatLngs(latlngs);
8191        },
8192  
8193        // @method getLatLngs(): LatLng[]
8194        // Returns an array of the points in the path, or nested arrays of points in case of multi-polyline.
8195        getLatLngs: function () {
8196            return this._latlngs;
8197        },
8198  
8199        // @method setLatLngs(latlngs: LatLng[]): this
8200        // Replaces all the points in the polyline with the given array of geographical points.
8201        setLatLngs: function (latlngs) {
8202            this._setLatLngs(latlngs);
8203            return this.redraw();
8204        },
8205  
8206        // @method isEmpty(): Boolean
8207        // Returns `true` if the Polyline has no LatLngs.
8208        isEmpty: function () {
8209            return !this._latlngs.length;
8210        },
8211  
8212        // @method closestLayerPoint(p: Point): Point
8213        // Returns the point closest to `p` on the Polyline.
8214        closestLayerPoint: function (p) {
8215            var minDistance = Infinity,
8216                minPoint = null,
8217                closest = _sqClosestPointOnSegment,
8218                p1, p2;
8219  
8220            for (var j = 0, jLen = this._parts.length; j < jLen; j++) {
8221                var points = this._parts[j];
8222  
8223                for (var i = 1, len = points.length; i < len; i++) {
8224                    p1 = points[i - 1];
8225                    p2 = points[i];
8226  
8227                    var sqDist = closest(p, p1, p2, true);
8228  
8229                    if (sqDist < minDistance) {
8230                        minDistance = sqDist;
8231                        minPoint = closest(p, p1, p2);
8232                    }
8233                }
8234            }
8235            if (minPoint) {
8236                minPoint.distance = Math.sqrt(minDistance);
8237            }
8238            return minPoint;
8239        },
8240  
8241        // @method getCenter(): LatLng
8242        // Returns the center ([centroid](https://en.wikipedia.org/wiki/Centroid)) of the polyline.
8243        getCenter: function () {
8244            // throws error when not yet added to map as this center calculation requires projected coordinates
8245            if (!this._map) {
8246                throw new Error('Must add layer to map before using getCenter()');
8247            }
8248  
8249            var i, halfDist, segDist, dist, p1, p2, ratio,
8250                points = this._rings[0],
8251                len = points.length;
8252  
8253            if (!len) { return null; }
8254  
8255            // polyline centroid algorithm; only uses the first ring if there are multiple
8256  
8257            for (i = 0, halfDist = 0; i < len - 1; i++) {
8258                halfDist += points[i].distanceTo(points[i + 1]) / 2;
8259            }
8260  
8261            // The line is so small in the current view that all points are on the same pixel.
8262            if (halfDist === 0) {
8263                return this._map.layerPointToLatLng(points[0]);
8264            }
8265  
8266            for (i = 0, dist = 0; i < len - 1; i++) {
8267                p1 = points[i];
8268                p2 = points[i + 1];
8269                segDist = p1.distanceTo(p2);
8270                dist += segDist;
8271  
8272                if (dist > halfDist) {
8273                    ratio = (dist - halfDist) / segDist;
8274                    return this._map.layerPointToLatLng([
8275                        p2.x - ratio * (p2.x - p1.x),
8276                        p2.y - ratio * (p2.y - p1.y)
8277                    ]);
8278                }
8279            }
8280        },
8281  
8282        // @method getBounds(): LatLngBounds
8283        // Returns the `LatLngBounds` of the path.
8284        getBounds: function () {
8285            return this._bounds;
8286        },
8287  
8288        // @method addLatLng(latlng: LatLng, latlngs?: LatLng[]): this
8289        // Adds a given point to the polyline. By default, adds to the first ring of
8290        // the polyline in case of a multi-polyline, but can be overridden by passing
8291        // a specific ring as a LatLng array (that you can earlier access with [`getLatLngs`](#polyline-getlatlngs)).
8292        addLatLng: function (latlng, latlngs) {
8293            latlngs = latlngs || this._defaultShape();
8294            latlng = toLatLng(latlng);
8295            latlngs.push(latlng);
8296            this._bounds.extend(latlng);
8297            return this.redraw();
8298        },
8299  
8300        _setLatLngs: function (latlngs) {
8301            this._bounds = new LatLngBounds();
8302            this._latlngs = this._convertLatLngs(latlngs);
8303        },
8304  
8305        _defaultShape: function () {
8306            return isFlat(this._latlngs) ? this._latlngs : this._latlngs[0];
8307        },
8308  
8309        // recursively convert latlngs input into actual LatLng instances; calculate bounds along the way
8310        _convertLatLngs: function (latlngs) {
8311            var result = [],
8312                flat = isFlat(latlngs);
8313  
8314            for (var i = 0, len = latlngs.length; i < len; i++) {
8315                if (flat) {
8316                    result[i] = toLatLng(latlngs[i]);
8317                    this._bounds.extend(result[i]);
8318                } else {
8319                    result[i] = this._convertLatLngs(latlngs[i]);
8320                }
8321            }
8322  
8323            return result;
8324        },
8325  
8326        _project: function () {
8327            var pxBounds = new Bounds();
8328            this._rings = [];
8329            this._projectLatlngs(this._latlngs, this._rings, pxBounds);
8330  
8331            if (this._bounds.isValid() && pxBounds.isValid()) {
8332                this._rawPxBounds = pxBounds;
8333                this._updateBounds();
8334            }
8335        },
8336  
8337        _updateBounds: function () {
8338            var w = this._clickTolerance(),
8339                p = new Point(w, w);
8340  
8341            if (!this._rawPxBounds) {
8342                return;
8343            }
8344  
8345            this._pxBounds = new Bounds([
8346                this._rawPxBounds.min.subtract(p),
8347                this._rawPxBounds.max.add(p)
8348            ]);
8349        },
8350  
8351        // recursively turns latlngs into a set of rings with projected coordinates
8352        _projectLatlngs: function (latlngs, result, projectedBounds) {
8353            var flat = latlngs[0] instanceof LatLng,
8354                len = latlngs.length,
8355                i, ring;
8356  
8357            if (flat) {
8358                ring = [];
8359                for (i = 0; i < len; i++) {
8360                    ring[i] = this._map.latLngToLayerPoint(latlngs[i]);
8361                    projectedBounds.extend(ring[i]);
8362                }
8363                result.push(ring);
8364            } else {
8365                for (i = 0; i < len; i++) {
8366                    this._projectLatlngs(latlngs[i], result, projectedBounds);
8367                }
8368            }
8369        },
8370  
8371        // clip polyline by renderer bounds so that we have less to render for performance
8372        _clipPoints: function () {
8373            var bounds = this._renderer._bounds;
8374  
8375            this._parts = [];
8376            if (!this._pxBounds || !this._pxBounds.intersects(bounds)) {
8377                return;
8378            }
8379  
8380            if (this.options.noClip) {
8381                this._parts = this._rings;
8382                return;
8383            }
8384  
8385            var parts = this._parts,
8386                i, j, k, len, len2, segment, points;
8387  
8388            for (i = 0, k = 0, len = this._rings.length; i < len; i++) {
8389                points = this._rings[i];
8390  
8391                for (j = 0, len2 = points.length; j < len2 - 1; j++) {
8392                    segment = clipSegment(points[j], points[j + 1], bounds, j, true);
8393  
8394                    if (!segment) { continue; }
8395  
8396                    parts[k] = parts[k] || [];
8397                    parts[k].push(segment[0]);
8398  
8399                    // if segment goes out of screen, or it's the last one, it's the end of the line part
8400                    if ((segment[1] !== points[j + 1]) || (j === len2 - 2)) {
8401                        parts[k].push(segment[1]);
8402                        k++;
8403                    }
8404                }
8405            }
8406        },
8407  
8408        // simplify each clipped part of the polyline for performance
8409        _simplifyPoints: function () {
8410            var parts = this._parts,
8411                tolerance = this.options.smoothFactor;
8412  
8413            for (var i = 0, len = parts.length; i < len; i++) {
8414                parts[i] = simplify(parts[i], tolerance);
8415            }
8416        },
8417  
8418        _update: function () {
8419            if (!this._map) { return; }
8420  
8421            this._clipPoints();
8422            this._simplifyPoints();
8423            this._updatePath();
8424        },
8425  
8426        _updatePath: function () {
8427            this._renderer._updatePoly(this);
8428        },
8429  
8430        // Needed by the `Canvas` renderer for interactivity
8431        _containsPoint: function (p, closed) {
8432            var i, j, k, len, len2, part,
8433                w = this._clickTolerance();
8434  
8435            if (!this._pxBounds || !this._pxBounds.contains(p)) { return false; }
8436  
8437            // hit detection for polylines
8438            for (i = 0, len = this._parts.length; i < len; i++) {
8439                part = this._parts[i];
8440  
8441                for (j = 0, len2 = part.length, k = len2 - 1; j < len2; k = j++) {
8442                    if (!closed && (j === 0)) { continue; }
8443  
8444                    if (pointToSegmentDistance(p, part[k], part[j]) <= w) {
8445                        return true;
8446                    }
8447                }
8448            }
8449            return false;
8450        }
8451    });
8452  
8453    // @factory L.polyline(latlngs: LatLng[], options?: Polyline options)
8454    // Instantiates a polyline object given an array of geographical points and
8455    // optionally an options object. You can create a `Polyline` object with
8456    // multiple separate lines (`MultiPolyline`) by passing an array of arrays
8457    // of geographic points.
8458    function polyline(latlngs, options) {
8459        return new Polyline(latlngs, options);
8460    }
8461  
8462    // Retrocompat. Allow plugins to support Leaflet versions before and after 1.1.
8463    Polyline._flat = _flat;
8464  
8465    /*
8466     * @class Polygon
8467     * @aka L.Polygon
8468     * @inherits Polyline
8469     *
8470     * A class for drawing polygon overlays on a map. Extends `Polyline`.
8471     *
8472     * Note that points you pass when creating a polygon shouldn't have an additional last point equal to the first one — it's better to filter out such points.
8473     *
8474     *
8475     * @example
8476     *
8477     * ```js
8478     * // create a red polygon from an array of LatLng points
8479     * var latlngs = [[37, -109.05],[41, -109.03],[41, -102.05],[37, -102.04]];
8480     *
8481     * var polygon = L.polygon(latlngs, {color: 'red'}).addTo(map);
8482     *
8483     * // zoom the map to the polygon
8484     * map.fitBounds(polygon.getBounds());
8485     * ```
8486     *
8487     * You can also pass an array of arrays of latlngs, with the first array representing the outer shape and the other arrays representing holes in the outer shape:
8488     *
8489     * ```js
8490     * var latlngs = [
8491     *   [[37, -109.05],[41, -109.03],[41, -102.05],[37, -102.04]], // outer ring
8492     *   [[37.29, -108.58],[40.71, -108.58],[40.71, -102.50],[37.29, -102.50]] // hole
8493     * ];
8494     * ```
8495     *
8496     * Additionally, you can pass a multi-dimensional array to represent a MultiPolygon shape.
8497     *
8498     * ```js
8499     * var latlngs = [
8500     *   [ // first polygon
8501     *     [[37, -109.05],[41, -109.03],[41, -102.05],[37, -102.04]], // outer ring
8502     *     [[37.29, -108.58],[40.71, -108.58],[40.71, -102.50],[37.29, -102.50]] // hole
8503     *   ],
8504     *   [ // second polygon
8505     *     [[41, -111.03],[45, -111.04],[45, -104.05],[41, -104.05]]
8506     *   ]
8507     * ];
8508     * ```
8509     */
8510  
8511    var Polygon = Polyline.extend({
8512  
8513        options: {
8514            fill: true
8515        },
8516  
8517        isEmpty: function () {
8518            return !this._latlngs.length || !this._latlngs[0].length;
8519        },
8520  
8521        getCenter: function () {
8522            // throws error when not yet added to map as this center calculation requires projected coordinates
8523            if (!this._map) {
8524                throw new Error('Must add layer to map before using getCenter()');
8525            }
8526  
8527            var i, j, p1, p2, f, area, x, y, center,
8528                points = this._rings[0],
8529                len = points.length;
8530  
8531            if (!len) { return null; }
8532  
8533            // polygon centroid algorithm; only uses the first ring if there are multiple
8534  
8535            area = x = y = 0;
8536  
8537            for (i = 0, j = len - 1; i < len; j = i++) {
8538                p1 = points[i];
8539                p2 = points[j];
8540  
8541                f = p1.y * p2.x - p2.y * p1.x;
8542                x += (p1.x + p2.x) * f;
8543                y += (p1.y + p2.y) * f;
8544                area += f * 3;
8545            }
8546  
8547            if (area === 0) {
8548                // Polygon is so small that all points are on same pixel.
8549                center = points[0];
8550            } else {
8551                center = [x / area, y / area];
8552            }
8553            return this._map.layerPointToLatLng(center);
8554        },
8555  
8556        _convertLatLngs: function (latlngs) {
8557            var result = Polyline.prototype._convertLatLngs.call(this, latlngs),
8558                len = result.length;
8559  
8560            // remove last point if it equals first one
8561            if (len >= 2 && result[0] instanceof LatLng && result[0].equals(result[len - 1])) {
8562                result.pop();
8563            }
8564            return result;
8565        },
8566  
8567        _setLatLngs: function (latlngs) {
8568            Polyline.prototype._setLatLngs.call(this, latlngs);
8569            if (isFlat(this._latlngs)) {
8570                this._latlngs = [this._latlngs];
8571            }
8572        },
8573  
8574        _defaultShape: function () {
8575            return isFlat(this._latlngs[0]) ? this._latlngs[0] : this._latlngs[0][0];
8576        },
8577  
8578        _clipPoints: function () {
8579            // polygons need a different clipping algorithm so we redefine that
8580  
8581            var bounds = this._renderer._bounds,
8582                w = this.options.weight,
8583                p = new Point(w, w);
8584  
8585            // increase clip padding by stroke width to avoid stroke on clip edges
8586            bounds = new Bounds(bounds.min.subtract(p), bounds.max.add(p));
8587  
8588            this._parts = [];
8589            if (!this._pxBounds || !this._pxBounds.intersects(bounds)) {
8590                return;
8591            }
8592  
8593            if (this.options.noClip) {
8594                this._parts = this._rings;
8595                return;
8596            }
8597  
8598            for (var i = 0, len = this._rings.length, clipped; i < len; i++) {
8599                clipped = clipPolygon(this._rings[i], bounds, true);
8600                if (clipped.length) {
8601                    this._parts.push(clipped);
8602                }
8603            }
8604        },
8605  
8606        _updatePath: function () {
8607            this._renderer._updatePoly(this, true);
8608        },
8609  
8610        // Needed by the `Canvas` renderer for interactivity
8611        _containsPoint: function (p) {
8612            var inside = false,
8613                part, p1, p2, i, j, k, len, len2;
8614  
8615            if (!this._pxBounds || !this._pxBounds.contains(p)) { return false; }
8616  
8617            // ray casting algorithm for detecting if point is in polygon
8618            for (i = 0, len = this._parts.length; i < len; i++) {
8619                part = this._parts[i];
8620  
8621                for (j = 0, len2 = part.length, k = len2 - 1; j < len2; k = j++) {
8622                    p1 = part[j];
8623                    p2 = part[k];
8624  
8625                    if (((p1.y > p.y) !== (p2.y > p.y)) && (p.x < (p2.x - p1.x) * (p.y - p1.y) / (p2.y - p1.y) + p1.x)) {
8626                        inside = !inside;
8627                    }
8628                }
8629            }
8630  
8631            // also check if it's on polygon stroke
8632            return inside || Polyline.prototype._containsPoint.call(this, p, true);
8633        }
8634  
8635    });
8636  
8637  
8638    // @factory L.polygon(latlngs: LatLng[], options?: Polyline options)
8639    function polygon(latlngs, options) {
8640        return new Polygon(latlngs, options);
8641    }
8642  
8643    /*
8644     * @class GeoJSON
8645     * @aka L.GeoJSON
8646     * @inherits FeatureGroup
8647     *
8648     * Represents a GeoJSON object or an array of GeoJSON objects. Allows you to parse
8649     * GeoJSON data and display it on the map. Extends `FeatureGroup`.
8650     *
8651     * @example
8652     *
8653     * ```js
8654     * L.geoJSON(data, {
8655     * 	style: function (feature) {
8656     * 		return {color: feature.properties.color};
8657     * 	}
8658     * }).bindPopup(function (layer) {
8659     * 	return layer.feature.properties.description;
8660     * }).addTo(map);
8661     * ```
8662     */
8663  
8664    var GeoJSON = FeatureGroup.extend({
8665  
8666        /* @section
8667         * @aka GeoJSON options
8668         *
8669         * @option pointToLayer: Function = *
8670         * A `Function` defining how GeoJSON points spawn Leaflet layers. It is internally
8671         * called when data is added, passing the GeoJSON point feature and its `LatLng`.
8672         * The default is to spawn a default `Marker`:
8673         * ```js
8674         * function(geoJsonPoint, latlng) {
8675         * 	return L.marker(latlng);
8676         * }
8677         * ```
8678         *
8679         * @option style: Function = *
8680         * A `Function` defining the `Path options` for styling GeoJSON lines and polygons,
8681         * called internally when data is added.
8682         * The default value is to not override any defaults:
8683         * ```js
8684         * function (geoJsonFeature) {
8685         * 	return {}
8686         * }
8687         * ```
8688         *
8689         * @option onEachFeature: Function = *
8690         * A `Function` that will be called once for each created `Feature`, after it has
8691         * been created and styled. Useful for attaching events and popups to features.
8692         * The default is to do nothing with the newly created layers:
8693         * ```js
8694         * function (feature, layer) {}
8695         * ```
8696         *
8697         * @option filter: Function = *
8698         * A `Function` that will be used to decide whether to include a feature or not.
8699         * The default is to include all features:
8700         * ```js
8701         * function (geoJsonFeature) {
8702         * 	return true;
8703         * }
8704         * ```
8705         * Note: dynamically changing the `filter` option will have effect only on newly
8706         * added data. It will _not_ re-evaluate already included features.
8707         *
8708         * @option coordsToLatLng: Function = *
8709         * A `Function` that will be used for converting GeoJSON coordinates to `LatLng`s.
8710         * The default is the `coordsToLatLng` static method.
8711         *
8712         * @option markersInheritOptions: Boolean = false
8713         * Whether default Markers for "Point" type Features inherit from group options.
8714         */
8715  
8716        initialize: function (geojson, options) {
8717            setOptions(this, options);
8718  
8719            this._layers = {};
8720  
8721            if (geojson) {
8722                this.addData(geojson);
8723            }
8724        },
8725  
8726        // @method addData( <GeoJSON> data ): this
8727        // Adds a GeoJSON object to the layer.
8728        addData: function (geojson) {
8729            var features = isArray(geojson) ? geojson : geojson.features,
8730                i, len, feature;
8731  
8732            if (features) {
8733                for (i = 0, len = features.length; i < len; i++) {
8734                    // only add this if geometry or geometries are set and not null
8735                    feature = features[i];
8736                    if (feature.geometries || feature.geometry || feature.features || feature.coordinates) {
8737                        this.addData(feature);
8738                    }
8739                }
8740                return this;
8741            }
8742  
8743            var options = this.options;
8744  
8745            if (options.filter && !options.filter(geojson)) { return this; }
8746  
8747            var layer = geometryToLayer(geojson, options);
8748            if (!layer) {
8749                return this;
8750            }
8751            layer.feature = asFeature(geojson);
8752  
8753            layer.defaultOptions = layer.options;
8754            this.resetStyle(layer);
8755  
8756            if (options.onEachFeature) {
8757                options.onEachFeature(geojson, layer);
8758            }
8759  
8760            return this.addLayer(layer);
8761        },
8762  
8763        // @method resetStyle( <Path> layer? ): this
8764        // Resets the given vector layer's style to the original GeoJSON style, useful for resetting style after hover events.
8765        // If `layer` is omitted, the style of all features in the current layer is reset.
8766        resetStyle: function (layer) {
8767            if (layer === undefined) {
8768                return this.eachLayer(this.resetStyle, this);
8769            }
8770            // reset any custom styles
8771            layer.options = extend({}, layer.defaultOptions);
8772            this._setLayerStyle(layer, this.options.style);
8773            return this;
8774        },
8775  
8776        // @method setStyle( <Function> style ): this
8777        // Changes styles of GeoJSON vector layers with the given style function.
8778        setStyle: function (style) {
8779            return this.eachLayer(function (layer) {
8780                this._setLayerStyle(layer, style);
8781            }, this);
8782        },
8783  
8784        _setLayerStyle: function (layer, style) {
8785            if (layer.setStyle) {
8786                if (typeof style === 'function') {
8787                    style = style(layer.feature);
8788                }
8789                layer.setStyle(style);
8790            }
8791        }
8792    });
vendor: 148,295 bytes, lines 8793-12815
8793  
8794    // @section
8795    // There are several static functions which can be called without instantiating L.GeoJSON:
8796  
8797    // @function geometryToLayer(featureData: Object, options?: GeoJSON options): Layer
8798    // Creates a `Layer` from a given GeoJSON feature. Can use a custom
8799    // [`pointToLayer`](#geojson-pointtolayer) and/or [`coordsToLatLng`](#geojson-coordstolatlng)
8800    // functions if provided as options.
8801    function geometryToLayer(geojson, options) {
8802  
8803        var geometry = geojson.type === 'Feature' ? geojson.geometry : geojson,
8804            coords = geometry ? geometry.coordinates : null,
8805            layers = [],
8806            pointToLayer = options && options.pointToLayer,
8807            _coordsToLatLng = options && options.coordsToLatLng || coordsToLatLng,
8808            latlng, latlngs, i, len;
8809  
8810        if (!coords && !geometry) {
8811            return null;
8812        }
8813  
8814        switch (geometry.type) {
8815        case 'Point':
8816            latlng = _coordsToLatLng(coords);
8817            return _pointToLayer(pointToLayer, geojson, latlng, options);
8818  
8819        case 'MultiPoint':
8820            for (i = 0, len = coords.length; i < len; i++) {
8821                latlng = _coordsToLatLng(coords[i]);
8822                layers.push(_pointToLayer(pointToLayer, geojson, latlng, options));
8823            }
8824            return new FeatureGroup(layers);
8825  
8826        case 'LineString':
8827        case 'MultiLineString':
8828            latlngs = coordsToLatLngs(coords, geometry.type === 'LineString' ? 0 : 1, _coordsToLatLng);
8829            return new Polyline(latlngs, options);
8830  
8831        case 'Polygon':
8832        case 'MultiPolygon':
8833            latlngs = coordsToLatLngs(coords, geometry.type === 'Polygon' ? 1 : 2, _coordsToLatLng);
8834            return new Polygon(latlngs, options);
8835  
8836        case 'GeometryCollection':
8837            for (i = 0, len = geometry.geometries.length; i < len; i++) {
8838                var layer = geometryToLayer({
8839                    geometry: geometry.geometries[i],
8840                    type: 'Feature',
8841                    properties: geojson.properties
8842                }, options);
8843  
8844                if (layer) {
8845                    layers.push(layer);
8846                }
8847            }
8848            return new FeatureGroup(layers);
8849  
8850        default:
8851            throw new Error('Invalid GeoJSON object.');
8852        }
8853    }
8854  
8855    function _pointToLayer(pointToLayerFn, geojson, latlng, options) {
8856        return pointToLayerFn ?
8857            pointToLayerFn(geojson, latlng) :
8858            new Marker(latlng, options && options.markersInheritOptions && options);
8859    }
8860  
8861    // @function coordsToLatLng(coords: Array): LatLng
8862    // Creates a `LatLng` object from an array of 2 numbers (longitude, latitude)
8863    // or 3 numbers (longitude, latitude, altitude) used in GeoJSON for points.
8864    function coordsToLatLng(coords) {
8865        return new LatLng(coords[1], coords[0], coords[2]);
8866    }
8867  
8868    // @function coordsToLatLngs(coords: Array, levelsDeep?: Number, coordsToLatLng?: Function): Array
8869    // Creates a multidimensional array of `LatLng`s from a GeoJSON coordinates array.
8870    // `levelsDeep` specifies the nesting level (0 is for an array of points, 1 for an array of arrays of points, etc., 0 by default).
8871    // Can use a custom [`coordsToLatLng`](#geojson-coordstolatlng) function.
8872    function coordsToLatLngs(coords, levelsDeep, _coordsToLatLng) {
8873        var latlngs = [];
8874  
8875        for (var i = 0, len = coords.length, latlng; i < len; i++) {
8876            latlng = levelsDeep ?
8877                coordsToLatLngs(coords[i], levelsDeep - 1, _coordsToLatLng) :
8878                (_coordsToLatLng || coordsToLatLng)(coords[i]);
8879  
8880            latlngs.push(latlng);
8881        }
8882  
8883        return latlngs;
8884    }
8885  
8886    // @function latLngToCoords(latlng: LatLng, precision?: Number|false): Array
8887    // Reverse of [`coordsToLatLng`](#geojson-coordstolatlng)
8888    // Coordinates values are rounded with [`formatNum`](#util-formatnum) function.
8889    function latLngToCoords(latlng, precision) {
8890        return latlng.alt !== undefined ?
8891            [formatNum(latlng.lng, precision), formatNum(latlng.lat, precision), formatNum(latlng.alt, precision)] :
8892            [formatNum(latlng.lng, precision), formatNum(latlng.lat, precision)];
8893    }
8894  
8895    // @function latLngsToCoords(latlngs: Array, levelsDeep?: Number, closed?: Boolean, precision?: Number|false): Array
8896    // Reverse of [`coordsToLatLngs`](#geojson-coordstolatlngs)
8897    // `closed` determines whether the first point should be appended to the end of the array to close the feature, only used when `levelsDeep` is 0. False by default.
8898    // Coordinates values are rounded with [`formatNum`](#util-formatnum) function.
8899    function latLngsToCoords(latlngs, levelsDeep, closed, precision) {
8900        var coords = [];
8901  
8902        for (var i = 0, len = latlngs.length; i < len; i++) {
8903            coords.push(levelsDeep ?
8904                latLngsToCoords(latlngs[i], levelsDeep - 1, closed, precision) :
8905                latLngToCoords(latlngs[i], precision));
8906        }
8907  
8908        if (!levelsDeep && closed) {
8909            coords.push(coords[0]);
8910        }
8911  
8912        return coords;
8913    }
8914  
8915    function getFeature(layer, newGeometry) {
8916        return layer.feature ?
8917            extend({}, layer.feature, {geometry: newGeometry}) :
8918            asFeature(newGeometry);
8919    }
8920  
8921    // @function asFeature(geojson: Object): Object
8922    // Normalize GeoJSON geometries/features into GeoJSON features.
8923    function asFeature(geojson) {
8924        if (geojson.type === 'Feature' || geojson.type === 'FeatureCollection') {
8925            return geojson;
8926        }
8927  
8928        return {
8929            type: 'Feature',
8930            properties: {},
8931            geometry: geojson
8932        };
8933    }
8934  
8935    var PointToGeoJSON = {
8936        toGeoJSON: function (precision) {
8937            return getFeature(this, {
8938                type: 'Point',
8939                coordinates: latLngToCoords(this.getLatLng(), precision)
8940            });
8941        }
8942    };
8943  
8944    // @namespace Marker
8945    // @section Other methods
8946    // @method toGeoJSON(precision?: Number|false): Object
8947    // Coordinates values are rounded with [`formatNum`](#util-formatnum) function with given `precision`.
8948    // Returns a [`GeoJSON`](https://en.wikipedia.org/wiki/GeoJSON) representation of the marker (as a GeoJSON `Point` Feature).
8949    Marker.include(PointToGeoJSON);
8950  
8951    // @namespace CircleMarker
8952    // @method toGeoJSON(precision?: Number|false): Object
8953    // Coordinates values are rounded with [`formatNum`](#util-formatnum) function with given `precision`.
8954    // Returns a [`GeoJSON`](https://en.wikipedia.org/wiki/GeoJSON) representation of the circle marker (as a GeoJSON `Point` Feature).
8955    Circle.include(PointToGeoJSON);
8956    CircleMarker.include(PointToGeoJSON);
8957  
8958  
8959    // @namespace Polyline
8960    // @method toGeoJSON(precision?: Number|false): Object
8961    // Coordinates values are rounded with [`formatNum`](#util-formatnum) function with given `precision`.
8962    // Returns a [`GeoJSON`](https://en.wikipedia.org/wiki/GeoJSON) representation of the polyline (as a GeoJSON `LineString` or `MultiLineString` Feature).
8963    Polyline.include({
8964        toGeoJSON: function (precision) {
8965            var multi = !isFlat(this._latlngs);
8966  
8967            var coords = latLngsToCoords(this._latlngs, multi ? 1 : 0, false, precision);
8968  
8969            return getFeature(this, {
8970                type: (multi ? 'Multi' : '') + 'LineString',
8971                coordinates: coords
8972            });
8973        }
8974    });
8975  
8976    // @namespace Polygon
8977    // @method toGeoJSON(precision?: Number|false): Object
8978    // Coordinates values are rounded with [`formatNum`](#util-formatnum) function with given `precision`.
8979    // Returns a [`GeoJSON`](https://en.wikipedia.org/wiki/GeoJSON) representation of the polygon (as a GeoJSON `Polygon` or `MultiPolygon` Feature).
8980    Polygon.include({
8981        toGeoJSON: function (precision) {
8982            var holes = !isFlat(this._latlngs),
8983                multi = holes && !isFlat(this._latlngs[0]);
8984  
8985            var coords = latLngsToCoords(this._latlngs, multi ? 2 : holes ? 1 : 0, true, precision);
8986  
8987            if (!holes) {
8988                coords = [coords];
8989            }
8990  
8991            return getFeature(this, {
8992                type: (multi ? 'Multi' : '') + 'Polygon',
8993                coordinates: coords
8994            });
8995        }
8996    });
8997  
8998  
8999    // @namespace LayerGroup
9000    LayerGroup.include({
9001        toMultiPoint: function (precision) {
9002            var coords = [];
9003  
9004            this.eachLayer(function (layer) {
9005                coords.push(layer.toGeoJSON(precision).geometry.coordinates);
9006            });
9007  
9008            return getFeature(this, {
9009                type: 'MultiPoint',
9010                coordinates: coords
9011            });
9012        },
9013  
9014        // @method toGeoJSON(precision?: Number|false): Object
9015        // Coordinates values are rounded with [`formatNum`](#util-formatnum) function with given `precision`.
9016        // Returns a [`GeoJSON`](https://en.wikipedia.org/wiki/GeoJSON) representation of the layer group (as a GeoJSON `FeatureCollection`, `GeometryCollection`, or `MultiPoint`).
9017        toGeoJSON: function (precision) {
9018  
9019            var type = this.feature && this.feature.geometry && this.feature.geometry.type;
9020  
9021            if (type === 'MultiPoint') {
9022                return this.toMultiPoint(precision);
9023            }
9024  
9025            var isGeometryCollection = type === 'GeometryCollection',
9026                jsons = [];
9027  
9028            this.eachLayer(function (layer) {
9029                if (layer.toGeoJSON) {
9030                    var json = layer.toGeoJSON(precision);
9031                    if (isGeometryCollection) {
9032                        jsons.push(json.geometry);
9033                    } else {
9034                        var feature = asFeature(json);
9035                        // Squash nested feature collections
9036                        if (feature.type === 'FeatureCollection') {
9037                            jsons.push.apply(jsons, feature.features);
9038                        } else {
9039                            jsons.push(feature);
9040                        }
9041                    }
9042                }
9043            });
9044  
9045            if (isGeometryCollection) {
9046                return getFeature(this, {
9047                    geometries: jsons,
9048                    type: 'GeometryCollection'
9049                });
9050            }
9051  
9052            return {
9053                type: 'FeatureCollection',
9054                features: jsons
9055            };
9056        }
9057    });
9058  
9059    // @namespace GeoJSON
9060    // @factory L.geoJSON(geojson?: Object, options?: GeoJSON options)
9061    // Creates a GeoJSON layer. Optionally accepts an object in
9062    // [GeoJSON format](https://tools.ietf.org/html/rfc7946) to display on the map
9063    // (you can alternatively add it later with `addData` method) and an `options` object.
9064    function geoJSON(geojson, options) {
9065        return new GeoJSON(geojson, options);
9066    }
9067  
9068    // Backward compatibility.
9069    var geoJson = geoJSON;
9070  
9071    /*
9072     * @class ImageOverlay
9073     * @aka L.ImageOverlay
9074     * @inherits Interactive layer
9075     *
9076     * Used to load and display a single image over specific bounds of the map. Extends `Layer`.
9077     *
9078     * @example
9079     *
9080     * ```js
9081     * var imageUrl = 'https://maps.lib.utexas.edu/maps/historical/newark_nj_1922.jpg',
9082     * 	imageBounds = [[40.712216, -74.22655], [40.773941, -74.12544]];
9083     * L.imageOverlay(imageUrl, imageBounds).addTo(map);
9084     * ```
9085     */
9086  
9087    var ImageOverlay = Layer.extend({
9088  
9089        // @section
9090        // @aka ImageOverlay options
9091        options: {
9092            // @option opacity: Number = 1.0
9093            // The opacity of the image overlay.
9094            opacity: 1,
9095  
9096            // @option alt: String = ''
9097            // Text for the `alt` attribute of the image (useful for accessibility).
9098            alt: '',
9099  
9100            // @option interactive: Boolean = false
9101            // If `true`, the image overlay will emit [mouse events](#interactive-layer) when clicked or hovered.
9102            interactive: false,
9103  
9104            // @option crossOrigin: Boolean|String = false
9105            // Whether the crossOrigin attribute will be added to the image.
9106            // If a String is provided, the image will have its crossOrigin attribute set to the String provided. This is needed if you want to access image pixel data.
9107            // Refer to [CORS Settings](https://developer.mozilla.org/en-US/docs/Web/HTML/CORS_settings_attributes) for valid String values.
9108            crossOrigin: false,
9109  
9110            // @option errorOverlayUrl: String = ''
9111            // URL to the overlay image to show in place of the overlay that failed to load.
9112            errorOverlayUrl: '',
9113  
9114            // @option zIndex: Number = 1
9115            // The explicit [zIndex](https://developer.mozilla.org/docs/Web/CSS/CSS_Positioning/Understanding_z_index) of the overlay layer.
9116            zIndex: 1,
9117  
9118            // @option className: String = ''
9119            // A custom class name to assign to the image. Empty by default.
9120            className: ''
9121        },
9122  
9123        initialize: function (url, bounds, options) { // (String, LatLngBounds, Object)
9124            this._url = url;
9125            this._bounds = toLatLngBounds(bounds);
9126  
9127            setOptions(this, options);
9128        },
9129  
9130        onAdd: function () {
9131            if (!this._image) {
9132                this._initImage();
9133  
9134                if (this.options.opacity < 1) {
9135                    this._updateOpacity();
9136                }
9137            }
9138  
9139            if (this.options.interactive) {
9140                addClass(this._image, 'leaflet-interactive');
9141                this.addInteractiveTarget(this._image);
9142            }
9143  
9144            this.getPane().appendChild(this._image);
9145            this._reset();
9146        },
9147  
9148        onRemove: function () {
9149            remove(this._image);
9150            if (this.options.interactive) {
9151                this.removeInteractiveTarget(this._image);
9152            }
9153        },
9154  
9155        // @method setOpacity(opacity: Number): this
9156        // Sets the opacity of the overlay.
9157        setOpacity: function (opacity) {
9158            this.options.opacity = opacity;
9159  
9160            if (this._image) {
9161                this._updateOpacity();
9162            }
9163            return this;
9164        },
9165  
9166        setStyle: function (styleOpts) {
9167            if (styleOpts.opacity) {
9168                this.setOpacity(styleOpts.opacity);
9169            }
9170            return this;
9171        },
9172  
9173        // @method bringToFront(): this
9174        // Brings the layer to the top of all overlays.
9175        bringToFront: function () {
9176            if (this._map) {
9177                toFront(this._image);
9178            }
9179            return this;
9180        },
9181  
9182        // @method bringToBack(): this
9183        // Brings the layer to the bottom of all overlays.
9184        bringToBack: function () {
9185            if (this._map) {
9186                toBack(this._image);
9187            }
9188            return this;
9189        },
9190  
9191        // @method setUrl(url: String): this
9192        // Changes the URL of the image.
9193        setUrl: function (url) {
9194            this._url = url;
9195  
9196            if (this._image) {
9197                this._image.src = url;
9198            }
9199            return this;
9200        },
9201  
9202        // @method setBounds(bounds: LatLngBounds): this
9203        // Update the bounds that this ImageOverlay covers
9204        setBounds: function (bounds) {
9205            this._bounds = toLatLngBounds(bounds);
9206  
9207            if (this._map) {
9208                this._reset();
9209            }
9210            return this;
9211        },
9212  
9213        getEvents: function () {
9214            var events = {
9215                zoom: this._reset,
9216                viewreset: this._reset
9217            };
9218  
9219            if (this._zoomAnimated) {
9220                events.zoomanim = this._animateZoom;
9221            }
9222  
9223            return events;
9224        },
9225  
9226        // @method setZIndex(value: Number): this
9227        // Changes the [zIndex](#imageoverlay-zindex) of the image overlay.
9228        setZIndex: function (value) {
9229            this.options.zIndex = value;
9230            this._updateZIndex();
9231            return this;
9232        },
9233  
9234        // @method getBounds(): LatLngBounds
9235        // Get the bounds that this ImageOverlay covers
9236        getBounds: function () {
9237            return this._bounds;
9238        },
9239  
9240        // @method getElement(): HTMLElement
9241        // Returns the instance of [`HTMLImageElement`](https://developer.mozilla.org/docs/Web/API/HTMLImageElement)
9242        // used by this overlay.
9243        getElement: function () {
9244            return this._image;
9245        },
9246  
9247        _initImage: function () {
9248            var wasElementSupplied = this._url.tagName === 'IMG';
9249            var img = this._image = wasElementSupplied ? this._url : create$1('img');
9250  
9251            addClass(img, 'leaflet-image-layer');
9252            if (this._zoomAnimated) { addClass(img, 'leaflet-zoom-animated'); }
9253            if (this.options.className) { addClass(img, this.options.className); }
9254  
9255            img.onselectstart = falseFn;
9256            img.onmousemove = falseFn;
9257  
9258            // @event load: Event
9259            // Fired when the ImageOverlay layer has loaded its image
9260            img.onload = bind(this.fire, this, 'load');
9261            img.onerror = bind(this._overlayOnError, this, 'error');
9262  
9263            if (this.options.crossOrigin || this.options.crossOrigin === '') {
9264                img.crossOrigin = this.options.crossOrigin === true ? '' : this.options.crossOrigin;
9265            }
9266  
9267            if (this.options.zIndex) {
9268                this._updateZIndex();
9269            }
9270  
9271            if (wasElementSupplied) {
9272                this._url = img.src;
9273                return;
9274            }
9275  
9276            img.src = this._url;
9277            img.alt = this.options.alt;
9278        },
9279  
9280        _animateZoom: function (e) {
9281            var scale = this._map.getZoomScale(e.zoom),
9282                offset = this._map._latLngBoundsToNewLayerBounds(this._bounds, e.zoom, e.center).min;
9283  
9284            setTransform(this._image, offset, scale);
9285        },
9286  
9287        _reset: function () {
9288            var image = this._image,
9289                bounds = new Bounds(
9290                    this._map.latLngToLayerPoint(this._bounds.getNorthWest()),
9291                    this._map.latLngToLayerPoint(this._bounds.getSouthEast())),
9292                size = bounds.getSize();
9293  
9294            setPosition(image, bounds.min);
9295  
9296            image.style.width  = size.x + 'px';
9297            image.style.height = size.y + 'px';
9298        },
9299  
9300        _updateOpacity: function () {
9301            setOpacity(this._image, this.options.opacity);
9302        },
9303  
9304        _updateZIndex: function () {
9305            if (this._image && this.options.zIndex !== undefined && this.options.zIndex !== null) {
9306                this._image.style.zIndex = this.options.zIndex;
9307            }
9308        },
9309  
9310        _overlayOnError: function () {
9311            // @event error: Event
9312            // Fired when the ImageOverlay layer fails to load its image
9313            this.fire('error');
9314  
9315            var errorUrl = this.options.errorOverlayUrl;
9316            if (errorUrl && this._url !== errorUrl) {
9317                this._url = errorUrl;
9318                this._image.src = errorUrl;
9319            }
9320        },
9321  
9322        // @method getCenter(): LatLng
9323        // Returns the center of the ImageOverlay.
9324        getCenter: function () {
9325            return this._bounds.getCenter();
9326        }
9327    });
9328  
9329    // @factory L.imageOverlay(imageUrl: String, bounds: LatLngBounds, options?: ImageOverlay options)
9330    // Instantiates an image overlay object given the URL of the image and the
9331    // geographical bounds it is tied to.
9332    var imageOverlay = function (url, bounds, options) {
9333        return new ImageOverlay(url, bounds, options);
9334    };
9335  
9336    /*
9337     * @class VideoOverlay
9338     * @aka L.VideoOverlay
9339     * @inherits ImageOverlay
9340     *
9341     * Used to load and display a video player over specific bounds of the map. Extends `ImageOverlay`.
9342     *
9343     * A video overlay uses the [`<video>`](https://developer.mozilla.org/docs/Web/HTML/Element/video)
9344     * HTML5 element.
9345     *
9346     * @example
9347     *
9348     * ```js
9349     * var videoUrl = 'https://www.mapbox.com/bites/00188/patricia_nasa.webm',
9350     * 	videoBounds = [[ 32, -130], [ 13, -100]];
9351     * L.videoOverlay(videoUrl, videoBounds ).addTo(map);
9352     * ```
9353     */
9354  
9355    var VideoOverlay = ImageOverlay.extend({
9356  
9357        // @section
9358        // @aka VideoOverlay options
9359        options: {
9360            // @option autoplay: Boolean = true
9361            // Whether the video starts playing automatically when loaded.
9362            // On some browsers autoplay will only work with `muted: true`
9363            autoplay: true,
9364  
9365            // @option loop: Boolean = true
9366            // Whether the video will loop back to the beginning when played.
9367            loop: true,
9368  
9369            // @option keepAspectRatio: Boolean = true
9370            // Whether the video will save aspect ratio after the projection.
9371            // Relevant for supported browsers. See [browser compatibility](https://developer.mozilla.org/en-US/docs/Web/CSS/object-fit)
9372            keepAspectRatio: true,
9373  
9374            // @option muted: Boolean = false
9375            // Whether the video starts on mute when loaded.
9376            muted: false,
9377  
9378            // @option playsInline: Boolean = true
9379            // Mobile browsers will play the video right where it is instead of open it up in fullscreen mode.
9380            playsInline: true
9381        },
9382  
9383        _initImage: function () {
9384            var wasElementSupplied = this._url.tagName === 'VIDEO';
9385            var vid = this._image = wasElementSupplied ? this._url : create$1('video');
9386  
9387            addClass(vid, 'leaflet-image-layer');
9388            if (this._zoomAnimated) { addClass(vid, 'leaflet-zoom-animated'); }
9389            if (this.options.className) { addClass(vid, this.options.className); }
9390  
9391            vid.onselectstart = falseFn;
9392            vid.onmousemove = falseFn;
9393  
9394            // @event load: Event
9395            // Fired when the video has finished loading the first frame
9396            vid.onloadeddata = bind(this.fire, this, 'load');
9397  
9398            if (wasElementSupplied) {
9399                var sourceElements = vid.getElementsByTagName('source');
9400                var sources = [];
9401                for (var j = 0; j < sourceElements.length; j++) {
9402                    sources.push(sourceElements[j].src);
9403                }
9404  
9405                this._url = (sourceElements.length > 0) ? sources : [vid.src];
9406                return;
9407            }
9408  
9409            if (!isArray(this._url)) { this._url = [this._url]; }
9410  
9411            if (!this.options.keepAspectRatio && Object.prototype.hasOwnProperty.call(vid.style, 'objectFit')) {
9412                vid.style['objectFit'] = 'fill';
9413            }
9414            vid.autoplay = !!this.options.autoplay;
9415            vid.loop = !!this.options.loop;
9416            vid.muted = !!this.options.muted;
9417            vid.playsInline = !!this.options.playsInline;
9418            for (var i = 0; i < this._url.length; i++) {
9419                var source = create$1('source');
9420                source.src = this._url[i];
9421                vid.appendChild(source);
9422            }
9423        }
9424  
9425        // @method getElement(): HTMLVideoElement
9426        // Returns the instance of [`HTMLVideoElement`](https://developer.mozilla.org/docs/Web/API/HTMLVideoElement)
9427        // used by this overlay.
9428    });
9429  
9430  
9431    // @factory L.videoOverlay(video: String|Array|HTMLVideoElement, bounds: LatLngBounds, options?: VideoOverlay options)
9432    // Instantiates an image overlay object given the URL of the video (or array of URLs, or even a video element) and the
9433    // geographical bounds it is tied to.
9434  
9435    function videoOverlay(video, bounds, options) {
9436        return new VideoOverlay(video, bounds, options);
9437    }
9438  
9439    /*
9440     * @class SVGOverlay
9441     * @aka L.SVGOverlay
9442     * @inherits ImageOverlay
9443     *
9444     * Used to load, display and provide DOM access to an SVG file over specific bounds of the map. Extends `ImageOverlay`.
9445     *
9446     * An SVG overlay uses the [`<svg>`](https://developer.mozilla.org/docs/Web/SVG/Element/svg) element.
9447     *
9448     * @example
9449     *
9450     * ```js
9451     * var svgElement = document.createElementNS("http://www.w3.org/2000/svg", "svg");
9452     * svgElement.setAttribute('xmlns', "http://www.w3.org/2000/svg");
9453     * svgElement.setAttribute('viewBox', "0 0 200 200");
9454     * svgElement.innerHTML = '<rect width="200" height="200"/><rect x="75" y="23" width="50" height="50" style="fill:red"/><rect x="75" y="123" width="50" height="50" style="fill:#0013ff"/>';
9455     * var svgElementBounds = [ [ 32, -130 ], [ 13, -100 ] ];
9456     * L.svgOverlay(svgElement, svgElementBounds).addTo(map);
9457     * ```
9458     */
9459  
9460    var SVGOverlay = ImageOverlay.extend({
9461        _initImage: function () {
9462            var el = this._image = this._url;
9463  
9464            addClass(el, 'leaflet-image-layer');
9465            if (this._zoomAnimated) { addClass(el, 'leaflet-zoom-animated'); }
9466            if (this.options.className) { addClass(el, this.options.className); }
9467  
9468            el.onselectstart = falseFn;
9469            el.onmousemove = falseFn;
9470        }
9471  
9472        // @method getElement(): SVGElement
9473        // Returns the instance of [`SVGElement`](https://developer.mozilla.org/docs/Web/API/SVGElement)
9474        // used by this overlay.
9475    });
9476  
9477  
9478    // @factory L.svgOverlay(svg: String|SVGElement, bounds: LatLngBounds, options?: SVGOverlay options)
9479    // Instantiates an image overlay object given an SVG element and the geographical bounds it is tied to.
9480    // A viewBox attribute is required on the SVG element to zoom in and out properly.
9481  
9482    function svgOverlay(el, bounds, options) {
9483        return new SVGOverlay(el, bounds, options);
9484    }
9485  
9486    /*
9487     * @class DivOverlay
9488     * @inherits Interactive layer
9489     * @aka L.DivOverlay
9490     * Base model for L.Popup and L.Tooltip. Inherit from it for custom overlays like plugins.
9491     */
9492  
9493    // @namespace DivOverlay
9494    var DivOverlay = Layer.extend({
9495  
9496        // @section
9497        // @aka DivOverlay options
9498        options: {
9499            // @option interactive: Boolean = false
9500            // If true, the popup/tooltip will listen to the mouse events.
9501            interactive: false,
9502  
9503            // @option offset: Point = Point(0, 0)
9504            // The offset of the overlay position.
9505            offset: [0, 0],
9506  
9507            // @option className: String = ''
9508            // A custom CSS class name to assign to the overlay.
9509            className: '',
9510  
9511            // @option pane: String = undefined
9512            // `Map pane` where the overlay will be added.
9513            pane: undefined
9514        },
9515  
9516        initialize: function (options, source) {
9517            setOptions(this, options);
9518  
9519            this._source = source;
9520        },
9521  
9522        // @method openOn(map: Map): this
9523        // Adds the overlay to the map.
9524        // Alternative to `map.openPopup(popup)`/`.openTooltip(tooltip)`.
9525        openOn: function (map) {
9526            map = arguments.length ? map : this._source._map; // experimental, not the part of public api
9527            if (!map.hasLayer(this)) {
9528                map.addLayer(this);
9529            }
9530            return this;
9531        },
9532  
9533        // @method close(): this
9534        // Closes the overlay.
9535        // Alternative to `map.closePopup(popup)`/`.closeTooltip(tooltip)`
9536        // and `layer.closePopup()`/`.closeTooltip()`.
9537        close: function () {
9538            if (this._map) {
9539                this._map.removeLayer(this);
9540            }
9541            return this;
9542        },
9543  
9544        // @method toggle(layer?: Layer): this
9545        // Opens or closes the overlay bound to layer depending on its current state.
9546        // Argument may be omitted only for overlay bound to layer.
9547        // Alternative to `layer.togglePopup()`/`.toggleTooltip()`.
9548        toggle: function (layer) {
9549            if (this._map) {
9550                this.close();
9551            } else {
9552                if (arguments.length) {
9553                    this._source = layer;
9554                } else {
9555                    layer = this._source;
9556                }
9557                this._prepareOpen();
9558  
9559                // open the overlay on the map
9560                this.openOn(layer._map);
9561            }
9562            return this;
9563        },
9564  
9565        onAdd: function (map) {
9566            this._zoomAnimated = map._zoomAnimated;
9567  
9568            if (!this._container) {
9569                this._initLayout();
9570            }
9571  
9572            if (map._fadeAnimated) {
9573                setOpacity(this._container, 0);
9574            }
9575  
9576            clearTimeout(this._removeTimeout);
9577            this.getPane().appendChild(this._container);
9578            this.update();
9579  
9580            if (map._fadeAnimated) {
9581                setOpacity(this._container, 1);
9582            }
9583  
9584            this.bringToFront();
9585  
9586            if (this.options.interactive) {
9587                addClass(this._container, 'leaflet-interactive');
9588                this.addInteractiveTarget(this._container);
9589            }
9590        },
9591  
9592        onRemove: function (map) {
9593            if (map._fadeAnimated) {
9594                setOpacity(this._container, 0);
9595                this._removeTimeout = setTimeout(bind(remove, undefined, this._container), 200);
9596            } else {
9597                remove(this._container);
9598            }
9599  
9600            if (this.options.interactive) {
9601                removeClass(this._container, 'leaflet-interactive');
9602                this.removeInteractiveTarget(this._container);
9603            }
9604        },
9605  
9606        // @namespace DivOverlay
9607        // @method getLatLng: LatLng
9608        // Returns the geographical point of the overlay.
9609        getLatLng: function () {
9610            return this._latlng;
9611        },
9612  
9613        // @method setLatLng(latlng: LatLng): this
9614        // Sets the geographical point where the overlay will open.
9615        setLatLng: function (latlng) {
9616            this._latlng = toLatLng(latlng);
9617            if (this._map) {
9618                this._updatePosition();
9619                this._adjustPan();
9620            }
9621            return this;
9622        },
9623  
9624        // @method getContent: String|HTMLElement
9625        // Returns the content of the overlay.
9626        getContent: function () {
9627            return this._content;
9628        },
9629  
9630        // @method setContent(htmlContent: String|HTMLElement|Function): this
9631        // Sets the HTML content of the overlay. If a function is passed the source layer will be passed to the function.
9632        // The function should return a `String` or `HTMLElement` to be used in the overlay.
9633        setContent: function (content) {
9634            this._content = content;
9635            this.update();
9636            return this;
9637        },
9638  
9639        // @method getElement: String|HTMLElement
9640        // Returns the HTML container of the overlay.
9641        getElement: function () {
9642            return this._container;
9643        },
9644  
9645        // @method update: null
9646        // Updates the overlay content, layout and position. Useful for updating the overlay after something inside changed, e.g. image loaded.
9647        update: function () {
9648            if (!this._map) { return; }
9649  
9650            this._container.style.visibility = 'hidden';
9651  
9652            this._updateContent();
9653            this._updateLayout();
9654            this._updatePosition();
9655  
9656            this._container.style.visibility = '';
9657  
9658            this._adjustPan();
9659        },
9660  
9661        getEvents: function () {
9662            var events = {
9663                zoom: this._updatePosition,
9664                viewreset: this._updatePosition
9665            };
9666  
9667            if (this._zoomAnimated) {
9668                events.zoomanim = this._animateZoom;
9669            }
9670            return events;
9671        },
9672  
9673        // @method isOpen: Boolean
9674        // Returns `true` when the overlay is visible on the map.
9675        isOpen: function () {
9676            return !!this._map && this._map.hasLayer(this);
9677        },
9678  
9679        // @method bringToFront: this
9680        // Brings this overlay in front of other overlays (in the same map pane).
9681        bringToFront: function () {
9682            if (this._map) {
9683                toFront(this._container);
9684            }
9685            return this;
9686        },
9687  
9688        // @method bringToBack: this
9689        // Brings this overlay to the back of other overlays (in the same map pane).
9690        bringToBack: function () {
9691            if (this._map) {
9692                toBack(this._container);
9693            }
9694            return this;
9695        },
9696  
9697        // prepare bound overlay to open: update latlng pos / content source (for FeatureGroup)
9698        _prepareOpen: function (latlng) {
9699            var source = this._source;
9700            if (!source._map) { return false; }
9701  
9702            if (source instanceof FeatureGroup) {
9703                source = null;
9704                var layers = this._source._layers;
9705                for (var id in layers) {
9706                    if (layers[id]._map) {
9707                        source = layers[id];
9708                        break;
9709                    }
9710                }
9711                if (!source) { return false; } // Unable to get source layer.
9712  
9713                // set overlay source to this layer
9714                this._source = source;
9715            }
9716  
9717            if (!latlng) {
9718                if (source.getCenter) {
9719                    latlng = source.getCenter();
9720                } else if (source.getLatLng) {
9721                    latlng = source.getLatLng();
9722                } else if (source.getBounds) {
9723                    latlng = source.getBounds().getCenter();
9724                } else {
9725                    throw new Error('Unable to get source layer LatLng.');
9726                }
9727            }
9728            this.setLatLng(latlng);
9729  
9730            if (this._map) {
9731                // update the overlay (content, layout, etc...)
9732                this.update();
9733            }
9734  
9735            return true;
9736        },
9737  
9738        _updateContent: function () {
9739            if (!this._content) { return; }
9740  
9741            var node = this._contentNode;
9742            var content = (typeof this._content === 'function') ? this._content(this._source || this) : this._content;
9743  
9744            if (typeof content === 'string') {
9745                node.innerHTML = content;
9746            } else {
9747                while (node.hasChildNodes()) {
9748                    node.removeChild(node.firstChild);
9749                }
9750                node.appendChild(content);
9751            }
9752  
9753            // @namespace DivOverlay
9754            // @section DivOverlay events
9755            // @event contentupdate: Event
9756            // Fired when the content of the overlay is updated
9757            this.fire('contentupdate');
9758        },
9759  
9760        _updatePosition: function () {
9761            if (!this._map) { return; }
9762  
9763            var pos = this._map.latLngToLayerPoint(this._latlng),
9764                offset = toPoint(this.options.offset),
9765                anchor = this._getAnchor();
9766  
9767            if (this._zoomAnimated) {
9768                setPosition(this._container, pos.add(anchor));
9769            } else {
9770                offset = offset.add(pos).add(anchor);
9771            }
9772  
9773            var bottom = this._containerBottom = -offset.y,
9774                left = this._containerLeft = -Math.round(this._containerWidth / 2) + offset.x;
9775  
9776            // bottom position the overlay in case the height of the overlay changes (images loading etc)
9777            this._container.style.bottom = bottom + 'px';
9778            this._container.style.left = left + 'px';
9779        },
9780  
9781        _getAnchor: function () {
9782            return [0, 0];
9783        }
9784  
9785    });
9786  
9787    Map.include({
9788        _initOverlay: function (OverlayClass, content, latlng, options) {
9789            var overlay = content;
9790            if (!(overlay instanceof OverlayClass)) {
9791                overlay = new OverlayClass(options).setContent(content);
9792            }
9793            if (latlng) {
9794                overlay.setLatLng(latlng);
9795            }
9796            return overlay;
9797        }
9798    });
9799  
9800  
9801    Layer.include({
9802        _initOverlay: function (OverlayClass, old, content, options) {
9803            var overlay = content;
9804            if (overlay instanceof OverlayClass) {
9805                setOptions(overlay, options);
9806                overlay._source = this;
9807            } else {
9808                overlay = (old && !options) ? old : new OverlayClass(options, this);
9809                overlay.setContent(content);
9810            }
9811            return overlay;
9812        }
9813    });
9814  
9815    /*
9816     * @class Popup
9817     * @inherits DivOverlay
9818     * @aka L.Popup
9819     * Used to open popups in certain places of the map. Use [Map.openPopup](#map-openpopup) to
9820     * open popups while making sure that only one popup is open at one time
9821     * (recommended for usability), or use [Map.addLayer](#map-addlayer) to open as many as you want.
9822     *
9823     * @example
9824     *
9825     * If you want to just bind a popup to marker click and then open it, it's really easy:
9826     *
9827     * ```js
9828     * marker.bindPopup(popupContent).openPopup();
9829     * ```
9830     * Path overlays like polylines also have a `bindPopup` method.
9831     * Here's a more complicated way to open a popup on a map:
9832     *
9833     * ```js
9834     * var popup = L.popup()
9835     * 	.setLatLng(latlng)
9836     * 	.setContent('<p>Hello world!<br />This is a nice popup.</p>')
9837     * 	.openOn(map);
9838     * ```
9839     */
9840  
9841  
9842    // @namespace Popup
9843    var Popup = DivOverlay.extend({
9844  
9845        // @section
9846        // @aka Popup options
9847        options: {
9848            // @option pane: String = 'popupPane'
9849            // `Map pane` where the popup will be added.
9850            pane: 'popupPane',
9851  
9852            // @option offset: Point = Point(0, 7)
9853            // The offset of the popup position.
9854            offset: [0, 7],
9855  
9856            // @option maxWidth: Number = 300
9857            // Max width of the popup, in pixels.
9858            maxWidth: 300,
9859  
9860            // @option minWidth: Number = 50
9861            // Min width of the popup, in pixels.
9862            minWidth: 50,
9863  
9864            // @option maxHeight: Number = null
9865            // If set, creates a scrollable container of the given height
9866            // inside a popup if its content exceeds it.
9867            maxHeight: null,
9868  
9869            // @option autoPan: Boolean = true
9870            // Set it to `false` if you don't want the map to do panning animation
9871            // to fit the opened popup.
9872            autoPan: true,
9873  
9874            // @option autoPanPaddingTopLeft: Point = null
9875            // The margin between the popup and the top left corner of the map
9876            // view after autopanning was performed.
9877            autoPanPaddingTopLeft: null,
9878  
9879            // @option autoPanPaddingBottomRight: Point = null
9880            // The margin between the popup and the bottom right corner of the map
9881            // view after autopanning was performed.
9882            autoPanPaddingBottomRight: null,
9883  
9884            // @option autoPanPadding: Point = Point(5, 5)
9885            // Equivalent of setting both top left and bottom right autopan padding to the same value.
9886            autoPanPadding: [5, 5],
9887  
9888            // @option keepInView: Boolean = false
9889            // Set it to `true` if you want to prevent users from panning the popup
9890            // off of the screen while it is open.
9891            keepInView: false,
9892  
9893            // @option closeButton: Boolean = true
9894            // Controls the presence of a close button in the popup.
9895            closeButton: true,
9896  
9897            // @option autoClose: Boolean = true
9898            // Set it to `false` if you want to override the default behavior of
9899            // the popup closing when another popup is opened.
9900            autoClose: true,
9901  
9902            // @option closeOnEscapeKey: Boolean = true
9903            // Set it to `false` if you want to override the default behavior of
9904            // the ESC key for closing of the popup.
9905            closeOnEscapeKey: true,
9906  
9907            // @option closeOnClick: Boolean = *
9908            // Set it if you want to override the default behavior of the popup closing when user clicks
9909            // on the map. Defaults to the map's [`closePopupOnClick`](#map-closepopuponclick) option.
9910  
9911            // @option className: String = ''
9912            // A custom CSS class name to assign to the popup.
9913            className: ''
9914        },
9915  
9916        // @namespace Popup
9917        // @method openOn(map: Map): this
9918        // Alternative to `map.openPopup(popup)`.
9919        // Adds the popup to the map and closes the previous one.
9920        openOn: function (map) {
9921            map = arguments.length ? map : this._source._map; // experimental, not the part of public api
9922  
9923            if (!map.hasLayer(this) && map._popup && map._popup.options.autoClose) {
9924                map.removeLayer(map._popup);
9925            }
9926            map._popup = this;
9927  
9928            return DivOverlay.prototype.openOn.call(this, map);
9929        },
9930  
9931        onAdd: function (map) {
9932            DivOverlay.prototype.onAdd.call(this, map);
9933  
9934            // @namespace Map
9935            // @section Popup events
9936            // @event popupopen: PopupEvent
9937            // Fired when a popup is opened in the map
9938            map.fire('popupopen', {popup: this});
9939  
9940            if (this._source) {
9941                // @namespace Layer
9942                // @section Popup events
9943                // @event popupopen: PopupEvent
9944                // Fired when a popup bound to this layer is opened
9945                this._source.fire('popupopen', {popup: this}, true);
9946                // For non-path layers, we toggle the popup when clicking
9947                // again the layer, so prevent the map to reopen it.
9948                if (!(this._source instanceof Path)) {
9949                    this._source.on('preclick', stopPropagation);
9950                }
9951            }
9952        },
9953  
9954        onRemove: function (map) {
9955            DivOverlay.prototype.onRemove.call(this, map);
9956  
9957            // @namespace Map
9958            // @section Popup events
9959            // @event popupclose: PopupEvent
9960            // Fired when a popup in the map is closed
9961            map.fire('popupclose', {popup: this});
9962  
9963            if (this._source) {
9964                // @namespace Layer
9965                // @section Popup events
9966                // @event popupclose: PopupEvent
9967                // Fired when a popup bound to this layer is closed
9968                this._source.fire('popupclose', {popup: this}, true);
9969                if (!(this._source instanceof Path)) {
9970                    this._source.off('preclick', stopPropagation);
9971                }
9972            }
9973        },
9974  
9975        getEvents: function () {
9976            var events = DivOverlay.prototype.getEvents.call(this);
9977  
9978            if (this.options.closeOnClick !== undefined ? this.options.closeOnClick : this._map.options.closePopupOnClick) {
9979                events.preclick = this.close;
9980            }
9981  
9982            if (this.options.keepInView) {
9983                events.moveend = this._adjustPan;
9984            }
9985  
9986            return events;
9987        },
9988  
9989        _initLayout: function () {
9990            var prefix = 'leaflet-popup',
9991                container = this._container = create$1('div',
9992                prefix + ' ' + (this.options.className || '') +
9993                ' leaflet-zoom-animated');
9994  
9995            var wrapper = this._wrapper = create$1('div', prefix + '-content-wrapper', container);
9996            this._contentNode = create$1('div', prefix + '-content', wrapper);
9997  
9998            disableClickPropagation(container);
9999            disableScrollPropagation(this._contentNode);
10000            on(container, 'contextmenu', stopPropagation);
10001  
10002            this._tipContainer = create$1('div', prefix + '-tip-container', container);
10003            this._tip = create$1('div', prefix + '-tip', this._tipContainer);
10004  
10005            if (this.options.closeButton) {
10006                var closeButton = this._closeButton = create$1('a', prefix + '-close-button', container);
10007                closeButton.setAttribute('role', 'button'); // overrides the implicit role=link of <a> elements #7399
10008                closeButton.setAttribute('aria-label', 'Close popup');
10009                closeButton.href = '#close';
10010                closeButton.innerHTML = '<span aria-hidden="true">&#215;</span>';
10011  
10012                on(closeButton, 'click', this.close, this);
10013            }
10014        },
10015  
10016        _updateLayout: function () {
10017            var container = this._contentNode,
10018                style = container.style;
10019  
10020            style.width = '';
10021            style.whiteSpace = 'nowrap';
10022  
10023            var width = container.offsetWidth;
10024            width = Math.min(width, this.options.maxWidth);
10025            width = Math.max(width, this.options.minWidth);
10026  
10027            style.width = (width + 1) + 'px';
10028            style.whiteSpace = '';
10029  
10030            style.height = '';
10031  
10032            var height = container.offsetHeight,
10033                maxHeight = this.options.maxHeight,
10034                scrolledClass = 'leaflet-popup-scrolled';
10035  
10036            if (maxHeight && height > maxHeight) {
10037                style.height = maxHeight + 'px';
10038                addClass(container, scrolledClass);
10039            } else {
10040                removeClass(container, scrolledClass);
10041            }
10042  
10043            this._containerWidth = this._container.offsetWidth;
10044        },
10045  
10046        _animateZoom: function (e) {
10047            var pos = this._map._latLngToNewLayerPoint(this._latlng, e.zoom, e.center),
10048                anchor = this._getAnchor();
10049            setPosition(this._container, pos.add(anchor));
10050        },
10051  
10052        _adjustPan: function (e) {
10053            if (!this.options.autoPan) { return; }
10054            if (this._map._panAnim) { this._map._panAnim.stop(); }
10055  
10056            var map = this._map,
10057                marginBottom = parseInt(getStyle(this._container, 'marginBottom'), 10) || 0,
10058                containerHeight = this._container.offsetHeight + marginBottom,
10059                containerWidth = this._containerWidth,
10060                layerPos = new Point(this._containerLeft, -containerHeight - this._containerBottom);
10061  
10062            layerPos._add(getPosition(this._container));
10063  
10064            var containerPos = map.layerPointToContainerPoint(layerPos),
10065                padding = toPoint(this.options.autoPanPadding),
10066                paddingTL = toPoint(this.options.autoPanPaddingTopLeft || padding),
10067                paddingBR = toPoint(this.options.autoPanPaddingBottomRight || padding),
10068                size = map.getSize(),
10069                dx = 0,
10070                dy = 0;
10071  
10072            if (containerPos.x + containerWidth + paddingBR.x > size.x) { // right
10073                dx = containerPos.x + containerWidth - size.x + paddingBR.x;
10074            }
10075            if (containerPos.x - dx - paddingTL.x < 0) { // left
10076                dx = containerPos.x - paddingTL.x;
10077            }
10078            if (containerPos.y + containerHeight + paddingBR.y > size.y) { // bottom
10079                dy = containerPos.y + containerHeight - size.y + paddingBR.y;
10080            }
10081            if (containerPos.y - dy - paddingTL.y < 0) { // top
10082                dy = containerPos.y - paddingTL.y;
10083            }
10084  
10085            // @namespace Map
10086            // @section Popup events
10087            // @event autopanstart: Event
10088            // Fired when the map starts autopanning when opening a popup.
10089            if (dx || dy) {
10090                map
10091                    .fire('autopanstart')
10092                    .panBy([dx, dy], {animate: e && e.type === 'moveend'});
10093            }
10094        },
10095  
10096        _getAnchor: function () {
10097            // Where should we anchor the popup on the source layer?
10098            return toPoint(this._source && this._source._getPopupAnchor ? this._source._getPopupAnchor() : [0, 0]);
10099        }
10100  
10101    });
10102  
10103    // @namespace Popup
10104    // @factory L.popup(options?: Popup options, source?: Layer)
10105    // Instantiates a `Popup` object given an optional `options` object that describes its appearance and location and an optional `source` object that is used to tag the popup with a reference to the Layer to which it refers.
10106    var popup = function (options, source) {
10107        return new Popup(options, source);
10108    };
10109  
10110  
10111    /* @namespace Map
10112     * @section Interaction Options
10113     * @option closePopupOnClick: Boolean = true
10114     * Set it to `false` if you don't want popups to close when user clicks the map.
10115     */
10116    Map.mergeOptions({
10117        closePopupOnClick: true
10118    });
10119  
10120  
10121    // @namespace Map
10122    // @section Methods for Layers and Controls
10123    Map.include({
10124        // @method openPopup(popup: Popup): this
10125        // Opens the specified popup while closing the previously opened (to make sure only one is opened at one time for usability).
10126        // @alternative
10127        // @method openPopup(content: String|HTMLElement, latlng: LatLng, options?: Popup options): this
10128        // Creates a popup with the specified content and options and opens it in the given point on a map.
10129        openPopup: function (popup, latlng, options) {
10130            this._initOverlay(Popup, popup, latlng, options)
10131              .openOn(this);
10132  
10133            return this;
10134        },
10135  
10136        // @method closePopup(popup?: Popup): this
10137        // Closes the popup previously opened with [openPopup](#map-openpopup) (or the given one).
10138        closePopup: function (popup) {
10139            popup = arguments.length ? popup : this._popup;
10140            if (popup) {
10141                popup.close();
10142            }
10143            return this;
10144        }
10145    });
10146  
10147    /*
10148     * @namespace Layer
10149     * @section Popup methods example
10150     *
10151     * All layers share a set of methods convenient for binding popups to it.
10152     *
10153     * ```js
10154     * var layer = L.Polygon(latlngs).bindPopup('Hi There!').addTo(map);
10155     * layer.openPopup();
10156     * layer.closePopup();
10157     * ```
10158     *
10159     * Popups will also be automatically opened when the layer is clicked on and closed when the layer is removed from the map or another popup is opened.
10160     */
10161  
10162    // @section Popup methods
10163    Layer.include({
10164  
10165        // @method bindPopup(content: String|HTMLElement|Function|Popup, options?: Popup options): this
10166        // Binds a popup to the layer with the passed `content` and sets up the
10167        // necessary event listeners. If a `Function` is passed it will receive
10168        // the layer as the first argument and should return a `String` or `HTMLElement`.
10169        bindPopup: function (content, options) {
10170            this._popup = this._initOverlay(Popup, this._popup, content, options);
10171            if (!this._popupHandlersAdded) {
10172                this.on({
10173                    click: this._openPopup,
10174                    keypress: this._onKeyPress,
10175                    remove: this.closePopup,
10176                    move: this._movePopup
10177                });
10178                this._popupHandlersAdded = true;
10179            }
10180  
10181            return this;
10182        },
10183  
10184        // @method unbindPopup(): this
10185        // Removes the popup previously bound with `bindPopup`.
10186        unbindPopup: function () {
10187            if (this._popup) {
10188                this.off({
10189                    click: this._openPopup,
10190                    keypress: this._onKeyPress,
10191                    remove: this.closePopup,
10192                    move: this._movePopup
10193                });
10194                this._popupHandlersAdded = false;
10195                this._popup = null;
10196            }
10197            return this;
10198        },
10199  
10200        // @method openPopup(latlng?: LatLng): this
10201        // Opens the bound popup at the specified `latlng` or at the default popup anchor if no `latlng` is passed.
10202        openPopup: function (latlng) {
10203            if (this._popup && this._popup._prepareOpen(latlng)) {
10204                // open the popup on the map
10205                this._popup.openOn(this._map);
10206            }
10207            return this;
10208        },
10209  
10210        // @method closePopup(): this
10211        // Closes the popup bound to this layer if it is open.
10212        closePopup: function () {
10213            if (this._popup) {
10214                this._popup.close();
10215            }
10216            return this;
10217        },
10218  
10219        // @method togglePopup(): this
10220        // Opens or closes the popup bound to this layer depending on its current state.
10221        togglePopup: function () {
10222            if (this._popup) {
10223                this._popup.toggle(this);
10224            }
10225            return this;
10226        },
10227  
10228        // @method isPopupOpen(): boolean
10229        // Returns `true` if the popup bound to this layer is currently open.
10230        isPopupOpen: function () {
10231            return (this._popup ? this._popup.isOpen() : false);
10232        },
10233  
10234        // @method setPopupContent(content: String|HTMLElement|Popup): this
10235        // Sets the content of the popup bound to this layer.
10236        setPopupContent: function (content) {
10237            if (this._popup) {
10238                this._popup.setContent(content);
10239            }
10240            return this;
10241        },
10242  
10243        // @method getPopup(): Popup
10244        // Returns the popup bound to this layer.
10245        getPopup: function () {
10246            return this._popup;
10247        },
10248  
10249        _openPopup: function (e) {
10250            if (!this._popup || !this._map) {
10251                return;
10252            }
10253            // prevent map click
10254            stop(e);
10255  
10256            var target = e.layer || e.target;
10257            if (this._popup._source === target && !(target instanceof Path)) {
10258                // treat it like a marker and figure out
10259                // if we should toggle it open/closed
10260                if (this._map.hasLayer(this._popup)) {
10261                    this.closePopup();
10262                } else {
10263                    this.openPopup(e.latlng);
10264                }
10265                return;
10266            }
10267            this._popup._source = target;
10268            this.openPopup(e.latlng);
10269        },
10270  
10271        _movePopup: function (e) {
10272            this._popup.setLatLng(e.latlng);
10273        },
10274  
10275        _onKeyPress: function (e) {
10276            if (e.originalEvent.keyCode === 13) {
10277                this._openPopup(e);
10278            }
10279        }
10280    });
10281  
10282    /*
10283     * @class Tooltip
10284     * @inherits DivOverlay
10285     * @aka L.Tooltip
10286     * Used to display small texts on top of map layers.
10287     *
10288     * @example
10289     *
10290     * ```js
10291     * marker.bindTooltip("my tooltip text").openTooltip();
10292     * ```
10293     * Note about tooltip offset. Leaflet takes two options in consideration
10294     * for computing tooltip offsetting:
10295     * - the `offset` Tooltip option: it defaults to [0, 0], and it's specific to one tooltip.
10296     *   Add a positive x offset to move the tooltip to the right, and a positive y offset to
10297     *   move it to the bottom. Negatives will move to the left and top.
10298     * - the `tooltipAnchor` Icon option: this will only be considered for Marker. You
10299     *   should adapt this value if you use a custom icon.
10300     */
10301  
10302  
10303    // @namespace Tooltip
10304    var Tooltip = DivOverlay.extend({
10305  
10306        // @section
10307        // @aka Tooltip options
10308        options: {
10309            // @option pane: String = 'tooltipPane'
10310            // `Map pane` where the tooltip will be added.
10311            pane: 'tooltipPane',
10312  
10313            // @option offset: Point = Point(0, 0)
10314            // Optional offset of the tooltip position.
10315            offset: [0, 0],
10316  
10317            // @option direction: String = 'auto'
10318            // Direction where to open the tooltip. Possible values are: `right`, `left`,
10319            // `top`, `bottom`, `center`, `auto`.
10320            // `auto` will dynamically switch between `right` and `left` according to the tooltip
10321            // position on the map.
10322            direction: 'auto',
10323  
10324            // @option permanent: Boolean = false
10325            // Whether to open the tooltip permanently or only on mouseover.
10326            permanent: false,
10327  
10328            // @option sticky: Boolean = false
10329            // If true, the tooltip will follow the mouse instead of being fixed at the feature center.
10330            sticky: false,
10331  
10332            // @option opacity: Number = 0.9
10333            // Tooltip container opacity.
10334            opacity: 0.9
10335        },
10336  
10337        onAdd: function (map) {
10338            DivOverlay.prototype.onAdd.call(this, map);
10339            this.setOpacity(this.options.opacity);
10340  
10341            // @namespace Map
10342            // @section Tooltip events
10343            // @event tooltipopen: TooltipEvent
10344            // Fired when a tooltip is opened in the map.
10345            map.fire('tooltipopen', {tooltip: this});
10346  
10347            if (this._source) {
10348                this.addEventParent(this._source);
10349  
10350                // @namespace Layer
10351                // @section Tooltip events
10352                // @event tooltipopen: TooltipEvent
10353                // Fired when a tooltip bound to this layer is opened.
10354                this._source.fire('tooltipopen', {tooltip: this}, true);
10355            }
10356        },
10357  
10358        onRemove: function (map) {
10359            DivOverlay.prototype.onRemove.call(this, map);
10360  
10361            // @namespace Map
10362            // @section Tooltip events
10363            // @event tooltipclose: TooltipEvent
10364            // Fired when a tooltip in the map is closed.
10365            map.fire('tooltipclose', {tooltip: this});
10366  
10367            if (this._source) {
10368                this.removeEventParent(this._source);
10369  
10370                // @namespace Layer
10371                // @section Tooltip events
10372                // @event tooltipclose: TooltipEvent
10373                // Fired when a tooltip bound to this layer is closed.
10374                this._source.fire('tooltipclose', {tooltip: this}, true);
10375            }
10376        },
10377  
10378        getEvents: function () {
10379            var events = DivOverlay.prototype.getEvents.call(this);
10380  
10381            if (!this.options.permanent) {
10382                events.preclick = this.close;
10383            }
10384  
10385            return events;
10386        },
10387  
10388        _initLayout: function () {
10389            var prefix = 'leaflet-tooltip',
10390                className = prefix + ' ' + (this.options.className || '') + ' leaflet-zoom-' + (this._zoomAnimated ? 'animated' : 'hide');
10391  
10392            this._contentNode = this._container = create$1('div', className);
10393        },
10394  
10395        _updateLayout: function () {},
10396  
10397        _adjustPan: function () {},
10398  
10399        _setPosition: function (pos) {
10400            var subX, subY,
10401                map = this._map,
10402                container = this._container,
10403                centerPoint = map.latLngToContainerPoint(map.getCenter()),
10404                tooltipPoint = map.layerPointToContainerPoint(pos),
10405                direction = this.options.direction,
10406                tooltipWidth = container.offsetWidth,
10407                tooltipHeight = container.offsetHeight,
10408                offset = toPoint(this.options.offset),
10409                anchor = this._getAnchor();
10410  
10411            if (direction === 'top') {
10412                subX = tooltipWidth / 2;
10413                subY = tooltipHeight;
10414            } else if (direction === 'bottom') {
10415                subX = tooltipWidth / 2;
10416                subY = 0;
10417            } else if (direction === 'center') {
10418                subX = tooltipWidth / 2;
10419                subY = tooltipHeight / 2;
10420            } else if (direction === 'right') {
10421                subX = 0;
10422                subY = tooltipHeight / 2;
10423            } else if (direction === 'left') {
10424                subX = tooltipWidth;
10425                subY = tooltipHeight / 2;
10426            } else if (tooltipPoint.x < centerPoint.x) {
10427                direction = 'right';
10428                subX = 0;
10429                subY = tooltipHeight / 2;
10430            } else {
10431                direction = 'left';
10432                subX = tooltipWidth + (offset.x + anchor.x) * 2;
10433                subY = tooltipHeight / 2;
10434            }
10435  
10436            pos = pos.subtract(toPoint(subX, subY, true)).add(offset).add(anchor);
10437  
10438            removeClass(container, 'leaflet-tooltip-right');
10439            removeClass(container, 'leaflet-tooltip-left');
10440            removeClass(container, 'leaflet-tooltip-top');
10441            removeClass(container, 'leaflet-tooltip-bottom');
10442            addClass(container, 'leaflet-tooltip-' + direction);
10443            setPosition(container, pos);
10444        },
10445  
10446        _updatePosition: function () {
10447            var pos = this._map.latLngToLayerPoint(this._latlng);
10448            this._setPosition(pos);
10449        },
10450  
10451        setOpacity: function (opacity) {
10452            this.options.opacity = opacity;
10453  
10454            if (this._container) {
10455                setOpacity(this._container, opacity);
10456            }
10457        },
10458  
10459        _animateZoom: function (e) {
10460            var pos = this._map._latLngToNewLayerPoint(this._latlng, e.zoom, e.center);
10461            this._setPosition(pos);
10462        },
10463  
10464        _getAnchor: function () {
10465            // Where should we anchor the tooltip on the source layer?
10466            return toPoint(this._source && this._source._getTooltipAnchor && !this.options.sticky ? this._source._getTooltipAnchor() : [0, 0]);
10467        }
10468  
10469    });
10470  
10471    // @namespace Tooltip
10472    // @factory L.tooltip(options?: Tooltip options, source?: Layer)
10473    // Instantiates a Tooltip object given an optional `options` object that describes its appearance and location and an optional `source` object that is used to tag the tooltip with a reference to the Layer to which it refers.
10474    var tooltip = function (options, source) {
10475        return new Tooltip(options, source);
10476    };
10477  
10478    // @namespace Map
10479    // @section Methods for Layers and Controls
10480    Map.include({
10481  
10482        // @method openTooltip(tooltip: Tooltip): this
10483        // Opens the specified tooltip.
10484        // @alternative
10485        // @method openTooltip(content: String|HTMLElement, latlng: LatLng, options?: Tooltip options): this
10486        // Creates a tooltip with the specified content and options and open it.
10487        openTooltip: function (tooltip, latlng, options) {
10488            this._initOverlay(Tooltip, tooltip, latlng, options)
10489              .openOn(this);
10490  
10491            return this;
10492        },
10493  
10494        // @method closeTooltip(tooltip: Tooltip): this
10495        // Closes the tooltip given as parameter.
10496        closeTooltip: function (tooltip) {
10497            tooltip.close();
10498            return this;
10499        }
10500  
10501    });
10502  
10503    /*
10504     * @namespace Layer
10505     * @section Tooltip methods example
10506     *
10507     * All layers share a set of methods convenient for binding tooltips to it.
10508     *
10509     * ```js
10510     * var layer = L.Polygon(latlngs).bindTooltip('Hi There!').addTo(map);
10511     * layer.openTooltip();
10512     * layer.closeTooltip();
10513     * ```
10514     */
10515  
10516    // @section Tooltip methods
10517    Layer.include({
10518  
10519        // @method bindTooltip(content: String|HTMLElement|Function|Tooltip, options?: Tooltip options): this
10520        // Binds a tooltip to the layer with the passed `content` and sets up the
10521        // necessary event listeners. If a `Function` is passed it will receive
10522        // the layer as the first argument and should return a `String` or `HTMLElement`.
10523        bindTooltip: function (content, options) {
10524  
10525            if (this._tooltip && this.isTooltipOpen()) {
10526                this.unbindTooltip();
10527            }
10528  
10529            this._tooltip = this._initOverlay(Tooltip, this._tooltip, content, options);
10530            this._initTooltipInteractions();
10531  
10532            if (this._tooltip.options.permanent && this._map && this._map.hasLayer(this)) {
10533                this.openTooltip();
10534            }
10535  
10536            return this;
10537        },
10538  
10539        // @method unbindTooltip(): this
10540        // Removes the tooltip previously bound with `bindTooltip`.
10541        unbindTooltip: function () {
10542            if (this._tooltip) {
10543                this._initTooltipInteractions(true);
10544                this.closeTooltip();
10545                this._tooltip = null;
10546            }
10547            return this;
10548        },
10549  
10550        _initTooltipInteractions: function (remove) {
10551            if (!remove && this._tooltipHandlersAdded) { return; }
10552            var onOff = remove ? 'off' : 'on',
10553                events = {
10554                remove: this.closeTooltip,
10555                move: this._moveTooltip
10556                };
10557            if (!this._tooltip.options.permanent) {
10558                events.mouseover = this._openTooltip;
10559                events.mouseout = this.closeTooltip;
10560                events.click = this._openTooltip;
10561            } else {
10562                events.add = this._openTooltip;
10563            }
10564            if (this._tooltip.options.sticky) {
10565                events.mousemove = this._moveTooltip;
10566            }
10567            this[onOff](events);
10568            this._tooltipHandlersAdded = !remove;
10569        },
10570  
10571        // @method openTooltip(latlng?: LatLng): this
10572        // Opens the bound tooltip at the specified `latlng` or at the default tooltip anchor if no `latlng` is passed.
10573        openTooltip: function (latlng) {
10574            if (this._tooltip && this._tooltip._prepareOpen(latlng)) {
10575                // open the tooltip on the map
10576                this._tooltip.openOn(this._map);
10577            }
10578            return this;
10579        },
10580  
10581        // @method closeTooltip(): this
10582        // Closes the tooltip bound to this layer if it is open.
10583        closeTooltip: function () {
10584            if (this._tooltip) {
10585                return this._tooltip.close();
10586            }
10587        },
10588  
10589        // @method toggleTooltip(): this
10590        // Opens or closes the tooltip bound to this layer depending on its current state.
10591        toggleTooltip: function () {
10592            if (this._tooltip) {
10593                this._tooltip.toggle(this);
10594            }
10595            return this;
10596        },
10597  
10598        // @method isTooltipOpen(): boolean
10599        // Returns `true` if the tooltip bound to this layer is currently open.
10600        isTooltipOpen: function () {
10601            return this._tooltip.isOpen();
10602        },
10603  
10604        // @method setTooltipContent(content: String|HTMLElement|Tooltip): this
10605        // Sets the content of the tooltip bound to this layer.
10606        setTooltipContent: function (content) {
10607            if (this._tooltip) {
10608                this._tooltip.setContent(content);
10609            }
10610            return this;
10611        },
10612  
10613        // @method getTooltip(): Tooltip
10614        // Returns the tooltip bound to this layer.
10615        getTooltip: function () {
10616            return this._tooltip;
10617        },
10618  
10619        _openTooltip: function (e) {
10620            if (!this._tooltip || !this._map || (this._map.dragging && this._map.dragging.moving())) {
10621                return;
10622            }
10623            this._tooltip._source = e.layer || e.target;
10624  
10625            this.openTooltip(this._tooltip.options.sticky ? e.latlng : undefined);
10626        },
10627  
10628        _moveTooltip: function (e) {
10629            var latlng = e.latlng, containerPoint, layerPoint;
10630            if (this._tooltip.options.sticky && e.originalEvent) {
10631                containerPoint = this._map.mouseEventToContainerPoint(e.originalEvent);
10632                layerPoint = this._map.containerPointToLayerPoint(containerPoint);
10633                latlng = this._map.layerPointToLatLng(layerPoint);
10634            }
10635            this._tooltip.setLatLng(latlng);
10636        }
10637    });
10638  
10639    /*
10640     * @class DivIcon
10641     * @aka L.DivIcon
10642     * @inherits Icon
10643     *
10644     * Represents a lightweight icon for markers that uses a simple `<div>`
10645     * element instead of an image. Inherits from `Icon` but ignores the `iconUrl` and shadow options.
10646     *
10647     * @example
10648     * ```js
10649     * var myIcon = L.divIcon({className: 'my-div-icon'});
10650     * // you can set .my-div-icon styles in CSS
10651     *
10652     * L.marker([50.505, 30.57], {icon: myIcon}).addTo(map);
10653     * ```
10654     *
10655     * By default, it has a 'leaflet-div-icon' CSS class and is styled as a little white square with a shadow.
10656     */
10657  
10658    var DivIcon = Icon.extend({
10659        options: {
10660            // @section
10661            // @aka DivIcon options
10662            iconSize: [12, 12], // also can be set through CSS
10663  
10664            // iconAnchor: (Point),
10665            // popupAnchor: (Point),
10666  
10667            // @option html: String|HTMLElement = ''
10668            // Custom HTML code to put inside the div element, empty by default. Alternatively,
10669            // an instance of `HTMLElement`.
10670            html: false,
10671  
10672            // @option bgPos: Point = [0, 0]
10673            // Optional relative position of the background, in pixels
10674            bgPos: null,
10675  
10676            className: 'leaflet-div-icon'
10677        },
10678  
10679        createIcon: function (oldIcon) {
10680            var div = (oldIcon && oldIcon.tagName === 'DIV') ? oldIcon : document.createElement('div'),
10681                options = this.options;
10682  
10683            if (options.html instanceof Element) {
10684                empty(div);
10685                div.appendChild(options.html);
10686            } else {
10687                div.innerHTML = options.html !== false ? options.html : '';
10688            }
10689  
10690            if (options.bgPos) {
10691                var bgPos = toPoint(options.bgPos);
10692                div.style.backgroundPosition = (-bgPos.x) + 'px ' + (-bgPos.y) + 'px';
10693            }
10694            this._setIconStyles(div, 'icon');
10695  
10696            return div;
10697        },
10698  
10699        createShadow: function () {
10700            return null;
10701        }
10702    });
10703  
10704    // @factory L.divIcon(options: DivIcon options)
10705    // Creates a `DivIcon` instance with the given options.
10706    function divIcon(options) {
10707        return new DivIcon(options);
10708    }
10709  
10710    Icon.Default = IconDefault;
10711  
10712    /*
10713     * @class GridLayer
10714     * @inherits Layer
10715     * @aka L.GridLayer
10716     *
10717     * Generic class for handling a tiled grid of HTML elements. This is the base class for all tile layers and replaces `TileLayer.Canvas`.
10718     * GridLayer can be extended to create a tiled grid of HTML elements like `<canvas>`, `<img>` or `<div>`. GridLayer will handle creating and animating these DOM elements for you.
10719     *
10720     *
10721     * @section Synchronous usage
10722     * @example
10723     *
10724     * To create a custom layer, extend GridLayer and implement the `createTile()` method, which will be passed a `Point` object with the `x`, `y`, and `z` (zoom level) coordinates to draw your tile.
10725     *
10726     * ```js
10727     * var CanvasLayer = L.GridLayer.extend({
10728     *     createTile: function(coords){
10729     *         // create a <canvas> element for drawing
10730     *         var tile = L.DomUtil.create('canvas', 'leaflet-tile');
10731     *
10732     *         // setup tile width and height according to the options
10733     *         var size = this.getTileSize();
10734     *         tile.width = size.x;
10735     *         tile.height = size.y;
10736     *
10737     *         // get a canvas context and draw something on it using coords.x, coords.y and coords.z
10738     *         var ctx = tile.getContext('2d');
10739     *
10740     *         // return the tile so it can be rendered on screen
10741     *         return tile;
10742     *     }
10743     * });
10744     * ```
10745     *
10746     * @section Asynchronous usage
10747     * @example
10748     *
10749     * Tile creation can also be asynchronous, this is useful when using a third-party drawing library. Once the tile is finished drawing it can be passed to the `done()` callback.
10750     *
10751     * ```js
10752     * var CanvasLayer = L.GridLayer.extend({
10753     *     createTile: function(coords, done){
10754     *         var error;
10755     *
10756     *         // create a <canvas> element for drawing
10757     *         var tile = L.DomUtil.create('canvas', 'leaflet-tile');
10758     *
10759     *         // setup tile width and height according to the options
10760     *         var size = this.getTileSize();
10761     *         tile.width = size.x;
10762     *         tile.height = size.y;
10763     *
10764     *         // draw something asynchronously and pass the tile to the done() callback
10765     *         setTimeout(function() {
10766     *             done(error, tile);
10767     *         }, 1000);
10768     *
10769     *         return tile;
10770     *     }
10771     * });
10772     * ```
10773     *
10774     * @section
10775     */
10776  
10777  
10778    var GridLayer = Layer.extend({
10779  
10780        // @section
10781        // @aka GridLayer options
10782        options: {
10783            // @option tileSize: Number|Point = 256
10784            // Width and height of tiles in the grid. Use a number if width and height are equal, or `L.point(width, height)` otherwise.
10785            tileSize: 256,
10786  
10787            // @option opacity: Number = 1.0
10788            // Opacity of the tiles. Can be used in the `createTile()` function.
10789            opacity: 1,
10790  
10791            // @option updateWhenIdle: Boolean = (depends)
10792            // Load new tiles only when panning ends.
10793            // `true` by default on mobile browsers, in order to avoid too many requests and keep smooth navigation.
10794            // `false` otherwise in order to display new tiles _during_ panning, since it is easy to pan outside the
10795            // [`keepBuffer`](#gridlayer-keepbuffer) option in desktop browsers.
10796            updateWhenIdle: Browser.mobile,
10797  
10798            // @option updateWhenZooming: Boolean = true
10799            // By default, a smooth zoom animation (during a [touch zoom](#map-touchzoom) or a [`flyTo()`](#map-flyto)) will update grid layers every integer zoom level. Setting this option to `false` will update the grid layer only when the smooth animation ends.
10800            updateWhenZooming: true,
10801  
10802            // @option updateInterval: Number = 200
10803            // Tiles will not update more than once every `updateInterval` milliseconds when panning.
10804            updateInterval: 200,
10805  
10806            // @option zIndex: Number = 1
10807            // The explicit zIndex of the tile layer.
10808            zIndex: 1,
10809  
10810            // @option bounds: LatLngBounds = undefined
10811            // If set, tiles will only be loaded inside the set `LatLngBounds`.
10812            bounds: null,
10813  
10814            // @option minZoom: Number = 0
10815            // The minimum zoom level down to which this layer will be displayed (inclusive).
10816            minZoom: 0,
10817  
10818            // @option maxZoom: Number = undefined
10819            // The maximum zoom level up to which this layer will be displayed (inclusive).
10820            maxZoom: undefined,
10821  
10822            // @option maxNativeZoom: Number = undefined
10823            // Maximum zoom number the tile source has available. If it is specified,
10824            // the tiles on all zoom levels higher than `maxNativeZoom` will be loaded
10825            // from `maxNativeZoom` level and auto-scaled.
10826            maxNativeZoom: undefined,
10827  
10828            // @option minNativeZoom: Number = undefined
10829            // Minimum zoom number the tile source has available. If it is specified,
10830            // the tiles on all zoom levels lower than `minNativeZoom` will be loaded
10831            // from `minNativeZoom` level and auto-scaled.
10832            minNativeZoom: undefined,
10833  
10834            // @option noWrap: Boolean = false
10835            // Whether the layer is wrapped around the antimeridian. If `true`, the
10836            // GridLayer will only be displayed once at low zoom levels. Has no
10837            // effect when the [map CRS](#map-crs) doesn't wrap around. Can be used
10838            // in combination with [`bounds`](#gridlayer-bounds) to prevent requesting
10839            // tiles outside the CRS limits.
10840            noWrap: false,
10841  
10842            // @option pane: String = 'tilePane'
10843            // `Map pane` where the grid layer will be added.
10844            pane: 'tilePane',
10845  
10846            // @option className: String = ''
10847            // A custom class name to assign to the tile layer. Empty by default.
10848            className: '',
10849  
10850            // @option keepBuffer: Number = 2
10851            // When panning the map, keep this many rows and columns of tiles before unloading them.
10852            keepBuffer: 2
10853        },
10854  
10855        initialize: function (options) {
10856            setOptions(this, options);
10857        },
10858  
10859        onAdd: function () {
10860            this._initContainer();
10861  
10862            this._levels = {};
10863            this._tiles = {};
10864  
10865            this._resetView(); // implicit _update() call
10866        },
10867  
10868        beforeAdd: function (map) {
10869            map._addZoomLimit(this);
10870        },
10871  
10872        onRemove: function (map) {
10873            this._removeAllTiles();
10874            remove(this._container);
10875            map._removeZoomLimit(this);
10876            this._container = null;
10877            this._tileZoom = undefined;
10878        },
10879  
10880        // @method bringToFront: this
10881        // Brings the tile layer to the top of all tile layers.
10882        bringToFront: function () {
10883            if (this._map) {
10884                toFront(this._container);
10885                this._setAutoZIndex(Math.max);
10886            }
10887            return this;
10888        },
10889  
10890        // @method bringToBack: this
10891        // Brings the tile layer to the bottom of all tile layers.
10892        bringToBack: function () {
10893            if (this._map) {
10894                toBack(this._container);
10895                this._setAutoZIndex(Math.min);
10896            }
10897            return this;
10898        },
10899  
10900        // @method getContainer: HTMLElement
10901        // Returns the HTML element that contains the tiles for this layer.
10902        getContainer: function () {
10903            return this._container;
10904        },
10905  
10906        // @method setOpacity(opacity: Number): this
10907        // Changes the [opacity](#gridlayer-opacity) of the grid layer.
10908        setOpacity: function (opacity) {
10909            this.options.opacity = opacity;
10910            this._updateOpacity();
10911            return this;
10912        },
10913  
10914        // @method setZIndex(zIndex: Number): this
10915        // Changes the [zIndex](#gridlayer-zindex) of the grid layer.
10916        setZIndex: function (zIndex) {
10917            this.options.zIndex = zIndex;
10918            this._updateZIndex();
10919  
10920            return this;
10921        },
10922  
10923        // @method isLoading: Boolean
10924        // Returns `true` if any tile in the grid layer has not finished loading.
10925        isLoading: function () {
10926            return this._loading;
10927        },
10928  
10929        // @method redraw: this
10930        // Causes the layer to clear all the tiles and request them again.
10931        redraw: function () {
10932            if (this._map) {
10933                this._removeAllTiles();
10934                var tileZoom = this._clampZoom(this._map.getZoom());
10935                if (tileZoom !== this._tileZoom) {
10936                    this._tileZoom = tileZoom;
10937                    this._updateLevels();
10938                }
10939                this._update();
10940            }
10941            return this;
10942        },
10943  
10944        getEvents: function () {
10945            var events = {
10946                viewprereset: this._invalidateAll,
10947                viewreset: this._resetView,
10948                zoom: this._resetView,
10949                moveend: this._onMoveEnd
10950            };
10951  
10952            if (!this.options.updateWhenIdle) {
10953                // update tiles on move, but not more often than once per given interval
10954                if (!this._onMove) {
10955                    this._onMove = throttle(this._onMoveEnd, this.options.updateInterval, this);
10956                }
10957  
10958                events.move = this._onMove;
10959            }
10960  
10961            if (this._zoomAnimated) {
10962                events.zoomanim = this._animateZoom;
10963            }
10964  
10965            return events;
10966        },
10967  
10968        // @section Extension methods
10969        // Layers extending `GridLayer` shall reimplement the following method.
10970        // @method createTile(coords: Object, done?: Function): HTMLElement
10971        // Called only internally, must be overridden by classes extending `GridLayer`.
10972        // Returns the `HTMLElement` corresponding to the given `coords`. If the `done` callback
10973        // is specified, it must be called when the tile has finished loading and drawing.
10974        createTile: function () {
10975            return document.createElement('div');
10976        },
10977  
10978        // @section
10979        // @method getTileSize: Point
10980        // Normalizes the [tileSize option](#gridlayer-tilesize) into a point. Used by the `createTile()` method.
10981        getTileSize: function () {
10982            var s = this.options.tileSize;
10983            return s instanceof Point ? s : new Point(s, s);
10984        },
10985  
10986        _updateZIndex: function () {
10987            if (this._container && this.options.zIndex !== undefined && this.options.zIndex !== null) {
10988                this._container.style.zIndex = this.options.zIndex;
10989            }
10990        },
10991  
10992        _setAutoZIndex: function (compare) {
10993            // go through all other layers of the same pane, set zIndex to max + 1 (front) or min - 1 (back)
10994  
10995            var layers = this.getPane().children,
10996                edgeZIndex = -compare(-Infinity, Infinity); // -Infinity for max, Infinity for min
10997  
10998            for (var i = 0, len = layers.length, zIndex; i < len; i++) {
10999  
11000                zIndex = layers[i].style.zIndex;
11001  
11002                if (layers[i] !== this._container && zIndex) {
11003                    edgeZIndex = compare(edgeZIndex, +zIndex);
11004                }
11005            }
11006  
11007            if (isFinite(edgeZIndex)) {
11008                this.options.zIndex = edgeZIndex + compare(-1, 1);
11009                this._updateZIndex();
11010            }
11011        },
11012  
11013        _updateOpacity: function () {
11014            if (!this._map) { return; }
11015  
11016            // IE doesn't inherit filter opacity properly, so we're forced to set it on tiles
11017            if (Browser.ielt9) { return; }
11018  
11019            setOpacity(this._container, this.options.opacity);
11020  
11021            var now = +new Date(),
11022                nextFrame = false,
11023                willPrune = false;
11024  
11025            for (var key in this._tiles) {
11026                var tile = this._tiles[key];
11027                if (!tile.current || !tile.loaded) { continue; }
11028  
11029                var fade = Math.min(1, (now - tile.loaded) / 200);
11030  
11031                setOpacity(tile.el, fade);
11032                if (fade < 1) {
11033                    nextFrame = true;
11034                } else {
11035                    if (tile.active) {
11036                        willPrune = true;
11037                    } else {
11038                        this._onOpaqueTile(tile);
11039                    }
11040                    tile.active = true;
11041                }
11042            }
11043  
11044            if (willPrune && !this._noPrune) { this._pruneTiles(); }
11045  
11046            if (nextFrame) {
11047                cancelAnimFrame(this._fadeFrame);
11048                this._fadeFrame = requestAnimFrame(this._updateOpacity, this);
11049            }
11050        },
11051  
11052        _onOpaqueTile: falseFn,
11053  
11054        _initContainer: function () {
11055            if (this._container) { return; }
11056  
11057            this._container = create$1('div', 'leaflet-layer ' + (this.options.className || ''));
11058            this._updateZIndex();
11059  
11060            if (this.options.opacity < 1) {
11061                this._updateOpacity();
11062            }
11063  
11064            this.getPane().appendChild(this._container);
11065        },
11066  
11067        _updateLevels: function () {
11068  
11069            var zoom = this._tileZoom,
11070                maxZoom = this.options.maxZoom;
11071  
11072            if (zoom === undefined) { return undefined; }
11073  
11074            for (var z in this._levels) {
11075                z = Number(z);
11076                if (this._levels[z].el.children.length || z === zoom) {
11077                    this._levels[z].el.style.zIndex = maxZoom - Math.abs(zoom - z);
11078                    this._onUpdateLevel(z);
11079                } else {
11080                    remove(this._levels[z].el);
11081                    this._removeTilesAtZoom(z);
11082                    this._onRemoveLevel(z);
11083                    delete this._levels[z];
11084                }
11085            }
11086  
11087            var level = this._levels[zoom],
11088                map = this._map;
11089  
11090            if (!level) {
11091                level = this._levels[zoom] = {};
11092  
11093                level.el = create$1('div', 'leaflet-tile-container leaflet-zoom-animated', this._container);
11094                level.el.style.zIndex = maxZoom;
11095  
11096                level.origin = map.project(map.unproject(map.getPixelOrigin()), zoom).round();
11097                level.zoom = zoom;
11098  
11099                this._setZoomTransform(level, map.getCenter(), map.getZoom());
11100  
11101                // force the browser to consider the newly added element for transition
11102                falseFn(level.el.offsetWidth);
11103  
11104                this._onCreateLevel(level);
11105            }
11106  
11107            this._level = level;
11108  
11109            return level;
11110        },
11111  
11112        _onUpdateLevel: falseFn,
11113  
11114        _onRemoveLevel: falseFn,
11115  
11116        _onCreateLevel: falseFn,
11117  
11118        _pruneTiles: function () {
11119            if (!this._map) {
11120                return;
11121            }
11122  
11123            var key, tile;
11124  
11125            var zoom = this._map.getZoom();
11126            if (zoom > this.options.maxZoom ||
11127                zoom < this.options.minZoom) {
11128                this._removeAllTiles();
11129                return;
11130            }
11131  
11132            for (key in this._tiles) {
11133                tile = this._tiles[key];
11134                tile.retain = tile.current;
11135            }
11136  
11137            for (key in this._tiles) {
11138                tile = this._tiles[key];
11139                if (tile.current && !tile.active) {
11140                    var coords = tile.coords;
11141                    if (!this._retainParent(coords.x, coords.y, coords.z, coords.z - 5)) {
11142                        this._retainChildren(coords.x, coords.y, coords.z, coords.z + 2);
11143                    }
11144                }
11145            }
11146  
11147            for (key in this._tiles) {
11148                if (!this._tiles[key].retain) {
11149                    this._removeTile(key);
11150                }
11151            }
11152        },
11153  
11154        _removeTilesAtZoom: function (zoom) {
11155            for (var key in this._tiles) {
11156                if (this._tiles[key].coords.z !== zoom) {
11157                    continue;
11158                }
11159                this._removeTile(key);
11160            }
11161        },
11162  
11163        _removeAllTiles: function () {
11164            for (var key in this._tiles) {
11165                this._removeTile(key);
11166            }
11167        },
11168  
11169        _invalidateAll: function () {
11170            for (var z in this._levels) {
11171                remove(this._levels[z].el);
11172                this._onRemoveLevel(Number(z));
11173                delete this._levels[z];
11174            }
11175            this._removeAllTiles();
11176  
11177            this._tileZoom = undefined;
11178        },
11179  
11180        _retainParent: function (x, y, z, minZoom) {
11181            var x2 = Math.floor(x / 2),
11182                y2 = Math.floor(y / 2),
11183                z2 = z - 1,
11184                coords2 = new Point(+x2, +y2);
11185            coords2.z = +z2;
11186  
11187            var key = this._tileCoordsToKey(coords2),
11188                tile = this._tiles[key];
11189  
11190            if (tile && tile.active) {
11191                tile.retain = true;
11192                return true;
11193  
11194            } else if (tile && tile.loaded) {
11195                tile.retain = true;
11196            }
11197  
11198            if (z2 > minZoom) {
11199                return this._retainParent(x2, y2, z2, minZoom);
11200            }
11201  
11202            return false;
11203        },
11204  
11205        _retainChildren: function (x, y, z, maxZoom) {
11206  
11207            for (var i = 2 * x; i < 2 * x + 2; i++) {
11208                for (var j = 2 * y; j < 2 * y + 2; j++) {
11209  
11210                    var coords = new Point(i, j);
11211                    coords.z = z + 1;
11212  
11213                    var key = this._tileCoordsToKey(coords),
11214                        tile = this._tiles[key];
11215  
11216                    if (tile && tile.active) {
11217                        tile.retain = true;
11218                        continue;
11219  
11220                    } else if (tile && tile.loaded) {
11221                        tile.retain = true;
11222                    }
11223  
11224                    if (z + 1 < maxZoom) {
11225                        this._retainChildren(i, j, z + 1, maxZoom);
11226                    }
11227                }
11228            }
11229        },
11230  
11231        _resetView: function (e) {
11232            var animating = e && (e.pinch || e.flyTo);
11233            this._setView(this._map.getCenter(), this._map.getZoom(), animating, animating);
11234        },
11235  
11236        _animateZoom: function (e) {
11237            this._setView(e.center, e.zoom, true, e.noUpdate);
11238        },
11239  
11240        _clampZoom: function (zoom) {
11241            var options = this.options;
11242  
11243            if (undefined !== options.minNativeZoom && zoom < options.minNativeZoom) {
11244                return options.minNativeZoom;
11245            }
11246  
11247            if (undefined !== options.maxNativeZoom && options.maxNativeZoom < zoom) {
11248                return options.maxNativeZoom;
11249            }
11250  
11251            return zoom;
11252        },
11253  
11254        _setView: function (center, zoom, noPrune, noUpdate) {
11255            var tileZoom = Math.round(zoom);
11256            if ((this.options.maxZoom !== undefined && tileZoom > this.options.maxZoom) ||
11257                (this.options.minZoom !== undefined && tileZoom < this.options.minZoom)) {
11258                tileZoom = undefined;
11259            } else {
11260                tileZoom = this._clampZoom(tileZoom);
11261            }
11262  
11263            var tileZoomChanged = this.options.updateWhenZooming && (tileZoom !== this._tileZoom);
11264  
11265            if (!noUpdate || tileZoomChanged) {
11266  
11267                this._tileZoom = tileZoom;
11268  
11269                if (this._abortLoading) {
11270                    this._abortLoading();
11271                }
11272  
11273                this._updateLevels();
11274                this._resetGrid();
11275  
11276                if (tileZoom !== undefined) {
11277                    this._update(center);
11278                }
11279  
11280                if (!noPrune) {
11281                    this._pruneTiles();
11282                }
11283  
11284                // Flag to prevent _updateOpacity from pruning tiles during
11285                // a zoom anim or a pinch gesture
11286                this._noPrune = !!noPrune;
11287            }
11288  
11289            this._setZoomTransforms(center, zoom);
11290        },
11291  
11292        _setZoomTransforms: function (center, zoom) {
11293            for (var i in this._levels) {
11294                this._setZoomTransform(this._levels[i], center, zoom);
11295            }
11296        },
11297  
11298        _setZoomTransform: function (level, center, zoom) {
11299            var scale = this._map.getZoomScale(zoom, level.zoom),
11300                translate = level.origin.multiplyBy(scale)
11301                    .subtract(this._map._getNewPixelOrigin(center, zoom)).round();
11302  
11303            if (Browser.any3d) {
11304                setTransform(level.el, translate, scale);
11305            } else {
11306                setPosition(level.el, translate);
11307            }
11308        },
11309  
11310        _resetGrid: function () {
11311            var map = this._map,
11312                crs = map.options.crs,
11313                tileSize = this._tileSize = this.getTileSize(),
11314                tileZoom = this._tileZoom;
11315  
11316            var bounds = this._map.getPixelWorldBounds(this._tileZoom);
11317            if (bounds) {
11318                this._globalTileRange = this._pxBoundsToTileRange(bounds);
11319            }
11320  
11321            this._wrapX = crs.wrapLng && !this.options.noWrap && [
11322                Math.floor(map.project([0, crs.wrapLng[0]], tileZoom).x / tileSize.x),
11323                Math.ceil(map.project([0, crs.wrapLng[1]], tileZoom).x / tileSize.y)
11324            ];
11325            this._wrapY = crs.wrapLat && !this.options.noWrap && [
11326                Math.floor(map.project([crs.wrapLat[0], 0], tileZoom).y / tileSize.x),
11327                Math.ceil(map.project([crs.wrapLat[1], 0], tileZoom).y / tileSize.y)
11328            ];
11329        },
11330  
11331        _onMoveEnd: function () {
11332            if (!this._map || this._map._animatingZoom) { return; }
11333  
11334            this._update();
11335        },
11336  
11337        _getTiledPixelBounds: function (center) {
11338            var map = this._map,
11339                mapZoom = map._animatingZoom ? Math.max(map._animateToZoom, map.getZoom()) : map.getZoom(),
11340                scale = map.getZoomScale(mapZoom, this._tileZoom),
11341                pixelCenter = map.project(center, this._tileZoom).floor(),
11342                halfSize = map.getSize().divideBy(scale * 2);
11343  
11344            return new Bounds(pixelCenter.subtract(halfSize), pixelCenter.add(halfSize));
11345        },
11346  
11347        // Private method to load tiles in the grid's active zoom level according to map bounds
11348        _update: function (center) {
11349            var map = this._map;
11350            if (!map) { return; }
11351            var zoom = this._clampZoom(map.getZoom());
11352  
11353            if (center === undefined) { center = map.getCenter(); }
11354            if (this._tileZoom === undefined) { return; }	// if out of minzoom/maxzoom
11355  
11356            var pixelBounds = this._getTiledPixelBounds(center),
11357                tileRange = this._pxBoundsToTileRange(pixelBounds),
11358                tileCenter = tileRange.getCenter(),
11359                queue = [],
11360                margin = this.options.keepBuffer,
11361                noPruneRange = new Bounds(tileRange.getBottomLeft().subtract([margin, -margin]),
11362                                          tileRange.getTopRight().add([margin, -margin]));
11363  
11364            // Sanity check: panic if the tile range contains Infinity somewhere.
11365            if (!(isFinite(tileRange.min.x) &&
11366                  isFinite(tileRange.min.y) &&
11367                  isFinite(tileRange.max.x) &&
11368                  isFinite(tileRange.max.y))) { throw new Error('Attempted to load an infinite number of tiles'); }
11369  
11370            for (var key in this._tiles) {
11371                var c = this._tiles[key].coords;
11372                if (c.z !== this._tileZoom || !noPruneRange.contains(new Point(c.x, c.y))) {
11373                    this._tiles[key].current = false;
11374                }
11375            }
11376  
11377            // _update just loads more tiles. If the tile zoom level differs too much
11378            // from the map's, let _setView reset levels and prune old tiles.
11379            if (Math.abs(zoom - this._tileZoom) > 1) { this._setView(center, zoom); return; }
11380  
11381            // create a queue of coordinates to load tiles from
11382            for (var j = tileRange.min.y; j <= tileRange.max.y; j++) {
11383                for (var i = tileRange.min.x; i <= tileRange.max.x; i++) {
11384                    var coords = new Point(i, j);
11385                    coords.z = this._tileZoom;
11386  
11387                    if (!this._isValidTile(coords)) { continue; }
11388  
11389                    var tile = this._tiles[this._tileCoordsToKey(coords)];
11390                    if (tile) {
11391                        tile.current = true;
11392                    } else {
11393                        queue.push(coords);
11394                    }
11395                }
11396            }
11397  
11398            // sort tile queue to load tiles in order of their distance to center
11399            queue.sort(function (a, b) {
11400                return a.distanceTo(tileCenter) - b.distanceTo(tileCenter);
11401            });
11402  
11403            if (queue.length !== 0) {
11404                // if it's the first batch of tiles to load
11405                if (!this._loading) {
11406                    this._loading = true;
11407                    // @event loading: Event
11408                    // Fired when the grid layer starts loading tiles.
11409                    this.fire('loading');
11410                }
11411  
11412                // create DOM fragment to append tiles in one batch
11413                var fragment = document.createDocumentFragment();
11414  
11415                for (i = 0; i < queue.length; i++) {
11416                    this._addTile(queue[i], fragment);
11417                }
11418  
11419                this._level.el.appendChild(fragment);
11420            }
11421        },
11422  
11423        _isValidTile: function (coords) {
11424            var crs = this._map.options.crs;
11425  
11426            if (!crs.infinite) {
11427                // don't load tile if it's out of bounds and not wrapped
11428                var bounds = this._globalTileRange;
11429                if ((!crs.wrapLng && (coords.x < bounds.min.x || coords.x > bounds.max.x)) ||
11430                    (!crs.wrapLat && (coords.y < bounds.min.y || coords.y > bounds.max.y))) { return false; }
11431            }
11432  
11433            if (!this.options.bounds) { return true; }
11434  
11435            // don't load tile if it doesn't intersect the bounds in options
11436            var tileBounds = this._tileCoordsToBounds(coords);
11437            return toLatLngBounds(this.options.bounds).overlaps(tileBounds);
11438        },
11439  
11440        _keyToBounds: function (key) {
11441            return this._tileCoordsToBounds(this._keyToTileCoords(key));
11442        },
11443  
11444        _tileCoordsToNwSe: function (coords) {
11445            var map = this._map,
11446                tileSize = this.getTileSize(),
11447                nwPoint = coords.scaleBy(tileSize),
11448                sePoint = nwPoint.add(tileSize),
11449                nw = map.unproject(nwPoint, coords.z),
11450                se = map.unproject(sePoint, coords.z);
11451            return [nw, se];
11452        },
11453  
11454        // converts tile coordinates to its geographical bounds
11455        _tileCoordsToBounds: function (coords) {
11456            var bp = this._tileCoordsToNwSe(coords),
11457                bounds = new LatLngBounds(bp[0], bp[1]);
11458  
11459            if (!this.options.noWrap) {
11460                bounds = this._map.wrapLatLngBounds(bounds);
11461            }
11462            return bounds;
11463        },
11464        // converts tile coordinates to key for the tile cache
11465        _tileCoordsToKey: function (coords) {
11466            return coords.x + ':' + coords.y + ':' + coords.z;
11467        },
11468  
11469        // converts tile cache key to coordinates
11470        _keyToTileCoords: function (key) {
11471            var k = key.split(':'),
11472                coords = new Point(+k[0], +k[1]);
11473            coords.z = +k[2];
11474            return coords;
11475        },
11476  
11477        _removeTile: function (key) {
11478            var tile = this._tiles[key];
11479            if (!tile) { return; }
11480  
11481            remove(tile.el);
11482  
11483            delete this._tiles[key];
11484  
11485            // @event tileunload: TileEvent
11486            // Fired when a tile is removed (e.g. when a tile goes off the screen).
11487            this.fire('tileunload', {
11488                tile: tile.el,
11489                coords: this._keyToTileCoords(key)
11490            });
11491        },
11492  
11493        _initTile: function (tile) {
11494            addClass(tile, 'leaflet-tile');
11495  
11496            var tileSize = this.getTileSize();
11497            tile.style.width = tileSize.x + 'px';
11498            tile.style.height = tileSize.y + 'px';
11499  
11500            tile.onselectstart = falseFn;
11501            tile.onmousemove = falseFn;
11502  
11503            // update opacity on tiles in IE7-8 because of filter inheritance problems
11504            if (Browser.ielt9 && this.options.opacity < 1) {
11505                setOpacity(tile, this.options.opacity);
11506            }
11507        },
11508  
11509        _addTile: function (coords, container) {
11510            var tilePos = this._getTilePos(coords),
11511                key = this._tileCoordsToKey(coords);
11512  
11513            var tile = this.createTile(this._wrapCoords(coords), bind(this._tileReady, this, coords));
11514  
11515            this._initTile(tile);
11516  
11517            // if createTile is defined with a second argument ("done" callback),
11518            // we know that tile is async and will be ready later; otherwise
11519            if (this.createTile.length < 2) {
11520                // mark tile as ready, but delay one frame for opacity animation to happen
11521                requestAnimFrame(bind(this._tileReady, this, coords, null, tile));
11522            }
11523  
11524            setPosition(tile, tilePos);
11525  
11526            // save tile in cache
11527            this._tiles[key] = {
11528                el: tile,
11529                coords: coords,
11530                current: true
11531            };
11532  
11533            container.appendChild(tile);
11534            // @event tileloadstart: TileEvent
11535            // Fired when a tile is requested and starts loading.
11536            this.fire('tileloadstart', {
11537                tile: tile,
11538                coords: coords
11539            });
11540        },
11541  
11542        _tileReady: function (coords, err, tile) {
11543            if (err) {
11544                // @event tileerror: TileErrorEvent
11545                // Fired when there is an error loading a tile.
11546                this.fire('tileerror', {
11547                    error: err,
11548                    tile: tile,
11549                    coords: coords
11550                });
11551            }
11552  
11553            var key = this._tileCoordsToKey(coords);
11554  
11555            tile = this._tiles[key];
11556            if (!tile) { return; }
11557  
11558            tile.loaded = +new Date();
11559            if (this._map._fadeAnimated) {
11560                setOpacity(tile.el, 0);
11561                cancelAnimFrame(this._fadeFrame);
11562                this._fadeFrame = requestAnimFrame(this._updateOpacity, this);
11563            } else {
11564                tile.active = true;
11565                this._pruneTiles();
11566            }
11567  
11568            if (!err) {
11569                addClass(tile.el, 'leaflet-tile-loaded');
11570  
11571                // @event tileload: TileEvent
11572                // Fired when a tile loads.
11573                this.fire('tileload', {
11574                    tile: tile.el,
11575                    coords: coords
11576                });
11577            }
11578  
11579            if (this._noTilesToLoad()) {
11580                this._loading = false;
11581                // @event load: Event
11582                // Fired when the grid layer loaded all visible tiles.
11583                this.fire('load');
11584  
11585                if (Browser.ielt9 || !this._map._fadeAnimated) {
11586                    requestAnimFrame(this._pruneTiles, this);
11587                } else {
11588                    // Wait a bit more than 0.2 secs (the duration of the tile fade-in)
11589                    // to trigger a pruning.
11590                    setTimeout(bind(this._pruneTiles, this), 250);
11591                }
11592            }
11593        },
11594  
11595        _getTilePos: function (coords) {
11596            return coords.scaleBy(this.getTileSize()).subtract(this._level.origin);
11597        },
11598  
11599        _wrapCoords: function (coords) {
11600            var newCoords = new Point(
11601                this._wrapX ? wrapNum(coords.x, this._wrapX) : coords.x,
11602                this._wrapY ? wrapNum(coords.y, this._wrapY) : coords.y);
11603            newCoords.z = coords.z;
11604            return newCoords;
11605        },
11606  
11607        _pxBoundsToTileRange: function (bounds) {
11608            var tileSize = this.getTileSize();
11609            return new Bounds(
11610                bounds.min.unscaleBy(tileSize).floor(),
11611                bounds.max.unscaleBy(tileSize).ceil().subtract([1, 1]));
11612        },
11613  
11614        _noTilesToLoad: function () {
11615            for (var key in this._tiles) {
11616                if (!this._tiles[key].loaded) { return false; }
11617            }
11618            return true;
11619        }
11620    });
11621  
11622    // @factory L.gridLayer(options?: GridLayer options)
11623    // Creates a new instance of GridLayer with the supplied options.
11624    function gridLayer(options) {
11625        return new GridLayer(options);
11626    }
11627  
11628    /*
11629     * @class TileLayer
11630     * @inherits GridLayer
11631     * @aka L.TileLayer
11632     * Used to load and display tile layers on the map. Note that most tile servers require attribution, which you can set under `Layer`. Extends `GridLayer`.
11633     *
11634     * @example
11635     *
11636     * ```js
11637     * L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png?{foo}', {foo: 'bar', attribution: '&copy; <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors'}).addTo(map);
11638     * ```
11639     *
11640     * @section URL template
11641     * @example
11642     *
11643     * A string of the following form:
11644     *
11645     * ```
11646     * 'https://{s}.somedomain.com/blabla/{z}/{x}/{y}{r}.png'
11647     * ```
11648     *
11649     * `{s}` means one of the available subdomains (used sequentially to help with browser parallel requests per domain limitation; subdomain values are specified in options; `a`, `b` or `c` by default, can be omitted), `{z}` — zoom level, `{x}` and `{y}` — tile coordinates. `{r}` can be used to add "&commat;2x" to the URL to load retina tiles.
11650     *
11651     * You can use custom keys in the template, which will be [evaluated](#util-template) from TileLayer options, like this:
11652     *
11653     * ```
11654     * L.tileLayer('https://{s}.somedomain.com/{foo}/{z}/{x}/{y}.png', {foo: 'bar'});
11655     * ```
11656     */
11657  
11658  
11659    var TileLayer = GridLayer.extend({
11660  
11661        // @section
11662        // @aka TileLayer options
11663        options: {
11664            // @option minZoom: Number = 0
11665            // The minimum zoom level down to which this layer will be displayed (inclusive).
11666            minZoom: 0,
11667  
11668            // @option maxZoom: Number = 18
11669            // The maximum zoom level up to which this layer will be displayed (inclusive).
11670            maxZoom: 18,
11671  
11672            // @option subdomains: String|String[] = 'abc'
11673            // Subdomains of the tile service. Can be passed in the form of one string (where each letter is a subdomain name) or an array of strings.
11674            subdomains: 'abc',
11675  
11676            // @option errorTileUrl: String = ''
11677            // URL to the tile image to show in place of the tile that failed to load.
11678            errorTileUrl: '',
11679  
11680            // @option zoomOffset: Number = 0
11681            // The zoom number used in tile URLs will be offset with this value.
11682            zoomOffset: 0,
11683  
11684            // @option tms: Boolean = false
11685            // If `true`, inverses Y axis numbering for tiles (turn this on for [TMS](https://en.wikipedia.org/wiki/Tile_Map_Service) services).
11686            tms: false,
11687  
11688            // @option zoomReverse: Boolean = false
11689            // If set to true, the zoom number used in tile URLs will be reversed (`maxZoom - zoom` instead of `zoom`)
11690            zoomReverse: false,
11691  
11692            // @option detectRetina: Boolean = false
11693            // If `true` and user is on a retina display, it will request four tiles of half the specified size and a bigger zoom level in place of one to utilize the high resolution.
11694            detectRetina: false,
11695  
11696            // @option crossOrigin: Boolean|String = false
11697            // Whether the crossOrigin attribute will be added to the tiles.
11698            // If a String is provided, all tiles will have their crossOrigin attribute set to the String provided. This is needed if you want to access tile pixel data.
11699            // Refer to [CORS Settings](https://developer.mozilla.org/en-US/docs/Web/HTML/CORS_settings_attributes) for valid String values.
11700            crossOrigin: false,
11701  
11702            // @option referrerPolicy: Boolean|String = false
11703            // Whether the referrerPolicy attribute will be added to the tiles.
11704            // If a String is provided, all tiles will have their referrerPolicy attribute set to the String provided.
11705            // This may be needed if your map's rendering context has a strict default but your tile provider expects a valid referrer
11706            // (e.g. to validate an API token).
11707            // Refer to [HTMLImageElement.referrerPolicy](https://developer.mozilla.org/en-US/docs/Web/API/HTMLImageElement/referrerPolicy) for valid String values.
11708            referrerPolicy: false
11709        },
11710  
11711        initialize: function (url, options) {
11712  
11713            this._url = url;
11714  
11715            options = setOptions(this, options);
11716  
11717            // detecting retina displays, adjusting tileSize and zoom levels
11718            if (options.detectRetina && Browser.retina && options.maxZoom > 0) {
11719  
11720                options.tileSize = Math.floor(options.tileSize / 2);
11721  
11722                if (!options.zoomReverse) {
11723                    options.zoomOffset++;
11724                    options.maxZoom--;
11725                } else {
11726                    options.zoomOffset--;
11727                    options.minZoom++;
11728                }
11729  
11730                options.minZoom = Math.max(0, options.minZoom);
11731            }
11732  
11733            if (typeof options.subdomains === 'string') {
11734                options.subdomains = options.subdomains.split('');
11735            }
11736  
11737            this.on('tileunload', this._onTileRemove);
11738        },
11739  
11740        // @method setUrl(url: String, noRedraw?: Boolean): this
11741        // Updates the layer's URL template and redraws it (unless `noRedraw` is set to `true`).
11742        // If the URL does not change, the layer will not be redrawn unless
11743        // the noRedraw parameter is set to false.
11744        setUrl: function (url, noRedraw) {
11745            if (this._url === url && noRedraw === undefined) {
11746                noRedraw = true;
11747            }
11748  
11749            this._url = url;
11750  
11751            if (!noRedraw) {
11752                this.redraw();
11753            }
11754            return this;
11755        },
11756  
11757        // @method createTile(coords: Object, done?: Function): HTMLElement
11758        // Called only internally, overrides GridLayer's [`createTile()`](#gridlayer-createtile)
11759        // to return an `<img>` HTML element with the appropriate image URL given `coords`. The `done`
11760        // callback is called when the tile has been loaded.
11761        createTile: function (coords, done) {
11762            var tile = document.createElement('img');
11763  
11764            on(tile, 'load', bind(this._tileOnLoad, this, done, tile));
11765            on(tile, 'error', bind(this._tileOnError, this, done, tile));
11766  
11767            if (this.options.crossOrigin || this.options.crossOrigin === '') {
11768                tile.crossOrigin = this.options.crossOrigin === true ? '' : this.options.crossOrigin;
11769            }
11770  
11771            // for this new option we follow the documented behavior
11772            // more closely by only setting the property when string
11773            if (typeof this.options.referrerPolicy === 'string') {
11774                tile.referrerPolicy = this.options.referrerPolicy;
11775            }
11776  
11777            /*
11778             Alt tag is set to empty string to keep screen readers from reading URL and for compliance reasons
11779             https://www.w3.org/TR/WCAG20-TECHS/H67
11780            */
11781            tile.alt = '';
11782  
11783            /*
11784             Set role="presentation" to force screen readers to ignore this
11785             https://www.w3.org/TR/wai-aria/roles#textalternativecomputation
11786            */
11787            tile.setAttribute('role', 'presentation');
11788  
11789            tile.src = this.getTileUrl(coords);
11790  
11791            return tile;
11792        },
11793  
11794        // @section Extension methods
11795        // @uninheritable
11796        // Layers extending `TileLayer` might reimplement the following method.
11797        // @method getTileUrl(coords: Object): String
11798        // Called only internally, returns the URL for a tile given its coordinates.
11799        // Classes extending `TileLayer` can override this function to provide custom tile URL naming schemes.
11800        getTileUrl: function (coords) {
11801            var data = {
11802                r: Browser.retina ? '@2x' : '',
11803                s: this._getSubdomain(coords),
11804                x: coords.x,
11805                y: coords.y,
11806                z: this._getZoomForUrl()
11807            };
11808            if (this._map && !this._map.options.crs.infinite) {
11809                var invertedY = this._globalTileRange.max.y - coords.y;
11810                if (this.options.tms) {
11811                    data['y'] = invertedY;
11812                }
11813                data['-y'] = invertedY;
11814            }
11815  
11816            return template(this._url, extend(data, this.options));
11817        },
11818  
11819        _tileOnLoad: function (done, tile) {
11820            // For https://github.com/Leaflet/Leaflet/issues/3332
11821            if (Browser.ielt9) {
11822                setTimeout(bind(done, this, null, tile), 0);
11823            } else {
11824                done(null, tile);
11825            }
11826        },
11827  
11828        _tileOnError: function (done, tile, e) {
11829            var errorUrl = this.options.errorTileUrl;
11830            if (errorUrl && tile.getAttribute('src') !== errorUrl) {
11831                tile.src = errorUrl;
11832            }
11833            done(e, tile);
11834        },
11835  
11836        _onTileRemove: function (e) {
11837            e.tile.onload = null;
11838        },
11839  
11840        _getZoomForUrl: function () {
11841            var zoom = this._tileZoom,
11842            maxZoom = this.options.maxZoom,
11843            zoomReverse = this.options.zoomReverse,
11844            zoomOffset = this.options.zoomOffset;
11845  
11846            if (zoomReverse) {
11847                zoom = maxZoom - zoom;
11848            }
11849  
11850            return zoom + zoomOffset;
11851        },
11852  
11853        _getSubdomain: function (tilePoint) {
11854            var index = Math.abs(tilePoint.x + tilePoint.y) % this.options.subdomains.length;
11855            return this.options.subdomains[index];
11856        },
11857  
11858        // stops loading all tiles in the background layer
11859        _abortLoading: function () {
11860            var i, tile;
11861            for (i in this._tiles) {
11862                if (this._tiles[i].coords.z !== this._tileZoom) {
11863                    tile = this._tiles[i].el;
11864  
11865                    tile.onload = falseFn;
11866                    tile.onerror = falseFn;
11867  
11868                    if (!tile.complete) {
11869                        tile.src = emptyImageUrl;
11870                        var coords = this._tiles[i].coords;
11871                        remove(tile);
11872                        delete this._tiles[i];
11873                        // @event tileabort: TileEvent
11874                        // Fired when a tile was loading but is now not wanted.
11875                        this.fire('tileabort', {
11876                            tile: tile,
11877                            coords: coords
11878                        });
11879                    }
11880                }
11881            }
11882        },
11883  
11884        _removeTile: function (key) {
11885            var tile = this._tiles[key];
11886            if (!tile) { return; }
11887  
11888            // Cancels any pending http requests associated with the tile
11889            tile.el.setAttribute('src', emptyImageUrl);
11890  
11891            return GridLayer.prototype._removeTile.call(this, key);
11892        },
11893  
11894        _tileReady: function (coords, err, tile) {
11895            if (!this._map || (tile && tile.getAttribute('src') === emptyImageUrl)) {
11896                return;
11897            }
11898  
11899            return GridLayer.prototype._tileReady.call(this, coords, err, tile);
11900        }
11901    });
11902  
11903  
11904    // @factory L.tilelayer(urlTemplate: String, options?: TileLayer options)
11905    // Instantiates a tile layer object given a `URL template` and optionally an options object.
11906  
11907    function tileLayer(url, options) {
11908        return new TileLayer(url, options);
11909    }
11910  
11911    /*
11912     * @class TileLayer.WMS
11913     * @inherits TileLayer
11914     * @aka L.TileLayer.WMS
11915     * Used to display [WMS](https://en.wikipedia.org/wiki/Web_Map_Service) services as tile layers on the map. Extends `TileLayer`.
11916     *
11917     * @example
11918     *
11919     * ```js
11920     * var nexrad = L.tileLayer.wms("http://mesonet.agron.iastate.edu/cgi-bin/wms/nexrad/n0r.cgi", {
11921     * 	layers: 'nexrad-n0r-900913',
11922     * 	format: 'image/png',
11923     * 	transparent: true,
11924     * 	attribution: "Weather data © 2012 IEM Nexrad"
11925     * });
11926     * ```
11927     */
11928  
11929    var TileLayerWMS = TileLayer.extend({
11930  
11931        // @section
11932        // @aka TileLayer.WMS options
11933        // If any custom options not documented here are used, they will be sent to the
11934        // WMS server as extra parameters in each request URL. This can be useful for
11935        // [non-standard vendor WMS parameters](https://docs.geoserver.org/stable/en/user/services/wms/vendor.html).
11936        defaultWmsParams: {
11937            service: 'WMS',
11938            request: 'GetMap',
11939  
11940            // @option layers: String = ''
11941            // **(required)** Comma-separated list of WMS layers to show.
11942            layers: '',
11943  
11944            // @option styles: String = ''
11945            // Comma-separated list of WMS styles.
11946            styles: '',
11947  
11948            // @option format: String = 'image/jpeg'
11949            // WMS image format (use `'image/png'` for layers with transparency).
11950            format: 'image/jpeg',
11951  
11952            // @option transparent: Boolean = false
11953            // If `true`, the WMS service will return images with transparency.
11954            transparent: false,
11955  
11956            // @option version: String = '1.1.1'
11957            // Version of the WMS service to use
11958            version: '1.1.1'
11959        },
11960  
11961        options: {
11962            // @option crs: CRS = null
11963            // Coordinate Reference System to use for the WMS requests, defaults to
11964            // map CRS. Don't change this if you're not sure what it means.
11965            crs: null,
11966  
11967            // @option uppercase: Boolean = false
11968            // If `true`, WMS request parameter keys will be uppercase.
11969            uppercase: false
11970        },
11971  
11972        initialize: function (url, options) {
11973  
11974            this._url = url;
11975  
11976            var wmsParams = extend({}, this.defaultWmsParams);
11977  
11978            // all keys that are not TileLayer options go to WMS params
11979            for (var i in options) {
11980                if (!(i in this.options)) {
11981                    wmsParams[i] = options[i];
11982                }
11983            }
11984  
11985            options = setOptions(this, options);
11986  
11987            var realRetina = options.detectRetina && Browser.retina ? 2 : 1;
11988            var tileSize = this.getTileSize();
11989            wmsParams.width = tileSize.x * realRetina;
11990            wmsParams.height = tileSize.y * realRetina;
11991  
11992            this.wmsParams = wmsParams;
11993        },
11994  
11995        onAdd: function (map) {
11996  
11997            this._crs = this.options.crs || map.options.crs;
11998            this._wmsVersion = parseFloat(this.wmsParams.version);
11999  
12000            var projectionKey = this._wmsVersion >= 1.3 ? 'crs' : 'srs';
12001            this.wmsParams[projectionKey] = this._crs.code;
12002  
12003            TileLayer.prototype.onAdd.call(this, map);
12004        },
12005  
12006        getTileUrl: function (coords) {
12007  
12008            var tileBounds = this._tileCoordsToNwSe(coords),
12009                crs = this._crs,
12010                bounds = toBounds(crs.project(tileBounds[0]), crs.project(tileBounds[1])),
12011                min = bounds.min,
12012                max = bounds.max,
12013                bbox = (this._wmsVersion >= 1.3 && this._crs === EPSG4326 ?
12014                [min.y, min.x, max.y, max.x] :
12015                [min.x, min.y, max.x, max.y]).join(','),
12016                url = TileLayer.prototype.getTileUrl.call(this, coords);
12017            return url +
12018                getParamString(this.wmsParams, url, this.options.uppercase) +
12019                (this.options.uppercase ? '&BBOX=' : '&bbox=') + bbox;
12020        },
12021  
12022        // @method setParams(params: Object, noRedraw?: Boolean): this
12023        // Merges an object with the new parameters and re-requests tiles on the current screen (unless `noRedraw` was set to true).
12024        setParams: function (params, noRedraw) {
12025  
12026            extend(this.wmsParams, params);
12027  
12028            if (!noRedraw) {
12029                this.redraw();
12030            }
12031  
12032            return this;
12033        }
12034    });
12035  
12036  
12037    // @factory L.tileLayer.wms(baseUrl: String, options: TileLayer.WMS options)
12038    // Instantiates a WMS tile layer object given a base URL of the WMS service and a WMS parameters/options object.
12039    function tileLayerWMS(url, options) {
12040        return new TileLayerWMS(url, options);
12041    }
12042  
12043    TileLayer.WMS = TileLayerWMS;
12044    tileLayer.wms = tileLayerWMS;
12045  
12046    /*
12047     * @class Renderer
12048     * @inherits Layer
12049     * @aka L.Renderer
12050     *
12051     * Base class for vector renderer implementations (`SVG`, `Canvas`). Handles the
12052     * DOM container of the renderer, its bounds, and its zoom animation.
12053     *
12054     * A `Renderer` works as an implicit layer group for all `Path`s - the renderer
12055     * itself can be added or removed to the map. All paths use a renderer, which can
12056     * be implicit (the map will decide the type of renderer and use it automatically)
12057     * or explicit (using the [`renderer`](#path-renderer) option of the path).
12058     *
12059     * Do not use this class directly, use `SVG` and `Canvas` instead.
12060     *
12061     * @event update: Event
12062     * Fired when the renderer updates its bounds, center and zoom, for example when
12063     * its map has moved
12064     */
12065  
12066    var Renderer = Layer.extend({
12067  
12068        // @section
12069        // @aka Renderer options
12070        options: {
12071            // @option padding: Number = 0.1
12072            // How much to extend the clip area around the map view (relative to its size)
12073            // e.g. 0.1 would be 10% of map view in each direction
12074            padding: 0.1
12075        },
12076  
12077        initialize: function (options) {
12078            setOptions(this, options);
12079            stamp(this);
12080            this._layers = this._layers || {};
12081        },
12082  
12083        onAdd: function () {
12084            if (!this._container) {
12085                this._initContainer(); // defined by renderer implementations
12086  
12087                if (this._zoomAnimated) {
12088                    addClass(this._container, 'leaflet-zoom-animated');
12089                }
12090            }
12091  
12092            this.getPane().appendChild(this._container);
12093            this._update();
12094            this.on('update', this._updatePaths, this);
12095        },
12096  
12097        onRemove: function () {
12098            this.off('update', this._updatePaths, this);
12099            this._destroyContainer();
12100        },
12101  
12102        getEvents: function () {
12103            var events = {
12104                viewreset: this._reset,
12105                zoom: this._onZoom,
12106                moveend: this._update,
12107                zoomend: this._onZoomEnd
12108            };
12109            if (this._zoomAnimated) {
12110                events.zoomanim = this._onAnimZoom;
12111            }
12112            return events;
12113        },
12114  
12115        _onAnimZoom: function (ev) {
12116            this._updateTransform(ev.center, ev.zoom);
12117        },
12118  
12119        _onZoom: function () {
12120            this._updateTransform(this._map.getCenter(), this._map.getZoom());
12121        },
12122  
12123        _updateTransform: function (center, zoom) {
12124            var scale = this._map.getZoomScale(zoom, this._zoom),
12125                position = getPosition(this._container),
12126                viewHalf = this._map.getSize().multiplyBy(0.5 + this.options.padding),
12127                currentCenterPoint = this._map.project(this._center, zoom),
12128                destCenterPoint = this._map.project(center, zoom),
12129                centerOffset = destCenterPoint.subtract(currentCenterPoint),
12130  
12131                topLeftOffset = viewHalf.multiplyBy(-scale).add(position).add(viewHalf).subtract(centerOffset);
12132  
12133            if (Browser.any3d) {
12134                setTransform(this._container, topLeftOffset, scale);
12135            } else {
12136                setPosition(this._container, topLeftOffset);
12137            }
12138        },
12139  
12140        _reset: function () {
12141            this._update();
12142            this._updateTransform(this._center, this._zoom);
12143  
12144            for (var id in this._layers) {
12145                this._layers[id]._reset();
12146            }
12147        },
12148  
12149        _onZoomEnd: function () {
12150            for (var id in this._layers) {
12151                this._layers[id]._project();
12152            }
12153        },
12154  
12155        _updatePaths: function () {
12156            for (var id in this._layers) {
12157                this._layers[id]._update();
12158            }
12159        },
12160  
12161        _update: function () {
12162            // Update pixel bounds of renderer container (for positioning/sizing/clipping later)
12163            // Subclasses are responsible of firing the 'update' event.
12164            var p = this.options.padding,
12165                size = this._map.getSize(),
12166                min = this._map.containerPointToLayerPoint(size.multiplyBy(-p)).round();
12167  
12168            this._bounds = new Bounds(min, min.add(size.multiplyBy(1 + p * 2)).round());
12169  
12170            this._center = this._map.getCenter();
12171            this._zoom = this._map.getZoom();
12172        }
12173    });
12174  
12175    /*
12176     * @class Canvas
12177     * @inherits Renderer
12178     * @aka L.Canvas
12179     *
12180     * Allows vector layers to be displayed with [`<canvas>`](https://developer.mozilla.org/docs/Web/API/Canvas_API).
12181     * Inherits `Renderer`.
12182     *
12183     * Due to [technical limitations](https://caniuse.com/canvas), Canvas is not
12184     * available in all web browsers, notably IE8, and overlapping geometries might
12185     * not display properly in some edge cases.
12186     *
12187     * @example
12188     *
12189     * Use Canvas by default for all paths in the map:
12190     *
12191     * ```js
12192     * var map = L.map('map', {
12193     * 	renderer: L.canvas()
12194     * });
12195     * ```
12196     *
12197     * Use a Canvas renderer with extra padding for specific vector geometries:
12198     *
12199     * ```js
12200     * var map = L.map('map');
12201     * var myRenderer = L.canvas({ padding: 0.5 });
12202     * var line = L.polyline( coordinates, { renderer: myRenderer } );
12203     * var circle = L.circle( center, { renderer: myRenderer } );
12204     * ```
12205     */
12206  
12207    var Canvas = Renderer.extend({
12208  
12209        // @section
12210        // @aka Canvas options
12211        options: {
12212            // @option tolerance: Number = 0
12213            // How much to extend the click tolerance around a path/object on the map.
12214            tolerance: 0
12215        },
12216  
12217        getEvents: function () {
12218            var events = Renderer.prototype.getEvents.call(this);
12219            events.viewprereset = this._onViewPreReset;
12220            return events;
12221        },
12222  
12223        _onViewPreReset: function () {
12224            // Set a flag so that a viewprereset+moveend+viewreset only updates&redraws once
12225            this._postponeUpdatePaths = true;
12226        },
12227  
12228        onAdd: function () {
12229            Renderer.prototype.onAdd.call(this);
12230  
12231            // Redraw vectors since canvas is cleared upon removal,
12232            // in case of removing the renderer itself from the map.
12233            this._draw();
12234        },
12235  
12236        _initContainer: function () {
12237            var container = this._container = document.createElement('canvas');
12238  
12239            on(container, 'mousemove', this._onMouseMove, this);
12240            on(container, 'click dblclick mousedown mouseup contextmenu', this._onClick, this);
12241            on(container, 'mouseout', this._handleMouseOut, this);
12242            container['_leaflet_disable_events'] = true;
12243  
12244            this._ctx = container.getContext('2d');
12245        },
12246  
12247        _destroyContainer: function () {
12248            cancelAnimFrame(this._redrawRequest);
12249            delete this._ctx;
12250            remove(this._container);
12251            off(this._container);
12252            delete this._container;
12253        },
12254  
12255        _updatePaths: function () {
12256            if (this._postponeUpdatePaths) { return; }
12257  
12258            var layer;
12259            this._redrawBounds = null;
12260            for (var id in this._layers) {
12261                layer = this._layers[id];
12262                layer._update();
12263            }
12264            this._redraw();
12265        },
12266  
12267        _update: function () {
12268            if (this._map._animatingZoom && this._bounds) { return; }
12269  
12270            Renderer.prototype._update.call(this);
12271  
12272            var b = this._bounds,
12273                container = this._container,
12274                size = b.getSize(),
12275                m = Browser.retina ? 2 : 1;
12276  
12277            setPosition(container, b.min);
12278  
12279            // set canvas size (also clearing it); use double size on retina
12280            container.width = m * size.x;
12281            container.height = m * size.y;
12282            container.style.width = size.x + 'px';
12283            container.style.height = size.y + 'px';
12284  
12285            if (Browser.retina) {
12286                this._ctx.scale(2, 2);
12287            }
12288  
12289            // translate so we use the same path coordinates after canvas element moves
12290            this._ctx.translate(-b.min.x, -b.min.y);
12291  
12292            // Tell paths to redraw themselves
12293            this.fire('update');
12294        },
12295  
12296        _reset: function () {
12297            Renderer.prototype._reset.call(this);
12298  
12299            if (this._postponeUpdatePaths) {
12300                this._postponeUpdatePaths = false;
12301                this._updatePaths();
12302            }
12303        },
12304  
12305        _initPath: function (layer) {
12306            this._updateDashArray(layer);
12307            this._layers[stamp(layer)] = layer;
12308  
12309            var order = layer._order = {
12310                layer: layer,
12311                prev: this._drawLast,
12312                next: null
12313            };
12314            if (this._drawLast) { this._drawLast.next = order; }
12315            this._drawLast = order;
12316            this._drawFirst = this._drawFirst || this._drawLast;
12317        },
12318  
12319        _addPath: function (layer) {
12320            this._requestRedraw(layer);
12321        },
12322  
12323        _removePath: function (layer) {
12324            var order = layer._order;
12325            var next = order.next;
12326            var prev = order.prev;
12327  
12328            if (next) {
12329                next.prev = prev;
12330            } else {
12331                this._drawLast = prev;
12332            }
12333            if (prev) {
12334                prev.next = next;
12335            } else {
12336                this._drawFirst = next;
12337            }
12338  
12339            delete layer._order;
12340  
12341            delete this._layers[stamp(layer)];
12342  
12343            this._requestRedraw(layer);
12344        },
12345  
12346        _updatePath: function (layer) {
12347            // Redraw the union of the layer's old pixel
12348            // bounds and the new pixel bounds.
12349            this._extendRedrawBounds(layer);
12350            layer._project();
12351            layer._update();
12352            // The redraw will extend the redraw bounds
12353            // with the new pixel bounds.
12354            this._requestRedraw(layer);
12355        },
12356  
12357        _updateStyle: function (layer) {
12358            this._updateDashArray(layer);
12359            this._requestRedraw(layer);
12360        },
12361  
12362        _updateDashArray: function (layer) {
12363            if (typeof layer.options.dashArray === 'string') {
12364                var parts = layer.options.dashArray.split(/[, ]+/),
12365                    dashArray = [],
12366                    dashValue,
12367                    i;
12368                for (i = 0; i < parts.length; i++) {
12369                    dashValue = Number(parts[i]);
12370                    // Ignore dash array containing invalid lengths
12371                    if (isNaN(dashValue)) { return; }
12372                    dashArray.push(dashValue);
12373                }
12374                layer.options._dashArray = dashArray;
12375            } else {
12376                layer.options._dashArray = layer.options.dashArray;
12377            }
12378        },
12379  
12380        _requestRedraw: function (layer) {
12381            if (!this._map) { return; }
12382  
12383            this._extendRedrawBounds(layer);
12384            this._redrawRequest = this._redrawRequest || requestAnimFrame(this._redraw, this);
12385        },
12386  
12387        _extendRedrawBounds: function (layer) {
12388            if (layer._pxBounds) {
12389                var padding = (layer.options.weight || 0) + 1;
12390                this._redrawBounds = this._redrawBounds || new Bounds();
12391                this._redrawBounds.extend(layer._pxBounds.min.subtract([padding, padding]));
12392                this._redrawBounds.extend(layer._pxBounds.max.add([padding, padding]));
12393            }
12394        },
12395  
12396        _redraw: function () {
12397            this._redrawRequest = null;
12398  
12399            if (this._redrawBounds) {
12400                this._redrawBounds.min._floor();
12401                this._redrawBounds.max._ceil();
12402            }
12403  
12404            this._clear(); // clear layers in redraw bounds
12405            this._draw(); // draw layers
12406  
12407            this._redrawBounds = null;
12408        },
12409  
12410        _clear: function () {
12411            var bounds = this._redrawBounds;
12412            if (bounds) {
12413                var size = bounds.getSize();
12414                this._ctx.clearRect(bounds.min.x, bounds.min.y, size.x, size.y);
12415            } else {
12416                this._ctx.save();
12417                this._ctx.setTransform(1, 0, 0, 1, 0, 0);
12418                this._ctx.clearRect(0, 0, this._container.width, this._container.height);
12419                this._ctx.restore();
12420            }
12421        },
12422  
12423        _draw: function () {
12424            var layer, bounds = this._redrawBounds;
12425            this._ctx.save();
12426            if (bounds) {
12427                var size = bounds.getSize();
12428                this._ctx.beginPath();
12429                this._ctx.rect(bounds.min.x, bounds.min.y, size.x, size.y);
12430                this._ctx.clip();
12431            }
12432  
12433            this._drawing = true;
12434  
12435            for (var order = this._drawFirst; order; order = order.next) {
12436                layer = order.layer;
12437                if (!bounds || (layer._pxBounds && layer._pxBounds.intersects(bounds))) {
12438                    layer._updatePath();
12439                }
12440            }
12441  
12442            this._drawing = false;
12443  
12444            this._ctx.restore();  // Restore state before clipping.
12445        },
12446  
12447        _updatePoly: function (layer, closed) {
12448            if (!this._drawing) { return; }
12449  
12450            var i, j, len2, p,
12451                parts = layer._parts,
12452                len = parts.length,
12453                ctx = this._ctx;
12454  
12455            if (!len) { return; }
12456  
12457            ctx.beginPath();
12458  
12459            for (i = 0; i < len; i++) {
12460                for (j = 0, len2 = parts[i].length; j < len2; j++) {
12461                    p = parts[i][j];
12462                    ctx[j ? 'lineTo' : 'moveTo'](p.x, p.y);
12463                }
12464                if (closed) {
12465                    ctx.closePath();
12466                }
12467            }
12468  
12469            this._fillStroke(ctx, layer);
12470  
12471            // TODO optimization: 1 fill/stroke for all features with equal style instead of 1 for each feature
12472        },
12473  
12474        _updateCircle: function (layer) {
12475  
12476            if (!this._drawing || layer._empty()) { return; }
12477  
12478            var p = layer._point,
12479                ctx = this._ctx,
12480                r = Math.max(Math.round(layer._radius), 1),
12481                s = (Math.max(Math.round(layer._radiusY), 1) || r) / r;
12482  
12483            if (s !== 1) {
12484                ctx.save();
12485                ctx.scale(1, s);
12486            }
12487  
12488            ctx.beginPath();
12489            ctx.arc(p.x, p.y / s, r, 0, Math.PI * 2, false);
12490  
12491            if (s !== 1) {
12492                ctx.restore();
12493            }
12494  
12495            this._fillStroke(ctx, layer);
12496        },
12497  
12498        _fillStroke: function (ctx, layer) {
12499            var options = layer.options;
12500  
12501            if (options.fill) {
12502                ctx.globalAlpha = options.fillOpacity;
12503                ctx.fillStyle = options.fillColor || options.color;
12504                ctx.fill(options.fillRule || 'evenodd');
12505            }
12506  
12507            if (options.stroke && options.weight !== 0) {
12508                if (ctx.setLineDash) {
12509                    ctx.setLineDash(layer.options && layer.options._dashArray || []);
12510                }
12511                ctx.globalAlpha = options.opacity;
12512                ctx.lineWidth = options.weight;
12513                ctx.strokeStyle = options.color;
12514                ctx.lineCap = options.lineCap;
12515                ctx.lineJoin = options.lineJoin;
12516                ctx.stroke();
12517            }
12518        },
12519  
12520        // Canvas obviously doesn't have mouse events for individual drawn objects,
12521        // so we emulate that by calculating what's under the mouse on mousemove/click manually
12522  
12523        _onClick: function (e) {
12524            var point = this._map.mouseEventToLayerPoint(e), layer, clickedLayer;
12525  
12526            for (var order = this._drawFirst; order; order = order.next) {
12527                layer = order.layer;
12528                if (layer.options.interactive && layer._containsPoint(point)) {
12529                    if (!(e.type === 'click' || e.type === 'preclick') || !this._map._draggableMoved(layer)) {
12530                        clickedLayer = layer;
12531                    }
12532                }
12533            }
12534            this._fireEvent(clickedLayer ? [clickedLayer] : false, e);
12535        },
12536  
12537        _onMouseMove: function (e) {
12538            if (!this._map || this._map.dragging.moving() || this._map._animatingZoom) { return; }
12539  
12540            var point = this._map.mouseEventToLayerPoint(e);
12541            this._handleMouseHover(e, point);
12542        },
12543  
12544  
12545        _handleMouseOut: function (e) {
12546            var layer = this._hoveredLayer;
12547            if (layer) {
12548                // if we're leaving the layer, fire mouseout
12549                removeClass(this._container, 'leaflet-interactive');
12550                this._fireEvent([layer], e, 'mouseout');
12551                this._hoveredLayer = null;
12552                this._mouseHoverThrottled = false;
12553            }
12554        },
12555  
12556        _handleMouseHover: function (e, point) {
12557            if (this._mouseHoverThrottled) {
12558                return;
12559            }
12560  
12561            var layer, candidateHoveredLayer;
12562  
12563            for (var order = this._drawFirst; order; order = order.next) {
12564                layer = order.layer;
12565                if (layer.options.interactive && layer._containsPoint(point)) {
12566                    candidateHoveredLayer = layer;
12567                }
12568            }
12569  
12570            if (candidateHoveredLayer !== this._hoveredLayer) {
12571                this._handleMouseOut(e);
12572  
12573                if (candidateHoveredLayer) {
12574                    addClass(this._container, 'leaflet-interactive'); // change cursor
12575                    this._fireEvent([candidateHoveredLayer], e, 'mouseover');
12576                    this._hoveredLayer = candidateHoveredLayer;
12577                }
12578            }
12579  
12580            this._fireEvent(this._hoveredLayer ? [this._hoveredLayer] : false, e);
12581  
12582            this._mouseHoverThrottled = true;
12583            setTimeout(bind(function () {
12584                this._mouseHoverThrottled = false;
12585            }, this), 32);
12586        },
12587  
12588        _fireEvent: function (layers, e, type) {
12589            this._map._fireDOMEvent(e, type || e.type, layers);
12590        },
12591  
12592        _bringToFront: function (layer) {
12593            var order = layer._order;
12594  
12595            if (!order) { return; }
12596  
12597            var next = order.next;
12598            var prev = order.prev;
12599  
12600            if (next) {
12601                next.prev = prev;
12602            } else {
12603                // Already last
12604                return;
12605            }
12606            if (prev) {
12607                prev.next = next;
12608            } else if (next) {
12609                // Update first entry unless this is the
12610                // single entry
12611                this._drawFirst = next;
12612            }
12613  
12614            order.prev = this._drawLast;
12615            this._drawLast.next = order;
12616  
12617            order.next = null;
12618            this._drawLast = order;
12619  
12620            this._requestRedraw(layer);
12621        },
12622  
12623        _bringToBack: function (layer) {
12624            var order = layer._order;
12625  
12626            if (!order) { return; }
12627  
12628            var next = order.next;
12629            var prev = order.prev;
12630  
12631            if (prev) {
12632                prev.next = next;
12633            } else {
12634                // Already first
12635                return;
12636            }
12637            if (next) {
12638                next.prev = prev;
12639            } else if (prev) {
12640                // Update last entry unless this is the
12641                // single entry
12642                this._drawLast = prev;
12643            }
12644  
12645            order.prev = null;
12646  
12647            order.next = this._drawFirst;
12648            this._drawFirst.prev = order;
12649            this._drawFirst = order;
12650  
12651            this._requestRedraw(layer);
12652        }
12653    });
12654  
12655    // @factory L.canvas(options?: Renderer options)
12656    // Creates a Canvas renderer with the given options.
12657    function canvas(options) {
12658        return Browser.canvas ? new Canvas(options) : null;
12659    }
12660  
12661    /*
12662     * Thanks to Dmitry Baranovsky and his Raphael library for inspiration!
12663     */
12664  
12665  
12666    var vmlCreate = (function () {
12667        try {
12668            document.namespaces.add('lvml', 'urn:schemas-microsoft-com:vml');
12669            return function (name) {
12670                return document.createElement('<lvml:' + name + ' class="lvml">');
12671            };
12672        } catch (e) {
12673            // Do not return fn from catch block so `e` can be garbage collected
12674            // See https://github.com/Leaflet/Leaflet/pull/7279
12675        }
12676        return function (name) {
12677            return document.createElement('<' + name + ' xmlns="urn:schemas-microsoft.com:vml" class="lvml">');
12678        };
12679    })();
12680  
12681  
12682    /*
12683     * @class SVG
12684     *
12685     *
12686     * VML was deprecated in 2012, which means VML functionality exists only for backwards compatibility
12687     * with old versions of Internet Explorer.
12688     */
12689  
12690    // mixin to redefine some SVG methods to handle VML syntax which is similar but with some differences
12691    var vmlMixin = {
12692  
12693        _initContainer: function () {
12694            this._container = create$1('div', 'leaflet-vml-container');
12695        },
12696  
12697        _update: function () {
12698            if (this._map._animatingZoom) { return; }
12699            Renderer.prototype._update.call(this);
12700            this.fire('update');
12701        },
12702  
12703        _initPath: function (layer) {
12704            var container = layer._container = vmlCreate('shape');
12705  
12706            addClass(container, 'leaflet-vml-shape ' + (this.options.className || ''));
12707  
12708            container.coordsize = '1 1';
12709  
12710            layer._path = vmlCreate('path');
12711            container.appendChild(layer._path);
12712  
12713            this._updateStyle(layer);
12714            this._layers[stamp(layer)] = layer;
12715        },
12716  
12717        _addPath: function (layer) {
12718            var container = layer._container;
12719            this._container.appendChild(container);
12720  
12721            if (layer.options.interactive) {
12722                layer.addInteractiveTarget(container);
12723            }
12724        },
12725  
12726        _removePath: function (layer) {
12727            var container = layer._container;
12728            remove(container);
12729            layer.removeInteractiveTarget(container);
12730            delete this._layers[stamp(layer)];
12731        },
12732  
12733        _updateStyle: function (layer) {
12734            var stroke = layer._stroke,
12735                fill = layer._fill,
12736                options = layer.options,
12737                container = layer._container;
12738  
12739            container.stroked = !!options.stroke;
12740            container.filled = !!options.fill;
12741  
12742            if (options.stroke) {
12743                if (!stroke) {
12744                    stroke = layer._stroke = vmlCreate('stroke');
12745                }
12746                container.appendChild(stroke);
12747                stroke.weight = options.weight + 'px';
12748                stroke.color = options.color;
12749                stroke.opacity = options.opacity;
12750  
12751                if (options.dashArray) {
12752                    stroke.dashStyle = isArray(options.dashArray) ?
12753                        options.dashArray.join(' ') :
12754                        options.dashArray.replace(/( *, *)/g, ' ');
12755                } else {
12756                    stroke.dashStyle = '';
12757                }
12758                stroke.endcap = options.lineCap.replace('butt', 'flat');
12759                stroke.joinstyle = options.lineJoin;
12760  
12761            } else if (stroke) {
12762                container.removeChild(stroke);
12763                layer._stroke = null;
12764            }
12765  
12766            if (options.fill) {
12767                if (!fill) {
12768                    fill = layer._fill = vmlCreate('fill');
12769                }
12770                container.appendChild(fill);
12771                fill.color = options.fillColor || options.color;
12772                fill.opacity = options.fillOpacity;
12773  
12774            } else if (fill) {
12775                container.removeChild(fill);
12776                layer._fill = null;
12777            }
12778        },
12779  
12780        _updateCircle: function (layer) {
12781            var p = layer._point.round(),
12782                r = Math.round(layer._radius),
12783                r2 = Math.round(layer._radiusY || r);
12784  
12785            this._setPath(layer, layer._empty() ? 'M0 0' :
12786                'AL ' + p.x + ',' + p.y + ' ' + r + ',' + r2 + ' 0,' + (65535 * 360));
12787        },
12788  
12789        _setPath: function (layer, path) {
12790            layer._path.v = path;
12791        },
12792  
12793        _bringToFront: function (layer) {
12794            toFront(layer._container);
12795        },
12796  
12797        _bringToBack: function (layer) {
12798            toBack(layer._container);
12799        }
12800    };
12801  
12802    var create = Browser.vml ? vmlCreate : svgCreate;
12803  
12804    /*
12805     * @class SVG
12806     * @inherits Renderer
12807     * @aka L.SVG
12808     *
12809     * Allows vector layers to be displayed with [SVG](https://developer.mozilla.org/docs/Web/SVG).
12810     * Inherits `Renderer`.
12811     *
12812     * Due to [technical limitations](https://caniuse.com/svg), SVG is not
12813     * available in all web browsers, notably Android 2.x and 3.x.
12814     *
12815     * Although SVG is not available on IE7 and IE8, these browsers support
12816     * [VML](https://en.wikipedia.org/wiki/Vector_Markup_Language)
12817     * (a now deprecated technology), and the SVG renderer will fall back to VML in
12818     * this case.
12819     *
12820     * @example
12821     *
12822     * Use SVG by default for all paths in the map:
12823     *
12824     * ```js
12825     * var map = L.map('map', {
12826     * 	renderer: L.svg()
12827     * });
12828     * ```
12829     *
12830     * Use a SVG renderer with extra padding for specific vector geometries:
12831     *
12832     * ```js
12833     * var map = L.map('map');
12834     * var myRenderer = L.svg({ padding: 0.5 });
12835     * var line = L.polyline( coordinates, { renderer: myRenderer } );
12836     * var circle = L.circle( center, { renderer: myRenderer } );
12837     * ```
12838     */
12839  
12840    var SVG = Renderer.extend({
12841  
12842        getEvents: function () {
12843            var events = Renderer.prototype.getEvents.call(this);
12844            events.zoomstart = this._onZoomStart;
12845            return events;
12846        },
12847  
12848        _initContainer: function () {
12849            this._container = create('svg');
12850  
12851            // makes it possible to click through svg root; we'll reset it back in individual paths
12852            this._container.setAttribute('pointer-events', 'none');
12853  
12854            this._rootGroup = create('g');
12855            this._container.appendChild(this._rootGroup);
12856        },
12857  
12858        _destroyContainer: function () {
12859            remove(this._container);
12860            off(this._container);
12861            delete this._container;
12862            delete this._rootGroup;
12863            delete this._svgSize;
12864        },
12865  
12866        _onZoomStart: function () {
12867            // Drag-then-pinch interactions might mess up the center and zoom.
12868            // In this case, the easiest way to prevent this is re-do the renderer
12869            //   bounds and padding when the zooming starts.
12870            this._update();
12871        },
12872  
12873        _update: function () {
12874            if (this._map._animatingZoom && this._bounds) { return; }
12875  
12876            Renderer.prototype._update.call(this);
12877  
12878            var b = this._bounds,
12879                size = b.getSize(),
12880                container = this._container;
12881  
12882            // set size of svg-container if changed
12883            if (!this._svgSize || !this._svgSize.equals(size)) {
12884                this._svgSize = size;
12885                container.setAttribute('width', size.x);
12886                container.setAttribute('height', size.y);
12887            }
12888  
12889            // movement: update container viewBox so that we don't have to change coordinates of individual layers
12890            setPosition(container, b.min);
12891            container.setAttribute('viewBox', [b.min.x, b.min.y, size.x, size.y].join(' '));
12892  
12893            this.fire('update');
12894        },
12895  
12896        // methods below are called by vector layers implementations
12897  
12898        _initPath: function (layer) {
12899            var path = layer._path = create('path');
12900  
12901            // @namespace Path
12902            // @option className: String = null
12903            // Custom class name set on an element. Only for SVG renderer.
12904            if (layer.options.className) {
12905                addClass(path, layer.options.className);
12906            }
12907  
12908            if (layer.options.interactive) {
12909                addClass(path, 'leaflet-interactive');
12910            }
12911  
12912            this._updateStyle(layer);
12913            this._layers[stamp(layer)] = layer;
12914        },
12915  
12916        _addPath: function (layer) {
12917            if (!this._rootGroup) { this._initContainer(); }
12918            this._rootGroup.appendChild(layer._path);
12919            layer.addInteractiveTarget(layer._path);
12920        },
12921  
12922        _removePath: function (layer) {
12923            remove(layer._path);
12924            layer.removeInteractiveTarget(layer._path);
12925            delete this._layers[stamp(layer)];
12926        },
12927  
12928        _updatePath: function (layer) {
12929            layer._project();
12930            layer._update();
12931        },
12932  
12933        _updateStyle: function (layer) {
12934            var path = layer._path,
12935                options = layer.options;
12936  
12937            if (!path) { return; }
12938  
12939            if (options.stroke) {
12940                path.setAttribute('stroke', options.color);
12941                path.setAttribute('stroke-opacity', options.opacity);
12942                path.setAttribute('stroke-width', options.weight);
12943                path.setAttribute('stroke-linecap', options.lineCap);
12944                path.setAttribute('stroke-linejoin', options.lineJoin);
12945  
12946                if (options.dashArray) {
12947                    path.setAttribute('stroke-dasharray', options.dashArray);
vendor: 4,103 bytes, lines 12948-13057
12948                } else {
12949                    path.removeAttribute('stroke-dasharray');
12950                }
12951  
12952                if (options.dashOffset) {
12953                    path.setAttribute('stroke-dashoffset', options.dashOffset);
12954                } else {
12955                    path.removeAttribute('stroke-dashoffset');
12956                }
12957            } else {
12958                path.setAttribute('stroke', 'none');
12959            }
12960  
12961            if (options.fill) {
12962                path.setAttribute('fill', options.fillColor || options.color);
12963                path.setAttribute('fill-opacity', options.fillOpacity);
12964                path.setAttribute('fill-rule', options.fillRule || 'evenodd');
12965            } else {
12966                path.setAttribute('fill', 'none');
12967            }
12968        },
12969  
12970        _updatePoly: function (layer, closed) {
12971            this._setPath(layer, pointsToPath(layer._parts, closed));
12972        },
12973  
12974        _updateCircle: function (layer) {
12975            var p = layer._point,
12976                r = Math.max(Math.round(layer._radius), 1),
12977                r2 = Math.max(Math.round(layer._radiusY), 1) || r,
12978                arc = 'a' + r + ',' + r2 + ' 0 1,0 ';
12979  
12980            // drawing a circle with two half-arcs
12981            var d = layer._empty() ? 'M0 0' :
12982                'M' + (p.x - r) + ',' + p.y +
12983                arc + (r * 2) + ',0 ' +
12984                arc + (-r * 2) + ',0 ';
12985  
12986            this._setPath(layer, d);
12987        },
12988  
12989        _setPath: function (layer, path) {
12990            layer._path.setAttribute('d', path);
12991        },
12992  
12993        // SVG does not have the concept of zIndex so we resort to changing the DOM order of elements
12994        _bringToFront: function (layer) {
12995            toFront(layer._path);
12996        },
12997  
12998        _bringToBack: function (layer) {
12999            toBack(layer._path);
13000        }
13001    });
13002  
13003    if (Browser.vml) {
13004        SVG.include(vmlMixin);
13005    }
13006  
13007    // @namespace SVG
13008    // @factory L.svg(options?: Renderer options)
13009    // Creates a SVG renderer with the given options.
13010    function svg(options) {
13011        return Browser.svg || Browser.vml ? new SVG(options) : null;
13012    }
13013  
13014    Map.include({
13015        // @namespace Map; @method getRenderer(layer: Path): Renderer
13016        // Returns the instance of `Renderer` that should be used to render the given
13017        // `Path`. It will ensure that the `renderer` options of the map and paths
13018        // are respected, and that the renderers do exist on the map.
13019        getRenderer: function (layer) {
13020            // @namespace Path; @option renderer: Renderer
13021            // Use this specific instance of `Renderer` for this path. Takes
13022            // precedence over the map's [default renderer](#map-renderer).
13023            var renderer = layer.options.renderer || this._getPaneRenderer(layer.options.pane) || this.options.renderer || this._renderer;
13024  
13025            if (!renderer) {
13026                renderer = this._renderer = this._createRenderer();
13027            }
13028  
13029            if (!this.hasLayer(renderer)) {
13030                this.addLayer(renderer);
13031            }
13032            return renderer;
13033        },
13034  
13035        _getPaneRenderer: function (name) {
13036            if (name === 'overlayPane' || name === undefined) {
13037                return false;
13038            }
13039  
13040            var renderer = this._paneRenderers[name];
13041            if (renderer === undefined) {
13042                renderer = this._createRenderer({pane: name});
13043                this._paneRenderers[name] = renderer;
13044            }
13045            return renderer;
13046        },
13047  
13048        _createRenderer: function (options) {
13049            // @namespace Map; @option preferCanvas: Boolean = false
13050            // Whether `Path`s should be rendered on a `Canvas` renderer.
13051            // By default, all `Path`s are rendered in a `SVG` renderer.
13052            return (this.options.preferCanvas && canvas(options)) || svg(options);
13053        }
13054    });
13055  
13056    /*
13057     * L.Rectangle extends Polygon and creates a rectangle when passed a 
13057LatLngBounds object.
13058     */
13059  
13060    /*
13061     * @class Rectangle
13062     * @aka L.Rectangle
13063     * @inherits Polygon
13064     *
13065     * A class for drawing rectangle overlays on a map. Extends `Polygon`.
13066     *
13067     * @example
13068     *
13069     * ```js
13070     * // define rectangle geographical bounds
13071     * var bounds = [[54.559322, -5.767822], [56.1210604, -3.021240]];
13072     *
13073     * // create an orange rectangle
13074     * L.rectangle(bounds, {color: "#ff7800", weight: 1}).addTo(map);
13075     *
13076     * // zoom the map to the rectangle bounds
13077     * map.fitBounds(bounds);
13078     * ```
13079     *
13080     */
13081  
13082  
13083    var Rectangle = Polygon.extend({
13084        initialize: function (latLngBounds, options) {
13085            Polygon.prototype.initialize.call(this, this._boundsToLatLngs(latLngBounds), options);
13086        },
13087  
13088        // @method setBounds(latLngBounds: LatLngBounds): this
13089        // Redraws the rectangle with the passed bounds.
13090        setBounds: function (latLngBounds) {
13091            return this.setLatLngs(this._boundsToLatLngs(latLngBounds));
13092        },
13093  
13094        _boundsToLatLngs: function (latLngBounds) {
13095            latLngBounds = toLatLngBounds(latLngBounds);
13096            return [
vendor: 29,212 bytes, lines 13097-13923
13097                latLngBounds.getSouthWest(),
13098                latLngBounds.getNorthWest(),
13099                latLngBounds.getNorthEast(),
13100                latLngBounds.getSouthEast()
13101            ];
13102        }
13103    });
13104  
13105  
13106    // @factory L.rectangle(latLngBounds: LatLngBounds, options?: Polyline options)
13107    function rectangle(latLngBounds, options) {
13108        return new Rectangle(latLngBounds, options);
13109    }
13110  
13111    SVG.create = create;
13112    SVG.pointsToPath = pointsToPath;
13113  
13114    GeoJSON.geometryToLayer = geometryToLayer;
13115    GeoJSON.coordsToLatLng = coordsToLatLng;
13116    GeoJSON.coordsToLatLngs = coordsToLatLngs;
13117    GeoJSON.latLngToCoords = latLngToCoords;
13118    GeoJSON.latLngsToCoords = latLngsToCoords;
13119    GeoJSON.getFeature = getFeature;
13120    GeoJSON.asFeature = asFeature;
13121  
13122    /*
13123     * L.Handler.BoxZoom is used to add shift-drag zoom interaction to the map
13124     * (zoom to a selected bounding box), enabled by default.
13125     */
13126  
13127    // @namespace Map
13128    // @section Interaction Options
13129    Map.mergeOptions({
13130        // @option boxZoom: Boolean = true
13131        // Whether the map can be zoomed to a rectangular area specified by
13132        // dragging the mouse while pressing the shift key.
13133        boxZoom: true
13134    });
13135  
13136    var BoxZoom = Handler.extend({
13137        initialize: function (map) {
13138            this._map = map;
13139            this._container = map._container;
13140            this._pane = map._panes.overlayPane;
13141            this._resetStateTimeout = 0;
13142            map.on('unload', this._destroy, this);
13143        },
13144  
13145        addHooks: function () {
13146            on(this._container, 'mousedown', this._onMouseDown, this);
13147        },
13148  
13149        removeHooks: function () {
13150            off(this._container, 'mousedown', this._onMouseDown, this);
13151        },
13152  
13153        moved: function () {
13154            return this._moved;
13155        },
13156  
13157        _destroy: function () {
13158            remove(this._pane);
13159            delete this._pane;
13160        },
13161  
13162        _resetState: function () {
13163            this._resetStateTimeout = 0;
13164            this._moved = false;
13165        },
13166  
13167        _clearDeferredResetState: function () {
13168            if (this._resetStateTimeout !== 0) {
13169                clearTimeout(this._resetStateTimeout);
13170                this._resetStateTimeout = 0;
13171            }
13172        },
13173  
13174        _onMouseDown: function (e) {
13175            if (!e.shiftKey || ((e.which !== 1) && (e.button !== 1))) { return false; }
13176  
13177            // Clear the deferred resetState if it hasn't executed yet, otherwise it
13178            // will interrupt the interaction and orphan a box element in the container.
13179            this._clearDeferredResetState();
13180            this._resetState();
13181  
13182            disableTextSelection();
13183            disableImageDrag();
13184  
13185            this._startPoint = this._map.mouseEventToContainerPoint(e);
13186  
13187            on(document, {
13188                contextmenu: stop,
13189                mousemove: this._onMouseMove,
13190                mouseup: this._onMouseUp,
13191                keydown: this._onKeyDown
13192            }, this);
13193        },
13194  
13195        _onMouseMove: function (e) {
13196            if (!this._moved) {
13197                this._moved = true;
13198  
13199                this._box = create$1('div', 'leaflet-zoom-box', this._container);
13200                addClass(this._container, 'leaflet-crosshair');
13201  
13202                this._map.fire('boxzoomstart');
13203            }
13204  
13205            this._point = this._map.mouseEventToContainerPoint(e);
13206  
13207            var bounds = new Bounds(this._point, this._startPoint),
13208                size = bounds.getSize();
13209  
13210            setPosition(this._box, bounds.min);
13211  
13212            this._box.style.width  = size.x + 'px';
13213            this._box.style.height = size.y + 'px';
13214        },
13215  
13216        _finish: function () {
13217            if (this._moved) {
13218                remove(this._box);
13219                removeClass(this._container, 'leaflet-crosshair');
13220            }
13221  
13222            enableTextSelection();
13223            enableImageDrag();
13224  
13225            off(document, {
13226                contextmenu: stop,
13227                mousemove: this._onMouseMove,
13228                mouseup: this._onMouseUp,
13229                keydown: this._onKeyDown
13230            }, this);
13231        },
13232  
13233        _onMouseUp: function (e) {
13234            if ((e.which !== 1) && (e.button !== 1)) { return; }
13235  
13236            this._finish();
13237  
13238            if (!this._moved) { return; }
13239            // Postpone to next JS tick so internal click event handling
13240            // still see it as "moved".
13241            this._clearDeferredResetState();
13242            this._resetStateTimeout = setTimeout(bind(this._resetState, this), 0);
13243  
13244            var bounds = new LatLngBounds(
13245                    this._map.containerPointToLatLng(this._startPoint),
13246                    this._map.containerPointToLatLng(this._point));
13247  
13248            this._map
13249                .fitBounds(bounds)
13250                .fire('boxzoomend', {boxZoomBounds: bounds});
13251        },
13252  
13253        _onKeyDown: function (e) {
13254            if (e.keyCode === 27) {
13255                this._finish();
13256                this._clearDeferredResetState();
13257                this._resetState();
13258            }
13259        }
13260    });
13261  
13262    // @section Handlers
13263    // @property boxZoom: Handler
13264    // Box (shift-drag with mouse) zoom handler.
13265    Map.addInitHook('addHandler', 'boxZoom', BoxZoom);
13266  
13267    /*
13268     * L.Handler.DoubleClickZoom is used to handle double-click zoom on the map, enabled by default.
13269     */
13270  
13271    // @namespace Map
13272    // @section Interaction Options
13273  
13274    Map.mergeOptions({
13275        // @option doubleClickZoom: Boolean|String = true
13276        // Whether the map can be zoomed in by double clicking on it and
13277        // zoomed out by double clicking while holding shift. If passed
13278        // `'center'`, double-click zoom will zoom to the center of the
13279        //  view regardless of where the mouse was.
13280        doubleClickZoom: true
13281    });
13282  
13283    var DoubleClickZoom = Handler.extend({
13284        addHooks: function () {
13285            this._map.on('dblclick', this._onDoubleClick, this);
13286        },
13287  
13288        removeHooks: function () {
13289            this._map.off('dblclick', this._onDoubleClick, this);
13290        },
13291  
13292        _onDoubleClick: function (e) {
13293            var map = this._map,
13294                oldZoom = map.getZoom(),
13295                delta = map.options.zoomDelta,
13296                zoom = e.originalEvent.shiftKey ? oldZoom - delta : oldZoom + delta;
13297  
13298            if (map.options.doubleClickZoom === 'center') {
13299                map.setZoom(zoom);
13300            } else {
13301                map.setZoomAround(e.containerPoint, zoom);
13302            }
13303        }
13304    });
13305  
13306    // @section Handlers
13307    //
13308    // Map properties include interaction handlers that allow you to control
13309    // interaction behavior in runtime, enabling or disabling certain features such
13310    // as dragging or touch zoom (see `Handler` methods). For example:
13311    //
13312    // ```js
13313    // map.doubleClickZoom.disable();
13314    // ```
13315    //
13316    // @property doubleClickZoom: Handler
13317    // Double click zoom handler.
13318    Map.addInitHook('addHandler', 'doubleClickZoom', DoubleClickZoom);
13319  
13320    /*
13321     * L.Handler.MapDrag is used to make the map draggable (with panning inertia), enabled by default.
13322     */
13323  
13324    // @namespace Map
13325    // @section Interaction Options
13326    Map.mergeOptions({
13327        // @option dragging: Boolean = true
13328        // Whether the map be draggable with mouse/touch or not.
13329        dragging: true,
13330  
13331        // @section Panning Inertia Options
13332        // @option inertia: Boolean = *
13333        // If enabled, panning of the map will have an inertia effect where
13334        // the map builds momentum while dragging and continues moving in
13335        // the same direction for some time. Feels especially nice on touch
13336        // devices. Enabled by default.
13337        inertia: true,
13338  
13339        // @option inertiaDeceleration: Number = 3000
13340        // The rate with which the inertial movement slows down, in pixels/second².
13341        inertiaDeceleration: 3400, // px/s^2
13342  
13343        // @option inertiaMaxSpeed: Number = Infinity
13344        // Max speed of the inertial movement, in pixels/second.
13345        inertiaMaxSpeed: Infinity, // px/s
13346  
13347        // @option easeLinearity: Number = 0.2
13348        easeLinearity: 0.2,
13349  
13350        // TODO refactor, move to CRS
13351        // @option worldCopyJump: Boolean = false
13352        // With this option enabled, the map tracks when you pan to another "copy"
13353        // of the world and seamlessly jumps to the original one so that all overlays
13354        // like markers and vector layers are still visible.
13355        worldCopyJump: false,
13356  
13357        // @option maxBoundsViscosity: Number = 0.0
13358        // If `maxBounds` is set, this option will control how solid the bounds
13359        // are when dragging the map around. The default value of `0.0` allows the
13360        // user to drag outside the bounds at normal speed, higher values will
13361        // slow down map dragging outside bounds, and `1.0` makes the bounds fully
13362        // solid, preventing the user from dragging outside the bounds.
13363        maxBoundsViscosity: 0.0
13364    });
13365  
13366    var Drag = Handler.extend({
13367        addHooks: function () {
13368            if (!this._draggable) {
13369                var map = this._map;
13370  
13371                this._draggable = new Draggable(map._mapPane, map._container);
13372  
13373                this._draggable.on({
13374                    dragstart: this._onDragStart,
13375                    drag: this._onDrag,
13376                    dragend: this._onDragEnd
13377                }, this);
13378  
13379                this._draggable.on('predrag', this._onPreDragLimit, this);
13380                if (map.options.worldCopyJump) {
13381                    this._draggable.on('predrag', this._onPreDragWrap, this);
13382                    map.on('zoomend', this._onZoomEnd, this);
13383  
13384                    map.whenReady(this._onZoomEnd, this);
13385                }
13386            }
13387            addClass(this._map._container, 'leaflet-grab leaflet-touch-drag');
13388            this._draggable.enable();
13389            this._positions = [];
13390            this._times = [];
13391        },
13392  
13393        removeHooks: function () {
13394            removeClass(this._map._container, 'leaflet-grab');
13395            removeClass(this._map._container, 'leaflet-touch-drag');
13396            this._draggable.disable();
13397        },
13398  
13399        moved: function () {
13400            return this._draggable && this._draggable._moved;
13401        },
13402  
13403        moving: function () {
13404            return this._draggable && this._draggable._moving;
13405        },
13406  
13407        _onDragStart: function () {
13408            var map = this._map;
13409  
13410            map._stop();
13411            if (this._map.options.maxBounds && this._map.options.maxBoundsViscosity) {
13412                var bounds = toLatLngBounds(this._map.options.maxBounds);
13413  
13414                this._offsetLimit = toBounds(
13415                    this._map.latLngToContainerPoint(bounds.getNorthWest()).multiplyBy(-1),
13416                    this._map.latLngToContainerPoint(bounds.getSouthEast()).multiplyBy(-1)
13417                        .add(this._map.getSize()));
13418  
13419                this._viscosity = Math.min(1.0, Math.max(0.0, this._map.options.maxBoundsViscosity));
13420            } else {
13421                this._offsetLimit = null;
13422            }
13423  
13424            map
13425                .fire('movestart')
13426                .fire('dragstart');
13427  
13428            if (map.options.inertia) {
13429                this._positions = [];
13430                this._times = [];
13431            }
13432        },
13433  
13434        _onDrag: function (e) {
13435            if (this._map.options.inertia) {
13436                var time = this._lastTime = +new Date(),
13437                    pos = this._lastPos = this._draggable._absPos || this._draggable._newPos;
13438  
13439                this._positions.push(pos);
13440                this._times.push(time);
13441  
13442                this._prunePositions(time);
13443            }
13444  
13445            this._map
13446                .fire('move', e)
13447                .fire('drag', e);
13448        },
13449  
13450        _prunePositions: function (time) {
13451            while (this._positions.length > 1 && time - this._times[0] > 50) {
13452                this._positions.shift();
13453                this._times.shift();
13454            }
13455        },
13456  
13457        _onZoomEnd: function () {
13458            var pxCenter = this._map.getSize().divideBy(2),
13459                pxWorldCenter = this._map.latLngToLayerPoint([0, 0]);
13460  
13461            this._initialWorldOffset = pxWorldCenter.subtract(pxCenter).x;
13462            this._worldWidth = this._map.getPixelWorldBounds().getSize().x;
13463        },
13464  
13465        _viscousLimit: function (value, threshold) {
13466            return value - (value - threshold) * this._viscosity;
13467        },
13468  
13469        _onPreDragLimit: function () {
13470            if (!this._viscosity || !this._offsetLimit) { return; }
13471  
13472            var offset = this._draggable._newPos.subtract(this._draggable._startPos);
13473  
13474            var limit = this._offsetLimit;
13475            if (offset.x < limit.min.x) { offset.x = this._viscousLimit(offset.x, limit.min.x); }
13476            if (offset.y < limit.min.y) { offset.y = this._viscousLimit(offset.y, limit.min.y); }
13477            if (offset.x > limit.max.x) { offset.x = this._viscousLimit(offset.x, limit.max.x); }
13478            if (offset.y > limit.max.y) { offset.y = this._viscousLimit(offset.y, limit.max.y); }
13479  
13480            this._draggable._newPos = this._draggable._startPos.add(offset);
13481        },
13482  
13483        _onPreDragWrap: function () {
13484            // TODO refactor to be able to adjust map pane position after zoom
13485            var worldWidth = this._worldWidth,
13486                halfWidth = Math.round(worldWidth / 2),
13487                dx = this._initialWorldOffset,
13488                x = this._draggable._newPos.x,
13489                newX1 = (x - halfWidth + dx) % worldWidth + halfWidth - dx,
13490                newX2 = (x + halfWidth + dx) % worldWidth - halfWidth - dx,
13491                newX = Math.abs(newX1 + dx) < Math.abs(newX2 + dx) ? newX1 : newX2;
13492  
13493            this._draggable._absPos = this._draggable._newPos.clone();
13494            this._draggable._newPos.x = newX;
13495        },
13496  
13497        _onDragEnd: function (e) {
13498            var map = this._map,
13499                options = map.options,
13500  
13501                noInertia = !options.inertia || this._times.length < 2;
13502  
13503            map.fire('dragend', e);
13504  
13505            if (noInertia) {
13506                map.fire('moveend');
13507  
13508            } else {
13509                this._prunePositions(+new Date());
13510  
13511                var direction = this._lastPos.subtract(this._positions[0]),
13512                    duration = (this._lastTime - this._times[0]) / 1000,
13513                    ease = options.easeLinearity,
13514  
13515                    speedVector = direction.multiplyBy(ease / duration),
13516                    speed = speedVector.distanceTo([0, 0]),
13517  
13518                    limitedSpeed = Math.min(options.inertiaMaxSpeed, speed),
13519                    limitedSpeedVector = speedVector.multiplyBy(limitedSpeed / speed),
13520  
13521                    decelerationDuration = limitedSpeed / (options.inertiaDeceleration * ease),
13522                    offset = limitedSpeedVector.multiplyBy(-decelerationDuration / 2).round();
13523  
13524                if (!offset.x && !offset.y) {
13525                    map.fire('moveend');
13526  
13527                } else {
13528                    offset = map._limitOffset(offset, map.options.maxBounds);
13529  
13530                    requestAnimFrame(function () {
13531                        map.panBy(offset, {
13532                            duration: decelerationDuration,
13533                            easeLinearity: ease,
13534                            noMoveStart: true,
13535                            animate: true
13536                        });
13537                    });
13538                }
13539            }
13540        }
13541    });
13542  
13543    // @section Handlers
13544    // @property dragging: Handler
13545    // Map dragging handler (by both mouse and touch).
13546    Map.addInitHook('addHandler', 'dragging', Drag);
13547  
13548    /*
13549     * L.Map.Keyboard is handling keyboard interaction with the map, enabled by default.
13550     */
13551  
13552    // @namespace Map
13553    // @section Keyboard Navigation Options
13554    Map.mergeOptions({
13555        // @option keyboard: Boolean = true
13556        // Makes the map focusable and allows users to navigate the map with keyboard
13557        // arrows and `+`/`-` keys.
13558        keyboard: true,
13559  
13560        // @option keyboardPanDelta: Number = 80
13561        // Amount of pixels to pan when pressing an arrow key.
13562        keyboardPanDelta: 80
13563    });
13564  
13565    var Keyboard = Handler.extend({
13566  
13567        keyCodes: {
13568            left:    [37],
13569            right:   [39],
13570            down:    [40],
13571            up:      [38],
13572            zoomIn:  [187, 107, 61, 171],
13573            zoomOut: [189, 109, 54, 173]
13574        },
13575  
13576        initialize: function (map) {
13577            this._map = map;
13578  
13579            this._setPanDelta(map.options.keyboardPanDelta);
13580            this._setZoomDelta(map.options.zoomDelta);
13581        },
13582  
13583        addHooks: function () {
13584            var container = this._map._container;
13585  
13586            // make the container focusable by tabbing
13587            if (container.tabIndex <= 0) {
13588                container.tabIndex = '0';
13589            }
13590  
13591            on(container, {
13592                focus: this._onFocus,
13593                blur: this._onBlur,
13594                mousedown: this._onMouseDown
13595            }, this);
13596  
13597            this._map.on({
13598                focus: this._addHooks,
13599                blur: this._removeHooks
13600            }, this);
13601        },
13602  
13603        removeHooks: function () {
13604            this._removeHooks();
13605  
13606            off(this._map._container, {
13607                focus: this._onFocus,
13608                blur: this._onBlur,
13609                mousedown: this._onMouseDown
13610            }, this);
13611  
13612            this._map.off({
13613                focus: this._addHooks,
13614                blur: this._removeHooks
13615            }, this);
13616        },
13617  
13618        _onMouseDown: function () {
13619            if (this._focused) { return; }
13620  
13621            var body = document.body,
13622                docEl = document.documentElement,
13623                top = body.scrollTop || docEl.scrollTop,
13624                left = body.scrollLeft || docEl.scrollLeft;
13625  
13626            this._map._container.focus();
13627  
13628            window.scrollTo(left, top);
13629        },
13630  
13631        _onFocus: function () {
13632            this._focused = true;
13633            this._map.fire('focus');
13634        },
13635  
13636        _onBlur: function () {
13637            this._focused = false;
13638            this._map.fire('blur');
13639        },
13640  
13641        _setPanDelta: function (panDelta) {
13642            var keys = this._panKeys = {},
13643                codes = this.keyCodes,
13644                i, len;
13645  
13646            for (i = 0, len = codes.left.length; i < len; i++) {
13647                keys[codes.left[i]] = [-1 * panDelta, 0];
13648            }
13649            for (i = 0, len = codes.right.length; i < len; i++) {
13650                keys[codes.right[i]] = [panDelta, 0];
13651            }
13652            for (i = 0, len = codes.down.length; i < len; i++) {
13653                keys[codes.down[i]] = [0, panDelta];
13654            }
13655            for (i = 0, len = codes.up.length; i < len; i++) {
13656                keys[codes.up[i]] = [0, -1 * panDelta];
13657            }
13658        },
13659  
13660        _setZoomDelta: function (zoomDelta) {
13661            var keys = this._zoomKeys = {},
13662                codes = this.keyCodes,
13663                i, len;
13664  
13665            for (i = 0, len = codes.zoomIn.length; i < len; i++) {
13666                keys[codes.zoomIn[i]] = zoomDelta;
13667            }
13668            for (i = 0, len = codes.zoomOut.length; i < len; i++) {
13669                keys[codes.zoomOut[i]] = -zoomDelta;
13670            }
13671        },
13672  
13673        _addHooks: function () {
13674            on(document, 'keydown', this._onKeyDown, this);
13675        },
13676  
13677        _removeHooks: function () {
13678            off(document, 'keydown', this._onKeyDown, this);
13679        },
13680  
13681        _onKeyDown: function (e) {
13682            if (e.altKey || e.ctrlKey || e.metaKey) { return; }
13683  
13684            var key = e.keyCode,
13685                map = this._map,
13686                offset;
13687  
13688            if (key in this._panKeys) {
13689                if (!map._panAnim || !map._panAnim._inProgress) {
13690                    offset = this._panKeys[key];
13691                    if (e.shiftKey) {
13692                        offset = toPoint(offset).multiplyBy(3);
13693                    }
13694  
13695                    map.panBy(offset);
13696  
13697                    if (map.options.maxBounds) {
13698                        map.panInsideBounds(map.options.maxBounds);
13699                    }
13700                }
13701            } else if (key in this._zoomKeys) {
13702                map.setZoom(map.getZoom() + (e.shiftKey ? 3 : 1) * this._zoomKeys[key]);
13703  
13704            } else if (key === 27 && map._popup && map._popup.options.closeOnEscapeKey) {
13705                map.closePopup();
13706  
13707            } else {
13708                return;
13709            }
13710  
13711            stop(e);
13712        }
13713    });
13714  
13715    // @section Handlers
13716    // @section Handlers
13717    // @property keyboard: Handler
13718    // Keyboard navigation handler.
13719    Map.addInitHook('addHandler', 'keyboard', Keyboard);
13720  
13721    /*
13722     * L.Handler.ScrollWheelZoom is used by L.Map to enable mouse scroll wheel zoom on the map.
13723     */
13724  
13725    // @namespace Map
13726    // @section Interaction Options
13727    Map.mergeOptions({
13728        // @section Mouse wheel options
13729        // @option scrollWheelZoom: Boolean|String = true
13730        // Whether the map can be zoomed by using the mouse wheel. If passed `'center'`,
13731        // it will zoom to the center of the view regardless of where the mouse was.
13732        scrollWheelZoom: true,
13733  
13734        // @option wheelDebounceTime: Number = 40
13735        // Limits the rate at which a wheel can fire (in milliseconds). By default
13736        // user can't zoom via wheel more often than once per 40 ms.
13737        wheelDebounceTime: 40,
13738  
13739        // @option wheelPxPerZoomLevel: Number = 60
13740        // How many scroll pixels (as reported by [L.DomEvent.getWheelDelta](#domevent-getwheeldelta))
13741        // mean a change of one full zoom level. Smaller values will make wheel-zooming
13742        // faster (and vice versa).
13743        wheelPxPerZoomLevel: 60
13744    });
13745  
13746    var ScrollWheelZoom = Handler.extend({
13747        addHooks: function () {
13748            on(this._map._container, 'wheel', this._onWheelScroll, this);
13749  
13750            this._delta = 0;
13751        },
13752  
13753        removeHooks: function () {
13754            off(this._map._container, 'wheel', this._onWheelScroll, this);
13755        },
13756  
13757        _onWheelScroll: function (e) {
13758            var delta = getWheelDelta(e);
13759  
13760            var debounce = this._map.options.wheelDebounceTime;
13761  
13762            this._delta += delta;
13763            this._lastMousePos = this._map.mouseEventToContainerPoint(e);
13764  
13765            if (!this._startTime) {
13766                this._startTime = +new Date();
13767            }
13768  
13769            var left = Math.max(debounce - (+new Date() - this._startTime), 0);
13770  
13771            clearTimeout(this._timer);
13772            this._timer = setTimeout(bind(this._performZoom, this), left);
13773  
13774            stop(e);
13775        },
13776  
13777        _performZoom: function () {
13778            var map = this._map,
13779                zoom = map.getZoom(),
13780                snap = this._map.options.zoomSnap || 0;
13781  
13782            map._stop(); // stop panning and fly animations if any
13783  
13784            // map the delta with a sigmoid function to -4..4 range leaning on -1..1
13785            var d2 = this._delta / (this._map.options.wheelPxPerZoomLevel * 4),
13786                d3 = 4 * Math.log(2 / (1 + Math.exp(-Math.abs(d2)))) / Math.LN2,
13787                d4 = snap ? Math.ceil(d3 / snap) * snap : d3,
13788                delta = map._limitZoom(zoom + (this._delta > 0 ? d4 : -d4)) - zoom;
13789  
13790            this._delta = 0;
13791            this._startTime = null;
13792  
13793            if (!delta) { return; }
13794  
13795            if (map.options.scrollWheelZoom === 'center') {
13796                map.setZoom(zoom + delta);
13797            } else {
13798                map.setZoomAround(this._lastMousePos, zoom + delta);
13799            }
13800        }
13801    });
13802  
13803    // @section Handlers
13804    // @property scrollWheelZoom: Handler
13805    // Scroll wheel zoom handler.
13806    Map.addInitHook('addHandler', 'scrollWheelZoom', ScrollWheelZoom);
13807  
13808    /*
13809     * L.Map.TapHold is used to simulate `contextmenu` event on long hold,
13810     * which otherwise is not fired by mobile Safari.
13811     */
13812  
13813    var tapHoldDelay = 600;
13814  
13815    // @namespace Map
13816    // @section Interaction Options
13817    Map.mergeOptions({
13818        // @section Touch interaction options
13819        // @option tapHold: Boolean
13820        // Enables simulation of `contextmenu` event, default is `true` for mobile Safari.
13821        tapHold: Browser.touchNative && Browser.safari && Browser.mobile,
13822  
13823        // @option tapTolerance: Number = 15
13824        // The max number of pixels a user can shift his finger during touch
13825        // for it to be considered a valid tap.
13826        tapTolerance: 15
13827    });
13828  
13829    var TapHold = Handler.extend({
13830        addHooks: function () {
13831            on(this._map._container, 'touchstart', this._onDown, this);
13832        },
13833  
13834        removeHooks: function () {
13835            off(this._map._container, 'touchstart', this._onDown, this);
13836        },
13837  
13838        _onDown: function (e) {
13839            clearTimeout(this._holdTimeout);
13840            if (e.touches.length !== 1) { return; }
13841  
13842            var first = e.touches[0];
13843            this._startPos = this._newPos = new Point(first.clientX, first.clientY);
13844  
13845            this._holdTimeout = setTimeout(bind(function () {
13846                this._cancel();
13847                if (!this._isTapValid()) { return; }
13848  
13849                // prevent simulated mouse events https://w3c.github.io/touch-events/#mouse-events
13850                on(document, 'touchend', preventDefault);
13851                on(document, 'touchend touchcancel', this._cancelClickPrevent);
13852                this._simulateEvent('contextmenu', first);
13853            }, this), tapHoldDelay);
13854  
13855            on(document, 'touchend touchcancel contextmenu', this._cancel, this);
13856            on(document, 'touchmove', this._onMove, this);
13857        },
13858  
13859        _cancelClickPrevent: function cancelClickPrevent() {
13860            off(document, 'touchend', preventDefault);
13861            off(document, 'touchend touchcancel', cancelClickPrevent);
13862        },
13863  
13864        _cancel: function () {
13865            clearTimeout(this._holdTimeout);
13866            off(document, 'touchend touchcancel contextmenu', this._cancel, this);
13867            off(document, 'touchmove', this._onMove, this);
13868        },
13869  
13870        _onMove: function (e) {
13871            var first = e.touches[0];
13872            this._newPos = new Point(first.clientX, first.clientY);
13873        },
13874  
13875        _isTapValid: function () {
13876            return this._newPos.distanceTo(this._startPos) <= this._map.options.tapTolerance;
13877        },
13878  
13879        _simulateEvent: function (type, e) {
13880            var simulatedEvent = new MouseEvent(type, {
13881                bubbles: true,
13882                cancelable: true,
13883                view: window,
13884                // detail: 1,
13885                screenX: e.screenX,
13886                screenY: e.screenY,
13887                clientX: e.clientX,
13888                clientY: e.clientY,
13889                // button: 2,
13890                // buttons: 2
13891            });
13892  
13893            simulatedEvent._simulated = true;
13894  
13895            e.target.dispatchEvent(simulatedEvent);
13896        }
13897    });
13898  
13899    // @section Handlers
13900    // @property tapHold: Handler
13901    // Long tap handler to simulate `contextmenu` event (useful in mobile Safari).
13902    Map.addInitHook('addHandler', 'tapHold', TapHold);
13903  
13904    /*
13905     * L.Handler.TouchZoom is used by L.Map to add pinch zoom on supported mobile browsers.
13906     */
13907  
13908    // @namespace Map
13909    // @section Interaction Options
13910    Map.mergeOptions({
13911        // @section Touch interaction options
13912        // @option touchZoom: Boolean|String = *
13913        // Whether the map can be zoomed by touch-dragging with two fingers. If
13914        // passed `'center'`, it will zoom to the center of the view regardless of
13915        // where the touch events (fingers) were. Enabled for touch-capable web
13916        // browsers.
13917        touchZoom: Browser.touch,
13918  
13919        // @option bounceAtZoomLimits: Boolean = true
13920        // Set it to false if you don't want the map to zoom beyond min/max zoom
13921        // and then bounce back when pinch-zooming.
13922        bounceAtZoomLimits: true
13923    });
vendor: 7,170 bytes, lines 13924-14120
13924  
13925    var TouchZoom = Handler.extend({
13926        addHooks: function () {
13927            addClass(this._map._container, 'leaflet-touch-zoom');
13928            on(this._map._container, 'touchstart', this._onTouchStart, this);
13929        },
13930  
13931        removeHooks: function () {
13932            removeClass(this._map._container, 'leaflet-touch-zoom');
13933            off(this._map._container, 'touchstart', this._onTouchStart, this);
13934        },
13935  
13936        _onTouchStart: function (e) {
13937            var map = this._map;
13938            if (!e.touches || e.touches.length !== 2 || map._animatingZoom || this._zooming) { return; }
13939  
13940            var p1 = map.mouseEventToContainerPoint(e.touches[0]),
13941                p2 = map.mouseEventToContainerPoint(e.touches[1]);
13942  
13943            this._centerPoint = map.getSize()._divideBy(2);
13944            this._startLatLng = map.containerPointToLatLng(this._centerPoint);
13945            if (map.options.touchZoom !== 'center') {
13946                this._pinchStartLatLng = map.containerPointToLatLng(p1.add(p2)._divideBy(2));
13947            }
13948  
13949            this._startDist = p1.distanceTo(p2);
13950            this._startZoom = map.getZoom();
13951  
13952            this._moved = false;
13953            this._zooming = true;
13954  
13955            map._stop();
13956  
13957            on(document, 'touchmove', this._onTouchMove, this);
13958            on(document, 'touchend touchcancel', this._onTouchEnd, this);
13959  
13960            preventDefault(e);
13961        },
13962  
13963        _onTouchMove: function (e) {
13964            if (!e.touches || e.touches.length !== 2 || !this._zooming) { return; }
13965  
13966            var map = this._map,
13967                p1 = map.mouseEventToContainerPoint(e.touches[0]),
13968                p2 = map.mouseEventToContainerPoint(e.touches[1]),
13969                scale = p1.distanceTo(p2) / this._startDist;
13970  
13971            this._zoom = map.getScaleZoom(scale, this._startZoom);
13972  
13973            if (!map.options.bounceAtZoomLimits && (
13974                (this._zoom < map.getMinZoom() && scale < 1) ||
13975                (this._zoom > map.getMaxZoom() && scale > 1))) {
13976                this._zoom = map._limitZoom(this._zoom);
13977            }
13978  
13979            if (map.options.touchZoom === 'center') {
13980                this._center = this._startLatLng;
13981                if (scale === 1) { return; }
13982            } else {
13983                // Get delta from pinch to center, so centerLatLng is delta applied to initial pinchLatLng
13984                var delta = p1._add(p2)._divideBy(2)._subtract(this._centerPoint);
13985                if (scale === 1 && delta.x === 0 && delta.y === 0) { return; }
13986                this._center = map.unproject(map.project(this._pinchStartLatLng, this._zoom).subtract(delta), this._zoom);
13987            }
13988  
13989            if (!this._moved) {
13990                map._moveStart(true, false);
13991                this._moved = true;
13992            }
13993  
13994            cancelAnimFrame(this._animRequest);
13995  
13996            var moveFn = bind(map._move, map, this._center, this._zoom, {pinch: true, round: false});
13997            this._animRequest = requestAnimFrame(moveFn, this, true);
13998  
13999            preventDefault(e);
14000        },
14001  
14002        _onTouchEnd: function () {
14003            if (!this._moved || !this._zooming) {
14004                this._zooming = false;
14005                return;
14006            }
14007  
14008            this._zooming = false;
14009            cancelAnimFrame(this._animRequest);
14010  
14011            off(document, 'touchmove', this._onTouchMove, this);
14012            off(document, 'touchend touchcancel', this._onTouchEnd, this);
14013  
14014            // Pinch updates GridLayers' levels only when zoomSnap is off, so zoomSnap becomes noUpdate.
14015            if (this._map.options.zoomAnimation) {
14016                this._map._animateZoom(this._center, this._map._limitZoom(this._zoom), true, this._map.options.zoomSnap);
14017            } else {
14018                this._map._resetView(this._center, this._map._limitZoom(this._zoom));
14019            }
14020        }
14021    });
14022  
14023    // @section Handlers
14024    // @property touchZoom: Handler
14025    // Touch zoom handler.
14026    Map.addInitHook('addHandler', 'touchZoom', TouchZoom);
14027  
14028    Map.BoxZoom = BoxZoom;
14029    Map.DoubleClickZoom = DoubleClickZoom;
14030    Map.Drag = Drag;
14031    Map.Keyboard = Keyboard;
14032    Map.ScrollWheelZoom = ScrollWheelZoom;
14033    Map.TapHold = TapHold;
14034    Map.TouchZoom = TouchZoom;
14035  
14036    exports.Bounds = Bounds;
14037    exports.Browser = Browser;
14038    exports.CRS = CRS;
14039    exports.Canvas = Canvas;
14040    exports.Circle = Circle;
14041    exports.CircleMarker = CircleMarker;
14042    exports.Class = Class;
14043    exports.Control = Control;
14044    exports.DivIcon = DivIcon;
14045    exports.DivOverlay = DivOverlay;
14046    exports.DomEvent = DomEvent;
14047    exports.DomUtil = DomUtil;
14048    exports.Draggable = Draggable;
14049    exports.Evented = Evented;
14050    exports.FeatureGroup = FeatureGroup;
14051    exports.GeoJSON = GeoJSON;
14052    exports.GridLayer = GridLayer;
14053    exports.Handler = Handler;
14054    exports.Icon = Icon;
14055    exports.ImageOverlay = ImageOverlay;
14056    exports.LatLng = LatLng;
14057    exports.LatLngBounds = LatLngBounds;
14058    exports.Layer = Layer;
14059    exports.LayerGroup = LayerGroup;
14060    exports.LineUtil = LineUtil;
14061    exports.Map = Map;
14062    exports.Marker = Marker;
14063    exports.Mixin = Mixin;
14064    exports.Path = Path;
14065    exports.Point = Point;
14066    exports.PolyUtil = PolyUtil;
14067    exports.Polygon = Polygon;
14068    exports.Polyline = Polyline;
14069    exports.Popup = Popup;
14070    exports.PosAnimation = PosAnimation;
14071    exports.Projection = index;
14072    exports.Rectangle = Rectangle;
14073    exports.Renderer = Renderer;
14074    exports.SVG = SVG;
14075    exports.SVGOverlay = SVGOverlay;
14076    exports.TileLayer = TileLayer;
14077    exports.Tooltip = Tooltip;
14078    exports.Transformation = Transformation;
14079    exports.Util = Util;
14080    exports.VideoOverlay = VideoOverlay;
14081    exports.bind = bind;
14082    exports.bounds = toBounds;
14083    exports.canvas = canvas;
14084    exports.circle = circle;
14085    exports.circleMarker = circleMarker;
14086    exports.control = control;
14087    exports.divIcon = divIcon;
14088    exports.extend = extend;
14089    exports.featureGroup = featureGroup;
14090    exports.geoJSON = geoJSON;
14091    exports.geoJson = geoJson;
14092    exports.gridLayer = gridLayer;
14093    exports.icon = icon;
14094    exports.imageOverlay = imageOverlay;
14095    exports.latLng = toLatLng;
14096    exports.latLngBounds = toLatLngBounds;
14097    exports.layerGroup = layerGroup;
14098    exports.map = createMap;
14099    exports.marker = marker;
14100    exports.point = toPoint;
14101    exports.polygon = polygon;
14102    exports.polyline = polyline;
14103    exports.popup = popup;
14104    exports.rectangle = rectangle;
14105    exports.setOptions = setOptions;
14106    exports.stamp = stamp;
14107    exports.svg = svg;
14108    exports.svgOverlay = svgOverlay;
14109    exports.tileLayer = tileLayer;
14110    exports.tooltip = tooltip;
14111    exports.transformation = toTransformation;
14112    exports.version = version;
14113    exports.videoOverlay = videoOverlay;
14114  
14115    var oldL = window.L;
14116    exports.noConflict = function() {
14117        window.L = oldL;
14118        return this;
14119    }
14120    // Always export us to window global (see #2364)
14121    window.L = exports;
14122  
14123  }));
14124  //# sourceMappingURL=leaflet-src.js.map

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