2  * Leaflet 1.9.4, a JS library for interactive maps. https://leafletjs.com
 
   3  * (c) 2010-2023 Vladimir Agafonkin, (c) 2010-2011 CloudMade
 
   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';
 
  12   var version = "1.9.4";
 
  17    * Various utility functions, used by Leaflet internally.
\r 
  20   // @function extend(dest: Object, src?: Object): Object
\r 
  21   // Merges the properties of the `src` object (or multiple objects) into `dest` object and returns the latter. Has an `L.extend` shortcut.
\r 
  22   function extend(dest) {
\r 
  25         for (j = 1, len = arguments.length; j < len; j++) {
\r 
  34   // @function create(proto: Object, properties?: Object): Object
\r 
  35   // Compatibility polyfill for [Object.create](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Object/create)
\r 
  36   var create$2 = Object.create || (function () {
\r 
  38         return function (proto) {
\r 
  39                 F.prototype = proto;
\r 
  44   // @function bind(fn: Function, …): Function
\r 
  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).
\r 
  46   // Has a `L.bind()` shortcut.
\r 
  47   function bind(fn, obj) {
\r 
  48         var slice = Array.prototype.slice;
\r 
  51                 return fn.bind.apply(fn, slice.call(arguments, 1));
\r 
  54         var args = slice.call(arguments, 2);
\r 
  56         return function () {
\r 
  57                 return fn.apply(obj, args.length ? args.concat(slice.call(arguments)) : arguments);
\r 
  61   // @property lastId: Number
\r 
  62   // Last unique ID used by [`stamp()`](#util-stamp)
\r 
  65   // @function stamp(obj: Object): Number
\r 
  66   // Returns the unique ID of an object, assigning it one if it doesn't have it.
\r 
  67   function stamp(obj) {
\r 
  68         if (!('_leaflet_id' in obj)) {
\r 
  69                 obj['_leaflet_id'] = ++lastId;
\r 
  71         return obj._leaflet_id;
\r 
  74   // @function throttle(fn: Function, time: Number, context: Object): Function
\r 
  75   // Returns a function which executes function `fn` with the given scope `context`
\r 
  76   // (so that the `this` keyword refers to `context` inside `fn`'s code). The function
\r 
  77   // `fn` will be called no more than one time per given amount of `time`. The arguments
\r 
  78   // received by the bound function will be any arguments passed when binding the
\r 
  79   // function, followed by any arguments passed when invoking the bound function.
\r 
  80   // Has an `L.throttle` shortcut.
\r 
  81   function throttle(fn, time, context) {
\r 
  82         var lock, args, wrapperFn, later;
\r 
  84         later = function () {
\r 
  85                 // reset lock and call if queued
\r 
  88                         wrapperFn.apply(context, args);
\r 
  93         wrapperFn = function () {
\r 
  95                         // called too soon, queue to call later
\r 
  99                         // call and lock until later
\r 
 100                         fn.apply(context, arguments);
\r 
 101                         setTimeout(later, time);
\r 
 109   // @function wrapNum(num: Number, range: Number[], includeMax?: Boolean): Number
\r 
 110   // Returns the number `num` modulo `range` in such a way so it lies within
\r 
 111   // `range[0]` and `range[1]`. The returned value will be always smaller than
\r 
 112   // `range[1]` unless `includeMax` is set to `true`.
\r 
 113   function wrapNum(x, range, includeMax) {
\r 
 114         var max = range[1],
\r 
 117         return x === max && includeMax ? x : ((x - min) % d + d) % d + min;
\r 
 120   // @function falseFn(): Function
\r 
 121   // Returns a function which always returns `false`.
\r 
 122   function falseFn() { return false; }
\r 
 124   // @function formatNum(num: Number, precision?: Number|false): Number
\r 
 125   // Returns the number `num` rounded with specified `precision`.
\r 
 126   // The default `precision` value is 6 decimal places.
\r 
 127   // `false` can be passed to skip any processing (can be useful to avoid round-off errors).
\r 
 128   function formatNum(num, precision) {
\r 
 129         if (precision === false) { return num; }
\r 
 130         var pow = Math.pow(10, precision === undefined ? 6 : precision);
\r 
 131         return Math.round(num * pow) / pow;
\r 
 134   // @function trim(str: String): String
\r 
 135   // Compatibility polyfill for [String.prototype.trim](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String/Trim)
\r 
 136   function trim(str) {
\r 
 137         return str.trim ? str.trim() : str.replace(/^\s+|\s+$/g, '');
\r 
 140   // @function splitWords(str: String): String[]
\r 
 141   // Trims and splits the string on whitespace and returns the array of parts.
\r 
 142   function splitWords(str) {
\r 
 143         return trim(str).split(/\s+/);
\r 
 146   // @function setOptions(obj: Object, options: Object): Object
\r 
 147   // Merges the given properties to the `options` of the `obj` object, returning the resulting options. See `Class options`. Has an `L.setOptions` shortcut.
\r 
 148   function setOptions(obj, options) {
\r 
 149         if (!Object.prototype.hasOwnProperty.call(obj, 'options')) {
\r 
 150                 obj.options = obj.options ? create$2(obj.options) : {};
\r 
 152         for (var i in options) {
\r 
 153                 obj.options[i] = options[i];
\r 
 155         return obj.options;
\r 
 158   // @function getParamString(obj: Object, existingUrl?: String, uppercase?: Boolean): String
\r 
 159   // Converts an object into a parameter URL string, e.g. `{a: "foo", b: "bar"}`
\r 
 160   // translates to `'?a=foo&b=bar'`. If `existingUrl` is set, the parameters will
\r 
 161   // be appended at the end. If `uppercase` is `true`, the parameter names will
\r 
 162   // be uppercased (e.g. `'?A=foo&B=bar'`)
\r 
 163   function getParamString(obj, existingUrl, uppercase) {
\r 
 165         for (var i in obj) {
\r 
 166                 params.push(encodeURIComponent(uppercase ? i.toUpperCase() : i) + '=' + encodeURIComponent(obj[i]));
\r 
 168         return ((!existingUrl || existingUrl.indexOf('?') === -1) ? '?' : '&') + params.join('&');
\r 
 171   var templateRe = /\{ *([\w_ -]+) *\}/g;
\r 
 173   // @function template(str: String, data: Object): String
\r 
 174   // Simple templating facility, accepts a template string of the form `'Hello {a}, {b}'`
\r 
 175   // and a data object like `{a: 'foo', b: 'bar'}`, returns evaluated string
\r 
 176   // `('Hello foo, bar')`. You can also specify functions instead of strings for
\r 
 177   // data values — they will be evaluated passing `data` as an argument.
\r 
 178   function template(str, data) {
\r 
 179         return str.replace(templateRe, function (str, key) {
\r 
 180                 var value = data[key];
\r 
 182                 if (value === undefined) {
\r 
 183                         throw new Error('No value provided for variable ' + str);
\r 
 185                 } else if (typeof value === 'function') {
\r 
 186                         value = value(data);
\r 
 192   // @function isArray(obj): Boolean
\r 
 193   // Compatibility polyfill for [Array.isArray](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Array/isArray)
\r 
 194   var isArray = Array.isArray || function (obj) {
\r 
 195         return (Object.prototype.toString.call(obj) === '[object Array]');
\r 
 198   // @function indexOf(array: Array, el: Object): Number
\r 
 199   // Compatibility polyfill for [Array.prototype.indexOf](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Array/indexOf)
\r 
 200   function indexOf(array, el) {
\r 
 201         for (var i = 0; i < array.length; i++) {
\r 
 202                 if (array[i] === el) { return i; }
\r 
 207   // @property emptyImageUrl: String
\r 
 208   // Data URI string containing a base64-encoded empty GIF image.
\r 
 209   // Used as a hack to free memory from unused images on WebKit-powered
\r 
 210   // mobile devices (by setting image `src` to this string).
\r 
 211   var emptyImageUrl = 'data:image/gif;base64,R0lGODlhAQABAAD/ACwAAAAAAQABAAACADs=';
\r 
 213   // inspired by https://paulirish.com/2011/requestanimationframe-for-smart-animating/
\r 
 215   function getPrefixed(name) {
\r 
 216         return window['webkit' + name] || window['moz' + name] || window['ms' + name];
\r 
 221   // fallback for IE 7-8
\r 
 222   function timeoutDefer(fn) {
\r 
 223         var time = +new Date(),
\r 
 224             timeToCall = Math.max(0, 16 - (time - lastTime));
\r 
 226         lastTime = time + timeToCall;
\r 
 227         return window.setTimeout(fn, timeToCall);
\r 
 230   var requestFn = window.requestAnimationFrame || getPrefixed('RequestAnimationFrame') || timeoutDefer;
\r 
 231   var cancelFn = window.cancelAnimationFrame || getPrefixed('CancelAnimationFrame') ||
\r 
 232                 getPrefixed('CancelRequestAnimationFrame') || function (id) { window.clearTimeout(id); };
\r 
 234   // @function requestAnimFrame(fn: Function, context?: Object, immediate?: Boolean): Number
\r 
 235   // Schedules `fn` to be executed when the browser repaints. `fn` is bound to
\r 
 236   // `context` if given. When `immediate` is set, `fn` is called immediately if
\r 
 237   // the browser doesn't have native support for
\r 
 238   // [`window.requestAnimationFrame`](https://developer.mozilla.org/docs/Web/API/window/requestAnimationFrame),
\r 
 239   // otherwise it's delayed. Returns a request ID that can be used to cancel the request.
\r 
 240   function requestAnimFrame(fn, context, immediate) {
\r 
 241         if (immediate && requestFn === timeoutDefer) {
\r 
 244                 return requestFn.call(window, bind(fn, context));
\r 
 248   // @function cancelAnimFrame(id: Number): undefined
\r 
 249   // Cancels a previous `requestAnimFrame`. See also [window.cancelAnimationFrame](https://developer.mozilla.org/docs/Web/API/window/cancelAnimationFrame).
\r 
 250   function cancelAnimFrame(id) {
\r 
 252                 cancelFn.call(window, id);
\r 
 261     get lastId () { return lastId; },
 
 266     formatNum: formatNum,
 
 268     splitWords: splitWords,
 
 269     setOptions: setOptions,
 
 270     getParamString: getParamString,
 
 274     emptyImageUrl: emptyImageUrl,
 
 275     requestFn: requestFn,
 
 277     requestAnimFrame: requestAnimFrame,
 
 278     cancelAnimFrame: cancelAnimFrame
 
 287   // Thanks to John Resig and Dean Edwards for inspiration!
\r 
 289   function Class() {}
\r 
 291   Class.extend = function (props) {
\r 
 293         // @function extend(props: Object): Function
\r 
 294         // [Extends the current class](#class-inheritance) given the properties to be included.
\r 
 295         // Returns a Javascript function that is a class constructor (to be called with `new`).
\r 
 296         var NewClass = function () {
\r 
 300                 // call the constructor
\r 
 301                 if (this.initialize) {
\r 
 302                         this.initialize.apply(this, arguments);
\r 
 305                 // call all constructor hooks
\r 
 306                 this.callInitHooks();
\r 
 309         var parentProto = NewClass.__super__ = this.prototype;
\r 
 311         var proto = create$2(parentProto);
\r 
 312         proto.constructor = NewClass;
\r 
 314         NewClass.prototype = proto;
\r 
 316         // inherit parent's statics
\r 
 317         for (var i in this) {
\r 
 318                 if (Object.prototype.hasOwnProperty.call(this, i) && i !== 'prototype' && i !== '__super__') {
\r 
 319                         NewClass[i] = this[i];
\r 
 323         // mix static properties into the class
\r 
 324         if (props.statics) {
\r 
 325                 extend(NewClass, props.statics);
\r 
 328         // mix includes into the prototype
\r 
 329         if (props.includes) {
\r 
 330                 checkDeprecatedMixinEvents(props.includes);
\r 
 331                 extend.apply(null, [proto].concat(props.includes));
\r 
 334         // mix given properties into the prototype
\r 
 335         extend(proto, props);
\r 
 336         delete proto.statics;
\r 
 337         delete proto.includes;
\r 
 340         if (proto.options) {
\r 
 341                 proto.options = parentProto.options ? create$2(parentProto.options) : {};
\r 
 342                 extend(proto.options, props.options);
\r 
 345         proto._initHooks = [];
\r 
 347         // add method for calling all hooks
\r 
 348         proto.callInitHooks = function () {
\r 
 350                 if (this._initHooksCalled) { return; }
\r 
 352                 if (parentProto.callInitHooks) {
\r 
 353                         parentProto.callInitHooks.call(this);
\r 
 356                 this._initHooksCalled = true;
\r 
 358                 for (var i = 0, len = proto._initHooks.length; i < len; i++) {
\r 
 359                         proto._initHooks[i].call(this);
\r 
 367   // @function include(properties: Object): this
\r 
 368   // [Includes a mixin](#class-includes) into the current class.
\r 
 369   Class.include = function (props) {
\r 
 370         var parentOptions = this.prototype.options;
\r 
 371         extend(this.prototype, props);
\r 
 372         if (props.options) {
\r 
 373                 this.prototype.options = parentOptions;
\r 
 374                 this.mergeOptions(props.options);
\r 
 379   // @function mergeOptions(options: Object): this
\r 
 380   // [Merges `options`](#class-options) into the defaults of the class.
\r 
 381   Class.mergeOptions = function (options) {
\r 
 382         extend(this.prototype.options, options);
\r 
 386   // @function addInitHook(fn: Function): this
\r 
 387   // Adds a [constructor hook](#class-constructor-hooks) to the class.
\r 
 388   Class.addInitHook = function (fn) { // (Function) || (String, args...)
\r 
 389         var args = Array.prototype.slice.call(arguments, 1);
\r 
 391         var init = typeof fn === 'function' ? fn : function () {
\r 
 392                 this[fn].apply(this, args);
\r 
 395         this.prototype._initHooks = this.prototype._initHooks || [];
\r 
 396         this.prototype._initHooks.push(init);
\r 
 400   function checkDeprecatedMixinEvents(includes) {
\r 
 401         /* global L: true */
\r 
 402         if (typeof L === 'undefined' || !L || !L.Mixin) { return; }
\r 
 404         includes = isArray(includes) ? includes : [includes];
\r 
 406         for (var i = 0; i < includes.length; i++) {
\r 
 407                 if (includes[i] === L.Mixin.Events) {
\r 
 408                         console.warn('Deprecated include of L.Mixin.Events: ' +
\r 
 409                                 'this property will be removed in future releases, ' +
\r 
 410                                 'please inherit from L.Evented instead.', new Error().stack);
\r 
 420    * 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).
\r 
 425    * map.on('click', function(e) {
\r 
 430    * Leaflet deals with event listeners by reference, so if you want to add a listener and then remove it, define it as a function:
\r 
 433    * function onClick(e) { ... }
\r 
 435    * map.on('click', onClick);
\r 
 436    * map.off('click', onClick);
\r 
 441         /* @method on(type: String, fn: Function, context?: Object): this
\r 
 442          * 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'`).
\r 
 445          * @method on(eventMap: Object): this
\r 
 446          * Adds a set of type/listener pairs, e.g. `{click: onClick, mousemove: onMouseMove}`
\r 
 448         on: function (types, fn, context) {
\r 
 450                 // types can be a map of types/handlers
\r 
 451                 if (typeof types === 'object') {
\r 
 452                         for (var type in types) {
\r 
 453                                 // we don't process space-separated events here for performance;
\r 
 454                                 // it's a hot path since Layer uses the on(obj) syntax
\r 
 455                                 this._on(type, types[type], fn);
\r 
 459                         // types can be a string of space-separated words
\r 
 460                         types = splitWords(types);
\r 
 462                         for (var i = 0, len = types.length; i < len; i++) {
\r 
 463                                 this._on(types[i], fn, context);
\r 
 470         /* @method off(type: String, fn?: Function, context?: Object): this
\r 
 471          * 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.
\r 
 474          * @method off(eventMap: Object): this
\r 
 475          * Removes a set of type/listener pairs.
\r 
 478          * @method off: this
\r 
 479          * Removes all listeners to all events on the object. This includes implicitly attached events.
\r 
 481         off: function (types, fn, context) {
\r 
 483                 if (!arguments.length) {
\r 
 484                         // clear all listeners if called without arguments
\r 
 485                         delete this._events;
\r 
 487                 } else if (typeof types === 'object') {
\r 
 488                         for (var type in types) {
\r 
 489                                 this._off(type, types[type], fn);
\r 
 493                         types = splitWords(types);
\r 
 495                         var removeAll = arguments.length === 1;
\r 
 496                         for (var i = 0, len = types.length; i < len; i++) {
\r 
 498                                         this._off(types[i]);
\r 
 500                                         this._off(types[i], fn, context);
\r 
 508         // attach listener (without syntactic sugar now)
\r 
 509         _on: function (type, fn, context, _once) {
\r 
 510                 if (typeof fn !== 'function') {
\r 
 511                         console.warn('wrong listener type: ' + typeof fn);
\r 
 515                 // check if fn already there
\r 
 516                 if (this._listens(type, fn, context) !== false) {
\r 
 520                 if (context === this) {
\r 
 521                         // Less memory footprint.
\r 
 522                         context = undefined;
\r 
 525                 var newListener = {fn: fn, ctx: context};
\r 
 527                         newListener.once = true;
\r 
 530                 this._events = this._events || {};
\r 
 531                 this._events[type] = this._events[type] || [];
\r 
 532                 this._events[type].push(newListener);
\r 
 535         _off: function (type, fn, context) {
\r 
 540                 if (!this._events) {
\r 
 544                 listeners = this._events[type];
\r 
 549                 if (arguments.length === 1) { // remove all
\r 
 550                         if (this._firingCount) {
\r 
 551                                 // Set all removed listeners to noop
\r 
 552                                 // so they are not called if remove happens in fire
\r 
 553                                 for (i = 0, len = listeners.length; i < len; i++) {
\r 
 554                                         listeners[i].fn = falseFn;
\r 
 557                         // clear all listeners for a type if function isn't specified
\r 
 558                         delete this._events[type];
\r 
 562                 if (typeof fn !== 'function') {
\r 
 563                         console.warn('wrong listener type: ' + typeof fn);
\r 
 567                 // find fn and remove it
\r 
 568                 var index = this._listens(type, fn, context);
\r 
 569                 if (index !== false) {
\r 
 570                         var listener = listeners[index];
\r 
 571                         if (this._firingCount) {
\r 
 572                                 // set the removed listener to noop so that's not called if remove happens in fire
\r 
 573                                 listener.fn = falseFn;
\r 
 575                                 /* copy array in case events are being fired */
\r 
 576                                 this._events[type] = listeners = listeners.slice();
\r 
 578                         listeners.splice(index, 1);
\r 
 582         // @method fire(type: String, data?: Object, propagate?: Boolean): this
\r 
 583         // Fires an event of the specified type. You can optionally provide a data
\r 
 584         // object — the first argument of the listener function will contain its
\r 
 585         // properties. The event can optionally be propagated to event parents.
\r 
 586         fire: function (type, data, propagate) {
\r 
 587                 if (!this.listens(type, propagate)) { return this; }
\r 
 589                 var event = extend({}, data, {
\r 
 592                         sourceTarget: data && data.sourceTarget || this
\r 
 595                 if (this._events) {
\r 
 596                         var listeners = this._events[type];
\r 
 598                                 this._firingCount = (this._firingCount + 1) || 1;
\r 
 599                                 for (var i = 0, len = listeners.length; i < len; i++) {
\r 
 600                                         var l = listeners[i];
\r 
 601                                         // off overwrites l.fn, so we need to copy fn to a var
\r 
 604                                                 this.off(type, fn, l.ctx);
\r 
 606                                         fn.call(l.ctx || this, event);
\r 
 609                                 this._firingCount--;
\r 
 614                         // propagate the event to parents (set with addEventParent)
\r 
 615                         this._propagateEvent(event);
\r 
 621         // @method listens(type: String, propagate?: Boolean): Boolean
\r 
 622         // @method listens(type: String, fn: Function, context?: Object, propagate?: Boolean): Boolean
\r 
 623         // Returns `true` if a particular event type has any listeners attached to it.
\r 
 624         // The verification can optionally be propagated, it will return `true` if parents have the listener attached to it.
\r 
 625         listens: function (type, fn, context, propagate) {
\r 
 626                 if (typeof type !== 'string') {
\r 
 627                         console.warn('"string" type argument expected');
\r 
 630                 // we don't overwrite the input `fn` value, because we need to use it for propagation
\r 
 632                 if (typeof fn !== 'function') {
\r 
 635                         context = undefined;
\r 
 638                 var listeners = this._events && this._events[type];
\r 
 639                 if (listeners && listeners.length) {
\r 
 640                         if (this._listens(type, _fn, context) !== false) {
\r 
 646                         // also check parents for listeners if event propagates
\r 
 647                         for (var id in this._eventParents) {
\r 
 648                                 if (this._eventParents[id].listens(type, fn, context, propagate)) { return true; }
\r 
 654         // returns the index (number) or false
\r 
 655         _listens: function (type, fn, context) {
\r 
 656                 if (!this._events) {
\r 
 660                 var listeners = this._events[type] || [];
\r 
 662                         return !!listeners.length;
\r 
 665                 if (context === this) {
\r 
 666                         // Less memory footprint.
\r 
 667                         context = undefined;
\r 
 670                 for (var i = 0, len = listeners.length; i < len; i++) {
\r 
 671                         if (listeners[i].fn === fn && listeners[i].ctx === context) {
\r 
 679         // @method once(…): this
\r 
 680         // Behaves as [`on(…)`](#evented-on), except the listener will only get fired once and then removed.
\r 
 681         once: function (types, fn, context) {
\r 
 683                 // types can be a map of types/handlers
\r 
 684                 if (typeof types === 'object') {
\r 
 685                         for (var type in types) {
\r 
 686                                 // we don't process space-separated events here for performance;
\r 
 687                                 // it's a hot path since Layer uses the on(obj) syntax
\r 
 688                                 this._on(type, types[type], fn, true);
\r 
 692                         // types can be a string of space-separated words
\r 
 693                         types = splitWords(types);
\r 
 695                         for (var i = 0, len = types.length; i < len; i++) {
\r 
 696                                 this._on(types[i], fn, context, true);
\r 
 703         // @method addEventParent(obj: Evented): this
\r 
 704         // Adds an event parent - an `Evented` that will receive propagated events
\r 
 705         addEventParent: function (obj) {
\r 
 706                 this._eventParents = this._eventParents || {};
\r 
 707                 this._eventParents[stamp(obj)] = obj;
\r 
 711         // @method removeEventParent(obj: Evented): this
\r 
 712         // Removes an event parent, so it will stop receiving propagated events
\r 
 713         removeEventParent: function (obj) {
\r 
 714                 if (this._eventParents) {
\r 
 715                         delete this._eventParents[stamp(obj)];
\r 
 720         _propagateEvent: function (e) {
\r 
 721                 for (var id in this._eventParents) {
\r 
 722                         this._eventParents[id].fire(e.type, extend({
\r 
 724                                 propagatedFrom: e.target
\r 
 730   // aliases; we should ditch those eventually
\r 
 732   // @method addEventListener(…): this
\r 
 733   // Alias to [`on(…)`](#evented-on)
\r 
 734   Events.addEventListener = Events.on;
\r 
 736   // @method removeEventListener(…): this
\r 
 737   // Alias to [`off(…)`](#evented-off)
\r 
 739   // @method clearAllEventListeners(…): this
\r 
 740   // Alias to [`off()`](#evented-off)
\r 
 741   Events.removeEventListener = Events.clearAllEventListeners = Events.off;
\r 
 743   // @method addOneTimeEventListener(…): this
\r 
 744   // Alias to [`once(…)`](#evented-once)
\r 
 745   Events.addOneTimeEventListener = Events.once;
\r 
 747   // @method fireEvent(…): this
\r 
 748   // Alias to [`fire(…)`](#evented-fire)
\r 
 749   Events.fireEvent = Events.fire;
\r 
 751   // @method hasEventListeners(…): Boolean
\r 
 752   // Alias to [`listens(…)`](#evented-listens)
\r 
 753   Events.hasEventListeners = Events.listens;
\r 
 755   var Evented = Class.extend(Events);
 
 761    * Represents a point with `x` and `y` coordinates in pixels.
\r 
 766    * var point = L.point(200, 300);
\r 
 769    * 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:
\r 
 772    * map.panBy([200, 300]);
\r 
 773    * map.panBy(L.point(200, 300));
\r 
 776    * Note that `Point` does not inherit from Leaflet's `Class` object,
\r 
 777    * which means new classes can't inherit from it, and new methods
\r 
 778    * can't be added to it with the `include` function.
\r 
 781   function Point(x, y, round) {
\r 
 782         // @property x: Number; The `x` coordinate of the point
\r 
 783         this.x = (round ? Math.round(x) : x);
\r 
 784         // @property y: Number; The `y` coordinate of the point
\r 
 785         this.y = (round ? Math.round(y) : y);
\r 
 788   var trunc = Math.trunc || function (v) {
\r 
 789         return v > 0 ? Math.floor(v) : Math.ceil(v);
\r 
 792   Point.prototype = {
\r 
 794         // @method clone(): Point
\r 
 795         // Returns a copy of the current point.
\r 
 796         clone: function () {
\r 
 797                 return new Point(this.x, this.y);
\r 
 800         // @method add(otherPoint: Point): Point
\r 
 801         // Returns the result of addition of the current and the given points.
\r 
 802         add: function (point) {
\r 
 803                 // non-destructive, returns a new point
\r 
 804                 return this.clone()._add(toPoint(point));
\r 
 807         _add: function (point) {
\r 
 808                 // destructive, used directly for performance in situations where it's safe to modify existing point
\r 
 814         // @method subtract(otherPoint: Point): Point
\r 
 815         // Returns the result of subtraction of the given point from the current.
\r 
 816         subtract: function (point) {
\r 
 817                 return this.clone()._subtract(toPoint(point));
\r 
 820         _subtract: function (point) {
\r 
 826         // @method divideBy(num: Number): Point
\r 
 827         // Returns the result of division of the current point by the given number.
\r 
 828         divideBy: function (num) {
\r 
 829                 return this.clone()._divideBy(num);
\r 
 832         _divideBy: function (num) {
\r 
 838         // @method multiplyBy(num: Number): Point
\r 
 839         // Returns the result of multiplication of the current point by the given number.
\r 
 840         multiplyBy: function (num) {
\r 
 841                 return this.clone()._multiplyBy(num);
\r 
 844         _multiplyBy: function (num) {
\r 
 850         // @method scaleBy(scale: Point): Point
\r 
 851         // Multiply each coordinate of the current point by each coordinate of
\r 
 852         // `scale`. In linear algebra terms, multiply the point by the
\r 
 853         // [scaling matrix](https://en.wikipedia.org/wiki/Scaling_%28geometry%29#Matrix_representation)
\r 
 854         // defined by `scale`.
\r 
 855         scaleBy: function (point) {
\r 
 856                 return new Point(this.x * point.x, this.y * point.y);
\r 
 859         // @method unscaleBy(scale: Point): Point
\r 
 860         // Inverse of `scaleBy`. Divide each coordinate of the current point by
\r 
 861         // each coordinate of `scale`.
\r 
 862         unscaleBy: function (point) {
\r 
 863                 return new Point(this.x / point.x, this.y / point.y);
\r 
 866         // @method round(): Point
\r 
 867         // Returns a copy of the current point with rounded coordinates.
\r 
 868         round: function () {
\r 
 869                 return this.clone()._round();
\r 
 872         _round: function () {
\r 
 873                 this.x = Math.round(this.x);
\r 
 874                 this.y = Math.round(this.y);
\r 
 878         // @method floor(): Point
\r 
 879         // Returns a copy of the current point with floored coordinates (rounded down).
\r 
 880         floor: function () {
\r 
 881                 return this.clone()._floor();
\r 
 884         _floor: function () {
\r 
 885                 this.x = Math.floor(this.x);
\r 
 886                 this.y = Math.floor(this.y);
\r 
 890         // @method ceil(): Point
\r 
 891         // Returns a copy of the current point with ceiled coordinates (rounded up).
\r 
 892         ceil: function () {
\r 
 893                 return this.clone()._ceil();
\r 
 896         _ceil: function () {
\r 
 897                 this.x = Math.ceil(this.x);
\r 
 898                 this.y = Math.ceil(this.y);
\r 
 902         // @method trunc(): Point
\r 
 903         // Returns a copy of the current point with truncated coordinates (rounded towards zero).
\r 
 904         trunc: function () {
\r 
 905                 return this.clone()._trunc();
\r 
 908         _trunc: function () {
\r 
 909                 this.x = trunc(this.x);
\r 
 910                 this.y = trunc(this.y);
\r 
 914         // @method distanceTo(otherPoint: Point): Number
\r 
 915         // Returns the cartesian distance between the current and the given points.
\r 
 916         distanceTo: function (point) {
\r 
 917                 point = toPoint(point);
\r 
 919                 var x = point.x - this.x,
\r 
 920                     y = point.y - this.y;
\r 
 922                 return Math.sqrt(x * x + y * y);
\r 
 925         // @method equals(otherPoint: Point): Boolean
\r 
 926         // Returns `true` if the given point has the same coordinates.
\r 
 927         equals: function (point) {
\r 
 928                 point = toPoint(point);
\r 
 930                 return point.x === this.x &&
\r 
 931                        point.y === this.y;
\r 
 934         // @method contains(otherPoint: Point): Boolean
\r 
 935         // Returns `true` if both coordinates of the given point are less than the corresponding current point coordinates (in absolute values).
\r 
 936         contains: function (point) {
\r 
 937                 point = toPoint(point);
\r 
 939                 return Math.abs(point.x) <= Math.abs(this.x) &&
\r 
 940                        Math.abs(point.y) <= Math.abs(this.y);
\r 
 943         // @method toString(): String
\r 
 944         // Returns a string representation of the point for debugging purposes.
\r 
 945         toString: function () {
\r 
 947                         formatNum(this.x) + ', ' +
\r 
 948                         formatNum(this.y) + ')';
\r 
 952   // @factory L.point(x: Number, y: Number, round?: Boolean)
\r 
 953   // Creates a Point object with the given `x` and `y` coordinates. If optional `round` is set to true, rounds the `x` and `y` values.
\r 
 956   // @factory L.point(coords: Number[])
\r 
 957   // Expects an array of the form `[x, y]` instead.
\r 
 960   // @factory L.point(coords: Object)
\r 
 961   // Expects a plain object of the form `{x: Number, y: Number}` instead.
\r 
 962   function toPoint(x, y, round) {
\r 
 963         if (x instanceof Point) {
\r 
 967                 return new Point(x[0], x[1]);
\r 
 969         if (x === undefined || x === null) {
\r 
 972         if (typeof x === 'object' && 'x' in x && 'y' in x) {
\r 
 973                 return new Point(x.x, x.y);
\r 
 975         return new Point(x, y, round);
\r 
 982    * Represents a rectangular area in pixel coordinates.
\r 
 987    * var p1 = L.point(10, 10),
\r 
 988    * p2 = L.point(40, 60),
\r 
 989    * bounds = L.bounds(p1, p2);
\r 
 992    * 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:
\r 
 995    * otherBounds.intersects([[10, 10], [40, 60]]);
\r 
 998    * Note that `Bounds` does not inherit from Leaflet's `Class` object,
\r 
 999    * which means new classes can't inherit from it, and new methods
\r 
1000    * can't be added to it with the `include` function.
\r 
1003   function Bounds(a, b) {
\r 
1004         if (!a) { return; }
\r 
1006         var points = b ? [a, b] : a;
\r 
1008         for (var i = 0, len = points.length; i < len; i++) {
\r 
1009                 this.extend(points[i]);
\r 
1013   Bounds.prototype = {
\r 
1014         // @method extend(point: Point): this
\r 
1015         // Extends the bounds to contain the given point.
\r 
1018         // @method extend(otherBounds: Bounds): this
\r 
1019         // Extend the bounds to contain the given bounds
\r 
1020         extend: function (obj) {
\r 
1022                 if (!obj) { return this; }
\r 
1024                 if (obj instanceof Point || typeof obj[0] === 'number' || 'x' in obj) {
\r 
1025                         min2 = max2 = toPoint(obj);
\r 
1027                         obj = toBounds(obj);
\r 
1031                         if (!min2 || !max2) { return this; }
\r 
1034                 // @property min: Point
\r 
1035                 // The top left corner of the rectangle.
\r 
1036                 // @property max: Point
\r 
1037                 // The bottom right corner of the rectangle.
\r 
1038                 if (!this.min && !this.max) {
\r 
1039                         this.min = min2.clone();
\r 
1040                         this.max = max2.clone();
\r 
1042                         this.min.x = Math.min(min2.x, this.min.x);
\r 
1043                         this.max.x = Math.max(max2.x, this.max.x);
\r 
1044                         this.min.y = Math.min(min2.y, this.min.y);
\r 
1045                         this.max.y = Math.max(max2.y, this.max.y);
\r 
1050         // @method getCenter(round?: Boolean): Point
\r 
1051         // Returns the center point of the bounds.
\r 
1052         getCenter: function (round) {
\r 
1054                         (this.min.x + this.max.x) / 2,
\r 
1055                         (this.min.y + this.max.y) / 2, round);
\r 
1058         // @method getBottomLeft(): Point
\r 
1059         // Returns the bottom-left point of the bounds.
\r 
1060         getBottomLeft: function () {
\r 
1061                 return toPoint(this.min.x, this.max.y);
\r 
1064         // @method getTopRight(): Point
\r 
1065         // Returns the top-right point of the bounds.
\r 
1066         getTopRight: function () { // -> Point
\r 
1067                 return toPoint(this.max.x, this.min.y);
\r 
1070         // @method getTopLeft(): Point
\r 
1071         // Returns the top-left point of the bounds (i.e. [`this.min`](#bounds-min)).
\r 
1072         getTopLeft: function () {
\r 
1073                 return this.min; // left, top
\r 
1076         // @method getBottomRight(): Point
\r 
1077         // Returns the bottom-right point of the bounds (i.e. [`this.max`](#bounds-max)).
\r 
1078         getBottomRight: function () {
\r 
1079                 return this.max; // right, bottom
\r 
1082         // @method getSize(): Point
\r 
1083         // Returns the size of the given bounds
\r 
1084         getSize: function () {
\r 
1085                 return this.max.subtract(this.min);
\r 
1088         // @method contains(otherBounds: Bounds): Boolean
\r 
1089         // Returns `true` if the rectangle contains the given one.
\r 
1091         // @method contains(point: Point): Boolean
\r 
1092         // Returns `true` if the rectangle contains the given point.
\r 
1093         contains: function (obj) {
\r 
1096                 if (typeof obj[0] === 'number' || obj instanceof Point) {
\r 
1097                         obj = toPoint(obj);
\r 
1099                         obj = toBounds(obj);
\r 
1102                 if (obj instanceof Bounds) {
\r 
1109                 return (min.x >= this.min.x) &&
\r 
1110                        (max.x <= this.max.x) &&
\r 
1111                        (min.y >= this.min.y) &&
\r 
1112                        (max.y <= this.max.y);
\r 
1115         // @method intersects(otherBounds: Bounds): Boolean
\r 
1116         // Returns `true` if the rectangle intersects the given bounds. Two bounds
\r 
1117         // intersect if they have at least one point in common.
\r 
1118         intersects: function (bounds) { // (Bounds) -> Boolean
\r 
1119                 bounds = toBounds(bounds);
\r 
1121                 var min = this.min,
\r 
1123                     min2 = bounds.min,
\r 
1124                     max2 = bounds.max,
\r 
1125                     xIntersects = (max2.x >= min.x) && (min2.x <= max.x),
\r 
1126                     yIntersects = (max2.y >= min.y) && (min2.y <= max.y);
\r 
1128                 return xIntersects && yIntersects;
\r 
1131         // @method overlaps(otherBounds: Bounds): Boolean
\r 
1132         // Returns `true` if the rectangle overlaps the given bounds. Two bounds
\r 
1133         // overlap if their intersection is an area.
\r 
1134         overlaps: function (bounds) { // (Bounds) -> Boolean
\r 
1135                 bounds = toBounds(bounds);
\r 
1137                 var min = this.min,
\r 
1139                     min2 = bounds.min,
\r 
1140                     max2 = bounds.max,
\r 
1141                     xOverlaps = (max2.x > min.x) && (min2.x < max.x),
\r 
1142                     yOverlaps = (max2.y > min.y) && (min2.y < max.y);
\r 
1144                 return xOverlaps && yOverlaps;
\r 
1147         // @method isValid(): Boolean
\r 
1148         // Returns `true` if the bounds are properly initialized.
\r 
1149         isValid: function () {
\r 
1150                 return !!(this.min && this.max);
\r 
1154         // @method pad(bufferRatio: Number): Bounds
\r 
1155         // Returns bounds created by extending or retracting the current bounds by a given ratio in each direction.
\r 
1156         // For example, a ratio of 0.5 extends the bounds by 50% in each direction.
\r 
1157         // Negative values will retract the bounds.
\r 
1158         pad: function (bufferRatio) {
\r 
1159                 var min = this.min,
\r 
1161                 heightBuffer = Math.abs(min.x - max.x) * bufferRatio,
\r 
1162                 widthBuffer = Math.abs(min.y - max.y) * bufferRatio;
\r 
1166                         toPoint(min.x - heightBuffer, min.y - widthBuffer),
\r 
1167                         toPoint(max.x + heightBuffer, max.y + widthBuffer));
\r 
1171         // @method equals(otherBounds: Bounds): Boolean
\r 
1172         // Returns `true` if the rectangle is equivalent to the given bounds.
\r 
1173         equals: function (bounds) {
\r 
1174                 if (!bounds) { return false; }
\r 
1176                 bounds = toBounds(bounds);
\r 
1178                 return this.min.equals(bounds.getTopLeft()) &&
\r 
1179                         this.max.equals(bounds.getBottomRight());
\r 
1184   // @factory L.bounds(corner1: Point, corner2: Point)
\r 
1185   // Creates a Bounds object from two corners coordinate pairs.
\r 
1187   // @factory L.bounds(points: Point[])
\r 
1188   // Creates a Bounds object from the given array of points.
\r 
1189   function toBounds(a, b) {
\r 
1190         if (!a || a instanceof Bounds) {
\r 
1193         return new Bounds(a, b);
\r 
1197    * @class LatLngBounds
\r 
1198    * @aka L.LatLngBounds
\r 
1200    * Represents a rectangular geographical area on a map.
\r 
1205    * var corner1 = L.latLng(40.712, -74.227),
\r 
1206    * corner2 = L.latLng(40.774, -74.125),
\r 
1207    * bounds = L.latLngBounds(corner1, corner2);
\r 
1210    * 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:
\r 
1214    *    [40.712, -74.227],
\r 
1215    *    [40.774, -74.125]
\r 
1219    * 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.
\r 
1221    * Note that `LatLngBounds` does not inherit from Leaflet's `Class` object,
\r 
1222    * which means new classes can't inherit from it, and new methods
\r 
1223    * can't be added to it with the `include` function.
\r 
1226   function LatLngBounds(corner1, corner2) { // (LatLng, LatLng) or (LatLng[])
\r 
1227         if (!corner1) { return; }
\r 
1229         var latlngs = corner2 ? [corner1, corner2] : corner1;
\r 
1231         for (var i = 0, len = latlngs.length; i < len; i++) {
\r 
1232                 this.extend(latlngs[i]);
\r 
1236   LatLngBounds.prototype = {
\r 
1238         // @method extend(latlng: LatLng): this
\r 
1239         // Extend the bounds to contain the given point
\r 
1242         // @method extend(otherBounds: LatLngBounds): this
\r 
1243         // Extend the bounds to contain the given bounds
\r 
1244         extend: function (obj) {
\r 
1245                 var sw = this._southWest,
\r 
1246                     ne = this._northEast,
\r 
1249                 if (obj instanceof LatLng) {
\r 
1253                 } else if (obj instanceof LatLngBounds) {
\r 
1254                         sw2 = obj._southWest;
\r 
1255                         ne2 = obj._northEast;
\r 
1257                         if (!sw2 || !ne2) { return this; }
\r 
1260                         return obj ? this.extend(toLatLng(obj) || toLatLngBounds(obj)) : this;
\r 
1264                         this._southWest = new LatLng(sw2.lat, sw2.lng);
\r 
1265                         this._northEast = new LatLng(ne2.lat, ne2.lng);
\r 
1267                         sw.lat = Math.min(sw2.lat, sw.lat);
\r 
1268                         sw.lng = Math.min(sw2.lng, sw.lng);
\r 
1269                         ne.lat = Math.max(ne2.lat, ne.lat);
\r 
1270                         ne.lng = Math.max(ne2.lng, ne.lng);
\r 
1276         // @method pad(bufferRatio: Number): LatLngBounds
\r 
1277         // Returns bounds created by extending or retracting the current bounds by a given ratio in each direction.
\r 
1278         // For example, a ratio of 0.5 extends the bounds by 50% in each direction.
\r 
1279         // Negative values will retract the bounds.
\r 
1280         pad: function (bufferRatio) {
\r 
1281                 var sw = this._southWest,
\r 
1282                     ne = this._northEast,
\r 
1283                     heightBuffer = Math.abs(sw.lat - ne.lat) * bufferRatio,
\r 
1284                     widthBuffer = Math.abs(sw.lng - ne.lng) * bufferRatio;
\r 
1286                 return new LatLngBounds(
\r 
1287                         new LatLng(sw.lat - heightBuffer, sw.lng - widthBuffer),
\r 
1288                         new LatLng(ne.lat + heightBuffer, ne.lng + widthBuffer));
\r 
1291         // @method getCenter(): LatLng
\r 
1292         // Returns the center point of the bounds.
\r 
1293         getCenter: function () {
\r 
1294                 return new LatLng(
\r 
1295                         (this._southWest.lat + this._northEast.lat) / 2,
\r 
1296                         (this._southWest.lng + this._northEast.lng) / 2);
\r 
1299         // @method getSouthWest(): LatLng
\r 
1300         // Returns the south-west point of the bounds.
\r 
1301         getSouthWest: function () {
\r 
1302                 return this._southWest;
\r 
1305         // @method getNorthEast(): LatLng
\r 
1306         // Returns the north-east point of the bounds.
\r 
1307         getNorthEast: function () {
\r 
1308                 return this._northEast;
\r 
1311         // @method getNorthWest(): LatLng
\r 
1312         // Returns the north-west point of the bounds.
\r 
1313         getNorthWest: function () {
\r 
1314                 return new LatLng(this.getNorth(), this.getWest());
\r 
1317         // @method getSouthEast(): LatLng
\r 
1318         // Returns the south-east point of the bounds.
\r 
1319         getSouthEast: function () {
\r 
1320                 return new LatLng(this.getSouth(), this.getEast());
\r 
1323         // @method getWest(): Number
\r 
1324         // Returns the west longitude of the bounds
\r 
1325         getWest: function () {
\r 
1326                 return this._southWest.lng;
\r 
1329         // @method getSouth(): Number
\r 
1330         // Returns the south latitude of the bounds
\r 
1331         getSouth: function () {
\r 
1332                 return this._southWest.lat;
\r 
1335         // @method getEast(): Number
\r 
1336         // Returns the east longitude of the bounds
\r 
1337         getEast: function () {
\r 
1338                 return this._northEast.lng;
\r 
1341         // @method getNorth(): Number
\r 
1342         // Returns the north latitude of the bounds
\r 
1343         getNorth: function () {
\r 
1344                 return this._northEast.lat;
\r 
1347         // @method contains(otherBounds: LatLngBounds): Boolean
\r 
1348         // Returns `true` if the rectangle contains the given one.
\r 
1351         // @method contains (latlng: LatLng): Boolean
\r 
1352         // Returns `true` if the rectangle contains the given point.
\r 
1353         contains: function (obj) { // (LatLngBounds) or (LatLng) -> Boolean
\r 
1354                 if (typeof obj[0] === 'number' || obj instanceof LatLng || 'lat' in obj) {
\r 
1355                         obj = toLatLng(obj);
\r 
1357                         obj = toLatLngBounds(obj);
\r 
1360                 var sw = this._southWest,
\r 
1361                     ne = this._northEast,
\r 
1364                 if (obj instanceof LatLngBounds) {
\r 
1365                         sw2 = obj.getSouthWest();
\r 
1366                         ne2 = obj.getNorthEast();
\r 
1371                 return (sw2.lat >= sw.lat) && (ne2.lat <= ne.lat) &&
\r 
1372                        (sw2.lng >= sw.lng) && (ne2.lng <= ne.lng);
\r 
1375         // @method intersects(otherBounds: LatLngBounds): Boolean
\r 
1376         // Returns `true` if the rectangle intersects the given bounds. Two bounds intersect if they have at least one point in common.
\r 
1377         intersects: function (bounds) {
\r 
1378                 bounds = toLatLngBounds(bounds);
\r 
1380                 var sw = this._southWest,
\r 
1381                     ne = this._northEast,
\r 
1382                     sw2 = bounds.getSouthWest(),
\r 
1383                     ne2 = bounds.getNorthEast(),
\r 
1385                     latIntersects = (ne2.lat >= sw.lat) && (sw2.lat <= ne.lat),
\r 
1386                     lngIntersects = (ne2.lng >= sw.lng) && (sw2.lng <= ne.lng);
\r 
1388                 return latIntersects && lngIntersects;
\r 
1391         // @method overlaps(otherBounds: LatLngBounds): Boolean
\r 
1392         // Returns `true` if the rectangle overlaps the given bounds. Two bounds overlap if their intersection is an area.
\r 
1393         overlaps: function (bounds) {
\r 
1394                 bounds = toLatLngBounds(bounds);
\r 
1396                 var sw = this._southWest,
\r 
1397                     ne = this._northEast,
\r 
1398                     sw2 = bounds.getSouthWest(),
\r 
1399                     ne2 = bounds.getNorthEast(),
\r 
1401                     latOverlaps = (ne2.lat > sw.lat) && (sw2.lat < ne.lat),
\r 
1402                     lngOverlaps = (ne2.lng > sw.lng) && (sw2.lng < ne.lng);
\r 
1404                 return latOverlaps && lngOverlaps;
\r 
1407         // @method toBBoxString(): String
\r 
1408         // 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.
\r 
1409         toBBoxString: function () {
\r 
1410                 return [this.getWest(), this.getSouth(), this.getEast(), this.getNorth()].join(',');
\r 
1413         // @method equals(otherBounds: LatLngBounds, maxMargin?: Number): Boolean
\r 
1414         // 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.
\r 
1415         equals: function (bounds, maxMargin) {
\r 
1416                 if (!bounds) { return false; }
\r 
1418                 bounds = toLatLngBounds(bounds);
\r 
1420                 return this._southWest.equals(bounds.getSouthWest(), maxMargin) &&
\r 
1421                        this._northEast.equals(bounds.getNorthEast(), maxMargin);
\r 
1424         // @method isValid(): Boolean
\r 
1425         // Returns `true` if the bounds are properly initialized.
\r 
1426         isValid: function () {
\r 
1427                 return !!(this._southWest && this._northEast);
\r 
1431   // TODO International date line?
\r 
1433   // @factory L.latLngBounds(corner1: LatLng, corner2: LatLng)
\r 
1434   // Creates a `LatLngBounds` object by defining two diagonally opposite corners of the rectangle.
\r 
1437   // @factory L.latLngBounds(latlngs: LatLng[])
\r 
1438   // 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).
\r 
1439   function toLatLngBounds(a, b) {
\r 
1440         if (a instanceof LatLngBounds) {
\r 
1443         return new LatLngBounds(a, b);
\r 
1449    * Represents a geographical point with a certain latitude and longitude.
\r 
1454    * var latlng = L.latLng(50.5, 30.5);
\r 
1457    * 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:
\r 
1460    * map.panTo([50, 30]);
\r 
1461    * map.panTo({lon: 30, lat: 50});
\r 
1462    * map.panTo({lat: 50, lng: 30});
\r 
1463    * map.panTo(L.latLng(50, 30));
\r 
1466    * Note that `LatLng` does not inherit from Leaflet's `Class` object,
\r 
1467    * which means new classes can't inherit from it, and new methods
\r 
1468    * can't be added to it with the `include` function.
\r 
1471   function LatLng(lat, lng, alt) {
\r 
1472         if (isNaN(lat) || isNaN(lng)) {
\r 
1473                 throw new Error('Invalid LatLng object: (' + lat + ', ' + lng + ')');
\r 
1476         // @property lat: Number
\r 
1477         // Latitude in degrees
\r 
1480         // @property lng: Number
\r 
1481         // Longitude in degrees
\r 
1484         // @property alt: Number
\r 
1485         // Altitude in meters (optional)
\r 
1486         if (alt !== undefined) {
\r 
1491   LatLng.prototype = {
\r 
1492         // @method equals(otherLatLng: LatLng, maxMargin?: Number): Boolean
\r 
1493         // 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.
\r 
1494         equals: function (obj, maxMargin) {
\r 
1495                 if (!obj) { return false; }
\r 
1497                 obj = toLatLng(obj);
\r 
1499                 var margin = Math.max(
\r 
1500                         Math.abs(this.lat - obj.lat),
\r 
1501                         Math.abs(this.lng - obj.lng));
\r 
1503                 return margin <= (maxMargin === undefined ? 1.0E-9 : maxMargin);
\r 
1506         // @method toString(): String
\r 
1507         // Returns a string representation of the point (for debugging purposes).
\r 
1508         toString: function (precision) {
\r 
1509                 return 'LatLng(' +
\r 
1510                         formatNum(this.lat, precision) + ', ' +
\r 
1511                         formatNum(this.lng, precision) + ')';
\r 
1514         // @method distanceTo(otherLatLng: LatLng): Number
\r 
1515         // 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).
\r 
1516         distanceTo: function (other) {
\r 
1517                 return Earth.distance(this, toLatLng(other));
\r 
1520         // @method wrap(): LatLng
\r 
1521         // Returns a new `LatLng` object with the longitude wrapped so it's always between -180 and +180 degrees.
\r 
1522         wrap: function () {
\r 
1523                 return Earth.wrapLatLng(this);
\r 
1526         // @method toBounds(sizeInMeters: Number): LatLngBounds
\r 
1527         // Returns a new `LatLngBounds` object in which each boundary is `sizeInMeters/2` meters apart from the `LatLng`.
\r 
1528         toBounds: function (sizeInMeters) {
\r 
1529                 var latAccuracy = 180 * sizeInMeters / 40075017,
\r 
1530                     lngAccuracy = latAccuracy / Math.cos((Math.PI / 180) * this.lat);
\r 
1532                 return toLatLngBounds(
\r 
1533                         [this.lat - latAccuracy, this.lng - lngAccuracy],
\r 
1534                         [this.lat + latAccuracy, this.lng + lngAccuracy]);
\r 
1537         clone: function () {
\r 
1538                 return new LatLng(this.lat, this.lng, this.alt);
\r 
1544   // @factory L.latLng(latitude: Number, longitude: Number, altitude?: Number): LatLng
\r 
1545   // Creates an object representing a geographical point with the given latitude and longitude (and optionally altitude).
\r 
1548   // @factory L.latLng(coords: Array): LatLng
\r 
1549   // Expects an array of the form `[Number, Number]` or `[Number, Number, Number]` instead.
\r 
1552   // @factory L.latLng(coords: Object): LatLng
\r 
1553   // Expects an plain object of the form `{lat: Number, lng: Number}` or `{lat: Number, lng: Number, alt: Number}` instead.
\r 
1555   function toLatLng(a, b, c) {
\r 
1556         if (a instanceof LatLng) {
\r 
1559         if (isArray(a) && typeof a[0] !== 'object') {
\r 
1560                 if (a.length === 3) {
\r 
1561                         return new LatLng(a[0], a[1], a[2]);
\r 
1563                 if (a.length === 2) {
\r 
1564                         return new LatLng(a[0], a[1]);
\r 
1568         if (a === undefined || a === null) {
\r 
1571         if (typeof a === 'object' && 'lat' in a) {
\r 
1572                 return new LatLng(a.lat, 'lng' in a ? a.lng : a.lon, a.alt);
\r 
1574         if (b === undefined) {
\r 
1577         return new LatLng(a, b, c);
\r 
1583    * Object that defines coordinate reference systems for projecting
\r 
1584    * geographical points into pixel (screen) coordinates and back (and to
\r 
1585    * coordinates in other units for [WMS](https://en.wikipedia.org/wiki/Web_Map_Service) services). See
\r 
1586    * [spatial reference system](https://en.wikipedia.org/wiki/Spatial_reference_system).
\r 
1588    * Leaflet defines the most usual CRSs by default. If you want to use a
\r 
1589    * CRS not defined by default, take a look at the
\r 
1590    * [Proj4Leaflet](https://github.com/kartena/Proj4Leaflet) plugin.
\r 
1592    * Note that the CRS instances do not inherit from Leaflet's `Class` object,
\r 
1593    * and can't be instantiated. Also, new classes can't inherit from them,
\r 
1594    * and methods can't be added to them with the `include` function.
\r 
1598         // @method latLngToPoint(latlng: LatLng, zoom: Number): Point
\r 
1599         // Projects geographical coordinates into pixel coordinates for a given zoom.
\r 
1600         latLngToPoint: function (latlng, zoom) {
\r 
1601                 var projectedPoint = this.projection.project(latlng),
\r 
1602                     scale = this.scale(zoom);
\r 
1604                 return this.transformation._transform(projectedPoint, scale);
\r 
1607         // @method pointToLatLng(point: Point, zoom: Number): LatLng
\r 
1608         // The inverse of `latLngToPoint`. Projects pixel coordinates on a given
\r 
1609         // zoom into geographical coordinates.
\r 
1610         pointToLatLng: function (point, zoom) {
\r 
1611                 var scale = this.scale(zoom),
\r 
1612                     untransformedPoint = this.transformation.untransform(point, scale);
\r 
1614                 return this.projection.unproject(untransformedPoint);
\r 
1617         // @method project(latlng: LatLng): Point
\r 
1618         // Projects geographical coordinates into coordinates in units accepted for
\r 
1619         // this CRS (e.g. meters for EPSG:3857, for passing it to WMS services).
\r 
1620         project: function (latlng) {
\r 
1621                 return this.projection.project(latlng);
\r 
1624         // @method unproject(point: Point): LatLng
\r 
1625         // Given a projected coordinate returns the corresponding LatLng.
\r 
1626         // The inverse of `project`.
\r 
1627         unproject: function (point) {
\r 
1628                 return this.projection.unproject(point);
\r 
1631         // @method scale(zoom: Number): Number
\r 
1632         // Returns the scale used when transforming projected coordinates into
\r 
1633         // pixel coordinates for a particular zoom. For example, it returns
\r 
1634         // `256 * 2^zoom` for Mercator-based CRS.
\r 
1635         scale: function (zoom) {
\r 
1636                 return 256 * Math.pow(2, zoom);
\r 
1639         // @method zoom(scale: Number): Number
\r 
1640         // Inverse of `scale()`, returns the zoom level corresponding to a scale
\r 
1641         // factor of `scale`.
\r 
1642         zoom: function (scale) {
\r 
1643                 return Math.log(scale / 256) / Math.LN2;
\r 
1646         // @method getProjectedBounds(zoom: Number): Bounds
\r 
1647         // Returns the projection's bounds scaled and transformed for the provided `zoom`.
\r 
1648         getProjectedBounds: function (zoom) {
\r 
1649                 if (this.infinite) { return null; }
\r 
1651                 var b = this.projection.bounds,
\r 
1652                     s = this.scale(zoom),
\r 
1653                     min = this.transformation.transform(b.min, s),
\r 
1654                     max = this.transformation.transform(b.max, s);
\r 
1656                 return new Bounds(min, max);
\r 
1659         // @method distance(latlng1: LatLng, latlng2: LatLng): Number
\r 
1660         // Returns the distance between two geographical coordinates.
\r 
1662         // @property code: String
\r 
1663         // Standard code name of the CRS passed into WMS services (e.g. `'EPSG:3857'`)
\r 
1665         // @property wrapLng: Number[]
\r 
1666         // An array of two numbers defining whether the longitude (horizontal) coordinate
\r 
1667         // axis wraps around a given range and how. Defaults to `[-180, 180]` in most
\r 
1668         // geographical CRSs. If `undefined`, the longitude axis does not wrap around.
\r 
1670         // @property wrapLat: Number[]
\r 
1671         // Like `wrapLng`, but for the latitude (vertical) axis.
\r 
1673         // wrapLng: [min, max],
\r 
1674         // wrapLat: [min, max],
\r 
1676         // @property infinite: Boolean
\r 
1677         // If true, the coordinate space will be unbounded (infinite in both axes)
\r 
1680         // @method wrapLatLng(latlng: LatLng): LatLng
\r 
1681         // Returns a `LatLng` where lat and lng has been wrapped according to the
\r 
1682         // CRS's `wrapLat` and `wrapLng` properties, if they are outside the CRS's bounds.
\r 
1683         wrapLatLng: function (latlng) {
\r 
1684                 var lng = this.wrapLng ? wrapNum(latlng.lng, this.wrapLng, true) : latlng.lng,
\r 
1685                     lat = this.wrapLat ? wrapNum(latlng.lat, this.wrapLat, true) : latlng.lat,
\r 
1688                 return new LatLng(lat, lng, alt);
\r 
1691         // @method wrapLatLngBounds(bounds: LatLngBounds): LatLngBounds
\r 
1692         // Returns a `LatLngBounds` with the same size as the given one, ensuring
\r 
1693         // that its center is within the CRS's bounds.
\r 
1694         // Only accepts actual `L.LatLngBounds` instances, not arrays.
\r 
1695         wrapLatLngBounds: function (bounds) {
\r 
1696                 var center = bounds.getCenter(),
\r 
1697                     newCenter = this.wrapLatLng(center),
\r 
1698                     latShift = center.lat - newCenter.lat,
\r 
1699                     lngShift = center.lng - newCenter.lng;
\r 
1701                 if (latShift === 0 && lngShift === 0) {
\r 
1705                 var sw = bounds.getSouthWest(),
\r 
1706                     ne = bounds.getNorthEast(),
\r 
1707                     newSw = new LatLng(sw.lat - latShift, sw.lng - lngShift),
\r 
1708                     newNe = new LatLng(ne.lat - latShift, ne.lng - lngShift);
\r 
1710                 return new LatLngBounds(newSw, newNe);
\r 
1718    * Serves as the base for CRS that are global such that they cover the earth.
 
1719    * Can only be used as the base for other CRS and cannot be used directly,
 
1720    * since it does not have a `code`, `projection` or `transformation`. `distance()` returns
 
1724   var Earth = extend({}, CRS, {
 
1725         wrapLng: [-180, 180],
 
1727         // Mean Earth Radius, as recommended for use by
 
1728         // the International Union of Geodesy and Geophysics,
 
1729         // see https://rosettacode.org/wiki/Haversine_formula
 
1732         // distance between two geographical points using spherical law of cosines approximation
 
1733         distance: function (latlng1, latlng2) {
 
1734                 var rad = Math.PI / 180,
 
1735                     lat1 = latlng1.lat * rad,
 
1736                     lat2 = latlng2.lat * rad,
 
1737                     sinDLat = Math.sin((latlng2.lat - latlng1.lat) * rad / 2),
 
1738                     sinDLon = Math.sin((latlng2.lng - latlng1.lng) * rad / 2),
 
1739                     a = sinDLat * sinDLat + Math.cos(lat1) * Math.cos(lat2) * sinDLon * sinDLon,
 
1740                     c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
 
1746    * @namespace Projection
\r 
1747    * @projection L.Projection.SphericalMercator
\r 
1749    * Spherical Mercator projection — the most common projection for online maps,
\r 
1750    * used by almost all free and commercial tile providers. Assumes that Earth is
\r 
1751    * a sphere. Used by the `EPSG:3857` CRS.
\r 
1754   var earthRadius = 6378137;
\r 
1756   var SphericalMercator = {
\r 
1759         MAX_LATITUDE: 85.0511287798,
\r 
1761         project: function (latlng) {
\r 
1762                 var d = Math.PI / 180,
\r 
1763                     max = this.MAX_LATITUDE,
\r 
1764                     lat = Math.max(Math.min(max, latlng.lat), -max),
\r 
1765                     sin = Math.sin(lat * d);
\r 
1768                         this.R * latlng.lng * d,
\r 
1769                         this.R * Math.log((1 + sin) / (1 - sin)) / 2);
\r 
1772         unproject: function (point) {
\r 
1773                 var d = 180 / Math.PI;
\r 
1775                 return new LatLng(
\r 
1776                         (2 * Math.atan(Math.exp(point.y / this.R)) - (Math.PI / 2)) * d,
\r 
1777                         point.x * d / this.R);
\r 
1780         bounds: (function () {
\r 
1781                 var d = earthRadius * Math.PI;
\r 
1782                 return new Bounds([-d, -d], [d, d]);
\r 
1787    * @class Transformation
\r 
1788    * @aka L.Transformation
\r 
1790    * Represents an affine transformation: a set of coefficients `a`, `b`, `c`, `d`
\r 
1791    * for transforming a point of a form `(x, y)` into `(a*x + b, c*y + d)` and doing
\r 
1792    * the reverse. Used by Leaflet in its projections code.
\r 
1797    * var transformation = L.transformation(2, 5, -1, 10),
\r 
1798    *    p = L.point(1, 2),
\r 
1799    *    p2 = transformation.transform(p), //  L.point(7, 8)
\r 
1800    *    p3 = transformation.untransform(p2); //  L.point(1, 2)
\r 
1805   // factory new L.Transformation(a: Number, b: Number, c: Number, d: Number)
\r 
1806   // Creates a `Transformation` object with the given coefficients.
\r 
1807   function Transformation(a, b, c, d) {
\r 
1809                 // use array properties
\r 
1822   Transformation.prototype = {
\r 
1823         // @method transform(point: Point, scale?: Number): Point
\r 
1824         // Returns a transformed point, optionally multiplied by the given scale.
\r 
1825         // Only accepts actual `L.Point` instances, not arrays.
\r 
1826         transform: function (point, scale) { // (Point, Number) -> Point
\r 
1827                 return this._transform(point.clone(), scale);
\r 
1830         // destructive transform (faster)
\r 
1831         _transform: function (point, scale) {
\r 
1832                 scale = scale || 1;
\r 
1833                 point.x = scale * (this._a * point.x + this._b);
\r 
1834                 point.y = scale * (this._c * point.y + this._d);
\r 
1838         // @method untransform(point: Point, scale?: Number): Point
\r 
1839         // Returns the reverse transformation of the given point, optionally divided
\r 
1840         // by the given scale. Only accepts actual `L.Point` instances, not arrays.
\r 
1841         untransform: function (point, scale) {
\r 
1842                 scale = scale || 1;
\r 
1844                         (point.x / scale - this._b) / this._a,
\r 
1845                         (point.y / scale - this._d) / this._c);
\r 
1849   // factory L.transformation(a: Number, b: Number, c: Number, d: Number)
\r 
1851   // @factory L.transformation(a: Number, b: Number, c: Number, d: Number)
\r 
1852   // Instantiates a Transformation object with the given coefficients.
\r 
1855   // @factory L.transformation(coefficients: Array): Transformation
\r 
1856   // Expects an coefficients array of the form
\r 
1857   // `[a: Number, b: Number, c: Number, d: Number]`.
\r 
1859   function toTransformation(a, b, c, d) {
\r 
1860         return new Transformation(a, b, c, d);
\r 
1865    * @crs L.CRS.EPSG3857
\r 
1867    * The most common CRS for online maps, used by almost all free and commercial
\r 
1868    * tile providers. Uses Spherical Mercator projection. Set in by default in
\r 
1869    * Map's `crs` option.
\r 
1872   var EPSG3857 = extend({}, Earth, {
\r 
1873         code: 'EPSG:3857',
\r 
1874         projection: SphericalMercator,
\r 
1876         transformation: (function () {
\r 
1877                 var scale = 0.5 / (Math.PI * SphericalMercator.R);
\r 
1878                 return toTransformation(scale, 0.5, -scale, 0.5);
\r 
1882   var EPSG900913 = extend({}, EPSG3857, {
\r 
1883         code: 'EPSG:900913'
\r 
1886   // @namespace SVG; @section
 
1887   // There are several static functions which can be called without instantiating L.SVG:
 
1889   // @function create(name: String): SVGElement
 
1890   // Returns a instance of [SVGElement](https://developer.mozilla.org/docs/Web/API/SVGElement),
 
1891   // corresponding to the class name passed. For example, using 'line' will return
 
1892   // an instance of [SVGLineElement](https://developer.mozilla.org/docs/Web/API/SVGLineElement).
 
1893   function svgCreate(name) {
 
1894         return document.createElementNS('http://www.w3.org/2000/svg', name);
 
1897   // @function pointsToPath(rings: Point[], closed: Boolean): String
 
1898   // Generates a SVG path string for multiple rings, with each ring turning
 
1899   // into "M..L..L.." instructions
 
1900   function pointsToPath(rings, closed) {
 
1902         i, j, len, len2, points, p;
 
1904         for (i = 0, len = rings.length; i < len; i++) {
 
1907                 for (j = 0, len2 = points.length; j < len2; j++) {
 
1909                         str += (j ? 'L' : 'M') + p.x + ' ' + p.y;
 
1912                 // closes the ring for polygons; "x" is VML syntax
 
1913                 str += closed ? (Browser.svg ? 'z' : 'x') : '';
 
1916         // SVG complains about empty path strings
 
1917         return str || 'M0 0';
 
1921    * @namespace Browser
\r 
1924    * A namespace with static properties for browser/feature detection used by Leaflet internally.
\r 
1929    * if (L.Browser.ielt9) {
\r 
1930    *   alert('Upgrade your browser, dude!');
\r 
1935   var style = document.documentElement.style;
\r 
1937   // @property ie: Boolean; `true` for all Internet Explorer versions (not Edge).
\r 
1938   var ie = 'ActiveXObject' in window;
\r 
1940   // @property ielt9: Boolean; `true` for Internet Explorer versions less than 9.
\r 
1941   var ielt9 = ie && !document.addEventListener;
\r 
1943   // @property edge: Boolean; `true` for the Edge web browser.
\r 
1944   var edge = 'msLaunchUri' in navigator && !('documentMode' in document);
\r 
1946   // @property webkit: Boolean;
\r 
1947   // `true` for webkit-based browsers like Chrome and Safari (including mobile versions).
\r 
1948   var webkit = userAgentContains('webkit');
\r 
1950   // @property android: Boolean
\r 
1951   // **Deprecated.** `true` for any browser running on an Android platform.
\r 
1952   var android = userAgentContains('android');
\r 
1954   // @property android23: Boolean; **Deprecated.** `true` for browsers running on Android 2 or Android 3.
\r 
1955   var android23 = userAgentContains('android 2') || userAgentContains('android 3');
\r 
1957   /* See https://stackoverflow.com/a/17961266 for details on detecting stock Android */
\r 
1958   var webkitVer = parseInt(/WebKit\/([0-9]+)|$/.exec(navigator.userAgent)[1], 10); // also matches AppleWebKit
\r 
1959   // @property androidStock: Boolean; **Deprecated.** `true` for the Android stock browser (i.e. not Chrome)
\r 
1960   var androidStock = android && userAgentContains('Google') && webkitVer < 537 && !('AudioNode' in window);
\r 
1962   // @property opera: Boolean; `true` for the Opera browser
\r 
1963   var opera = !!window.opera;
\r 
1965   // @property chrome: Boolean; `true` for the Chrome browser.
\r 
1966   var chrome = !edge && userAgentContains('chrome');
\r 
1968   // @property gecko: Boolean; `true` for gecko-based browsers like Firefox.
\r 
1969   var gecko = userAgentContains('gecko') && !webkit && !opera && !ie;
\r 
1971   // @property safari: Boolean; `true` for the Safari browser.
\r 
1972   var safari = !chrome && userAgentContains('safari');
\r 
1974   var phantom = userAgentContains('phantom');
\r 
1976   // @property opera12: Boolean
\r 
1977   // `true` for the Opera browser supporting CSS transforms (version 12 or later).
\r 
1978   var opera12 = 'OTransition' in style;
\r 
1980   // @property win: Boolean; `true` when the browser is running in a Windows platform
\r 
1981   var win = navigator.platform.indexOf('Win') === 0;
\r 
1983   // @property ie3d: Boolean; `true` for all Internet Explorer versions supporting CSS transforms.
\r 
1984   var ie3d = ie && ('transition' in style);
\r 
1986   // @property webkit3d: Boolean; `true` for webkit-based browsers supporting CSS transforms.
\r 
1987   var webkit3d = ('WebKitCSSMatrix' in window) && ('m11' in new window.WebKitCSSMatrix()) && !android23;
\r 
1989   // @property gecko3d: Boolean; `true` for gecko-based browsers supporting CSS transforms.
\r 
1990   var gecko3d = 'MozPerspective' in style;
\r 
1992   // @property any3d: Boolean
\r 
1993   // `true` for all browsers supporting CSS transforms.
\r 
1994   var any3d = !window.L_DISABLE_3D && (ie3d || webkit3d || gecko3d) && !opera12 && !phantom;
\r 
1996   // @property mobile: Boolean; `true` for all browsers running in a mobile device.
\r 
1997   var mobile = typeof orientation !== 'undefined' || userAgentContains('mobile');
\r 
1999   // @property mobileWebkit: Boolean; `true` for all webkit-based browsers in a mobile device.
\r 
2000   var mobileWebkit = mobile && webkit;
\r 
2002   // @property mobileWebkit3d: Boolean
\r 
2003   // `true` for all webkit-based browsers in a mobile device supporting CSS transforms.
\r 
2004   var mobileWebkit3d = mobile && webkit3d;
\r 
2006   // @property msPointer: Boolean
\r 
2007   // `true` for browsers implementing the Microsoft touch events model (notably IE10).
\r 
2008   var msPointer = !window.PointerEvent && window.MSPointerEvent;
\r 
2010   // @property pointer: Boolean
\r 
2011   // `true` for all browsers supporting [pointer events](https://msdn.microsoft.com/en-us/library/dn433244%28v=vs.85%29.aspx).
\r 
2012   var pointer = !!(window.PointerEvent || msPointer);
\r 
2014   // @property touchNative: Boolean
\r 
2015   // `true` for all browsers supporting [touch events](https://developer.mozilla.org/docs/Web/API/Touch_events).
\r 
2016   // **This does not necessarily mean** that the browser is running in a computer with
\r 
2017   // a touchscreen, it only means that the browser is capable of understanding
\r 
2019   var touchNative = 'ontouchstart' in window || !!window.TouchEvent;
\r 
2021   // @property touch: Boolean
\r 
2022   // `true` for all browsers supporting either [touch](#browser-touch) or [pointer](#browser-pointer) events.
\r 
2023   // Note: pointer events will be preferred (if available), and processed for all `touch*` listeners.
\r 
2024   var touch = !window.L_NO_TOUCH && (touchNative || pointer);
\r 
2026   // @property mobileOpera: Boolean; `true` for the Opera browser in a mobile device.
\r 
2027   var mobileOpera = mobile && opera;
\r 
2029   // @property mobileGecko: Boolean
\r 
2030   // `true` for gecko-based browsers running in a mobile device.
\r 
2031   var mobileGecko = mobile && gecko;
\r 
2033   // @property retina: Boolean
\r 
2034   // `true` for browsers on a high-resolution "retina" screen or on any screen when browser's display zoom is more than 100%.
\r 
2035   var retina = (window.devicePixelRatio || (window.screen.deviceXDPI / window.screen.logicalXDPI)) > 1;
\r 
2037   // @property passiveEvents: Boolean
\r 
2038   // `true` for browsers that support passive events.
\r 
2039   var passiveEvents = (function () {
\r 
2040         var supportsPassiveOption = false;
\r 
2042                 var opts = Object.defineProperty({}, 'passive', {
\r 
2043                         get: function () { // eslint-disable-line getter-return
\r 
2044                                 supportsPassiveOption = true;
\r 
2047                 window.addEventListener('testPassiveEventSupport', falseFn, opts);
\r 
2048                 window.removeEventListener('testPassiveEventSupport', falseFn, opts);
\r 
2050                 // Errors can safely be ignored since this is only a browser support test.
\r 
2052         return supportsPassiveOption;
\r 
2055   // @property canvas: Boolean
\r 
2056   // `true` when the browser supports [`<canvas>`](https://developer.mozilla.org/docs/Web/API/Canvas_API).
\r 
2057   var canvas$1 = (function () {
\r 
2058         return !!document.createElement('canvas').getContext;
\r 
2061   // @property svg: Boolean
\r 
2062   // `true` when the browser supports [SVG](https://developer.mozilla.org/docs/Web/SVG).
\r 
2063   var svg$1 = !!(document.createElementNS && svgCreate('svg').createSVGRect);
\r 
2065   var inlineSvg = !!svg$1 && (function () {
\r 
2066         var div = document.createElement('div');
\r 
2067         div.innerHTML = '<svg/>';
\r 
2068         return (div.firstChild && div.firstChild.namespaceURI) === 'http://www.w3.org/2000/svg';
\r 
2071   // @property vml: Boolean
\r 
2072   // `true` if the browser supports [VML](https://en.wikipedia.org/wiki/Vector_Markup_Language).
\r 
2073   var vml = !svg$1 && (function () {
\r 
2075                 var div = document.createElement('div');
\r 
2076                 div.innerHTML = '<v:shape adj="1"/>';
\r 
2078                 var shape = div.firstChild;
\r 
2079                 shape.style.behavior = 'url(#default#VML)';
\r 
2081                 return shape && (typeof shape.adj === 'object');
\r 
2089   // @property mac: Boolean; `true` when the browser is running in a Mac platform
\r 
2090   var mac = navigator.platform.indexOf('Mac') === 0;
\r 
2092   // @property mac: Boolean; `true` when the browser is running in a Linux platform
\r 
2093   var linux = navigator.platform.indexOf('Linux') === 0;
\r 
2095   function userAgentContains(str) {
\r 
2096         return navigator.userAgent.toLowerCase().indexOf(str) >= 0;
\r 
2106         android23: android23,
\r 
2107         androidStock: androidStock,
\r 
2116         webkit3d: webkit3d,
\r 
2120         mobileWebkit: mobileWebkit,
\r 
2121         mobileWebkit3d: mobileWebkit3d,
\r 
2122         msPointer: msPointer,
\r 
2125         touchNative: touchNative,
\r 
2126         mobileOpera: mobileOpera,
\r 
2127         mobileGecko: mobileGecko,
\r 
2129         passiveEvents: passiveEvents,
\r 
2133         inlineSvg: inlineSvg,
\r 
2139    * Extends L.DomEvent to provide touch support for Internet Explorer and Windows-based devices.
 
2142   var POINTER_DOWN =   Browser.msPointer ? 'MSPointerDown'   : 'pointerdown';
 
2143   var POINTER_MOVE =   Browser.msPointer ? 'MSPointerMove'   : 'pointermove';
 
2144   var POINTER_UP =     Browser.msPointer ? 'MSPointerUp'     : 'pointerup';
 
2145   var POINTER_CANCEL = Browser.msPointer ? 'MSPointerCancel' : 'pointercancel';
 
2147         touchstart  : POINTER_DOWN,
 
2148         touchmove   : POINTER_MOVE,
 
2149         touchend    : POINTER_UP,
 
2150         touchcancel : POINTER_CANCEL
 
2153         touchstart  : _onPointerStart,
 
2154         touchmove   : _handlePointer,
 
2155         touchend    : _handlePointer,
 
2156         touchcancel : _handlePointer
 
2159   var _pointerDocListener = false;
 
2161   // Provides a touch events wrapper for (ms)pointer events.
 
2162   // ref https://www.w3.org/TR/pointerevents/ https://www.w3.org/Bugs/Public/show_bug.cgi?id=22890
 
2164   function addPointerListener(obj, type, handler) {
 
2165         if (type === 'touchstart') {
 
2166                 _addPointerDocListener();
 
2168         if (!handle[type]) {
 
2169                 console.warn('wrong event specified:', type);
 
2172         handler = handle[type].bind(this, handler);
 
2173         obj.addEventListener(pEvent[type], handler, false);
 
2177   function removePointerListener(obj, type, handler) {
 
2178         if (!pEvent[type]) {
 
2179                 console.warn('wrong event specified:', type);
 
2182         obj.removeEventListener(pEvent[type], handler, false);
 
2185   function _globalPointerDown(e) {
 
2186         _pointers[e.pointerId] = e;
 
2189   function _globalPointerMove(e) {
 
2190         if (_pointers[e.pointerId]) {
 
2191                 _pointers[e.pointerId] = e;
 
2195   function _globalPointerUp(e) {
 
2196         delete _pointers[e.pointerId];
 
2199   function _addPointerDocListener() {
 
2200         // need to keep track of what pointers and how many are active to provide e.touches emulation
 
2201         if (!_pointerDocListener) {
 
2202                 // we listen document as any drags that end by moving the touch off the screen get fired there
 
2203                 document.addEventListener(POINTER_DOWN, _globalPointerDown, true);
 
2204                 document.addEventListener(POINTER_MOVE, _globalPointerMove, true);
 
2205                 document.addEventListener(POINTER_UP, _globalPointerUp, true);
 
2206                 document.addEventListener(POINTER_CANCEL, _globalPointerUp, true);
 
2208                 _pointerDocListener = true;
 
2212   function _handlePointer(handler, e) {
 
2213         if (e.pointerType === (e.MSPOINTER_TYPE_MOUSE || 'mouse')) { return; }
 
2216         for (var i in _pointers) {
 
2217                 e.touches.push(_pointers[i]);
 
2219         e.changedTouches = [e];
 
2224   function _onPointerStart(handler, e) {
 
2225         // IE10 specific: MsTouch needs preventDefault. See #2000
 
2226         if (e.MSPOINTER_TYPE_TOUCH && e.pointerType === e.MSPOINTER_TYPE_TOUCH) {
 
2229         _handlePointer(handler, e);
 
2233    * Extends the event handling code with double tap support for mobile browsers.
\r 
2235    * Note: currently most browsers fire native dblclick, with only a few exceptions
\r 
2236    * (see https://github.com/Leaflet/Leaflet/issues/7012#issuecomment-595087386)
\r 
2239   function makeDblclick(event) {
\r 
2240         // in modern browsers `type` cannot be just overridden:
\r 
2241         // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Errors/Getter_only
\r 
2242         var newEvent = {},
\r 
2244         for (i in event) {
\r 
2246                 newEvent[i] = prop && prop.bind ? prop.bind(event) : prop;
\r 
2249         newEvent.type = 'dblclick';
\r 
2250         newEvent.detail = 2;
\r 
2251         newEvent.isTrusted = false;
\r 
2252         newEvent._simulated = true; // for debug purposes
\r 
2257   function addDoubleTapListener(obj, handler) {
\r 
2258         // Most browsers handle double tap natively
\r 
2259         obj.addEventListener('dblclick', handler);
\r 
2261         // On some platforms the browser doesn't fire native dblclicks for touch events.
\r 
2262         // It seems that in all such cases `detail` property of `click` event is always `1`.
\r 
2263         // So here we rely on that fact to avoid excessive 'dblclick' simulation when not needed.
\r 
2266         function simDblclick(e) {
\r 
2267                 if (e.detail !== 1) {
\r 
2268                         detail = e.detail; // keep in sync to avoid false dblclick in some cases
\r 
2272                 if (e.pointerType === 'mouse' ||
\r 
2273                         (e.sourceCapabilities && !e.sourceCapabilities.firesTouchEvents)) {
\r 
2278                 // When clicking on an <input>, the browser generates a click on its
\r 
2279                 // <label> (and vice versa) triggering two clicks in quick succession.
\r 
2280                 // This ignores clicks on elements which are a label with a 'for'
\r 
2281                 // attribute (or children of such a label), but not children of
\r 
2283                 var path = getPropagationPath(e);
\r 
2284                 if (path.some(function (el) {
\r 
2285                         return el instanceof HTMLLabelElement && el.attributes.for;
\r 
2287                         !path.some(function (el) {
\r 
2289                                         el instanceof HTMLInputElement ||
\r 
2290                                         el instanceof HTMLSelectElement
\r 
2297                 var now = Date.now();
\r 
2298                 if (now - last <= delay) {
\r 
2300                         if (detail === 2) {
\r 
2301                                 handler(makeDblclick(e));
\r 
2309         obj.addEventListener('click', simDblclick);
\r 
2312                 dblclick: handler,
\r 
2313                 simDblclick: simDblclick
\r 
2317   function removeDoubleTapListener(obj, handlers) {
\r 
2318         obj.removeEventListener('dblclick', handlers.dblclick);
\r 
2319         obj.removeEventListener('click', handlers.simDblclick);
\r 
2323    * @namespace DomUtil
\r 
2325    * Utility functions to work with the [DOM](https://developer.mozilla.org/docs/Web/API/Document_Object_Model)
\r 
2326    * tree, used by Leaflet internally.
\r 
2328    * Most functions expecting or returning a `HTMLElement` also work for
\r 
2329    * SVG elements. The only difference is that classes refer to CSS classes
\r 
2330    * in HTML and SVG classes in SVG.
\r 
2334   // @property TRANSFORM: String
\r 
2335   // Vendor-prefixed transform style name (e.g. `'webkitTransform'` for WebKit).
\r 
2336   var TRANSFORM = testProp(
\r 
2337         ['transform', 'webkitTransform', 'OTransform', 'MozTransform', 'msTransform']);
\r 
2339   // webkitTransition comes first because some browser versions that drop vendor prefix don't do
\r 
2340   // the same for the transitionend event, in particular the Android 4.1 stock browser
\r 
2342   // @property TRANSITION: String
\r 
2343   // Vendor-prefixed transition style name.
\r 
2344   var TRANSITION = testProp(
\r 
2345         ['webkitTransition', 'transition', 'OTransition', 'MozTransition', 'msTransition']);
\r 
2347   // @property TRANSITION_END: String
\r 
2348   // Vendor-prefixed transitionend event name.
\r 
2349   var TRANSITION_END =
\r 
2350         TRANSITION === 'webkitTransition' || TRANSITION === 'OTransition' ? TRANSITION + 'End' : 'transitionend';
\r 
2353   // @function get(id: String|HTMLElement): HTMLElement
\r 
2354   // Returns an element given its DOM id, or returns the element itself
\r 
2355   // if it was passed directly.
\r 
2356   function get(id) {
\r 
2357         return typeof id === 'string' ? document.getElementById(id) : id;
\r 
2360   // @function getStyle(el: HTMLElement, styleAttrib: String): String
\r 
2361   // Returns the value for a certain style attribute on an element,
\r 
2362   // including computed values or values set through CSS.
\r 
2363   function getStyle(el, style) {
\r 
2364         var value = el.style[style] || (el.currentStyle && el.currentStyle[style]);
\r 
2366         if ((!value || value === 'auto') && document.defaultView) {
\r 
2367                 var css = document.defaultView.getComputedStyle(el, null);
\r 
2368                 value = css ? css[style] : null;
\r 
2370         return value === 'auto' ? null : value;
\r 
2373   // @function create(tagName: String, className?: String, container?: HTMLElement): HTMLElement
\r 
2374   // Creates an HTML element with `tagName`, sets its class to `className`, and optionally appends it to `container` element.
\r 
2375   function create$1(tagName, className, container) {
\r 
2376         var el = document.createElement(tagName);
\r 
2377         el.className = className || '';
\r 
2380                 container.appendChild(el);
\r 
2385   // @function remove(el: HTMLElement)
\r 
2386   // Removes `el` from its parent element
\r 
2387   function remove(el) {
\r 
2388         var parent = el.parentNode;
\r 
2390                 parent.removeChild(el);
\r 
2394   // @function empty(el: HTMLElement)
\r 
2395   // Removes all of `el`'s children elements from `el`
\r 
2396   function empty(el) {
\r 
2397         while (el.firstChild) {
\r 
2398                 el.removeChild(el.firstChild);
\r 
2402   // @function toFront(el: HTMLElement)
\r 
2403   // Makes `el` the last child of its parent, so it renders in front of the other children.
\r 
2404   function toFront(el) {
\r 
2405         var parent = el.parentNode;
\r 
2406         if (parent && parent.lastChild !== el) {
\r 
2407                 parent.appendChild(el);
\r 
2411   // @function toBack(el: HTMLElement)
\r 
2412   // Makes `el` the first child of its parent, so it renders behind the other children.
\r 
2413   function toBack(el) {
\r 
2414         var parent = el.parentNode;
\r 
2415         if (parent && parent.firstChild !== el) {
\r 
2416                 parent.insertBefore(el, parent.firstChild);
\r 
2420   // @function hasClass(el: HTMLElement, name: String): Boolean
\r 
2421   // Returns `true` if the element's class attribute contains `name`.
\r 
2422   function hasClass(el, name) {
\r 
2423         if (el.classList !== undefined) {
\r 
2424                 return el.classList.contains(name);
\r 
2426         var className = getClass(el);
\r 
2427         return className.length > 0 && new RegExp('(^|\\s)' + name + '(\\s|$)').test(className);
\r 
2430   // @function addClass(el: HTMLElement, name: String)
\r 
2431   // Adds `name` to the element's class attribute.
\r 
2432   function addClass(el, name) {
\r 
2433         if (el.classList !== undefined) {
\r 
2434                 var classes = splitWords(name);
\r 
2435                 for (var i = 0, len = classes.length; i < len; i++) {
\r 
2436                         el.classList.add(classes[i]);
\r 
2438         } else if (!hasClass(el, name)) {
\r 
2439                 var className = getClass(el);
\r 
2440                 setClass(el, (className ? className + ' ' : '') + name);
\r 
2444   // @function removeClass(el: HTMLElement, name: String)
\r 
2445   // Removes `name` from the element's class attribute.
\r 
2446   function removeClass(el, name) {
\r 
2447         if (el.classList !== undefined) {
\r 
2448                 el.classList.remove(name);
\r 
2450                 setClass(el, trim((' ' + getClass(el) + ' ').replace(' ' + name + ' ', ' ')));
\r 
2454   // @function setClass(el: HTMLElement, name: String)
\r 
2455   // Sets the element's class.
\r 
2456   function setClass(el, name) {
\r 
2457         if (el.className.baseVal === undefined) {
\r 
2458                 el.className = name;
\r 
2460                 // in case of SVG element
\r 
2461                 el.className.baseVal = name;
\r 
2465   // @function getClass(el: HTMLElement): String
\r 
2466   // Returns the element's class.
\r 
2467   function getClass(el) {
\r 
2468         // Check if the element is an SVGElementInstance and use the correspondingElement instead
\r 
2469         // (Required for linked SVG elements in IE11.)
\r 
2470         if (el.correspondingElement) {
\r 
2471                 el = el.correspondingElement;
\r 
2473         return el.className.baseVal === undefined ? el.className : el.className.baseVal;
\r 
2476   // @function setOpacity(el: HTMLElement, opacity: Number)
\r 
2477   // Set the opacity of an element (including old IE support).
\r 
2478   // `opacity` must be a number from `0` to `1`.
\r 
2479   function setOpacity(el, value) {
\r 
2480         if ('opacity' in el.style) {
\r 
2481                 el.style.opacity = value;
\r 
2482         } else if ('filter' in el.style) {
\r 
2483                 _setOpacityIE(el, value);
\r 
2487   function _setOpacityIE(el, value) {
\r 
2488         var filter = false,
\r 
2489             filterName = 'DXImageTransform.Microsoft.Alpha';
\r 
2491         // filters collection throws an error if we try to retrieve a filter that doesn't exist
\r 
2493                 filter = el.filters.item(filterName);
\r 
2495                 // don't set opacity to 1 if we haven't already set an opacity,
\r 
2496                 // it isn't needed and breaks transparent pngs.
\r 
2497                 if (value === 1) { return; }
\r 
2500         value = Math.round(value * 100);
\r 
2503                 filter.Enabled = (value !== 100);
\r 
2504                 filter.Opacity = value;
\r 
2506                 el.style.filter += ' progid:' + filterName + '(opacity=' + value + ')';
\r 
2510   // @function testProp(props: String[]): String|false
\r 
2511   // Goes through the array of style names and returns the first name
\r 
2512   // that is a valid style name for an element. If no such name is found,
\r 
2513   // it returns false. Useful for vendor-prefixed styles like `transform`.
\r 
2514   function testProp(props) {
\r 
2515         var style = document.documentElement.style;
\r 
2517         for (var i = 0; i < props.length; i++) {
\r 
2518                 if (props[i] in style) {
\r 
2525   // @function setTransform(el: HTMLElement, offset: Point, scale?: Number)
\r 
2526   // Resets the 3D CSS transform of `el` so it is translated by `offset` pixels
\r 
2527   // and optionally scaled by `scale`. Does not have an effect if the
\r 
2528   // browser doesn't support 3D CSS transforms.
\r 
2529   function setTransform(el, offset, scale) {
\r 
2530         var pos = offset || new Point(0, 0);
\r 
2532         el.style[TRANSFORM] =
\r 
2534                         'translate(' + pos.x + 'px,' + pos.y + 'px)' :
\r 
2535                         'translate3d(' + pos.x + 'px,' + pos.y + 'px,0)') +
\r 
2536                 (scale ? ' scale(' + scale + ')' : '');
\r 
2539   // @function setPosition(el: HTMLElement, position: Point)
\r 
2540   // Sets the position of `el` to coordinates specified by `position`,
\r 
2541   // using CSS translate or top/left positioning depending on the browser
\r 
2542   // (used by Leaflet internally to position its layers).
\r 
2543   function setPosition(el, point) {
\r 
2545         /*eslint-disable */
\r 
2546         el._leaflet_pos = point;
\r 
2547         /* eslint-enable */
\r 
2549         if (Browser.any3d) {
\r 
2550                 setTransform(el, point);
\r 
2552                 el.style.left = point.x + 'px';
\r 
2553                 el.style.top = point.y + 'px';
\r 
2557   // @function getPosition(el: HTMLElement): Point
\r 
2558   // Returns the coordinates of an element previously positioned with setPosition.
\r 
2559   function getPosition(el) {
\r 
2560         // this method is only used for elements previously positioned using setPosition,
\r 
2561         // so it's safe to cache the position for performance
\r 
2563         return el._leaflet_pos || new Point(0, 0);
\r 
2566   // @function disableTextSelection()
\r 
2567   // Prevents the user from generating `selectstart` DOM events, usually generated
\r 
2568   // when the user drags the mouse through a page with text. Used internally
\r 
2569   // by Leaflet to override the behaviour of any click-and-drag interaction on
\r 
2570   // the map. Affects drag interactions on the whole document.
\r 
2572   // @function enableTextSelection()
\r 
2573   // Cancels the effects of a previous [`L.DomUtil.disableTextSelection`](#domutil-disabletextselection).
\r 
2574   var disableTextSelection;
\r 
2575   var enableTextSelection;
\r 
2577   if ('onselectstart' in document) {
\r 
2578         disableTextSelection = function () {
\r 
2579                 on(window, 'selectstart', preventDefault);
\r 
2581         enableTextSelection = function () {
\r 
2582                 off(window, 'selectstart', preventDefault);
\r 
2585         var userSelectProperty = testProp(
\r 
2586                 ['userSelect', 'WebkitUserSelect', 'OUserSelect', 'MozUserSelect', 'msUserSelect']);
\r 
2588         disableTextSelection = function () {
\r 
2589                 if (userSelectProperty) {
\r 
2590                         var style = document.documentElement.style;
\r 
2591                         _userSelect = style[userSelectProperty];
\r 
2592                         style[userSelectProperty] = 'none';
\r 
2595         enableTextSelection = function () {
\r 
2596                 if (userSelectProperty) {
\r 
2597                         document.documentElement.style[userSelectProperty] = _userSelect;
\r 
2598                         _userSelect = undefined;
\r 
2603   // @function disableImageDrag()
\r 
2604   // As [`L.DomUtil.disableTextSelection`](#domutil-disabletextselection), but
\r 
2605   // for `dragstart` DOM events, usually generated when the user drags an image.
\r 
2606   function disableImageDrag() {
\r 
2607         on(window, 'dragstart', preventDefault);
\r 
2610   // @function enableImageDrag()
\r 
2611   // Cancels the effects of a previous [`L.DomUtil.disableImageDrag`](#domutil-disabletextselection).
\r 
2612   function enableImageDrag() {
\r 
2613         off(window, 'dragstart', preventDefault);
\r 
2616   var _outlineElement, _outlineStyle;
\r 
2617   // @function preventOutline(el: HTMLElement)
\r 
2618   // Makes the [outline](https://developer.mozilla.org/docs/Web/CSS/outline)
\r 
2619   // of the element `el` invisible. Used internally by Leaflet to prevent
\r 
2620   // focusable elements from displaying an outline when the user performs a
\r 
2621   // drag interaction on them.
\r 
2622   function preventOutline(element) {
\r 
2623         while (element.tabIndex === -1) {
\r 
2624                 element = element.parentNode;
\r 
2626         if (!element.style) { return; }
\r 
2628         _outlineElement = element;
\r 
2629         _outlineStyle = element.style.outlineStyle;
\r 
2630         element.style.outlineStyle = 'none';
\r 
2631         on(window, 'keydown', restoreOutline);
\r 
2634   // @function restoreOutline()
\r 
2635   // Cancels the effects of a previous [`L.DomUtil.preventOutline`]().
\r 
2636   function restoreOutline() {
\r 
2637         if (!_outlineElement) { return; }
\r 
2638         _outlineElement.style.outlineStyle = _outlineStyle;
\r 
2639         _outlineElement = undefined;
\r 
2640         _outlineStyle = undefined;
\r 
2641         off(window, 'keydown', restoreOutline);
\r 
2644   // @function getSizedParentNode(el: HTMLElement): HTMLElement
\r 
2645   // Finds the closest parent node which size (width and height) is not null.
\r 
2646   function getSizedParentNode(element) {
\r 
2648                 element = element.parentNode;
\r 
2649         } while ((!element.offsetWidth || !element.offsetHeight) && element !== document.body);
\r 
2653   // @function getScale(el: HTMLElement): Object
\r 
2654   // Computes the CSS scale currently applied on the element.
\r 
2655   // Returns an object with `x` and `y` members as horizontal and vertical scales respectively,
\r 
2656   // and `boundingClientRect` as the result of [`getBoundingClientRect()`](https://developer.mozilla.org/en-US/docs/Web/API/Element/getBoundingClientRect).
\r 
2657   function getScale(element) {
\r 
2658         var rect = element.getBoundingClientRect(); // Read-only in old browsers.
\r 
2661                 x: rect.width / element.offsetWidth || 1,
\r 
2662                 y: rect.height / element.offsetHeight || 1,
\r 
2663                 boundingClientRect: rect
\r 
2669     TRANSFORM: TRANSFORM,
 
2670     TRANSITION: TRANSITION,
 
2671     TRANSITION_END: TRANSITION_END,
 
2681     removeClass: removeClass,
 
2684     setOpacity: setOpacity,
 
2686     setTransform: setTransform,
 
2687     setPosition: setPosition,
 
2688     getPosition: getPosition,
 
2689     get disableTextSelection () { return disableTextSelection; },
 
2690     get enableTextSelection () { return enableTextSelection; },
 
2691     disableImageDrag: disableImageDrag,
 
2692     enableImageDrag: enableImageDrag,
 
2693     preventOutline: preventOutline,
 
2694     restoreOutline: restoreOutline,
 
2695     getSizedParentNode: getSizedParentNode,
 
2700    * @namespace DomEvent
\r 
2701    * Utility functions to work with the [DOM events](https://developer.mozilla.org/docs/Web/API/Event), used by Leaflet internally.
\r 
2704   // Inspired by John Resig, Dean Edwards and YUI addEvent implementations.
\r 
2706   // @function on(el: HTMLElement, types: String, fn: Function, context?: Object): this
\r 
2707   // Adds a listener function (`fn`) to a particular DOM event type of the
\r 
2708   // element `el`. You can optionally specify the context of the listener
\r 
2709   // (object the `this` keyword will point to). You can also pass several
\r 
2710   // space-separated types (e.g. `'click dblclick'`).
\r 
2713   // @function on(el: HTMLElement, eventMap: Object, context?: Object): this
\r 
2714   // Adds a set of type/listener pairs, e.g. `{click: onClick, mousemove: onMouseMove}`
\r 
2715   function on(obj, types, fn, context) {
\r 
2717         if (types && typeof types === 'object') {
\r 
2718                 for (var type in types) {
\r 
2719                         addOne(obj, type, types[type], fn);
\r 
2722                 types = splitWords(types);
\r 
2724                 for (var i = 0, len = types.length; i < len; i++) {
\r 
2725                         addOne(obj, types[i], fn, context);
\r 
2732   var eventsKey = '_leaflet_events';
\r 
2734   // @function off(el: HTMLElement, types: String, fn: Function, context?: Object): this
\r 
2735   // Removes a previously added listener function.
\r 
2736   // Note that if you passed a custom context to on, you must pass the same
\r 
2737   // context to `off` in order to remove the listener.
\r 
2740   // @function off(el: HTMLElement, eventMap: Object, context?: Object): this
\r 
2741   // Removes a set of type/listener pairs, e.g. `{click: onClick, mousemove: onMouseMove}`
\r 
2744   // @function off(el: HTMLElement, types: String): this
\r 
2745   // Removes all previously added listeners of given types.
\r 
2748   // @function off(el: HTMLElement): this
\r 
2749   // Removes all previously added listeners from given HTMLElement
\r 
2750   function off(obj, types, fn, context) {
\r 
2752         if (arguments.length === 1) {
\r 
2754                 delete obj[eventsKey];
\r 
2756         } else if (types && typeof types === 'object') {
\r 
2757                 for (var type in types) {
\r 
2758                         removeOne(obj, type, types[type], fn);
\r 
2762                 types = splitWords(types);
\r 
2764                 if (arguments.length === 2) {
\r 
2765                         batchRemove(obj, function (type) {
\r 
2766                                 return indexOf(types, type) !== -1;
\r 
2769                         for (var i = 0, len = types.length; i < len; i++) {
\r 
2770                                 removeOne(obj, types[i], fn, context);
\r 
2778   function batchRemove(obj, filterFn) {
\r 
2779         for (var id in obj[eventsKey]) {
\r 
2780                 var type = id.split(/\d/)[0];
\r 
2781                 if (!filterFn || filterFn(type)) {
\r 
2782                         removeOne(obj, type, null, null, id);
\r 
2787   var mouseSubst = {
\r 
2788         mouseenter: 'mouseover',
\r 
2789         mouseleave: 'mouseout',
\r 
2790         wheel: !('onwheel' in window) && 'mousewheel'
\r 
2793   function addOne(obj, type, fn, context) {
\r 
2794         var id = type + stamp(fn) + (context ? '_' + stamp(context) : '');
\r 
2796         if (obj[eventsKey] && obj[eventsKey][id]) { return this; }
\r 
2798         var handler = function (e) {
\r 
2799                 return fn.call(context || obj, e || window.event);
\r 
2802         var originalHandler = handler;
\r 
2804         if (!Browser.touchNative && Browser.pointer && type.indexOf('touch') === 0) {
\r 
2805                 // Needs DomEvent.Pointer.js
\r 
2806                 handler = addPointerListener(obj, type, handler);
\r 
2808         } else if (Browser.touch && (type === 'dblclick')) {
\r 
2809                 handler = addDoubleTapListener(obj, handler);
\r 
2811         } else if ('addEventListener' in obj) {
\r 
2813                 if (type === 'touchstart' || type === 'touchmove' || type === 'wheel' ||  type === 'mousewheel') {
\r 
2814                         obj.addEventListener(mouseSubst[type] || type, handler, Browser.passiveEvents ? {passive: false} : false);
\r 
2816                 } else if (type === 'mouseenter' || type === 'mouseleave') {
\r 
2817                         handler = function (e) {
\r 
2818                                 e = e || window.event;
\r 
2819                                 if (isExternalTarget(obj, e)) {
\r 
2820                                         originalHandler(e);
\r 
2823                         obj.addEventListener(mouseSubst[type], handler, false);
\r 
2826                         obj.addEventListener(type, originalHandler, false);
\r 
2830                 obj.attachEvent('on' + type, handler);
\r 
2833         obj[eventsKey] = obj[eventsKey] || {};
\r 
2834         obj[eventsKey][id] = handler;
\r 
2837   function removeOne(obj, type, fn, context, id) {
\r 
2838         id = id || type + stamp(fn) + (context ? '_' + stamp(context) : '');
\r 
2839         var handler = obj[eventsKey] && obj[eventsKey][id];
\r 
2841         if (!handler) { return this; }
\r 
2843         if (!Browser.touchNative && Browser.pointer && type.indexOf('touch') === 0) {
\r 
2844                 removePointerListener(obj, type, handler);
\r 
2846         } else if (Browser.touch && (type === 'dblclick')) {
\r 
2847                 removeDoubleTapListener(obj, handler);
\r 
2849         } else if ('removeEventListener' in obj) {
\r 
2851                 obj.removeEventListener(mouseSubst[type] || type, handler, false);
\r 
2854                 obj.detachEvent('on' + type, handler);
\r 
2857         obj[eventsKey][id] = null;
\r 
2860   // @function stopPropagation(ev: DOMEvent): this
\r 
2861   // Stop the given event from propagation to parent elements. Used inside the listener functions:
\r 
2863   // L.DomEvent.on(div, 'click', function (ev) {
\r 
2864   //    L.DomEvent.stopPropagation(ev);
\r 
2867   function stopPropagation(e) {
\r 
2869         if (e.stopPropagation) {
\r 
2870                 e.stopPropagation();
\r 
2871         } else if (e.originalEvent) {  // In case of Leaflet event.
\r 
2872                 e.originalEvent._stopped = true;
\r 
2874                 e.cancelBubble = true;
\r 
2880   // @function disableScrollPropagation(el: HTMLElement): this
\r 
2881   // Adds `stopPropagation` to the element's `'wheel'` events (plus browser variants).
\r 
2882   function disableScrollPropagation(el) {
\r 
2883         addOne(el, 'wheel', stopPropagation);
\r 
2887   // @function disableClickPropagation(el: HTMLElement): this
\r 
2888   // Adds `stopPropagation` to the element's `'click'`, `'dblclick'`, `'contextmenu'`,
\r 
2889   // `'mousedown'` and `'touchstart'` events (plus browser variants).
\r 
2890   function disableClickPropagation(el) {
\r 
2891         on(el, 'mousedown touchstart dblclick contextmenu', stopPropagation);
\r 
2892         el['_leaflet_disable_click'] = true;
\r 
2896   // @function preventDefault(ev: DOMEvent): this
\r 
2897   // Prevents the default action of the DOM Event `ev` from happening (such as
\r 
2898   // following a link in the href of the a element, or doing a POST request
\r 
2899   // with page reload when a `<form>` is submitted).
\r 
2900   // Use it inside listener functions.
\r 
2901   function preventDefault(e) {
\r 
2902         if (e.preventDefault) {
\r 
2903                 e.preventDefault();
\r 
2905                 e.returnValue = false;
\r 
2910   // @function stop(ev: DOMEvent): this
\r 
2911   // Does `stopPropagation` and `preventDefault` at the same time.
\r 
2912   function stop(e) {
\r 
2913         preventDefault(e);
\r 
2914         stopPropagation(e);
\r 
2918   // @function getPropagationPath(ev: DOMEvent): Array
\r 
2919   // Compatibility polyfill for [`Event.composedPath()`](https://developer.mozilla.org/en-US/docs/Web/API/Event/composedPath).
\r 
2920   // Returns an array containing the `HTMLElement`s that the given DOM event
\r 
2921   // should propagate to (if not stopped).
\r 
2922   function getPropagationPath(ev) {
\r 
2923         if (ev.composedPath) {
\r 
2924                 return ev.composedPath();
\r 
2928         var el = ev.target;
\r 
2932                 el = el.parentNode;
\r 
2938   // @function getMousePosition(ev: DOMEvent, container?: HTMLElement): Point
\r 
2939   // Gets normalized mouse position from a DOM event relative to the
\r 
2940   // `container` (border excluded) or to the whole page if not specified.
\r 
2941   function getMousePosition(e, container) {
\r 
2943                 return new Point(e.clientX, e.clientY);
\r 
2946         var scale = getScale(container),
\r 
2947             offset = scale.boundingClientRect; // left and top  values are in page scale (like the event clientX/Y)
\r 
2950                 // offset.left/top values are in page scale (like clientX/Y),
\r 
2951                 // whereas clientLeft/Top (border width) values are the original values (before CSS scale applies).
\r 
2952                 (e.clientX - offset.left) / scale.x - container.clientLeft,
\r 
2953                 (e.clientY - offset.top) / scale.y - container.clientTop
\r 
2958   //  except , Safari and
\r 
2959   // We need double the scroll pixels (see #7403 and #4538) for all Browsers
\r 
2960   // except OSX (Mac) -> 3x, Chrome running on Linux 1x
\r 
2962   var wheelPxFactor =
\r 
2963         (Browser.linux && Browser.chrome) ? window.devicePixelRatio :
\r 
2964         Browser.mac ? window.devicePixelRatio * 3 :
\r 
2965         window.devicePixelRatio > 0 ? 2 * window.devicePixelRatio : 1;
\r 
2966   // @function getWheelDelta(ev: DOMEvent): Number
\r 
2967   // Gets normalized wheel delta from a wheel DOM event, in vertical
\r 
2968   // pixels scrolled (negative if scrolling down).
\r 
2969   // Events from pointing devices without precise scrolling are mapped to
\r 
2970   // a best guess of 60 pixels.
\r 
2971   function getWheelDelta(e) {
\r 
2972         return (Browser.edge) ? e.wheelDeltaY / 2 : // Don't trust window-geometry-based delta
\r 
2973                (e.deltaY && e.deltaMode === 0) ? -e.deltaY / wheelPxFactor : // Pixels
\r 
2974                (e.deltaY && e.deltaMode === 1) ? -e.deltaY * 20 : // Lines
\r 
2975                (e.deltaY && e.deltaMode === 2) ? -e.deltaY * 60 : // Pages
\r 
2976                (e.deltaX || e.deltaZ) ? 0 :     // Skip horizontal/depth wheel events
\r 
2977                e.wheelDelta ? (e.wheelDeltaY || e.wheelDelta) / 2 : // Legacy IE pixels
\r 
2978                (e.detail && Math.abs(e.detail) < 32765) ? -e.detail * 20 : // Legacy Moz lines
\r 
2979                e.detail ? e.detail / -32765 * 60 : // Legacy Moz pages
\r 
2983   // check if element really left/entered the event target (for mouseenter/mouseleave)
\r 
2984   function isExternalTarget(el, e) {
\r 
2986         var related = e.relatedTarget;
\r 
2988         if (!related) { return true; }
\r 
2991                 while (related && (related !== el)) {
\r 
2992                         related = related.parentNode;
\r 
2997         return (related !== el);
\r 
3004     stopPropagation: stopPropagation,
 
3005     disableScrollPropagation: disableScrollPropagation,
 
3006     disableClickPropagation: disableClickPropagation,
 
3007     preventDefault: preventDefault,
 
3009     getPropagationPath: getPropagationPath,
 
3010     getMousePosition: getMousePosition,
 
3011     getWheelDelta: getWheelDelta,
 
3012     isExternalTarget: isExternalTarget,
 
3018    * @class PosAnimation
 
3019    * @aka L.PosAnimation
 
3021    * Used internally for panning animations, utilizing CSS3 Transitions for modern browsers and a timer fallback for IE6-9.
 
3025    * var myPositionMarker = L.marker([48.864716, 2.294694]).addTo(map);
 
3027    * myPositionMarker.on("click", function() {
 
3028    *    var pos = map.latLngToLayerPoint(myPositionMarker.getLatLng());
 
3030    *    var fx = new L.PosAnimation();
 
3032    *    fx.once('end',function() {
 
3034    *            fx.run(myPositionMarker._icon, pos, 0.8);
 
3037    *    fx.run(myPositionMarker._icon, pos, 0.3);
 
3042    * @constructor L.PosAnimation()
 
3043    * Creates a `PosAnimation` object.
 
3047   var PosAnimation = Evented.extend({
 
3049         // @method run(el: HTMLElement, newPos: Point, duration?: Number, easeLinearity?: Number)
 
3050         // Run an animation of a given element to a new position, optionally setting
 
3051         // duration in seconds (`0.25` by default) and easing linearity factor (3rd
 
3052         // argument of the [cubic bezier curve](https://cubic-bezier.com/#0,0,.5,1),
 
3053         // `0.5` by default).
 
3054         run: function (el, newPos, duration, easeLinearity) {
 
3058                 this._inProgress = true;
 
3059                 this._duration = duration || 0.25;
 
3060                 this._easeOutPower = 1 / Math.max(easeLinearity || 0.5, 0.2);
 
3062                 this._startPos = getPosition(el);
 
3063                 this._offset = newPos.subtract(this._startPos);
 
3064                 this._startTime = +new Date();
 
3066                 // @event start: Event
 
3067                 // Fired when the animation starts
 
3074         // Stops the animation (if currently running).
 
3076                 if (!this._inProgress) { return; }
 
3082         _animate: function () {
 
3084                 this._animId = requestAnimFrame(this._animate, this);
 
3088         _step: function (round) {
 
3089                 var elapsed = (+new Date()) - this._startTime,
 
3090                     duration = this._duration * 1000;
 
3092                 if (elapsed < duration) {
 
3093                         this._runFrame(this._easeOut(elapsed / duration), round);
 
3100         _runFrame: function (progress, round) {
 
3101                 var pos = this._startPos.add(this._offset.multiplyBy(progress));
 
3105                 setPosition(this._el, pos);
 
3107                 // @event step: Event
 
3108                 // Fired continuously during the animation.
 
3112         _complete: function () {
 
3113                 cancelAnimFrame(this._animId);
 
3115                 this._inProgress = false;
 
3116                 // @event end: Event
 
3117                 // Fired when the animation ends.
 
3121         _easeOut: function (t) {
 
3122                 return 1 - Math.pow(1 - t, this._easeOutPower);
 
3129    * @inherits Evented
\r 
3131    * The central class of the API — it is used to create a map on a page and manipulate it.
\r 
3136    * // initialize the map on the "map" div with a given center and zoom
\r 
3137    * var map = L.map('map', {
\r 
3138    *    center: [51.505, -0.09],
\r 
3145   var Map = Evented.extend({
\r 
3148                 // @section Map State Options
\r 
3149                 // @option crs: CRS = L.CRS.EPSG3857
\r 
3150                 // The [Coordinate Reference System](#crs) to use. Don't change this if you're not
\r 
3151                 // sure what it means.
\r 
3154                 // @option center: LatLng = undefined
\r 
3155                 // Initial geographic center of the map
\r 
3156                 center: undefined,
\r 
3158                 // @option zoom: Number = undefined
\r 
3159                 // Initial map zoom level
\r 
3162                 // @option minZoom: Number = *
\r 
3163                 // Minimum zoom level of the map.
\r 
3164                 // If not specified and at least one `GridLayer` or `TileLayer` is in the map,
\r 
3165                 // the lowest of their `minZoom` options will be used instead.
\r 
3166                 minZoom: undefined,
\r 
3168                 // @option maxZoom: Number = *
\r 
3169                 // Maximum zoom level of the map.
\r 
3170                 // If not specified and at least one `GridLayer` or `TileLayer` is in the map,
\r 
3171                 // the highest of their `maxZoom` options will be used instead.
\r 
3172                 maxZoom: undefined,
\r 
3174                 // @option layers: Layer[] = []
\r 
3175                 // Array of layers that will be added to the map initially
\r 
3178                 // @option maxBounds: LatLngBounds = null
\r 
3179                 // When this option is set, the map restricts the view to the given
\r 
3180                 // geographical bounds, bouncing the user back if the user tries to pan
\r 
3181                 // outside the view. To set the restriction dynamically, use
\r 
3182                 // [`setMaxBounds`](#map-setmaxbounds) method.
\r 
3183                 maxBounds: undefined,
\r 
3185                 // @option renderer: Renderer = *
\r 
3186                 // The default method for drawing vector layers on the map. `L.SVG`
\r 
3187                 // or `L.Canvas` by default depending on browser support.
\r 
3188                 renderer: undefined,
\r 
3191                 // @section Animation Options
\r 
3192                 // @option zoomAnimation: Boolean = true
\r 
3193                 // Whether the map zoom animation is enabled. By default it's enabled
\r 
3194                 // in all browsers that support CSS3 Transitions except Android.
\r 
3195                 zoomAnimation: true,
\r 
3197                 // @option zoomAnimationThreshold: Number = 4
\r 
3198                 // Won't animate zoom if the zoom difference exceeds this value.
\r 
3199                 zoomAnimationThreshold: 4,
\r 
3201                 // @option fadeAnimation: Boolean = true
\r 
3202                 // Whether the tile fade animation is enabled. By default it's enabled
\r 
3203                 // in all browsers that support CSS3 Transitions except Android.
\r 
3204                 fadeAnimation: true,
\r 
3206                 // @option markerZoomAnimation: Boolean = true
\r 
3207                 // Whether markers animate their zoom with the zoom animation, if disabled
\r 
3208                 // they will disappear for the length of the animation. By default it's
\r 
3209                 // enabled in all browsers that support CSS3 Transitions except Android.
\r 
3210                 markerZoomAnimation: true,
\r 
3212                 // @option transform3DLimit: Number = 2^23
\r 
3213                 // Defines the maximum size of a CSS translation transform. The default
\r 
3214                 // value should not be changed unless a web browser positions layers in
\r 
3215                 // the wrong place after doing a large `panBy`.
\r 
3216                 transform3DLimit: 8388608, // Precision limit of a 32-bit float
\r 
3218                 // @section Interaction Options
\r 
3219                 // @option zoomSnap: Number = 1
\r 
3220                 // Forces the map's zoom level to always be a multiple of this, particularly
\r 
3221                 // right after a [`fitBounds()`](#map-fitbounds) or a pinch-zoom.
\r 
3222                 // By default, the zoom level snaps to the nearest integer; lower values
\r 
3223                 // (e.g. `0.5` or `0.1`) allow for greater granularity. A value of `0`
\r 
3224                 // means the zoom level will not be snapped after `fitBounds` or a pinch-zoom.
\r 
3227                 // @option zoomDelta: Number = 1
\r 
3228                 // Controls how much the map's zoom level will change after a
\r 
3229                 // [`zoomIn()`](#map-zoomin), [`zoomOut()`](#map-zoomout), pressing `+`
\r 
3230                 // or `-` on the keyboard, or using the [zoom controls](#control-zoom).
\r 
3231                 // Values smaller than `1` (e.g. `0.5`) allow for greater granularity.
\r 
3234                 // @option trackResize: Boolean = true
\r 
3235                 // Whether the map automatically handles browser window resize to update itself.
\r 
3239         initialize: function (id, options) { // (HTMLElement or String, Object)
\r 
3240                 options = setOptions(this, options);
\r 
3242                 // Make sure to assign internal flags at the beginning,
\r 
3243                 // to avoid inconsistent state in some edge cases.
\r 
3244                 this._handlers = [];
\r 
3245                 this._layers = {};
\r 
3246                 this._zoomBoundLayers = {};
\r 
3247                 this._sizeChanged = true;
\r 
3249                 this._initContainer(id);
\r 
3250                 this._initLayout();
\r 
3252                 // hack for https://github.com/Leaflet/Leaflet/issues/1980
\r 
3253                 this._onResize = bind(this._onResize, this);
\r 
3255                 this._initEvents();
\r 
3257                 if (options.maxBounds) {
\r 
3258                         this.setMaxBounds(options.maxBounds);
\r 
3261                 if (options.zoom !== undefined) {
\r 
3262                         this._zoom = this._limitZoom(options.zoom);
\r 
3265                 if (options.center && options.zoom !== undefined) {
\r 
3266                         this.setView(toLatLng(options.center), options.zoom, {reset: true});
\r 
3269                 this.callInitHooks();
\r 
3271                 // don't animate on browsers without hardware-accelerated transitions or old Android/Opera
\r 
3272                 this._zoomAnimated = TRANSITION && Browser.any3d && !Browser.mobileOpera &&
\r 
3273                                 this.options.zoomAnimation;
\r 
3275                 // zoom transitions run with the same duration for all layers, so if one of transitionend events
\r 
3276                 // happens after starting zoom animation (propagating to the map pane), we know that it ended globally
\r 
3277                 if (this._zoomAnimated) {
\r 
3278                         this._createAnimProxy();
\r 
3279                         on(this._proxy, TRANSITION_END, this._catchTransitionEnd, this);
\r 
3282                 this._addLayers(this.options.layers);
\r 
3286         // @section Methods for modifying map state
\r 
3288         // @method setView(center: LatLng, zoom: Number, options?: Zoom/pan options): this
\r 
3289         // Sets the view of the map (geographical center and zoom) with the given
\r 
3290         // animation options.
\r 
3291         setView: function (center, zoom, options) {
\r 
3293                 zoom = zoom === undefined ? this._zoom : this._limitZoom(zoom);
\r 
3294                 center = this._limitCenter(toLatLng(center), zoom, this.options.maxBounds);
\r 
3295                 options = options || {};
\r 
3299                 if (this._loaded && !options.reset && options !== true) {
\r 
3301                         if (options.animate !== undefined) {
\r 
3302                                 options.zoom = extend({animate: options.animate}, options.zoom);
\r 
3303                                 options.pan = extend({animate: options.animate, duration: options.duration}, options.pan);
\r 
3306                         // try animating pan or zoom
\r 
3307                         var moved = (this._zoom !== zoom) ?
\r 
3308                                 this._tryAnimatedZoom && this._tryAnimatedZoom(center, zoom, options.zoom) :
\r 
3309                                 this._tryAnimatedPan(center, options.pan);
\r 
3312                                 // prevent resize handler call, the view will refresh after animation anyway
\r 
3313                                 clearTimeout(this._sizeTimer);
\r 
3318                 // animation didn't start, just reset the map view
\r 
3319                 this._resetView(center, zoom, options.pan && options.pan.noMoveStart);
\r 
3324         // @method setZoom(zoom: Number, options?: Zoom/pan options): this
\r 
3325         // Sets the zoom of the map.
\r 
3326         setZoom: function (zoom, options) {
\r 
3327                 if (!this._loaded) {
\r 
3328                         this._zoom = zoom;
\r 
3331                 return this.setView(this.getCenter(), zoom, {zoom: options});
\r 
3334         // @method zoomIn(delta?: Number, options?: Zoom options): this
\r 
3335         // Increases the zoom of the map by `delta` ([`zoomDelta`](#map-zoomdelta) by default).
\r 
3336         zoomIn: function (delta, options) {
\r 
3337                 delta = delta || (Browser.any3d ? this.options.zoomDelta : 1);
\r 
3338                 return this.setZoom(this._zoom + delta, options);
\r 
3341         // @method zoomOut(delta?: Number, options?: Zoom options): this
\r 
3342         // Decreases the zoom of the map by `delta` ([`zoomDelta`](#map-zoomdelta) by default).
\r 
3343         zoomOut: function (delta, options) {
\r 
3344                 delta = delta || (Browser.any3d ? this.options.zoomDelta : 1);
\r 
3345                 return this.setZoom(this._zoom - delta, options);
\r 
3348         // @method setZoomAround(latlng: LatLng, zoom: Number, options: Zoom options): this
\r 
3349         // Zooms the map while keeping a specified geographical point on the map
\r 
3350         // stationary (e.g. used internally for scroll zoom and double-click zoom).
\r 
3352         // @method setZoomAround(offset: Point, zoom: Number, options: Zoom options): this
\r 
3353         // Zooms the map while keeping a specified pixel on the map (relative to the top-left corner) stationary.
\r 
3354         setZoomAround: function (latlng, zoom, options) {
\r 
3355                 var scale = this.getZoomScale(zoom),
\r 
3356                     viewHalf = this.getSize().divideBy(2),
\r 
3357                     containerPoint = latlng instanceof Point ? latlng : this.latLngToContainerPoint(latlng),
\r 
3359                     centerOffset = containerPoint.subtract(viewHalf).multiplyBy(1 - 1 / scale),
\r 
3360                     newCenter = this.containerPointToLatLng(viewHalf.add(centerOffset));
\r 
3362                 return this.setView(newCenter, zoom, {zoom: options});
\r 
3365         _getBoundsCenterZoom: function (bounds, options) {
\r 
3367                 options = options || {};
\r 
3368                 bounds = bounds.getBounds ? bounds.getBounds() : toLatLngBounds(bounds);
\r 
3370                 var paddingTL = toPoint(options.paddingTopLeft || options.padding || [0, 0]),
\r 
3371                     paddingBR = toPoint(options.paddingBottomRight || options.padding || [0, 0]),
\r 
3373                     zoom = this.getBoundsZoom(bounds, false, paddingTL.add(paddingBR));
\r 
3375                 zoom = (typeof options.maxZoom === 'number') ? Math.min(options.maxZoom, zoom) : zoom;
\r 
3377                 if (zoom === Infinity) {
\r 
3379                                 center: bounds.getCenter(),
\r 
3384                 var paddingOffset = paddingBR.subtract(paddingTL).divideBy(2),
\r 
3386                     swPoint = this.project(bounds.getSouthWest(), zoom),
\r 
3387                     nePoint = this.project(bounds.getNorthEast(), zoom),
\r 
3388                     center = this.unproject(swPoint.add(nePoint).divideBy(2).add(paddingOffset), zoom);
\r 
3396         // @method fitBounds(bounds: LatLngBounds, options?: fitBounds options): this
\r 
3397         // Sets a map view that contains the given geographical bounds with the
\r 
3398         // maximum zoom level possible.
\r 
3399         fitBounds: function (bounds, options) {
\r 
3401                 bounds = toLatLngBounds(bounds);
\r 
3403                 if (!bounds.isValid()) {
\r 
3404                         throw new Error('Bounds are not valid.');
\r 
3407                 var target = this._getBoundsCenterZoom(bounds, options);
\r 
3408                 return this.setView(target.center, target.zoom, options);
\r 
3411         // @method fitWorld(options?: fitBounds options): this
\r 
3412         // Sets a map view that mostly contains the whole world with the maximum
\r 
3413         // zoom level possible.
\r 
3414         fitWorld: function (options) {
\r 
3415                 return this.fitBounds([[-90, -180], [90, 180]], options);
\r 
3418         // @method panTo(latlng: LatLng, options?: Pan options): this
\r 
3419         // Pans the map to a given center.
\r 
3420         panTo: function (center, options) { // (LatLng)
\r 
3421                 return this.setView(center, this._zoom, {pan: options});
\r 
3424         // @method panBy(offset: Point, options?: Pan options): this
\r 
3425         // Pans the map by a given number of pixels (animated).
\r 
3426         panBy: function (offset, options) {
\r 
3427                 offset = toPoint(offset).round();
\r 
3428                 options = options || {};
\r 
3430                 if (!offset.x && !offset.y) {
\r 
3431                         return this.fire('moveend');
\r 
3433                 // If we pan too far, Chrome gets issues with tiles
\r 
3434                 // and makes them disappear or appear in the wrong place (slightly offset) #2602
\r 
3435                 if (options.animate !== true && !this.getSize().contains(offset)) {
\r 
3436                         this._resetView(this.unproject(this.project(this.getCenter()).add(offset)), this.getZoom());
\r 
3440                 if (!this._panAnim) {
\r 
3441                         this._panAnim = new PosAnimation();
\r 
3443                         this._panAnim.on({
\r 
3444                                 'step': this._onPanTransitionStep,
\r 
3445                                 'end': this._onPanTransitionEnd
\r 
3449                 // don't fire movestart if animating inertia
\r 
3450                 if (!options.noMoveStart) {
\r 
3451                         this.fire('movestart');
\r 
3454                 // animate pan unless animate: false specified
\r 
3455                 if (options.animate !== false) {
\r 
3456                         addClass(this._mapPane, 'leaflet-pan-anim');
\r 
3458                         var newPos = this._getMapPanePos().subtract(offset).round();
\r 
3459                         this._panAnim.run(this._mapPane, newPos, options.duration || 0.25, options.easeLinearity);
\r 
3461                         this._rawPanBy(offset);
\r 
3462                         this.fire('move').fire('moveend');
\r 
3468         // @method flyTo(latlng: LatLng, zoom?: Number, options?: Zoom/pan options): this
\r 
3469         // Sets the view of the map (geographical center and zoom) performing a smooth
\r 
3470         // pan-zoom animation.
\r 
3471         flyTo: function (targetCenter, targetZoom, options) {
\r 
3473                 options = options || {};
\r 
3474                 if (options.animate === false || !Browser.any3d) {
\r 
3475                         return this.setView(targetCenter, targetZoom, options);
\r 
3480                 var from = this.project(this.getCenter()),
\r 
3481                     to = this.project(targetCenter),
\r 
3482                     size = this.getSize(),
\r 
3483                     startZoom = this._zoom;
\r 
3485                 targetCenter = toLatLng(targetCenter);
\r 
3486                 targetZoom = targetZoom === undefined ? startZoom : targetZoom;
\r 
3488                 var w0 = Math.max(size.x, size.y),
\r 
3489                     w1 = w0 * this.getZoomScale(startZoom, targetZoom),
\r 
3490                     u1 = (to.distanceTo(from)) || 1,
\r 
3495                         var s1 = i ? -1 : 1,
\r 
3497                             t1 = w1 * w1 - w0 * w0 + s1 * rho2 * rho2 * u1 * u1,
\r 
3498                             b1 = 2 * s2 * rho2 * u1,
\r 
3500                             sq = Math.sqrt(b * b + 1) - b;
\r 
3502                             // workaround for floating point precision bug when sq = 0, log = -Infinite,
\r 
3503                             // thus triggering an infinite loop in flyTo
\r 
3504                             var log = sq < 0.000000001 ? -18 : Math.log(sq);
\r 
3509                 function sinh(n) { return (Math.exp(n) - Math.exp(-n)) / 2; }
\r 
3510                 function cosh(n) { return (Math.exp(n) + Math.exp(-n)) / 2; }
\r 
3511                 function tanh(n) { return sinh(n) / cosh(n); }
\r 
3515                 function w(s) { return w0 * (cosh(r0) / cosh(r0 + rho * s)); }
\r 
3516                 function u(s) { return w0 * (cosh(r0) * tanh(r0 + rho * s) - sinh(r0)) / rho2; }
\r 
3518                 function easeOut(t) { return 1 - Math.pow(1 - t, 1.5); }
\r 
3520                 var start = Date.now(),
\r 
3521                     S = (r(1) - r0) / rho,
\r 
3522                     duration = options.duration ? 1000 * options.duration : 1000 * S * 0.8;
\r 
3524                 function frame() {
\r 
3525                         var t = (Date.now() - start) / duration,
\r 
3526                             s = easeOut(t) * S;
\r 
3529                                 this._flyToFrame = requestAnimFrame(frame, this);
\r 
3532                                         this.unproject(from.add(to.subtract(from).multiplyBy(u(s) / u1)), startZoom),
\r 
3533                                         this.getScaleZoom(w0 / w(s), startZoom),
\r 
3538                                         ._move(targetCenter, targetZoom)
\r 
3543                 this._moveStart(true, options.noMoveStart);
\r 
3549         // @method flyToBounds(bounds: LatLngBounds, options?: fitBounds options): this
\r 
3550         // Sets the view of the map with a smooth animation like [`flyTo`](#map-flyto),
\r 
3551         // but takes a bounds parameter like [`fitBounds`](#map-fitbounds).
\r 
3552         flyToBounds: function (bounds, options) {
\r 
3553                 var target = this._getBoundsCenterZoom(bounds, options);
\r 
3554                 return this.flyTo(target.center, target.zoom, options);
\r 
3557         // @method setMaxBounds(bounds: LatLngBounds): this
\r 
3558         // Restricts the map view to the given bounds (see the [maxBounds](#map-maxbounds) option).
\r 
3559         setMaxBounds: function (bounds) {
\r 
3560                 bounds = toLatLngBounds(bounds);
\r 
3562                 if (this.listens('moveend', this._panInsideMaxBounds)) {
\r 
3563                         this.off('moveend', this._panInsideMaxBounds);
\r 
3566                 if (!bounds.isValid()) {
\r 
3567                         this.options.maxBounds = null;
\r 
3571                 this.options.maxBounds = bounds;
\r 
3573                 if (this._loaded) {
\r 
3574                         this._panInsideMaxBounds();
\r 
3577                 return this.on('moveend', this._panInsideMaxBounds);
\r 
3580         // @method setMinZoom(zoom: Number): this
\r 
3581         // Sets the lower limit for the available zoom levels (see the [minZoom](#map-minzoom) option).
\r 
3582         setMinZoom: function (zoom) {
\r 
3583                 var oldZoom = this.options.minZoom;
\r 
3584                 this.options.minZoom = zoom;
\r 
3586                 if (this._loaded && oldZoom !== zoom) {
\r 
3587                         this.fire('zoomlevelschange');
\r 
3589                         if (this.getZoom() < this.options.minZoom) {
\r 
3590                                 return this.setZoom(zoom);
\r 
3597         // @method setMaxZoom(zoom: Number): this
\r 
3598         // Sets the upper limit for the available zoom levels (see the [maxZoom](#map-maxzoom) option).
\r 
3599         setMaxZoom: function (zoom) {
\r 
3600                 var oldZoom = this.options.maxZoom;
\r 
3601                 this.options.maxZoom = zoom;
\r 
3603                 if (this._loaded && oldZoom !== zoom) {
\r 
3604                         this.fire('zoomlevelschange');
\r 
3606                         if (this.getZoom() > this.options.maxZoom) {
\r 
3607                                 return this.setZoom(zoom);
\r 
3614         // @method panInsideBounds(bounds: LatLngBounds, options?: Pan options): this
\r 
3615         // 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.
\r 
3616         panInsideBounds: function (bounds, options) {
\r 
3617                 this._enforcingBounds = true;
\r 
3618                 var center = this.getCenter(),
\r 
3619                     newCenter = this._limitCenter(center, this._zoom, toLatLngBounds(bounds));
\r 
3621                 if (!center.equals(newCenter)) {
\r 
3622                         this.panTo(newCenter, options);
\r 
3625                 this._enforcingBounds = false;
\r 
3629         // @method panInside(latlng: LatLng, options?: padding options): this
\r 
3630         // Pans the map the minimum amount to make the `latlng` visible. Use
\r 
3631         // padding options to fit the display to more restricted bounds.
\r 
3632         // If `latlng` is already within the (optionally padded) display bounds,
\r 
3633         // the map will not be panned.
\r 
3634         panInside: function (latlng, options) {
\r 
3635                 options = options || {};
\r 
3637                 var paddingTL = toPoint(options.paddingTopLeft || options.padding || [0, 0]),
\r 
3638                     paddingBR = toPoint(options.paddingBottomRight || options.padding || [0, 0]),
\r 
3639                     pixelCenter = this.project(this.getCenter()),
\r 
3640                     pixelPoint = this.project(latlng),
\r 
3641                     pixelBounds = this.getPixelBounds(),
\r 
3642                     paddedBounds = toBounds([pixelBounds.min.add(paddingTL), pixelBounds.max.subtract(paddingBR)]),
\r 
3643                     paddedSize = paddedBounds.getSize();
\r 
3645                 if (!paddedBounds.contains(pixelPoint)) {
\r 
3646                         this._enforcingBounds = true;
\r 
3647                         var centerOffset = pixelPoint.subtract(paddedBounds.getCenter());
\r 
3648                         var offset = paddedBounds.extend(pixelPoint).getSize().subtract(paddedSize);
\r 
3649                         pixelCenter.x += centerOffset.x < 0 ? -offset.x : offset.x;
\r 
3650                         pixelCenter.y += centerOffset.y < 0 ? -offset.y : offset.y;
\r 
3651                         this.panTo(this.unproject(pixelCenter), options);
\r 
3652                         this._enforcingBounds = false;
\r 
3657         // @method invalidateSize(options: Zoom/pan options): this
\r 
3658         // Checks if the map container size changed and updates the map if so —
\r 
3659         // call it after you've changed the map size dynamically, also animating
\r 
3660         // pan by default. If `options.pan` is `false`, panning will not occur.
\r 
3661         // If `options.debounceMoveend` is `true`, it will delay `moveend` event so
\r 
3662         // that it doesn't happen often even if the method is called many
\r 
3663         // times in a row.
\r 
3666         // @method invalidateSize(animate: Boolean): this
\r 
3667         // Checks if the map container size changed and updates the map if so —
\r 
3668         // call it after you've changed the map size dynamically, also animating
\r 
3669         // pan by default.
\r 
3670         invalidateSize: function (options) {
\r 
3671                 if (!this._loaded) { return this; }
\r 
3673                 options = extend({
\r 
3676                 }, options === true ? {animate: true} : options);
\r 
3678                 var oldSize = this.getSize();
\r 
3679                 this._sizeChanged = true;
\r 
3680                 this._lastCenter = null;
\r 
3682                 var newSize = this.getSize(),
\r 
3683                     oldCenter = oldSize.divideBy(2).round(),
\r 
3684                     newCenter = newSize.divideBy(2).round(),
\r 
3685                     offset = oldCenter.subtract(newCenter);
\r 
3687                 if (!offset.x && !offset.y) { return this; }
\r 
3689                 if (options.animate && options.pan) {
\r 
3690                         this.panBy(offset);
\r 
3693                         if (options.pan) {
\r 
3694                                 this._rawPanBy(offset);
\r 
3697                         this.fire('move');
\r 
3699                         if (options.debounceMoveend) {
\r 
3700                                 clearTimeout(this._sizeTimer);
\r 
3701                                 this._sizeTimer = setTimeout(bind(this.fire, this, 'moveend'), 200);
\r 
3703                                 this.fire('moveend');
\r 
3707                 // @section Map state change events
\r 
3708                 // @event resize: ResizeEvent
\r 
3709                 // Fired when the map is resized.
\r 
3710                 return this.fire('resize', {
\r 
3716         // @section Methods for modifying map state
\r 
3717         // @method stop(): this
\r 
3718         // Stops the currently running `panTo` or `flyTo` animation, if any.
\r 
3719         stop: function () {
\r 
3720                 this.setZoom(this._limitZoom(this._zoom));
\r 
3721                 if (!this.options.zoomSnap) {
\r 
3722                         this.fire('viewreset');
\r 
3724                 return this._stop();
\r 
3727         // @section Geolocation methods
\r 
3728         // @method locate(options?: Locate options): this
\r 
3729         // Tries to locate the user using the Geolocation API, firing a [`locationfound`](#map-locationfound)
\r 
3730         // event with location data on success or a [`locationerror`](#map-locationerror) event on failure,
\r 
3731         // and optionally sets the map view to the user's location with respect to
\r 
3732         // detection accuracy (or to the world view if geolocation failed).
\r 
3733         // Note that, if your page doesn't use HTTPS, this method will fail in
\r 
3734         // modern browsers ([Chrome 50 and newer](https://sites.google.com/a/chromium.org/dev/Home/chromium-security/deprecating-powerful-features-on-insecure-origins))
\r 
3735         // See `Locate options` for more details.
\r 
3736         locate: function (options) {
\r 
3738                 options = this._locateOptions = extend({
\r 
3742                         // maxZoom: <Number>
\r 
3744                         // enableHighAccuracy: false
\r 
3747                 if (!('geolocation' in navigator)) {
\r 
3748                         this._handleGeolocationError({
\r 
3750                                 message: 'Geolocation not supported.'
\r 
3755                 var onResponse = bind(this._handleGeolocationResponse, this),
\r 
3756                     onError = bind(this._handleGeolocationError, this);
\r 
3758                 if (options.watch) {
\r 
3759                         this._locationWatchId =
\r 
3760                                 navigator.geolocation.watchPosition(onResponse, onError, options);
\r 
3762                         navigator.geolocation.getCurrentPosition(onResponse, onError, options);
\r 
3767         // @method stopLocate(): this
\r 
3768         // Stops watching location previously initiated by `map.locate({watch: true})`
\r 
3769         // and aborts resetting the map view if map.locate was called with
\r 
3770         // `{setView: true}`.
\r 
3771         stopLocate: function () {
\r 
3772                 if (navigator.geolocation && navigator.geolocation.clearWatch) {
\r 
3773                         navigator.geolocation.clearWatch(this._locationWatchId);
\r 
3775                 if (this._locateOptions) {
\r 
3776                         this._locateOptions.setView = false;
\r 
3781         _handleGeolocationError: function (error) {
\r 
3782                 if (!this._container._leaflet_id) { return; }
\r 
3784                 var c = error.code,
\r 
3785                     message = error.message ||
\r 
3786                             (c === 1 ? 'permission denied' :
\r 
3787                             (c === 2 ? 'position unavailable' : 'timeout'));
\r 
3789                 if (this._locateOptions.setView && !this._loaded) {
\r 
3793                 // @section Location events
\r 
3794                 // @event locationerror: ErrorEvent
\r 
3795                 // Fired when geolocation (using the [`locate`](#map-locate) method) failed.
\r 
3796                 this.fire('locationerror', {
\r 
3798                         message: 'Geolocation error: ' + message + '.'
\r 
3802         _handleGeolocationResponse: function (pos) {
\r 
3803                 if (!this._container._leaflet_id) { return; }
\r 
3805                 var lat = pos.coords.latitude,
\r 
3806                     lng = pos.coords.longitude,
\r 
3807                     latlng = new LatLng(lat, lng),
\r 
3808                     bounds = latlng.toBounds(pos.coords.accuracy * 2),
\r 
3809                     options = this._locateOptions;
\r 
3811                 if (options.setView) {
\r 
3812                         var zoom = this.getBoundsZoom(bounds);
\r 
3813                         this.setView(latlng, options.maxZoom ? Math.min(zoom, options.maxZoom) : zoom);
\r 
3819                         timestamp: pos.timestamp
\r 
3822                 for (var i in pos.coords) {
\r 
3823                         if (typeof pos.coords[i] === 'number') {
\r 
3824                                 data[i] = pos.coords[i];
\r 
3828                 // @event locationfound: LocationEvent
\r 
3829                 // Fired when geolocation (using the [`locate`](#map-locate) method)
\r 
3830                 // went successfully.
\r 
3831                 this.fire('locationfound', data);
\r 
3834         // TODO Appropriate docs section?
\r 
3835         // @section Other Methods
\r 
3836         // @method addHandler(name: String, HandlerClass: Function): this
\r 
3837         // Adds a new `Handler` to the map, given its name and constructor function.
\r 
3838         addHandler: function (name, HandlerClass) {
\r 
3839                 if (!HandlerClass) { return this; }
\r 
3841                 var handler = this[name] = new HandlerClass(this);
\r 
3843                 this._handlers.push(handler);
\r 
3845                 if (this.options[name]) {
\r 
3852         // @method remove(): this
\r 
3853         // Destroys the map and clears all related event listeners.
\r 
3854         remove: function () {
\r 
3856                 this._initEvents(true);
\r 
3857                 if (this.options.maxBounds) { this.off('moveend', this._panInsideMaxBounds); }
\r 
3859                 if (this._containerId !== this._container._leaflet_id) {
\r 
3860                         throw new Error('Map container is being reused by another instance');
\r 
3864                         // throws error in IE6-8
\r 
3865                         delete this._container._leaflet_id;
\r 
3866                         delete this._containerId;
\r 
3868                         /*eslint-disable */
\r 
3869                         this._container._leaflet_id = undefined;
\r 
3870                         /* eslint-enable */
\r 
3871                         this._containerId = undefined;
\r 
3874                 if (this._locationWatchId !== undefined) {
\r 
3875                         this.stopLocate();
\r 
3880                 remove(this._mapPane);
\r 
3882                 if (this._clearControlPos) {
\r 
3883                         this._clearControlPos();
\r 
3885                 if (this._resizeRequest) {
\r 
3886                         cancelAnimFrame(this._resizeRequest);
\r 
3887                         this._resizeRequest = null;
\r 
3890                 this._clearHandlers();
\r 
3892                 if (this._loaded) {
\r 
3893                         // @section Map state change events
\r 
3894                         // @event unload: Event
\r 
3895                         // Fired when the map is destroyed with [remove](#map-remove) method.
\r 
3896                         this.fire('unload');
\r 
3900                 for (i in this._layers) {
\r 
3901                         this._layers[i].remove();
\r 
3903                 for (i in this._panes) {
\r 
3904                         remove(this._panes[i]);
\r 
3907                 this._layers = [];
\r 
3909                 delete this._mapPane;
\r 
3910                 delete this._renderer;
\r 
3915         // @section Other Methods
\r 
3916         // @method createPane(name: String, container?: HTMLElement): HTMLElement
\r 
3917         // Creates a new [map pane](#map-pane) with the given name if it doesn't exist already,
\r 
3918         // then returns it. The pane is created as a child of `container`, or
\r 
3919         // as a child of the main map pane if not set.
\r 
3920         createPane: function (name, container) {
\r 
3921                 var className = 'leaflet-pane' + (name ? ' leaflet-' + name.replace('Pane', '') + '-pane' : ''),
\r 
3922                     pane = create$1('div', className, container || this._mapPane);
\r 
3925                         this._panes[name] = pane;
\r 
3930         // @section Methods for Getting Map State
\r 
3932         // @method getCenter(): LatLng
\r 
3933         // Returns the geographical center of the map view
\r 
3934         getCenter: function () {
\r 
3935                 this._checkIfLoaded();
\r 
3937                 if (this._lastCenter && !this._moved()) {
\r 
3938                         return this._lastCenter.clone();
\r 
3940                 return this.layerPointToLatLng(this._getCenterLayerPoint());
\r 
3943         // @method getZoom(): Number
\r 
3944         // Returns the current zoom level of the map view
\r 
3945         getZoom: function () {
\r 
3946                 return this._zoom;
\r 
3949         // @method getBounds(): LatLngBounds
\r 
3950         // Returns the geographical bounds visible in the current map view
\r 
3951         getBounds: function () {
\r 
3952                 var bounds = this.getPixelBounds(),
\r 
3953                     sw = this.unproject(bounds.getBottomLeft()),
\r 
3954                     ne = this.unproject(bounds.getTopRight());
\r 
3956                 return new LatLngBounds(sw, ne);
\r 
3959         // @method getMinZoom(): Number
\r 
3960         // 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.
\r 
3961         getMinZoom: function () {
\r 
3962                 return this.options.minZoom === undefined ? this._layersMinZoom || 0 : this.options.minZoom;
\r 
3965         // @method getMaxZoom(): Number
\r 
3966         // Returns the maximum zoom level of the map (if set in the `maxZoom` option of the map or of any layers).
\r 
3967         getMaxZoom: function () {
\r 
3968                 return this.options.maxZoom === undefined ?
\r 
3969                         (this._layersMaxZoom === undefined ? Infinity : this._layersMaxZoom) :
\r 
3970                         this.options.maxZoom;
\r 
3973         // @method getBoundsZoom(bounds: LatLngBounds, inside?: Boolean, padding?: Point): Number
\r 
3974         // Returns the maximum zoom level on which the given bounds fit to the map
\r 
3975         // view in its entirety. If `inside` (optional) is set to `true`, the method
\r 
3976         // instead returns the minimum zoom level on which the map view fits into
\r 
3977         // the given bounds in its entirety.
\r 
3978         getBoundsZoom: function (bounds, inside, padding) { // (LatLngBounds[, Boolean, Point]) -> Number
\r 
3979                 bounds = toLatLngBounds(bounds);
\r 
3980                 padding = toPoint(padding || [0, 0]);
\r 
3982                 var zoom = this.getZoom() || 0,
\r 
3983                     min = this.getMinZoom(),
\r 
3984                     max = this.getMaxZoom(),
\r 
3985                     nw = bounds.getNorthWest(),
\r 
3986                     se = bounds.getSouthEast(),
\r 
3987                     size = this.getSize().subtract(padding),
\r 
3988                     boundsSize = toBounds(this.project(se, zoom), this.project(nw, zoom)).getSize(),
\r 
3989                     snap = Browser.any3d ? this.options.zoomSnap : 1,
\r 
3990                     scalex = size.x / boundsSize.x,
\r 
3991                     scaley = size.y / boundsSize.y,
\r 
3992                     scale = inside ? Math.max(scalex, scaley) : Math.min(scalex, scaley);
\r 
3994                 zoom = this.getScaleZoom(scale, zoom);
\r 
3997                         zoom = Math.round(zoom / (snap / 100)) * (snap / 100); // don't jump if within 1% of a snap level
\r 
3998                         zoom = inside ? Math.ceil(zoom / snap) * snap : Math.floor(zoom / snap) * snap;
\r 
4001                 return Math.max(min, Math.min(max, zoom));
\r 
4004         // @method getSize(): Point
\r 
4005         // Returns the current size of the map container (in pixels).
\r 
4006         getSize: function () {
\r 
4007                 if (!this._size || this._sizeChanged) {
\r 
4008                         this._size = new Point(
\r 
4009                                 this._container.clientWidth || 0,
\r 
4010                                 this._container.clientHeight || 0);
\r 
4012                         this._sizeChanged = false;
\r 
4014                 return this._size.clone();
\r 
4017         // @method getPixelBounds(): Bounds
\r 
4018         // Returns the bounds of the current map view in projected pixel
\r 
4019         // coordinates (sometimes useful in layer and overlay implementations).
\r 
4020         getPixelBounds: function (center, zoom) {
\r 
4021                 var topLeftPoint = this._getTopLeftPoint(center, zoom);
\r 
4022                 return new Bounds(topLeftPoint, topLeftPoint.add(this.getSize()));
\r 
4025         // TODO: Check semantics - isn't the pixel origin the 0,0 coord relative to
\r 
4026         // the map pane? "left point of the map layer" can be confusing, specially
\r 
4027         // since there can be negative offsets.
\r 
4028         // @method getPixelOrigin(): Point
\r 
4029         // Returns the projected pixel coordinates of the top left point of
\r 
4030         // the map layer (useful in custom layer and overlay implementations).
\r 
4031         getPixelOrigin: function () {
\r 
4032                 this._checkIfLoaded();
\r 
4033                 return this._pixelOrigin;
\r 
4036         // @method getPixelWorldBounds(zoom?: Number): Bounds
\r 
4037         // Returns the world's bounds in pixel coordinates for zoom level `zoom`.
\r 
4038         // If `zoom` is omitted, the map's current zoom level is used.
\r 
4039         getPixelWorldBounds: function (zoom) {
\r 
4040                 return this.options.crs.getProjectedBounds(zoom === undefined ? this.getZoom() : zoom);
\r 
4043         // @section Other Methods
\r 
4045         // @method getPane(pane: String|HTMLElement): HTMLElement
\r 
4046         // Returns a [map pane](#map-pane), given its name or its HTML element (its identity).
\r 
4047         getPane: function (pane) {
\r 
4048                 return typeof pane === 'string' ? this._panes[pane] : pane;
\r 
4051         // @method getPanes(): Object
\r 
4052         // Returns a plain object containing the names of all [panes](#map-pane) as keys and
\r 
4053         // the panes as values.
\r 
4054         getPanes: function () {
\r 
4055                 return this._panes;
\r 
4058         // @method getContainer: HTMLElement
\r 
4059         // Returns the HTML element that contains the map.
\r 
4060         getContainer: function () {
\r 
4061                 return this._container;
\r 
4065         // @section Conversion Methods
\r 
4067         // @method getZoomScale(toZoom: Number, fromZoom: Number): Number
\r 
4068         // Returns the scale factor to be applied to a map transition from zoom level
\r 
4069         // `fromZoom` to `toZoom`. Used internally to help with zoom animations.
\r 
4070         getZoomScale: function (toZoom, fromZoom) {
\r 
4071                 // TODO replace with universal implementation after refactoring projections
\r 
4072                 var crs = this.options.crs;
\r 
4073                 fromZoom = fromZoom === undefined ? this._zoom : fromZoom;
\r 
4074                 return crs.scale(toZoom) / crs.scale(fromZoom);
\r 
4077         // @method getScaleZoom(scale: Number, fromZoom: Number): Number
\r 
4078         // Returns the zoom level that the map would end up at, if it is at `fromZoom`
\r 
4079         // level and everything is scaled by a factor of `scale`. Inverse of
\r 
4080         // [`getZoomScale`](#map-getZoomScale).
\r 
4081         getScaleZoom: function (scale, fromZoom) {
\r 
4082                 var crs = this.options.crs;
\r 
4083                 fromZoom = fromZoom === undefined ? this._zoom : fromZoom;
\r 
4084                 var zoom = crs.zoom(scale * crs.scale(fromZoom));
\r 
4085                 return isNaN(zoom) ? Infinity : zoom;
\r 
4088         // @method project(latlng: LatLng, zoom: Number): Point
\r 
4089         // Projects a geographical coordinate `LatLng` according to the projection
\r 
4090         // of the map's CRS, then scales it according to `zoom` and the CRS's
\r 
4091         // `Transformation`. The result is pixel coordinate relative to
\r 
4092         // the CRS origin.
\r 
4093         project: function (latlng, zoom) {
\r 
4094                 zoom = zoom === undefined ? this._zoom : zoom;
\r 
4095                 return this.options.crs.latLngToPoint(toLatLng(latlng), zoom);
\r 
4098         // @method unproject(point: Point, zoom: Number): LatLng
\r 
4099         // Inverse of [`project`](#map-project).
\r 
4100         unproject: function (point, zoom) {
\r 
4101                 zoom = zoom === undefined ? this._zoom : zoom;
\r 
4102                 return this.options.crs.pointToLatLng(toPoint(point), zoom);
\r 
4105         // @method layerPointToLatLng(point: Point): LatLng
\r 
4106         // Given a pixel coordinate relative to the [origin pixel](#map-getpixelorigin),
\r 
4107         // returns the corresponding geographical coordinate (for the current zoom level).
\r 
4108         layerPointToLatLng: function (point) {
\r 
4109                 var projectedPoint = toPoint(point).add(this.getPixelOrigin());
\r 
4110                 return this.unproject(projectedPoint);
\r 
4113         // @method latLngToLayerPoint(latlng: LatLng): Point
\r 
4114         // Given a geographical coordinate, returns the corresponding pixel coordinate
\r 
4115         // relative to the [origin pixel](#map-getpixelorigin).
\r 
4116         latLngToLayerPoint: function (latlng) {
\r 
4117                 var projectedPoint = this.project(toLatLng(latlng))._round();
\r 
4118                 return projectedPoint._subtract(this.getPixelOrigin());
\r 
4121         // @method wrapLatLng(latlng: LatLng): LatLng
\r 
4122         // Returns a `LatLng` where `lat` and `lng` has been wrapped according to the
\r 
4123         // map's CRS's `wrapLat` and `wrapLng` properties, if they are outside the
\r 
4125         // By default this means longitude is wrapped around the dateline so its
\r 
4126         // value is between -180 and +180 degrees.
\r 
4127         wrapLatLng: function (latlng) {
\r 
4128                 return this.options.crs.wrapLatLng(toLatLng(latlng));
\r 
4131         // @method wrapLatLngBounds(bounds: LatLngBounds): LatLngBounds
\r 
4132         // Returns a `LatLngBounds` with the same size as the given one, ensuring that
\r 
4133         // its center is within the CRS's bounds.
\r 
4134         // By default this means the center longitude is wrapped around the dateline so its
\r 
4135         // value is between -180 and +180 degrees, and the majority of the bounds
\r 
4136         // overlaps the CRS's bounds.
\r 
4137         wrapLatLngBounds: function (latlng) {
\r 
4138                 return this.options.crs.wrapLatLngBounds(toLatLngBounds(latlng));
\r 
4141         // @method distance(latlng1: LatLng, latlng2: LatLng): Number
\r 
4142         // Returns the distance between two geographical coordinates according to
\r 
4143         // the map's CRS. By default this measures distance in meters.
\r 
4144         distance: function (latlng1, latlng2) {
\r 
4145                 return this.options.crs.distance(toLatLng(latlng1), toLatLng(latlng2));
\r 
4148         // @method containerPointToLayerPoint(point: Point): Point
\r 
4149         // Given a pixel coordinate relative to the map container, returns the corresponding
\r 
4150         // pixel coordinate relative to the [origin pixel](#map-getpixelorigin).
\r 
4151         containerPointToLayerPoint: function (point) { // (Point)
\r 
4152                 return toPoint(point).subtract(this._getMapPanePos());
\r 
4155         // @method layerPointToContainerPoint(point: Point): Point
\r 
4156         // Given a pixel coordinate relative to the [origin pixel](#map-getpixelorigin),
\r 
4157         // returns the corresponding pixel coordinate relative to the map container.
\r 
4158         layerPointToContainerPoint: function (point) { // (Point)
\r 
4159                 return toPoint(point).add(this._getMapPanePos());
\r 
4162         // @method containerPointToLatLng(point: Point): LatLng
\r 
4163         // Given a pixel coordinate relative to the map container, returns
\r 
4164         // the corresponding geographical coordinate (for the current zoom level).
\r 
4165         containerPointToLatLng: function (point) {
\r 
4166                 var layerPoint = this.containerPointToLayerPoint(toPoint(point));
\r 
4167                 return this.layerPointToLatLng(layerPoint);
\r 
4170         // @method latLngToContainerPoint(latlng: LatLng): Point
\r 
4171         // Given a geographical coordinate, returns the corresponding pixel coordinate
\r 
4172         // relative to the map container.
\r 
4173         latLngToContainerPoint: function (latlng) {
\r 
4174                 return this.layerPointToContainerPoint(this.latLngToLayerPoint(toLatLng(latlng)));
\r 
4177         // @method mouseEventToContainerPoint(ev: MouseEvent): Point
\r 
4178         // Given a MouseEvent object, returns the pixel coordinate relative to the
\r 
4179         // map container where the event took place.
\r 
4180         mouseEventToContainerPoint: function (e) {
\r 
4181                 return getMousePosition(e, this._container);
\r 
4184         // @method mouseEventToLayerPoint(ev: MouseEvent): Point
\r 
4185         // Given a MouseEvent object, returns the pixel coordinate relative to
\r 
4186         // the [origin pixel](#map-getpixelorigin) where the event took place.
\r 
4187         mouseEventToLayerPoint: function (e) {
\r 
4188                 return this.containerPointToLayerPoint(this.mouseEventToContainerPoint(e));
\r 
4191         // @method mouseEventToLatLng(ev: MouseEvent): LatLng
\r 
4192         // Given a MouseEvent object, returns geographical coordinate where the
\r 
4193         // event took place.
\r 
4194         mouseEventToLatLng: function (e) { // (MouseEvent)
\r 
4195                 return this.layerPointToLatLng(this.mouseEventToLayerPoint(e));
\r 
4199         // map initialization methods
\r 
4201         _initContainer: function (id) {
\r 
4202                 var container = this._container = get(id);
\r 
4205                         throw new Error('Map container not found.');
\r 
4206                 } else if (container._leaflet_id) {
\r 
4207                         throw new Error('Map container is already initialized.');
\r 
4210                 on(container, 'scroll', this._onScroll, this);
\r 
4211                 this._containerId = stamp(container);
\r 
4214         _initLayout: function () {
\r 
4215                 var container = this._container;
\r 
4217                 this._fadeAnimated = this.options.fadeAnimation && Browser.any3d;
\r 
4219                 addClass(container, 'leaflet-container' +
\r 
4220                         (Browser.touch ? ' leaflet-touch' : '') +
\r 
4221                         (Browser.retina ? ' leaflet-retina' : '') +
\r 
4222                         (Browser.ielt9 ? ' leaflet-oldie' : '') +
\r 
4223                         (Browser.safari ? ' leaflet-safari' : '') +
\r 
4224                         (this._fadeAnimated ? ' leaflet-fade-anim' : ''));
\r 
4226                 var position = getStyle(container, 'position');
\r 
4228                 if (position !== 'absolute' && position !== 'relative' && position !== 'fixed' && position !== 'sticky') {
\r 
4229                         container.style.position = 'relative';
\r 
4232                 this._initPanes();
\r 
4234                 if (this._initControlPos) {
\r 
4235                         this._initControlPos();
\r 
4239         _initPanes: function () {
\r 
4240                 var panes = this._panes = {};
\r 
4241                 this._paneRenderers = {};
\r 
4245                 // Panes are DOM elements used to control the ordering of layers on the map. You
\r 
4246                 // can access panes with [`map.getPane`](#map-getpane) or
\r 
4247                 // [`map.getPanes`](#map-getpanes) methods. New panes can be created with the
\r 
4248                 // [`map.createPane`](#map-createpane) method.
\r 
4250                 // Every map has the following default panes that differ only in zIndex.
\r 
4252                 // @pane mapPane: HTMLElement = 'auto'
\r 
4253                 // Pane that contains all other map panes
\r 
4255                 this._mapPane = this.createPane('mapPane', this._container);
\r 
4256                 setPosition(this._mapPane, new Point(0, 0));
\r 
4258                 // @pane tilePane: HTMLElement = 200
\r 
4259                 // Pane for `GridLayer`s and `TileLayer`s
\r 
4260                 this.createPane('tilePane');
\r 
4261                 // @pane overlayPane: HTMLElement = 400
\r 
4262                 // Pane for vectors (`Path`s, like `Polyline`s and `Polygon`s), `ImageOverlay`s and `VideoOverlay`s
\r 
4263                 this.createPane('overlayPane');
\r 
4264                 // @pane shadowPane: HTMLElement = 500
\r 
4265                 // Pane for overlay shadows (e.g. `Marker` shadows)
\r 
4266                 this.createPane('shadowPane');
\r 
4267                 // @pane markerPane: HTMLElement = 600
\r 
4268                 // Pane for `Icon`s of `Marker`s
\r 
4269                 this.createPane('markerPane');
\r 
4270                 // @pane tooltipPane: HTMLElement = 650
\r 
4271                 // Pane for `Tooltip`s.
\r 
4272                 this.createPane('tooltipPane');
\r 
4273                 // @pane popupPane: HTMLElement = 700
\r 
4274                 // Pane for `Popup`s.
\r 
4275                 this.createPane('popupPane');
\r 
4277                 if (!this.options.markerZoomAnimation) {
\r 
4278                         addClass(panes.markerPane, 'leaflet-zoom-hide');
\r 
4279                         addClass(panes.shadowPane, 'leaflet-zoom-hide');
\r 
4284         // private methods that modify map state
\r 
4286         // @section Map state change events
\r 
4287         _resetView: function (center, zoom, noMoveStart) {
\r 
4288                 setPosition(this._mapPane, new Point(0, 0));
\r 
4290                 var loading = !this._loaded;
\r 
4291                 this._loaded = true;
\r 
4292                 zoom = this._limitZoom(zoom);
\r 
4294                 this.fire('viewprereset');
\r 
4296                 var zoomChanged = this._zoom !== zoom;
\r 
4298                         ._moveStart(zoomChanged, noMoveStart)
\r 
4299                         ._move(center, zoom)
\r 
4300                         ._moveEnd(zoomChanged);
\r 
4302                 // @event viewreset: Event
\r 
4303                 // Fired when the map needs to redraw its content (this usually happens
\r 
4304                 // on map zoom or load). Very useful for creating custom overlays.
\r 
4305                 this.fire('viewreset');
\r 
4307                 // @event load: Event
\r 
4308                 // Fired when the map is initialized (when its center and zoom are set
\r 
4309                 // for the first time).
\r 
4311                         this.fire('load');
\r 
4315         _moveStart: function (zoomChanged, noMoveStart) {
\r 
4316                 // @event zoomstart: Event
\r 
4317                 // Fired when the map zoom is about to change (e.g. before zoom animation).
\r 
4318                 // @event movestart: Event
\r 
4319                 // Fired when the view of the map starts changing (e.g. user starts dragging the map).
\r 
4320                 if (zoomChanged) {
\r 
4321                         this.fire('zoomstart');
\r 
4323                 if (!noMoveStart) {
\r 
4324                         this.fire('movestart');
\r 
4329         _move: function (center, zoom, data, supressEvent) {
\r 
4330                 if (zoom === undefined) {
\r 
4331                         zoom = this._zoom;
\r 
4333                 var zoomChanged = this._zoom !== zoom;
\r 
4335                 this._zoom = zoom;
\r 
4336                 this._lastCenter = center;
\r 
4337                 this._pixelOrigin = this._getNewPixelOrigin(center);
\r 
4339                 if (!supressEvent) {
\r 
4340                         // @event zoom: Event
\r 
4341                         // Fired repeatedly during any change in zoom level,
\r 
4342                         // including zoom and fly animations.
\r 
4343                         if (zoomChanged || (data && data.pinch)) {      // Always fire 'zoom' if pinching because #3530
\r 
4344                                 this.fire('zoom', data);
\r 
4347                         // @event move: Event
\r 
4348                         // Fired repeatedly during any movement of the map,
\r 
4349                         // including pan and fly animations.
\r 
4350                         this.fire('move', data);
\r 
4351                 } else if (data && data.pinch) {        // Always fire 'zoom' if pinching because #3530
\r 
4352                         this.fire('zoom', data);
\r 
4357         _moveEnd: function (zoomChanged) {
\r 
4358                 // @event zoomend: Event
\r 
4359                 // Fired when the map zoom changed, after any animations.
\r 
4360                 if (zoomChanged) {
\r 
4361                         this.fire('zoomend');
\r 
4364                 // @event moveend: Event
\r 
4365                 // Fired when the center of the map stops changing
\r 
4366                 // (e.g. user stopped dragging the map or after non-centered zoom).
\r 
4367                 return this.fire('moveend');
\r 
4370         _stop: function () {
\r 
4371                 cancelAnimFrame(this._flyToFrame);
\r 
4372                 if (this._panAnim) {
\r 
4373                         this._panAnim.stop();
\r 
4378         _rawPanBy: function (offset) {
\r 
4379                 setPosition(this._mapPane, this._getMapPanePos().subtract(offset));
\r 
4382         _getZoomSpan: function () {
\r 
4383                 return this.getMaxZoom() - this.getMinZoom();
\r 
4386         _panInsideMaxBounds: function () {
\r 
4387                 if (!this._enforcingBounds) {
\r 
4388                         this.panInsideBounds(this.options.maxBounds);
\r 
4392         _checkIfLoaded: function () {
\r 
4393                 if (!this._loaded) {
\r 
4394                         throw new Error('Set map center and zoom first.');
\r 
4398         // DOM event handling
\r 
4400         // @section Interaction events
\r 
4401         _initEvents: function (remove) {
\r 
4402                 this._targets = {};
\r 
4403                 this._targets[stamp(this._container)] = this;
\r 
4405                 var onOff = remove ? off : on;
\r 
4407                 // @event click: MouseEvent
\r 
4408                 // Fired when the user clicks (or taps) the map.
\r 
4409                 // @event dblclick: MouseEvent
\r 
4410                 // Fired when the user double-clicks (or double-taps) the map.
\r 
4411                 // @event mousedown: MouseEvent
\r 
4412                 // Fired when the user pushes the mouse button on the map.
\r 
4413                 // @event mouseup: MouseEvent
\r 
4414                 // Fired when the user releases the mouse button on the map.
\r 
4415                 // @event mouseover: MouseEvent
\r 
4416                 // Fired when the mouse enters the map.
\r 
4417                 // @event mouseout: MouseEvent
\r 
4418                 // Fired when the mouse leaves the map.
\r 
4419                 // @event mousemove: MouseEvent
\r 
4420                 // Fired while the mouse moves over the map.
\r 
4421                 // @event contextmenu: MouseEvent
\r 
4422                 // Fired when the user pushes the right mouse button on the map, prevents
\r 
4423                 // default browser context menu from showing if there are listeners on
\r 
4424                 // this event. Also fired on mobile when the user holds a single touch
\r 
4425                 // for a second (also called long press).
\r 
4426                 // @event keypress: KeyboardEvent
\r 
4427                 // Fired when the user presses a key from the keyboard that produces a character value while the map is focused.
\r 
4428                 // @event keydown: KeyboardEvent
\r 
4429                 // Fired when the user presses a key from the keyboard while the map is focused. Unlike the `keypress` event,
\r 
4430                 // the `keydown` event is fired for keys that produce a character value and for keys
\r 
4431                 // that do not produce a character value.
\r 
4432                 // @event keyup: KeyboardEvent
\r 
4433                 // Fired when the user releases a key from the keyboard while the map is focused.
\r 
4434                 onOff(this._container, 'click dblclick mousedown mouseup ' +
\r 
4435                         'mouseover mouseout mousemove contextmenu keypress keydown keyup', this._handleDOMEvent, this);
\r 
4437                 if (this.options.trackResize) {
\r 
4438                         onOff(window, 'resize', this._onResize, this);
\r 
4441                 if (Browser.any3d && this.options.transform3DLimit) {
\r 
4442                         (remove ? this.off : this.on).call(this, 'moveend', this._onMoveEnd);
\r 
4446         _onResize: function () {
\r 
4447                 cancelAnimFrame(this._resizeRequest);
\r 
4448                 this._resizeRequest = requestAnimFrame(
\r 
4449                         function () { this.invalidateSize({debounceMoveend: true}); }, this);
\r 
4452         _onScroll: function () {
\r 
4453                 this._container.scrollTop  = 0;
\r 
4454                 this._container.scrollLeft = 0;
\r 
4457         _onMoveEnd: function () {
\r 
4458                 var pos = this._getMapPanePos();
\r 
4459                 if (Math.max(Math.abs(pos.x), Math.abs(pos.y)) >= this.options.transform3DLimit) {
\r 
4460                         // https://bugzilla.mozilla.org/show_bug.cgi?id=1203873 but Webkit also have
\r 
4461                         // a pixel offset on very high values, see: https://jsfiddle.net/dg6r5hhb/
\r 
4462                         this._resetView(this.getCenter(), this.getZoom());
\r 
4466         _findEventTargets: function (e, type) {
\r 
4469                     isHover = type === 'mouseout' || type === 'mouseover',
\r 
4470                     src = e.target || e.srcElement,
\r 
4474                         target = this._targets[stamp(src)];
\r 
4475                         if (target && (type === 'click' || type === 'preclick') && this._draggableMoved(target)) {
\r 
4476                                 // Prevent firing click after you just dragged an object.
\r 
4480                         if (target && target.listens(type, true)) {
\r 
4481                                 if (isHover && !isExternalTarget(src, e)) { break; }
\r 
4482                                 targets.push(target);
\r 
4483                                 if (isHover) { break; }
\r 
4485                         if (src === this._container) { break; }
\r 
4486                         src = src.parentNode;
\r 
4488                 if (!targets.length && !dragging && !isHover && this.listens(type, true)) {
\r 
4494         _isClickDisabled: function (el) {
\r 
4495                 while (el && el !== this._container) {
\r 
4496                         if (el['_leaflet_disable_click']) { return true; }
\r 
4497                         el = el.parentNode;
\r 
4501         _handleDOMEvent: function (e) {
\r 
4502                 var el = (e.target || e.srcElement);
\r 
4503                 if (!this._loaded || el['_leaflet_disable_events'] || e.type === 'click' && this._isClickDisabled(el)) {
\r 
4507                 var type = e.type;
\r 
4509                 if (type === 'mousedown') {
\r 
4510                         // prevents outline when clicking on keyboard-focusable element
\r 
4511                         preventOutline(el);
\r 
4514                 this._fireDOMEvent(e, type);
\r 
4517         _mouseEvents: ['click', 'dblclick', 'mouseover', 'mouseout', 'contextmenu'],
\r 
4519         _fireDOMEvent: function (e, type, canvasTargets) {
\r 
4521                 if (e.type === 'click') {
\r 
4522                         // Fire a synthetic 'preclick' event which propagates up (mainly for closing popups).
\r 
4523                         // @event preclick: MouseEvent
\r 
4524                         // Fired before mouse click on the map (sometimes useful when you
\r 
4525                         // want something to happen on click before any existing click
\r 
4526                         // handlers start running).
\r 
4527                         var synth = extend({}, e);
\r 
4528                         synth.type = 'preclick';
\r 
4529                         this._fireDOMEvent(synth, synth.type, canvasTargets);
\r 
4532                 // Find the layer the event is propagating from and its parents.
\r 
4533                 var targets = this._findEventTargets(e, type);
\r 
4535                 if (canvasTargets) {
\r 
4536                         var filtered = []; // pick only targets with listeners
\r 
4537                         for (var i = 0; i < canvasTargets.length; i++) {
\r 
4538                                 if (canvasTargets[i].listens(type, true)) {
\r 
4539                                         filtered.push(canvasTargets[i]);
\r 
4542                         targets = filtered.concat(targets);
\r 
4545                 if (!targets.length) { return; }
\r 
4547                 if (type === 'contextmenu') {
\r 
4548                         preventDefault(e);
\r 
4551                 var target = targets[0];
\r 
4556                 if (e.type !== 'keypress' && e.type !== 'keydown' && e.type !== 'keyup') {
\r 
4557                         var isMarker = target.getLatLng && (!target._radius || target._radius <= 10);
\r 
4558                         data.containerPoint = isMarker ?
\r 
4559                                 this.latLngToContainerPoint(target.getLatLng()) : this.mouseEventToContainerPoint(e);
\r 
4560                         data.layerPoint = this.containerPointToLayerPoint(data.containerPoint);
\r 
4561                         data.latlng = isMarker ? target.getLatLng() : this.layerPointToLatLng(data.layerPoint);
\r 
4564                 for (i = 0; i < targets.length; i++) {
\r 
4565                         targets[i].fire(type, data, true);
\r 
4566                         if (data.originalEvent._stopped ||
\r 
4567                                 (targets[i].options.bubblingMouseEvents === false && indexOf(this._mouseEvents, type) !== -1)) { return; }
\r 
4571         _draggableMoved: function (obj) {
\r 
4572                 obj = obj.dragging && obj.dragging.enabled() ? obj : this;
\r 
4573                 return (obj.dragging && obj.dragging.moved()) || (this.boxZoom && this.boxZoom.moved());
\r 
4576         _clearHandlers: function () {
\r 
4577                 for (var i = 0, len = this._handlers.length; i < len; i++) {
\r 
4578                         this._handlers[i].disable();
\r 
4582         // @section Other Methods
\r 
4584         // @method whenReady(fn: Function, context?: Object): this
\r 
4585         // Runs the given function `fn` when the map gets initialized with
\r 
4586         // a view (center and zoom) and at least one layer, or immediately
\r 
4587         // if it's already initialized, optionally passing a function context.
\r 
4588         whenReady: function (callback, context) {
\r 
4589                 if (this._loaded) {
\r 
4590                         callback.call(context || this, {target: this});
\r 
4592                         this.on('load', callback, context);
\r 
4598         // private methods for getting map state
\r 
4600         _getMapPanePos: function () {
\r 
4601                 return getPosition(this._mapPane) || new Point(0, 0);
\r 
4604         _moved: function () {
\r 
4605                 var pos = this._getMapPanePos();
\r 
4606                 return pos && !pos.equals([0, 0]);
\r 
4609         _getTopLeftPoint: function (center, zoom) {
\r 
4610                 var pixelOrigin = center && zoom !== undefined ?
\r 
4611                         this._getNewPixelOrigin(center, zoom) :
\r 
4612                         this.getPixelOrigin();
\r 
4613                 return pixelOrigin.subtract(this._getMapPanePos());
\r 
4616         _getNewPixelOrigin: function (center, zoom) {
\r 
4617                 var viewHalf = this.getSize()._divideBy(2);
\r 
4618                 return this.project(center, zoom)._subtract(viewHalf)._add(this._getMapPanePos())._round();
\r 
4621         _latLngToNewLayerPoint: function (latlng, zoom, center) {
\r 
4622                 var topLeft = this._getNewPixelOrigin(center, zoom);
\r 
4623                 return this.project(latlng, zoom)._subtract(topLeft);
\r 
4626         _latLngBoundsToNewLayerBounds: function (latLngBounds, zoom, center) {
\r 
4627                 var topLeft = this._getNewPixelOrigin(center, zoom);
\r 
4629                         this.project(latLngBounds.getSouthWest(), zoom)._subtract(topLeft),
\r 
4630                         this.project(latLngBounds.getNorthWest(), zoom)._subtract(topLeft),
\r 
4631                         this.project(latLngBounds.getSouthEast(), zoom)._subtract(topLeft),
\r 
4632                         this.project(latLngBounds.getNorthEast(), zoom)._subtract(topLeft)
\r 
4636         // layer point of the current center
\r 
4637         _getCenterLayerPoint: function () {
\r 
4638                 return this.containerPointToLayerPoint(this.getSize()._divideBy(2));
\r 
4641         // offset of the specified place to the current center in pixels
\r 
4642         _getCenterOffset: function (latlng) {
\r 
4643                 return this.latLngToLayerPoint(latlng).subtract(this._getCenterLayerPoint());
\r 
4646         // adjust center for view to get inside bounds
\r 
4647         _limitCenter: function (center, zoom, bounds) {
\r 
4649                 if (!bounds) { return center; }
\r 
4651                 var centerPoint = this.project(center, zoom),
\r 
4652                     viewHalf = this.getSize().divideBy(2),
\r 
4653                     viewBounds = new Bounds(centerPoint.subtract(viewHalf), centerPoint.add(viewHalf)),
\r 
4654                     offset = this._getBoundsOffset(viewBounds, bounds, zoom);
\r 
4656                 // If offset is less than a pixel, ignore.
\r 
4657                 // This prevents unstable projections from getting into
\r 
4658                 // an infinite loop of tiny offsets.
\r 
4659                 if (Math.abs(offset.x) <= 1 && Math.abs(offset.y) <= 1) {
\r 
4663                 return this.unproject(centerPoint.add(offset), zoom);
\r 
4666         // adjust offset for view to get inside bounds
\r 
4667         _limitOffset: function (offset, bounds) {
\r 
4668                 if (!bounds) { return offset; }
\r 
4670                 var viewBounds = this.getPixelBounds(),
\r 
4671                     newBounds = new Bounds(viewBounds.min.add(offset), viewBounds.max.add(offset));
\r 
4673                 return offset.add(this._getBoundsOffset(newBounds, bounds));
\r 
4676         // returns offset needed for pxBounds to get inside maxBounds at a specified zoom
\r 
4677         _getBoundsOffset: function (pxBounds, maxBounds, zoom) {
\r 
4678                 var projectedMaxBounds = toBounds(
\r 
4679                         this.project(maxBounds.getNorthEast(), zoom),
\r 
4680                         this.project(maxBounds.getSouthWest(), zoom)
\r 
4682                     minOffset = projectedMaxBounds.min.subtract(pxBounds.min),
\r 
4683                     maxOffset = projectedMaxBounds.max.subtract(pxBounds.max),
\r 
4685                     dx = this._rebound(minOffset.x, -maxOffset.x),
\r 
4686                     dy = this._rebound(minOffset.y, -maxOffset.y);
\r 
4688                 return new Point(dx, dy);
\r 
4691         _rebound: function (left, right) {
\r 
4692                 return left + right > 0 ?
\r 
4693                         Math.round(left - right) / 2 :
\r 
4694                         Math.max(0, Math.ceil(left)) - Math.max(0, Math.floor(right));
\r 
4697         _limitZoom: function (zoom) {
\r 
4698                 var min = this.getMinZoom(),
\r 
4699                     max = this.getMaxZoom(),
\r 
4700                     snap = Browser.any3d ? this.options.zoomSnap : 1;
\r 
4702                         zoom = Math.round(zoom / snap) * snap;
\r 
4704                 return Math.max(min, Math.min(max, zoom));
\r 
4707         _onPanTransitionStep: function () {
\r 
4708                 this.fire('move');
\r 
4711         _onPanTransitionEnd: function () {
\r 
4712                 removeClass(this._mapPane, 'leaflet-pan-anim');
\r 
4713                 this.fire('moveend');
\r 
4716         _tryAnimatedPan: function (center, options) {
\r 
4717                 // difference between the new and current centers in pixels
\r 
4718                 var offset = this._getCenterOffset(center)._trunc();
\r 
4720                 // don't animate too far unless animate: true specified in options
\r 
4721                 if ((options && options.animate) !== true && !this.getSize().contains(offset)) { return false; }
\r 
4723                 this.panBy(offset, options);
\r 
4728         _createAnimProxy: function () {
\r 
4730                 var proxy = this._proxy = create$1('div', 'leaflet-proxy leaflet-zoom-animated');
\r 
4731                 this._panes.mapPane.appendChild(proxy);
\r 
4733                 this.on('zoomanim', function (e) {
\r 
4734                         var prop = TRANSFORM,
\r 
4735                             transform = this._proxy.style[prop];
\r 
4737                         setTransform(this._proxy, this.project(e.center, e.zoom), this.getZoomScale(e.zoom, 1));
\r 
4739                         // workaround for case when transform is the same and so transitionend event is not fired
\r 
4740                         if (transform === this._proxy.style[prop] && this._animatingZoom) {
\r 
4741                                 this._onZoomTransitionEnd();
\r 
4745                 this.on('load moveend', this._animMoveEnd, this);
\r 
4747                 this._on('unload', this._destroyAnimProxy, this);
\r 
4750         _destroyAnimProxy: function () {
\r 
4751                 remove(this._proxy);
\r 
4752                 this.off('load moveend', this._animMoveEnd, this);
\r 
4753                 delete this._proxy;
\r 
4756         _animMoveEnd: function () {
\r 
4757                 var c = this.getCenter(),
\r 
4758                     z = this.getZoom();
\r 
4759                 setTransform(this._proxy, this.project(c, z), this.getZoomScale(z, 1));
\r 
4762         _catchTransitionEnd: function (e) {
\r 
4763                 if (this._animatingZoom && e.propertyName.indexOf('transform') >= 0) {
\r 
4764                         this._onZoomTransitionEnd();
\r 
4768         _nothingToAnimate: function () {
\r 
4769                 return !this._container.getElementsByClassName('leaflet-zoom-animated').length;
\r 
4772         _tryAnimatedZoom: function (center, zoom, options) {
\r 
4774                 if (this._animatingZoom) { return true; }
\r 
4776                 options = options || {};
\r 
4778                 // don't animate if disabled, not supported or zoom difference is too large
\r 
4779                 if (!this._zoomAnimated || options.animate === false || this._nothingToAnimate() ||
\r 
4780                         Math.abs(zoom - this._zoom) > this.options.zoomAnimationThreshold) { return false; }
\r 
4782                 // offset is the pixel coords of the zoom origin relative to the current center
\r 
4783                 var scale = this.getZoomScale(zoom),
\r 
4784                     offset = this._getCenterOffset(center)._divideBy(1 - 1 / scale);
\r 
4786                 // don't animate if the zoom origin isn't within one screen from the current center, unless forced
\r 
4787                 if (options.animate !== true && !this.getSize().contains(offset)) { return false; }
\r 
4789                 requestAnimFrame(function () {
\r 
4791                             ._moveStart(true, options.noMoveStart || false)
\r 
4792                             ._animateZoom(center, zoom, true);
\r 
4798         _animateZoom: function (center, zoom, startAnim, noUpdate) {
\r 
4799                 if (!this._mapPane) { return; }
\r 
4802                         this._animatingZoom = true;
\r 
4804                         // remember what center/zoom to set after animation
\r 
4805                         this._animateToCenter = center;
\r 
4806                         this._animateToZoom = zoom;
\r 
4808                         addClass(this._mapPane, 'leaflet-zoom-anim');
\r 
4811                 // @section Other Events
\r 
4812                 // @event zoomanim: ZoomAnimEvent
\r 
4813                 // Fired at least once per zoom animation. For continuous zoom, like pinch zooming, fired once per frame during zoom.
\r 
4814                 this.fire('zoomanim', {
\r 
4817                         noUpdate: noUpdate
\r 
4820                 if (!this._tempFireZoomEvent) {
\r 
4821                         this._tempFireZoomEvent = this._zoom !== this._animateToZoom;
\r 
4824                 this._move(this._animateToCenter, this._animateToZoom, undefined, true);
\r 
4826                 // Work around webkit not firing 'transitionend', see https://github.com/Leaflet/Leaflet/issues/3689, 2693
\r 
4827                 setTimeout(bind(this._onZoomTransitionEnd, this), 250);
\r 
4830         _onZoomTransitionEnd: function () {
\r 
4831                 if (!this._animatingZoom) { return; }
\r 
4833                 if (this._mapPane) {
\r 
4834                         removeClass(this._mapPane, 'leaflet-zoom-anim');
\r 
4837                 this._animatingZoom = false;
\r 
4839                 this._move(this._animateToCenter, this._animateToZoom, undefined, true);
\r 
4841                 if (this._tempFireZoomEvent) {
\r 
4842                         this.fire('zoom');
\r 
4844                 delete this._tempFireZoomEvent;
\r 
4846                 this.fire('move');
\r 
4848                 this._moveEnd(true);
\r 
4854   // @factory L.map(id: String, options?: Map options)
\r 
4855   // Instantiates a map object given the DOM ID of a `<div>` element
\r 
4856   // and optionally an object literal with `Map options`.
\r 
4859   // @factory L.map(el: HTMLElement, options?: Map options)
\r 
4860   // Instantiates a map object given an instance of a `<div>` HTML element
\r 
4861   // and optionally an object literal with `Map options`.
\r 
4862   function createMap(id, options) {
\r 
4863         return new Map(id, options);
\r 
4871    * L.Control is a base class for implementing map controls. Handles positioning.
\r 
4872    * All other controls extend from this class.
\r 
4875   var Control = Class.extend({
\r 
4877         // @aka Control Options
\r 
4879                 // @option position: String = 'topright'
\r 
4880                 // The position of the control (one of the map corners). Possible values are `'topleft'`,
\r 
4881                 // `'topright'`, `'bottomleft'` or `'bottomright'`
\r 
4882                 position: 'topright'
\r 
4885         initialize: function (options) {
\r 
4886                 setOptions(this, options);
\r 
4890          * Classes extending L.Control will inherit the following methods:
\r 
4892          * @method getPosition: string
\r 
4893          * Returns the position of the control.
\r 
4895         getPosition: function () {
\r 
4896                 return this.options.position;
\r 
4899         // @method setPosition(position: string): this
\r 
4900         // Sets the position of the control.
\r 
4901         setPosition: function (position) {
\r 
4902                 var map = this._map;
\r 
4905                         map.removeControl(this);
\r 
4908                 this.options.position = position;
\r 
4911                         map.addControl(this);
\r 
4917         // @method getContainer: HTMLElement
\r 
4918         // Returns the HTMLElement that contains the control.
\r 
4919         getContainer: function () {
\r 
4920                 return this._container;
\r 
4923         // @method addTo(map: Map): this
\r 
4924         // Adds the control to the given map.
\r 
4925         addTo: function (map) {
\r 
4929                 var container = this._container = this.onAdd(map),
\r 
4930                     pos = this.getPosition(),
\r 
4931                     corner = map._controlCorners[pos];
\r 
4933                 addClass(container, 'leaflet-control');
\r 
4935                 if (pos.indexOf('bottom') !== -1) {
\r 
4936                         corner.insertBefore(container, corner.firstChild);
\r 
4938                         corner.appendChild(container);
\r 
4941                 this._map.on('unload', this.remove, this);
\r 
4946         // @method remove: this
\r 
4947         // Removes the control from the map it is currently active on.
\r 
4948         remove: function () {
\r 
4953                 remove(this._container);
\r 
4955                 if (this.onRemove) {
\r 
4956                         this.onRemove(this._map);
\r 
4959                 this._map.off('unload', this.remove, this);
\r 
4965         _refocusOnMap: function (e) {
\r 
4966                 // if map exists and event is not a keyboard event
\r 
4967                 if (this._map && e && e.screenX > 0 && e.screenY > 0) {
\r 
4968                         this._map.getContainer().focus();
\r 
4973   var control = function (options) {
\r 
4974         return new Control(options);
\r 
4977   /* @section Extension methods
\r 
4980    * Every control should extend from `L.Control` and (re-)implement the following methods.
\r 
4982    * @method onAdd(map: Map): HTMLElement
\r 
4983    * Should return the container DOM element for the control and add listeners on relevant map events. Called on [`control.addTo(map)`](#control-addTo).
\r 
4985    * @method onRemove(map: Map)
\r 
4986    * Optional method. Should contain all clean up code that removes the listeners previously added in [`onAdd`](#control-onadd). Called on [`control.remove()`](#control-remove).
\r 
4990    * @section Methods for Layers and Controls
\r 
4993         // @method addControl(control: Control): this
\r 
4994         // Adds the given control to the map
\r 
4995         addControl: function (control) {
\r 
4996                 control.addTo(this);
\r 
5000         // @method removeControl(control: Control): this
\r 
5001         // Removes the given control from the map
\r 
5002         removeControl: function (control) {
\r 
5007         _initControlPos: function () {
\r 
5008                 var corners = this._controlCorners = {},
\r 
5010                     container = this._controlContainer =
\r 
5011                             create$1('div', l + 'control-container', this._container);
\r 
5013                 function createCorner(vSide, hSide) {
\r 
5014                         var className = l + vSide + ' ' + l + hSide;
\r 
5016                         corners[vSide + hSide] = create$1('div', className, container);
\r 
5019                 createCorner('top', 'left');
\r 
5020                 createCorner('top', 'right');
\r 
5021                 createCorner('bottom', 'left');
\r 
5022                 createCorner('bottom', 'right');
\r 
5025         _clearControlPos: function () {
\r 
5026                 for (var i in this._controlCorners) {
\r 
5027                         remove(this._controlCorners[i]);
\r 
5029                 remove(this._controlContainer);
\r 
5030                 delete this._controlCorners;
\r 
5031                 delete this._controlContainer;
\r 
5036    * @class Control.Layers
\r 
5037    * @aka L.Control.Layers
\r 
5038    * @inherits Control
\r 
5040    * 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`.
\r 
5045    * var baseLayers = {
\r 
5046    *    "Mapbox": mapbox,
\r 
5047    *    "OpenStreetMap": osm
\r 
5050    * var overlays = {
\r 
5051    *    "Marker": marker,
\r 
5052    *    "Roads": roadsLayer
\r 
5055    * L.control.layers(baseLayers, overlays).addTo(map);
\r 
5058    * The `baseLayers` and `overlays` parameters are object literals with layer names as keys and `Layer` objects as values:
\r 
5062    *     "<someName1>": layer1,
\r 
5063    *     "<someName2>": layer2
\r 
5067    * The layer names can contain HTML, which allows you to add additional styling to the items:
\r 
5070    * {"<img src='my-layer-icon' /> <span class='my-layer-item'>My Layer</span>": myLayer}
\r 
5074   var Layers = Control.extend({
\r 
5076         // @aka Control.Layers options
\r 
5078                 // @option collapsed: Boolean = true
\r 
5079                 // If `true`, the control will be collapsed into an icon and expanded on mouse hover, touch, or keyboard activation.
\r 
5081                 position: 'topright',
\r 
5083                 // @option autoZIndex: Boolean = true
\r 
5084                 // 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.
\r 
5087                 // @option hideSingleBase: Boolean = false
\r 
5088                 // If `true`, the base layers in the control will be hidden when there is only one.
\r 
5089                 hideSingleBase: false,
\r 
5091                 // @option sortLayers: Boolean = false
\r 
5092                 // Whether to sort the layers. When `false`, layers will keep the order
\r 
5093                 // in which they were added to the control.
\r 
5094                 sortLayers: false,
\r 
5096                 // @option sortFunction: Function = *
\r 
5097                 // A [compare function](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Array/sort)
\r 
5098                 // that will be used for sorting the layers, when `sortLayers` is `true`.
\r 
5099                 // The function receives both the `L.Layer` instances and their names, as in
\r 
5100                 // `sortFunction(layerA, layerB, nameA, nameB)`.
\r 
5101                 // By default, it sorts layers alphabetically by their name.
\r 
5102                 sortFunction: function (layerA, layerB, nameA, nameB) {
\r 
5103                         return nameA < nameB ? -1 : (nameB < nameA ? 1 : 0);
\r 
5107         initialize: function (baseLayers, overlays, options) {
\r 
5108                 setOptions(this, options);
\r 
5110                 this._layerControlInputs = [];
\r 
5111                 this._layers = [];
\r 
5112                 this._lastZIndex = 0;
\r 
5113                 this._handlingClick = false;
\r 
5114                 this._preventClick = false;
\r 
5116                 for (var i in baseLayers) {
\r 
5117                         this._addLayer(baseLayers[i], i);
\r 
5120                 for (i in overlays) {
\r 
5121                         this._addLayer(overlays[i], i, true);
\r 
5125         onAdd: function (map) {
\r 
5126                 this._initLayout();
\r 
5130                 map.on('zoomend', this._checkDisabledLayers, this);
\r 
5132                 for (var i = 0; i < this._layers.length; i++) {
\r 
5133                         this._layers[i].layer.on('add remove', this._onLayerChange, this);
\r 
5136                 return this._container;
\r 
5139         addTo: function (map) {
\r 
5140                 Control.prototype.addTo.call(this, map);
\r 
5141                 // Trigger expand after Layers Control has been inserted into DOM so that is now has an actual height.
\r 
5142                 return this._expandIfNotCollapsed();
\r 
5145         onRemove: function () {
\r 
5146                 this._map.off('zoomend', this._checkDisabledLayers, this);
\r 
5148                 for (var i = 0; i < this._layers.length; i++) {
\r 
5149                         this._layers[i].layer.off('add remove', this._onLayerChange, this);
\r 
5153         // @method addBaseLayer(layer: Layer, name: String): this
\r 
5154         // Adds a base layer (radio button entry) with the given name to the control.
\r 
5155         addBaseLayer: function (layer, name) {
\r 
5156                 this._addLayer(layer, name);
\r 
5157                 return (this._map) ? this._update() : this;
\r 
5160         // @method addOverlay(layer: Layer, name: String): this
\r 
5161         // Adds an overlay (checkbox entry) with the given name to the control.
\r 
5162         addOverlay: function (layer, name) {
\r 
5163                 this._addLayer(layer, name, true);
\r 
5164                 return (this._map) ? this._update() : this;
\r 
5167         // @method removeLayer(layer: Layer): this
\r 
5168         // Remove the given layer from the control.
\r 
5169         removeLayer: function (layer) {
\r 
5170                 layer.off('add remove', this._onLayerChange, this);
\r 
5172                 var obj = this._getLayer(stamp(layer));
\r 
5174                         this._layers.splice(this._layers.indexOf(obj), 1);
\r 
5176                 return (this._map) ? this._update() : this;
\r 
5179         // @method expand(): this
\r 
5180         // Expand the control container if collapsed.
\r 
5181         expand: function () {
\r 
5182                 addClass(this._container, 'leaflet-control-layers-expanded');
\r 
5183                 this._section.style.height = null;
\r 
5184                 var acceptableHeight = this._map.getSize().y - (this._container.offsetTop + 50);
\r 
5185                 if (acceptableHeight < this._section.clientHeight) {
\r 
5186                         addClass(this._section, 'leaflet-control-layers-scrollbar');
\r 
5187                         this._section.style.height = acceptableHeight + 'px';
\r 
5189                         removeClass(this._section, 'leaflet-control-layers-scrollbar');
\r 
5191                 this._checkDisabledLayers();
\r 
5195         // @method collapse(): this
\r 
5196         // Collapse the control container if expanded.
\r 
5197         collapse: function () {
\r 
5198                 removeClass(this._container, 'leaflet-control-layers-expanded');
\r 
5202         _initLayout: function () {
\r 
5203                 var className = 'leaflet-control-layers',
\r 
5204                     container = this._container = create$1('div', className),
\r 
5205                     collapsed = this.options.collapsed;
\r 
5207                 // makes this work on IE touch devices by stopping it from firing a mouseout event when the touch is released
\r 
5208                 container.setAttribute('aria-haspopup', true);
\r 
5210                 disableClickPropagation(container);
\r 
5211                 disableScrollPropagation(container);
\r 
5213                 var section = this._section = create$1('section', className + '-list');
\r 
5216                         this._map.on('click', this.collapse, this);
\r 
5219                                 mouseenter: this._expandSafely,
\r 
5220                                 mouseleave: this.collapse
\r 
5224                 var link = this._layersLink = create$1('a', className + '-toggle', container);
\r 
5226                 link.title = 'Layers';
\r 
5227                 link.setAttribute('role', 'button');
\r 
5230                         keydown: function (e) {
\r 
5231                                 if (e.keyCode === 13) {
\r 
5232                                         this._expandSafely();
\r 
5235                         // Certain screen readers intercept the key event and instead send a click event
\r 
5236                         click: function (e) {
\r 
5237                                 preventDefault(e);
\r 
5238                                 this._expandSafely();
\r 
5246                 this._baseLayersList = create$1('div', className + '-base', section);
\r 
5247                 this._separator = create$1('div', className + '-separator', section);
\r 
5248                 this._overlaysList = create$1('div', className + '-overlays', section);
\r 
5250                 container.appendChild(section);
\r 
5253         _getLayer: function (id) {
\r 
5254                 for (var i = 0; i < this._layers.length; i++) {
\r 
5256                         if (this._layers[i] && stamp(this._layers[i].layer) === id) {
\r 
5257                                 return this._layers[i];
\r 
5262         _addLayer: function (layer, name, overlay) {
\r 
5264                         layer.on('add remove', this._onLayerChange, this);
\r 
5267                 this._layers.push({
\r 
5273                 if (this.options.sortLayers) {
\r 
5274                         this._layers.sort(bind(function (a, b) {
\r 
5275                                 return this.options.sortFunction(a.layer, b.layer, a.name, b.name);
\r 
5279                 if (this.options.autoZIndex && layer.setZIndex) {
\r 
5280                         this._lastZIndex++;
\r 
5281                         layer.setZIndex(this._lastZIndex);
\r 
5284                 this._expandIfNotCollapsed();
\r 
5287         _update: function () {
\r 
5288                 if (!this._container) { return this; }
\r 
5290                 empty(this._baseLayersList);
\r 
5291                 empty(this._overlaysList);
\r 
5293                 this._layerControlInputs = [];
\r 
5294                 var baseLayersPresent, overlaysPresent, i, obj, baseLayersCount = 0;
\r 
5296                 for (i = 0; i < this._layers.length; i++) {
\r 
5297                         obj = this._layers[i];
\r 
5298                         this._addItem(obj);
\r 
5299                         overlaysPresent = overlaysPresent || obj.overlay;
\r 
5300                         baseLayersPresent = baseLayersPresent || !obj.overlay;
\r 
5301                         baseLayersCount += !obj.overlay ? 1 : 0;
\r 
5304                 // Hide base layers section if there's only one layer.
\r 
5305                 if (this.options.hideSingleBase) {
\r 
5306                         baseLayersPresent = baseLayersPresent && baseLayersCount > 1;
\r 
5307                         this._baseLayersList.style.display = baseLayersPresent ? '' : 'none';
\r 
5310                 this._separator.style.display = overlaysPresent && baseLayersPresent ? '' : 'none';
\r 
5315         _onLayerChange: function (e) {
\r 
5316                 if (!this._handlingClick) {
\r 
5320                 var obj = this._getLayer(stamp(e.target));
\r 
5323                 // @section Layer events
\r 
5324                 // @event baselayerchange: LayersControlEvent
\r 
5325                 // Fired when the base layer is changed through the [layers control](#control-layers).
\r 
5326                 // @event overlayadd: LayersControlEvent
\r 
5327                 // Fired when an overlay is selected through the [layers control](#control-layers).
\r 
5328                 // @event overlayremove: LayersControlEvent
\r 
5329                 // Fired when an overlay is deselected through the [layers control](#control-layers).
\r 
5330                 // @namespace Control.Layers
\r 
5331                 var type = obj.overlay ?
\r 
5332                         (e.type === 'add' ? 'overlayadd' : 'overlayremove') :
\r 
5333                         (e.type === 'add' ? 'baselayerchange' : null);
\r 
5336                         this._map.fire(type, obj);
\r 
5340         // IE7 bugs out if you create a radio dynamically, so you have to do it this hacky way (see https://stackoverflow.com/a/119079)
\r 
5341         _createRadioElement: function (name, checked) {
\r 
5343                 var radioHtml = '<input type="radio" class="leaflet-control-layers-selector" name="' +
\r 
5344                                 name + '"' + (checked ? ' checked="checked"' : '') + '/>';
\r 
5346                 var radioFragment = document.createElement('div');
\r 
5347                 radioFragment.innerHTML = radioHtml;
\r 
5349                 return radioFragment.firstChild;
\r 
5352         _addItem: function (obj) {
\r 
5353                 var label = document.createElement('label'),
\r 
5354                     checked = this._map.hasLayer(obj.layer),
\r 
5357                 if (obj.overlay) {
\r 
5358                         input = document.createElement('input');
\r 
5359                         input.type = 'checkbox';
\r 
5360                         input.className = 'leaflet-control-layers-selector';
\r 
5361                         input.defaultChecked = checked;
\r 
5363                         input = this._createRadioElement('leaflet-base-layers_' + stamp(this), checked);
\r 
5366                 this._layerControlInputs.push(input);
\r 
5367                 input.layerId = stamp(obj.layer);
\r 
5369                 on(input, 'click', this._onInputClick, this);
\r 
5371                 var name = document.createElement('span');
\r 
5372                 name.innerHTML = ' ' + obj.name;
\r 
5374                 // Helps from preventing layer control flicker when checkboxes are disabled
\r 
5375                 // https://github.com/Leaflet/Leaflet/issues/2771
\r 
5376                 var holder = document.createElement('span');
\r 
5378                 label.appendChild(holder);
\r 
5379                 holder.appendChild(input);
\r 
5380                 holder.appendChild(name);
\r 
5382                 var container = obj.overlay ? this._overlaysList : this._baseLayersList;
\r 
5383                 container.appendChild(label);
\r 
5385                 this._checkDisabledLayers();
\r 
5389         _onInputClick: function () {
\r 
5390                 // expanding the control on mobile with a click can cause adding a layer - we don't want this
\r 
5391                 if (this._preventClick) {
\r 
5395                 var inputs = this._layerControlInputs,
\r 
5397                 var addedLayers = [],
\r 
5398                     removedLayers = [];
\r 
5400                 this._handlingClick = true;
\r 
5402                 for (var i = inputs.length - 1; i >= 0; i--) {
\r 
5403                         input = inputs[i];
\r 
5404                         layer = this._getLayer(input.layerId).layer;
\r 
5406                         if (input.checked) {
\r 
5407                                 addedLayers.push(layer);
\r 
5408                         } else if (!input.checked) {
\r 
5409                                 removedLayers.push(layer);
\r 
5413                 // Bugfix issue 2318: Should remove all old layers before readding new ones
\r 
5414                 for (i = 0; i < removedLayers.length; i++) {
\r 
5415                         if (this._map.hasLayer(removedLayers[i])) {
\r 
5416                                 this._map.removeLayer(removedLayers[i]);
\r 
5419                 for (i = 0; i < addedLayers.length; i++) {
\r 
5420                         if (!this._map.hasLayer(addedLayers[i])) {
\r 
5421                                 this._map.addLayer(addedLayers[i]);
\r 
5425                 this._handlingClick = false;
\r 
5427                 this._refocusOnMap();
\r 
5430         _checkDisabledLayers: function () {
\r 
5431                 var inputs = this._layerControlInputs,
\r 
5434                     zoom = this._map.getZoom();
\r 
5436                 for (var i = inputs.length - 1; i >= 0; i--) {
\r 
5437                         input = inputs[i];
\r 
5438                         layer = this._getLayer(input.layerId).layer;
\r 
5439                         input.disabled = (layer.options.minZoom !== undefined && zoom < layer.options.minZoom) ||
\r 
5440                                          (layer.options.maxZoom !== undefined && zoom > layer.options.maxZoom);
\r 
5445         _expandIfNotCollapsed: function () {
\r 
5446                 if (this._map && !this.options.collapsed) {
\r 
5452         _expandSafely: function () {
\r 
5453                 var section = this._section;
\r 
5454                 this._preventClick = true;
\r 
5455                 on(section, 'click', preventDefault);
\r 
5458                 setTimeout(function () {
\r 
5459                         off(section, 'click', preventDefault);
\r 
5460                         that._preventClick = false;
\r 
5467   // @factory L.control.layers(baselayers?: Object, overlays?: Object, options?: Control.Layers options)
\r 
5468   // 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.
\r 
5469   var layers = function (baseLayers, overlays, options) {
\r 
5470         return new Layers(baseLayers, overlays, options);
\r 
5474    * @class Control.Zoom
\r 
5475    * @aka L.Control.Zoom
\r 
5476    * @inherits Control
\r 
5478    * 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`.
\r 
5481   var Zoom = Control.extend({
\r 
5483         // @aka Control.Zoom options
\r 
5485                 position: 'topleft',
\r 
5487                 // @option zoomInText: String = '<span aria-hidden="true">+</span>'
\r 
5488                 // The text set on the 'zoom in' button.
\r 
5489                 zoomInText: '<span aria-hidden="true">+</span>',
\r 
5491                 // @option zoomInTitle: String = 'Zoom in'
\r 
5492                 // The title set on the 'zoom in' button.
\r 
5493                 zoomInTitle: 'Zoom in',
\r 
5495                 // @option zoomOutText: String = '<span aria-hidden="true">−</span>'
\r 
5496                 // The text set on the 'zoom out' button.
\r 
5497                 zoomOutText: '<span aria-hidden="true">−</span>',
\r 
5499                 // @option zoomOutTitle: String = 'Zoom out'
\r 
5500                 // The title set on the 'zoom out' button.
\r 
5501                 zoomOutTitle: 'Zoom out'
\r 
5504         onAdd: function (map) {
\r 
5505                 var zoomName = 'leaflet-control-zoom',
\r 
5506                     container = create$1('div', zoomName + ' leaflet-bar'),
\r 
5507                     options = this.options;
\r 
5509                 this._zoomInButton  = this._createButton(options.zoomInText, options.zoomInTitle,
\r 
5510                         zoomName + '-in',  container, this._zoomIn);
\r 
5511                 this._zoomOutButton = this._createButton(options.zoomOutText, options.zoomOutTitle,
\r 
5512                         zoomName + '-out', container, this._zoomOut);
\r 
5514                 this._updateDisabled();
\r 
5515                 map.on('zoomend zoomlevelschange', this._updateDisabled, this);
\r 
5520         onRemove: function (map) {
\r 
5521                 map.off('zoomend zoomlevelschange', this._updateDisabled, this);
\r 
5524         disable: function () {
\r 
5525                 this._disabled = true;
\r 
5526                 this._updateDisabled();
\r 
5530         enable: function () {
\r 
5531                 this._disabled = false;
\r 
5532                 this._updateDisabled();
\r 
5536         _zoomIn: function (e) {
\r 
5537                 if (!this._disabled && this._map._zoom < this._map.getMaxZoom()) {
\r 
5538                         this._map.zoomIn(this._map.options.zoomDelta * (e.shiftKey ? 3 : 1));
\r 
5542         _zoomOut: function (e) {
\r 
5543                 if (!this._disabled && this._map._zoom > this._map.getMinZoom()) {
\r 
5544                         this._map.zoomOut(this._map.options.zoomDelta * (e.shiftKey ? 3 : 1));
\r 
5548         _createButton: function (html, title, className, container, fn) {
\r 
5549                 var link = create$1('a', className, container);
\r 
5550                 link.innerHTML = html;
\r 
5552                 link.title = title;
\r 
5555                  * Will force screen readers like VoiceOver to read this as "Zoom in - button"
\r 
5557                 link.setAttribute('role', 'button');
\r 
5558                 link.setAttribute('aria-label', title);
\r 
5560                 disableClickPropagation(link);
\r 
5561                 on(link, 'click', stop);
\r 
5562                 on(link, 'click', fn, this);
\r 
5563                 on(link, 'click', this._refocusOnMap, this);
\r 
5568         _updateDisabled: function () {
\r 
5569                 var map = this._map,
\r 
5570                     className = 'leaflet-disabled';
\r 
5572                 removeClass(this._zoomInButton, className);
\r 
5573                 removeClass(this._zoomOutButton, className);
\r 
5574                 this._zoomInButton.setAttribute('aria-disabled', 'false');
\r 
5575                 this._zoomOutButton.setAttribute('aria-disabled', 'false');
\r 
5577                 if (this._disabled || map._zoom === map.getMinZoom()) {
\r 
5578                         addClass(this._zoomOutButton, className);
\r 
5579                         this._zoomOutButton.setAttribute('aria-disabled', 'true');
\r 
5581                 if (this._disabled || map._zoom === map.getMaxZoom()) {
\r 
5582                         addClass(this._zoomInButton, className);
\r 
5583                         this._zoomInButton.setAttribute('aria-disabled', 'true');
\r 
5589   // @section Control options
\r 
5590   // @option zoomControl: Boolean = true
\r 
5591   // Whether a [zoom control](#control-zoom) is added to the map by default.
\r 
5592   Map.mergeOptions({
\r 
5596   Map.addInitHook(function () {
\r 
5597         if (this.options.zoomControl) {
\r 
5598                 // @section Controls
\r 
5599                 // @property zoomControl: Control.Zoom
\r 
5600                 // The default zoom control (only available if the
\r 
5601                 // [`zoomControl` option](#map-zoomcontrol) was `true` when creating the map).
\r 
5602                 this.zoomControl = new Zoom();
\r 
5603                 this.addControl(this.zoomControl);
\r 
5607   // @namespace Control.Zoom
\r 
5608   // @factory L.control.zoom(options: Control.Zoom options)
\r 
5609   // Creates a zoom control
\r 
5610   var zoom = function (options) {
\r 
5611         return new Zoom(options);
\r 
5615    * @class Control.Scale
 
5616    * @aka L.Control.Scale
 
5619    * 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`.
 
5624    * L.control.scale().addTo(map);
 
5628   var Scale = Control.extend({
 
5630         // @aka Control.Scale options
 
5632                 position: 'bottomleft',
 
5634                 // @option maxWidth: Number = 100
 
5635                 // Maximum width of the control in pixels. The width is set dynamically to show round values (e.g. 100, 200, 500).
 
5638                 // @option metric: Boolean = True
 
5639                 // Whether to show the metric scale line (m/km).
 
5642                 // @option imperial: Boolean = True
 
5643                 // Whether to show the imperial scale line (mi/ft).
 
5646                 // @option updateWhenIdle: Boolean = false
 
5647                 // If `true`, the control is updated on [`moveend`](#map-moveend), otherwise it's always up-to-date (updated on [`move`](#map-move)).
 
5650         onAdd: function (map) {
 
5651                 var className = 'leaflet-control-scale',
 
5652                     container = create$1('div', className),
 
5653                     options = this.options;
 
5655                 this._addScales(options, className + '-line', container);
 
5657                 map.on(options.updateWhenIdle ? 'moveend' : 'move', this._update, this);
 
5658                 map.whenReady(this._update, this);
 
5663         onRemove: function (map) {
 
5664                 map.off(this.options.updateWhenIdle ? 'moveend' : 'move', this._update, this);
 
5667         _addScales: function (options, className, container) {
 
5668                 if (options.metric) {
 
5669                         this._mScale = create$1('div', className, container);
 
5671                 if (options.imperial) {
 
5672                         this._iScale = create$1('div', className, container);
 
5676         _update: function () {
 
5677                 var map = this._map,
 
5678                     y = map.getSize().y / 2;
 
5680                 var maxMeters = map.distance(
 
5681                         map.containerPointToLatLng([0, y]),
 
5682                         map.containerPointToLatLng([this.options.maxWidth, y]));
 
5684                 this._updateScales(maxMeters);
 
5687         _updateScales: function (maxMeters) {
 
5688                 if (this.options.metric && maxMeters) {
 
5689                         this._updateMetric(maxMeters);
 
5691                 if (this.options.imperial && maxMeters) {
 
5692                         this._updateImperial(maxMeters);
 
5696         _updateMetric: function (maxMeters) {
 
5697                 var meters = this._getRoundNum(maxMeters),
 
5698                     label = meters < 1000 ? meters + ' m' : (meters / 1000) + ' km';
 
5700                 this._updateScale(this._mScale, label, meters / maxMeters);
 
5703         _updateImperial: function (maxMeters) {
 
5704                 var maxFeet = maxMeters * 3.2808399,
 
5705                     maxMiles, miles, feet;
 
5707                 if (maxFeet > 5280) {
 
5708                         maxMiles = maxFeet / 5280;
 
5709                         miles = this._getRoundNum(maxMiles);
 
5710                         this._updateScale(this._iScale, miles + ' mi', miles / maxMiles);
 
5713                         feet = this._getRoundNum(maxFeet);
 
5714                         this._updateScale(this._iScale, feet + ' ft', feet / maxFeet);
 
5718         _updateScale: function (scale, text, ratio) {
 
5719                 scale.style.width = Math.round(this.options.maxWidth * ratio) + 'px';
 
5720                 scale.innerHTML = text;
 
5723         _getRoundNum: function (num) {
 
5724                 var pow10 = Math.pow(10, (Math.floor(num) + '').length - 1),
 
5737   // @factory L.control.scale(options?: Control.Scale options)
 
5738   // Creates an scale control with the given options.
 
5739   var scale = function (options) {
 
5740         return new Scale(options);
 
5743   var ukrainianFlag = '<svg aria-hidden="true" xmlns="http://www.w3.org/2000/svg" width="12" height="8" viewBox="0 0 12 8" class="leaflet-attribution-flag"><path fill="#4C7BE1" d="M0 0h12v4H0z"/><path fill="#FFD500" d="M0 4h12v3H0z"/><path fill="#E0BC00" d="M0 7h12v1H0z"/></svg>';
\r 
5747    * @class Control.Attribution
\r 
5748    * @aka L.Control.Attribution
\r 
5749    * @inherits Control
\r 
5751    * 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.
\r 
5754   var Attribution = Control.extend({
\r 
5756         // @aka Control.Attribution options
\r 
5758                 position: 'bottomright',
\r 
5760                 // @option prefix: String|false = 'Leaflet'
\r 
5761                 // The HTML text shown before the attributions. Pass `false` to disable.
\r 
5762                 prefix: '<a href="https://leafletjs.com" title="A JavaScript library for interactive maps">' + (Browser.inlineSvg ? ukrainianFlag + ' ' : '') + 'Leaflet</a>'
\r 
5765         initialize: function (options) {
\r 
5766                 setOptions(this, options);
\r 
5768                 this._attributions = {};
\r 
5771         onAdd: function (map) {
\r 
5772                 map.attributionControl = this;
\r 
5773                 this._container = create$1('div', 'leaflet-control-attribution');
\r 
5774                 disableClickPropagation(this._container);
\r 
5776                 // TODO ugly, refactor
\r 
5777                 for (var i in map._layers) {
\r 
5778                         if (map._layers[i].getAttribution) {
\r 
5779                                 this.addAttribution(map._layers[i].getAttribution());
\r 
5785                 map.on('layeradd', this._addAttribution, this);
\r 
5787                 return this._container;
\r 
5790         onRemove: function (map) {
\r 
5791                 map.off('layeradd', this._addAttribution, this);
\r 
5794         _addAttribution: function (ev) {
\r 
5795                 if (ev.layer.getAttribution) {
\r 
5796                         this.addAttribution(ev.layer.getAttribution());
\r 
5797                         ev.layer.once('remove', function () {
\r 
5798                                 this.removeAttribution(ev.layer.getAttribution());
\r 
5803         // @method setPrefix(prefix: String|false): this
\r 
5804         // The HTML text shown before the attributions. Pass `false` to disable.
\r 
5805         setPrefix: function (prefix) {
\r 
5806                 this.options.prefix = prefix;
\r 
5811         // @method addAttribution(text: String): this
\r 
5812         // Adds an attribution text (e.g. `'© OpenStreetMap contributors'`).
\r 
5813         addAttribution: function (text) {
\r 
5814                 if (!text) { return this; }
\r 
5816                 if (!this._attributions[text]) {
\r 
5817                         this._attributions[text] = 0;
\r 
5819                 this._attributions[text]++;
\r 
5826         // @method removeAttribution(text: String): this
\r 
5827         // Removes an attribution text.
\r 
5828         removeAttribution: function (text) {
\r 
5829                 if (!text) { return this; }
\r 
5831                 if (this._attributions[text]) {
\r 
5832                         this._attributions[text]--;
\r 
5839         _update: function () {
\r 
5840                 if (!this._map) { return; }
\r 
5844                 for (var i in this._attributions) {
\r 
5845                         if (this._attributions[i]) {
\r 
5850                 var prefixAndAttribs = [];
\r 
5852                 if (this.options.prefix) {
\r 
5853                         prefixAndAttribs.push(this.options.prefix);
\r 
5855                 if (attribs.length) {
\r 
5856                         prefixAndAttribs.push(attribs.join(', '));
\r 
5859                 this._container.innerHTML = prefixAndAttribs.join(' <span aria-hidden="true">|</span> ');
\r 
5864   // @section Control options
\r 
5865   // @option attributionControl: Boolean = true
\r 
5866   // Whether a [attribution control](#control-attribution) is added to the map by default.
\r 
5867   Map.mergeOptions({
\r 
5868         attributionControl: true
\r 
5871   Map.addInitHook(function () {
\r 
5872         if (this.options.attributionControl) {
\r 
5873                 new Attribution().addTo(this);
\r 
5877   // @namespace Control.Attribution
\r 
5878   // @factory L.control.attribution(options: Control.Attribution options)
\r 
5879   // Creates an attribution control.
\r 
5880   var attribution = function (options) {
\r 
5881         return new Attribution(options);
\r 
5884   Control.Layers = Layers;
 
5885   Control.Zoom = Zoom;
 
5886   Control.Scale = Scale;
 
5887   Control.Attribution = Attribution;
 
5889   control.layers = layers;
 
5890   control.zoom = zoom;
 
5891   control.scale = scale;
 
5892   control.attribution = attribution;
 
5895         L.Handler is a base class for handler classes that are used internally to inject
 
5896         interaction features like dragging to classes like Map and Marker.
 
5901   // Abstract class for map interaction handlers
 
5903   var Handler = Class.extend({
 
5904         initialize: function (map) {
 
5908         // @method enable(): this
 
5909         // Enables the handler
 
5910         enable: function () {
 
5911                 if (this._enabled) { return this; }
 
5913                 this._enabled = true;
 
5918         // @method disable(): this
 
5919         // Disables the handler
 
5920         disable: function () {
 
5921                 if (!this._enabled) { return this; }
 
5923                 this._enabled = false;
 
5928         // @method enabled(): Boolean
 
5929         // Returns `true` if the handler is enabled
 
5930         enabled: function () {
 
5931                 return !!this._enabled;
 
5934         // @section Extension methods
 
5935         // Classes inheriting from `Handler` must implement the two following methods:
 
5936         // @method addHooks()
 
5937         // Called when the handler is enabled, should add event hooks.
 
5938         // @method removeHooks()
 
5939         // Called when the handler is disabled, should remove the event hooks added previously.
 
5942   // @section There is static function which can be called without instantiating L.Handler:
 
5943   // @function addTo(map: Map, name: String): this
 
5944   // Adds a new Handler to the given map with the given name.
 
5945   Handler.addTo = function (map, name) {
 
5946         map.addHandler(name, this);
 
5950   var Mixin = {Events: Events};
 
5953    * @class Draggable
\r 
5954    * @aka L.Draggable
\r 
5955    * @inherits Evented
\r 
5957    * A class for making DOM elements draggable (including touch support).
\r 
5958    * Used internally for map and marker dragging. Only works for elements
\r 
5959    * that were positioned with [`L.DomUtil.setPosition`](#domutil-setposition).
\r 
5963    * var draggable = new L.Draggable(elementToDrag);
\r 
5964    * draggable.enable();
\r 
5968   var START = Browser.touch ? 'touchstart mousedown' : 'mousedown';
\r 
5970   var Draggable = Evented.extend({
\r 
5974                 // @aka Draggable options
\r 
5975                 // @option clickTolerance: Number = 3
\r 
5976                 // The max number of pixels a user can shift the mouse pointer during a click
\r 
5977                 // for it to be considered a valid click (as opposed to a mouse drag).
\r 
5981         // @constructor L.Draggable(el: HTMLElement, dragHandle?: HTMLElement, preventOutline?: Boolean, options?: Draggable options)
\r 
5982         // Creates a `Draggable` object for moving `el` when you start dragging the `dragHandle` element (equals `el` itself by default).
\r 
5983         initialize: function (element, dragStartTarget, preventOutline, options) {
\r 
5984                 setOptions(this, options);
\r 
5986                 this._element = element;
\r 
5987                 this._dragStartTarget = dragStartTarget || element;
\r 
5988                 this._preventOutline = preventOutline;
\r 
5991         // @method enable()
\r 
5992         // Enables the dragging ability
\r 
5993         enable: function () {
\r 
5994                 if (this._enabled) { return; }
\r 
5996                 on(this._dragStartTarget, START, this._onDown, this);
\r 
5998                 this._enabled = true;
\r 
6001         // @method disable()
\r 
6002         // Disables the dragging ability
\r 
6003         disable: function () {
\r 
6004                 if (!this._enabled) { return; }
\r 
6006                 // If we're currently dragging this draggable,
\r 
6007                 // disabling it counts as first ending the drag.
\r 
6008                 if (Draggable._dragging === this) {
\r 
6009                         this.finishDrag(true);
\r 
6012                 off(this._dragStartTarget, START, this._onDown, this);
\r 
6014                 this._enabled = false;
\r 
6015                 this._moved = false;
\r 
6018         _onDown: function (e) {
\r 
6019                 // Ignore the event if disabled; this happens in IE11
\r 
6020                 // under some circumstances, see #3666.
\r 
6021                 if (!this._enabled) { return; }
\r 
6023                 this._moved = false;
\r 
6025                 if (hasClass(this._element, 'leaflet-zoom-anim')) { return; }
\r 
6027                 if (e.touches && e.touches.length !== 1) {
\r 
6028                         // Finish dragging to avoid conflict with touchZoom
\r 
6029                         if (Draggable._dragging === this) {
\r 
6030                                 this.finishDrag();
\r 
6035                 if (Draggable._dragging || e.shiftKey || ((e.which !== 1) && (e.button !== 1) && !e.touches)) { return; }
\r 
6036                 Draggable._dragging = this;  // Prevent dragging multiple objects at once.
\r 
6038                 if (this._preventOutline) {
\r 
6039                         preventOutline(this._element);
\r 
6042                 disableImageDrag();
\r 
6043                 disableTextSelection();
\r 
6045                 if (this._moving) { return; }
\r 
6047                 // @event down: Event
\r 
6048                 // Fired when a drag is about to start.
\r 
6049                 this.fire('down');
\r 
6051                 var first = e.touches ? e.touches[0] : e,
\r 
6052                     sizedParent = getSizedParentNode(this._element);
\r 
6054                 this._startPoint = new Point(first.clientX, first.clientY);
\r 
6055                 this._startPos = getPosition(this._element);
\r 
6057                 // Cache the scale, so that we can continuously compensate for it during drag (_onMove).
\r 
6058                 this._parentScale = getScale(sizedParent);
\r 
6060                 var mouseevent = e.type === 'mousedown';
\r 
6061                 on(document, mouseevent ? 'mousemove' : 'touchmove', this._onMove, this);
\r 
6062                 on(document, mouseevent ? 'mouseup' : 'touchend touchcancel', this._onUp, this);
\r 
6065         _onMove: function (e) {
\r 
6066                 // Ignore the event if disabled; this happens in IE11
\r 
6067                 // under some circumstances, see #3666.
\r 
6068                 if (!this._enabled) { return; }
\r 
6070                 if (e.touches && e.touches.length > 1) {
\r 
6071                         this._moved = true;
\r 
6075                 var first = (e.touches && e.touches.length === 1 ? e.touches[0] : e),
\r 
6076                     offset = new Point(first.clientX, first.clientY)._subtract(this._startPoint);
\r 
6078                 if (!offset.x && !offset.y) { return; }
\r 
6079                 if (Math.abs(offset.x) + Math.abs(offset.y) < this.options.clickTolerance) { return; }
\r 
6081                 // We assume that the parent container's position, border and scale do not change for the duration of the drag.
\r 
6082                 // Therefore there is no need to account for the position and border (they are eliminated by the subtraction)
\r 
6083                 // and we can use the cached value for the scale.
\r 
6084                 offset.x /= this._parentScale.x;
\r 
6085                 offset.y /= this._parentScale.y;
\r 
6087                 preventDefault(e);
\r 
6089                 if (!this._moved) {
\r 
6090                         // @event dragstart: Event
\r 
6091                         // Fired when a drag starts
\r 
6092                         this.fire('dragstart');
\r 
6094                         this._moved = true;
\r 
6096                         addClass(document.body, 'leaflet-dragging');
\r 
6098                         this._lastTarget = e.target || e.srcElement;
\r 
6099                         // IE and Edge do not give the <use> element, so fetch it
\r 
6101                         if (window.SVGElementInstance && this._lastTarget instanceof window.SVGElementInstance) {
\r 
6102                                 this._lastTarget = this._lastTarget.correspondingUseElement;
\r 
6104                         addClass(this._lastTarget, 'leaflet-drag-target');
\r 
6107                 this._newPos = this._startPos.add(offset);
\r 
6108                 this._moving = true;
\r 
6110                 this._lastEvent = e;
\r 
6111                 this._updatePosition();
\r 
6114         _updatePosition: function () {
\r 
6115                 var e = {originalEvent: this._lastEvent};
\r 
6117                 // @event predrag: Event
\r 
6118                 // Fired continuously during dragging *before* each corresponding
\r 
6119                 // update of the element's position.
\r 
6120                 this.fire('predrag', e);
\r 
6121                 setPosition(this._element, this._newPos);
\r 
6123                 // @event drag: Event
\r 
6124                 // Fired continuously during dragging.
\r 
6125                 this.fire('drag', e);
\r 
6128         _onUp: function () {
\r 
6129                 // Ignore the event if disabled; this happens in IE11
\r 
6130                 // under some circumstances, see #3666.
\r 
6131                 if (!this._enabled) { return; }
\r 
6132                 this.finishDrag();
\r 
6135         finishDrag: function (noInertia) {
\r 
6136                 removeClass(document.body, 'leaflet-dragging');
\r 
6138                 if (this._lastTarget) {
\r 
6139                         removeClass(this._lastTarget, 'leaflet-drag-target');
\r 
6140                         this._lastTarget = null;
\r 
6143                 off(document, 'mousemove touchmove', this._onMove, this);
\r 
6144                 off(document, 'mouseup touchend touchcancel', this._onUp, this);
\r 
6146                 enableImageDrag();
\r 
6147                 enableTextSelection();
\r 
6149                 var fireDragend = this._moved && this._moving;
\r 
6151                 this._moving = false;
\r 
6152                 Draggable._dragging = false;
\r 
6154                 if (fireDragend) {
\r 
6155                         // @event dragend: DragEndEvent
\r 
6156                         // Fired when the drag ends.
\r 
6157                         this.fire('dragend', {
\r 
6158                                 noInertia: noInertia,
\r 
6159                                 distance: this._newPos.distanceTo(this._startPos)
\r 
6167    * @namespace PolyUtil
\r 
6168    * Various utility functions for polygon geometries.
\r 
6171   /* @function clipPolygon(points: Point[], bounds: Bounds, round?: Boolean): Point[]
\r 
6172    * 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)).
\r 
6173    * Used by Leaflet to only show polygon points that are on the screen or near, increasing
\r 
6174    * performance. Note that polygon points needs different algorithm for clipping
\r 
6175    * than polyline, so there's a separate method for it.
\r 
6177   function clipPolygon(points, bounds, round) {
\r 
6178         var clippedPoints,
\r 
6179             edges = [1, 4, 2, 8],
\r 
6184         for (i = 0, len = points.length; i < len; i++) {
\r 
6185                 points[i]._code = _getBitCode(points[i], bounds);
\r 
6188         // for each edge (left, bottom, right, top)
\r 
6189         for (k = 0; k < 4; k++) {
\r 
6191                 clippedPoints = [];
\r 
6193                 for (i = 0, len = points.length, j = len - 1; i < len; j = i++) {
\r 
6197                         // if a is inside the clip window
\r 
6198                         if (!(a._code & edge)) {
\r 
6199                                 // if b is outside the clip window (a->b goes out of screen)
\r 
6200                                 if (b._code & edge) {
\r 
6201                                         p = _getEdgeIntersection(b, a, edge, bounds, round);
\r 
6202                                         p._code = _getBitCode(p, bounds);
\r 
6203                                         clippedPoints.push(p);
\r 
6205                                 clippedPoints.push(a);
\r 
6207                         // else if b is inside the clip window (a->b enters the screen)
\r 
6208                         } else if (!(b._code & edge)) {
\r 
6209                                 p = _getEdgeIntersection(b, a, edge, bounds, round);
\r 
6210                                 p._code = _getBitCode(p, bounds);
\r 
6211                                 clippedPoints.push(p);
\r 
6214                 points = clippedPoints;
\r 
6220   /* @function polygonCenter(latlngs: LatLng[], crs: CRS): LatLng
\r 
6221    * Returns the center ([centroid](http://en.wikipedia.org/wiki/Centroid)) of the passed LatLngs (first ring) from a polygon.
\r 
6223   function polygonCenter(latlngs, crs) {
\r 
6224         var i, j, p1, p2, f, area, x, y, center;
\r 
6226         if (!latlngs || latlngs.length === 0) {
\r 
6227                 throw new Error('latlngs not passed');
\r 
6230         if (!isFlat(latlngs)) {
\r 
6231                 console.warn('latlngs are not flat! Only the first ring will be used');
\r 
6232                 latlngs = latlngs[0];
\r 
6235         var centroidLatLng = toLatLng([0, 0]);
\r 
6237         var bounds = toLatLngBounds(latlngs);
\r 
6238         var areaBounds = bounds.getNorthWest().distanceTo(bounds.getSouthWest()) * bounds.getNorthEast().distanceTo(bounds.getNorthWest());
\r 
6239         // tests showed that below 1700 rounding errors are happening
\r 
6240         if (areaBounds < 1700) {
\r 
6241                 // getting a inexact center, to move the latlngs near to [0, 0] to prevent rounding errors
\r 
6242                 centroidLatLng = centroid(latlngs);
\r 
6245         var len = latlngs.length;
\r 
6247         for (i = 0; i < len; i++) {
\r 
6248                 var latlng = toLatLng(latlngs[i]);
\r 
6249                 points.push(crs.project(toLatLng([latlng.lat - centroidLatLng.lat, latlng.lng - centroidLatLng.lng])));
\r 
6254         // polygon centroid algorithm;
\r 
6255         for (i = 0, j = len - 1; i < len; j = i++) {
\r 
6259                 f = p1.y * p2.x - p2.y * p1.x;
\r 
6260                 x += (p1.x + p2.x) * f;
\r 
6261                 y += (p1.y + p2.y) * f;
\r 
6266                 // Polygon is so small that all points are on same pixel.
\r 
6267                 center = points[0];
\r 
6269                 center = [x / area, y / area];
\r 
6272         var latlngCenter = crs.unproject(toPoint(center));
\r 
6273         return toLatLng([latlngCenter.lat + centroidLatLng.lat, latlngCenter.lng + centroidLatLng.lng]);
\r 
6276   /* @function centroid(latlngs: LatLng[]): LatLng
\r 
6277    * Returns the 'center of mass' of the passed LatLngs.
\r 
6279   function centroid(coords) {
\r 
6283         for (var i = 0; i < coords.length; i++) {
\r 
6284                 var latlng = toLatLng(coords[i]);
\r 
6285                 latSum += latlng.lat;
\r 
6286                 lngSum += latlng.lng;
\r 
6289         return toLatLng([latSum / len, lngSum / len]);
\r 
6294     clipPolygon: clipPolygon,
 
6295     polygonCenter: polygonCenter,
 
6300    * @namespace LineUtil
\r 
6302    * Various utility functions for polyline points processing, used by Leaflet internally to make polylines lightning-fast.
\r 
6305   // Simplify polyline with vertex reduction and Douglas-Peucker simplification.
\r 
6306   // Improves rendering performance dramatically by lessening the number of points to draw.
\r 
6308   // @function simplify(points: Point[], tolerance: Number): Point[]
\r 
6309   // Dramatically reduces the number of points in a polyline while retaining
\r 
6310   // its shape and returns a new array of simplified points, using the
\r 
6311   // [Ramer-Douglas-Peucker algorithm](https://en.wikipedia.org/wiki/Ramer-Douglas-Peucker_algorithm).
\r 
6312   // Used for a huge performance boost when processing/displaying Leaflet polylines for
\r 
6313   // each zoom level and also reducing visual noise. tolerance affects the amount of
\r 
6314   // simplification (lesser value means higher quality but slower and with more points).
\r 
6315   // Also released as a separated micro-library [Simplify.js](https://mourner.github.io/simplify-js/).
\r 
6316   function simplify(points, tolerance) {
\r 
6317         if (!tolerance || !points.length) {
\r 
6318                 return points.slice();
\r 
6321         var sqTolerance = tolerance * tolerance;
\r 
6323             // stage 1: vertex reduction
\r 
6324             points = _reducePoints(points, sqTolerance);
\r 
6326             // stage 2: Douglas-Peucker simplification
\r 
6327             points = _simplifyDP(points, sqTolerance);
\r 
6332   // @function pointToSegmentDistance(p: Point, p1: Point, p2: Point): Number
\r 
6333   // Returns the distance between point `p` and segment `p1` to `p2`.
\r 
6334   function pointToSegmentDistance(p, p1, p2) {
\r 
6335         return Math.sqrt(_sqClosestPointOnSegment(p, p1, p2, true));
\r 
6338   // @function closestPointOnSegment(p: Point, p1: Point, p2: Point): Number
\r 
6339   // Returns the closest point from a point `p` on a segment `p1` to `p2`.
\r 
6340   function closestPointOnSegment(p, p1, p2) {
\r 
6341         return _sqClosestPointOnSegment(p, p1, p2);
\r 
6344   // Ramer-Douglas-Peucker simplification, see https://en.wikipedia.org/wiki/Ramer-Douglas-Peucker_algorithm
\r 
6345   function _simplifyDP(points, sqTolerance) {
\r 
6347         var len = points.length,
\r 
6348             ArrayConstructor = typeof Uint8Array !== undefined + '' ? Uint8Array : Array,
\r 
6349             markers = new ArrayConstructor(len);
\r 
6351             markers[0] = markers[len - 1] = 1;
\r 
6353         _simplifyDPStep(points, markers, sqTolerance, 0, len - 1);
\r 
6358         for (i = 0; i < len; i++) {
\r 
6360                         newPoints.push(points[i]);
\r 
6367   function _simplifyDPStep(points, markers, sqTolerance, first, last) {
\r 
6369         var maxSqDist = 0,
\r 
6372         for (i = first + 1; i <= last - 1; i++) {
\r 
6373                 sqDist = _sqClosestPointOnSegment(points[i], points[first], points[last], true);
\r 
6375                 if (sqDist > maxSqDist) {
\r 
6377                         maxSqDist = sqDist;
\r 
6381         if (maxSqDist > sqTolerance) {
\r 
6382                 markers[index] = 1;
\r 
6384                 _simplifyDPStep(points, markers, sqTolerance, first, index);
\r 
6385                 _simplifyDPStep(points, markers, sqTolerance, index, last);
\r 
6389   // reduce points that are too close to each other to a single point
\r 
6390   function _reducePoints(points, sqTolerance) {
\r 
6391         var reducedPoints = [points[0]];
\r 
6393         for (var i = 1, prev = 0, len = points.length; i < len; i++) {
\r 
6394                 if (_sqDist(points[i], points[prev]) > sqTolerance) {
\r 
6395                         reducedPoints.push(points[i]);
\r 
6399         if (prev < len - 1) {
\r 
6400                 reducedPoints.push(points[len - 1]);
\r 
6402         return reducedPoints;
\r 
6407   // @function clipSegment(a: Point, b: Point, bounds: Bounds, useLastCode?: Boolean, round?: Boolean): Point[]|Boolean
\r 
6408   // Clips the segment a to b by rectangular bounds with the
\r 
6409   // [Cohen-Sutherland algorithm](https://en.wikipedia.org/wiki/Cohen%E2%80%93Sutherland_algorithm)
\r 
6410   // (modifying the segment points directly!). Used by Leaflet to only show polyline
\r 
6411   // points that are on the screen or near, increasing performance.
\r 
6412   function clipSegment(a, b, bounds, useLastCode, round) {
\r 
6413         var codeA = useLastCode ? _lastCode : _getBitCode(a, bounds),
\r 
6414             codeB = _getBitCode(b, bounds),
\r 
6416             codeOut, p, newCode;
\r 
6418             // save 2nd code to avoid calculating it on the next segment
\r 
6419             _lastCode = codeB;
\r 
6422                 // if a,b is inside the clip window (trivial accept)
\r 
6423                 if (!(codeA | codeB)) {
\r 
6427                 // if a,b is outside the clip window (trivial reject)
\r 
6428                 if (codeA & codeB) {
\r 
6433                 codeOut = codeA || codeB;
\r 
6434                 p = _getEdgeIntersection(a, b, codeOut, bounds, round);
\r 
6435                 newCode = _getBitCode(p, bounds);
\r 
6437                 if (codeOut === codeA) {
\r 
6447   function _getEdgeIntersection(a, b, code, bounds, round) {
\r 
6448         var dx = b.x - a.x,
\r 
6454         if (code & 8) { // top
\r 
6455                 x = a.x + dx * (max.y - a.y) / dy;
\r 
6458         } else if (code & 4) { // bottom
\r 
6459                 x = a.x + dx * (min.y - a.y) / dy;
\r 
6462         } else if (code & 2) { // right
\r 
6464                 y = a.y + dy * (max.x - a.x) / dx;
\r 
6466         } else if (code & 1) { // left
\r 
6468                 y = a.y + dy * (min.x - a.x) / dx;
\r 
6471         return new Point(x, y, round);
\r 
6474   function _getBitCode(p, bounds) {
\r 
6477         if (p.x < bounds.min.x) { // left
\r 
6479         } else if (p.x > bounds.max.x) { // right
\r 
6483         if (p.y < bounds.min.y) { // bottom
\r 
6485         } else if (p.y > bounds.max.y) { // top
\r 
6492   // square distance (to avoid unnecessary Math.sqrt calls)
\r 
6493   function _sqDist(p1, p2) {
\r 
6494         var dx = p2.x - p1.x,
\r 
6496         return dx * dx + dy * dy;
\r 
6499   // return closest point on segment or distance to that point
\r 
6500   function _sqClosestPointOnSegment(p, p1, p2, sqDist) {
\r 
6505             dot = dx * dx + dy * dy,
\r 
6509                 t = ((p.x - x) * dx + (p.y - y) * dy) / dot;
\r 
6514                 } else if (t > 0) {
\r 
6523         return sqDist ? dx * dx + dy * dy : new Point(x, y);
\r 
6527   // @function isFlat(latlngs: LatLng[]): Boolean
\r 
6528   // Returns true if `latlngs` is a flat array, false is nested.
\r 
6529   function isFlat(latlngs) {
\r 
6530         return !isArray(latlngs[0]) || (typeof latlngs[0][0] !== 'object' && typeof latlngs[0][0] !== 'undefined');
\r 
6533   function _flat(latlngs) {
\r 
6534         console.warn('Deprecated use of _flat, please use L.LineUtil.isFlat instead.');
\r 
6535         return isFlat(latlngs);
\r 
6538   /* @function polylineCenter(latlngs: LatLng[], crs: CRS): LatLng
\r 
6539    * Returns the center ([centroid](http://en.wikipedia.org/wiki/Centroid)) of the passed LatLngs (first ring) from a polyline.
\r 
6541   function polylineCenter(latlngs, crs) {
\r 
6542         var i, halfDist, segDist, dist, p1, p2, ratio, center;
\r 
6544         if (!latlngs || latlngs.length === 0) {
\r 
6545                 throw new Error('latlngs not passed');
\r 
6548         if (!isFlat(latlngs)) {
\r 
6549                 console.warn('latlngs are not flat! Only the first ring will be used');
\r 
6550                 latlngs = latlngs[0];
\r 
6553         var centroidLatLng = toLatLng([0, 0]);
\r 
6555         var bounds = toLatLngBounds(latlngs);
\r 
6556         var areaBounds = bounds.getNorthWest().distanceTo(bounds.getSouthWest()) * bounds.getNorthEast().distanceTo(bounds.getNorthWest());
\r 
6557         // tests showed that below 1700 rounding errors are happening
\r 
6558         if (areaBounds < 1700) {
\r 
6559                 // getting a inexact center, to move the latlngs near to [0, 0] to prevent rounding errors
\r 
6560                 centroidLatLng = centroid(latlngs);
\r 
6563         var len = latlngs.length;
\r 
6565         for (i = 0; i < len; i++) {
\r 
6566                 var latlng = toLatLng(latlngs[i]);
\r 
6567                 points.push(crs.project(toLatLng([latlng.lat - centroidLatLng.lat, latlng.lng - centroidLatLng.lng])));
\r 
6570         for (i = 0, halfDist = 0; i < len - 1; i++) {
\r 
6571                 halfDist += points[i].distanceTo(points[i + 1]) / 2;
\r 
6574         // The line is so small in the current view that all points are on the same pixel.
\r 
6575         if (halfDist === 0) {
\r 
6576                 center = points[0];
\r 
6578                 for (i = 0, dist = 0; i < len - 1; i++) {
\r 
6580                         p2 = points[i + 1];
\r 
6581                         segDist = p1.distanceTo(p2);
\r 
6584                         if (dist > halfDist) {
\r 
6585                                 ratio = (dist - halfDist) / segDist;
\r 
6587                                         p2.x - ratio * (p2.x - p1.x),
\r 
6588                                         p2.y - ratio * (p2.y - p1.y)
\r 
6595         var latlngCenter = crs.unproject(toPoint(center));
\r 
6596         return toLatLng([latlngCenter.lat + centroidLatLng.lat, latlngCenter.lng + centroidLatLng.lng]);
\r 
6602     pointToSegmentDistance: pointToSegmentDistance,
 
6603     closestPointOnSegment: closestPointOnSegment,
 
6604     clipSegment: clipSegment,
 
6605     _getEdgeIntersection: _getEdgeIntersection,
 
6606     _getBitCode: _getBitCode,
 
6607     _sqClosestPointOnSegment: _sqClosestPointOnSegment,
 
6610     polylineCenter: polylineCenter
 
6614    * @namespace Projection
\r 
6616    * Leaflet comes with a set of already defined Projections out of the box:
\r 
6618    * @projection L.Projection.LonLat
\r 
6620    * Equirectangular, or Plate Carree projection — the most simple projection,
\r 
6621    * mostly used by GIS enthusiasts. Directly maps `x` as longitude, and `y` as
\r 
6622    * latitude. Also suitable for flat worlds, e.g. game maps. Used by the
\r 
6623    * `EPSG:4326` and `Simple` CRS.
\r 
6627         project: function (latlng) {
\r 
6628                 return new Point(latlng.lng, latlng.lat);
\r 
6631         unproject: function (point) {
\r 
6632                 return new LatLng(point.y, point.x);
\r 
6635         bounds: new Bounds([-180, -90], [180, 90])
\r 
6639    * @namespace Projection
\r 
6640    * @projection L.Projection.Mercator
\r 
6642    * Elliptical Mercator projection — more complex than Spherical Mercator. Assumes that Earth is an ellipsoid. Used by the EPSG:3395 CRS.
\r 
6647         R_MINOR: 6356752.314245179,
\r 
6649         bounds: new Bounds([-20037508.34279, -15496570.73972], [20037508.34279, 18764656.23138]),
\r 
6651         project: function (latlng) {
\r 
6652                 var d = Math.PI / 180,
\r 
6654                     y = latlng.lat * d,
\r 
6655                     tmp = this.R_MINOR / r,
\r 
6656                     e = Math.sqrt(1 - tmp * tmp),
\r 
6657                     con = e * Math.sin(y);
\r 
6659                 var ts = Math.tan(Math.PI / 4 - y / 2) / Math.pow((1 - con) / (1 + con), e / 2);
\r 
6660                 y = -r * Math.log(Math.max(ts, 1E-10));
\r 
6662                 return new Point(latlng.lng * d * r, y);
\r 
6665         unproject: function (point) {
\r 
6666                 var d = 180 / Math.PI,
\r 
6668                     tmp = this.R_MINOR / r,
\r 
6669                     e = Math.sqrt(1 - tmp * tmp),
\r 
6670                     ts = Math.exp(-point.y / r),
\r 
6671                     phi = Math.PI / 2 - 2 * Math.atan(ts);
\r 
6673                 for (var i = 0, dphi = 0.1, con; i < 15 && Math.abs(dphi) > 1e-7; i++) {
\r 
6674                         con = e * Math.sin(phi);
\r 
6675                         con = Math.pow((1 - con) / (1 + con), e / 2);
\r 
6676                         dphi = Math.PI / 2 - 2 * Math.atan(ts * con) - phi;
\r 
6680                 return new LatLng(phi * d, point.x * d / r);
\r 
6687    * An object with methods for projecting geographical coordinates of the world onto
 
6688    * a flat surface (and back). See [Map projection](https://en.wikipedia.org/wiki/Map_projection).
 
6690    * @property bounds: Bounds
 
6691    * The bounds (specified in CRS units) where the projection is valid
 
6693    * @method project(latlng: LatLng): Point
 
6694    * Projects geographical coordinates into a 2D point.
 
6695    * Only accepts actual `L.LatLng` instances, not arrays.
 
6697    * @method unproject(point: Point): LatLng
 
6698    * The inverse of `project`. Projects a 2D point into a geographical location.
 
6699    * Only accepts actual `L.Point` instances, not arrays.
 
6701    * Note that the projection instances do not inherit from Leaflet's `Class` object,
 
6702    * and can't be instantiated. Also, new classes can't inherit from them,
 
6703    * and methods can't be added to them with the `include` function.
 
6711     SphericalMercator: SphericalMercator
 
6716    * @crs L.CRS.EPSG3395
\r 
6718    * Rarely used by some commercial tile providers. Uses Elliptical Mercator projection.
\r 
6720   var EPSG3395 = extend({}, Earth, {
\r 
6721         code: 'EPSG:3395',
\r 
6722         projection: Mercator,
\r 
6724         transformation: (function () {
\r 
6725                 var scale = 0.5 / (Math.PI * Mercator.R);
\r 
6726                 return toTransformation(scale, 0.5, -scale, 0.5);
\r 
6732    * @crs L.CRS.EPSG4326
\r 
6734    * A common CRS among GIS enthusiasts. Uses simple Equirectangular projection.
\r 
6736    * Leaflet 1.0.x complies with the [TMS coordinate scheme for EPSG:4326](https://wiki.osgeo.org/wiki/Tile_Map_Service_Specification#global-geodetic),
\r 
6737    * which is a breaking change from 0.7.x behaviour.  If you are using a `TileLayer`
\r 
6738    * with this CRS, ensure that there are two 256x256 pixel tiles covering the
\r 
6739    * whole earth at zoom level zero, and that the tile coordinate origin is (-180,+90),
\r 
6740    * or (-180,-90) for `TileLayer`s with [the `tms` option](#tilelayer-tms) set.
\r 
6743   var EPSG4326 = extend({}, Earth, {
\r 
6744         code: 'EPSG:4326',
\r 
6745         projection: LonLat,
\r 
6746         transformation: toTransformation(1 / 180, 1, -1 / 180, 0.5)
\r 
6753    * A simple CRS that maps longitude and latitude into `x` and `y` directly.
 
6754    * May be used for maps of flat surfaces (e.g. game maps). Note that the `y`
 
6755    * axis should still be inverted (going from bottom to top). `distance()` returns
 
6756    * simple euclidean distance.
 
6759   var Simple = extend({}, CRS, {
 
6761         transformation: toTransformation(1, 0, -1, 0),
 
6763         scale: function (zoom) {
 
6764                 return Math.pow(2, zoom);
 
6767         zoom: function (scale) {
 
6768                 return Math.log(scale) / Math.LN2;
 
6771         distance: function (latlng1, latlng2) {
 
6772                 var dx = latlng2.lng - latlng1.lng,
 
6773                     dy = latlng2.lat - latlng1.lat;
 
6775                 return Math.sqrt(dx * dx + dy * dy);
 
6782   CRS.EPSG3395 = EPSG3395;
 
6783   CRS.EPSG3857 = EPSG3857;
 
6784   CRS.EPSG900913 = EPSG900913;
 
6785   CRS.EPSG4326 = EPSG4326;
 
6786   CRS.Simple = Simple;
 
6794    * A set of methods from the Layer base class that all Leaflet layers use.
 
6795    * Inherits all methods, options and events from `L.Evented`.
 
6800    * var layer = L.marker(latlng).addTo(map);
 
6806    * Fired after the layer is added to a map
 
6808    * @event remove: Event
 
6809    * Fired after the layer is removed from a map
 
6813   var Layer = Evented.extend({
 
6815         // Classes extending `L.Layer` will inherit the following options:
 
6817                 // @option pane: String = 'overlayPane'
 
6818                 // 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.
 
6819                 pane: 'overlayPane',
 
6821                 // @option attribution: String = null
 
6822                 // 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.
 
6825                 bubblingMouseEvents: true
 
6829          * Classes extending `L.Layer` will inherit the following methods:
 
6831          * @method addTo(map: Map|LayerGroup): this
 
6832          * Adds the layer to the given map or layer group.
 
6834         addTo: function (map) {
 
6839         // @method remove: this
 
6840         // Removes the layer from the map it is currently active on.
 
6841         remove: function () {
 
6842                 return this.removeFrom(this._map || this._mapToAdd);
 
6845         // @method removeFrom(map: Map): this
 
6846         // Removes the layer from the given map
 
6849         // @method removeFrom(group: LayerGroup): this
 
6850         // Removes the layer from the given `LayerGroup`
 
6851         removeFrom: function (obj) {
 
6853                         obj.removeLayer(this);
 
6858         // @method getPane(name? : String): HTMLElement
 
6859         // Returns the `HTMLElement` representing the named pane on the map. If `name` is omitted, returns the pane for this layer.
 
6860         getPane: function (name) {
 
6861                 return this._map.getPane(name ? (this.options[name] || name) : this.options.pane);
 
6864         addInteractiveTarget: function (targetEl) {
 
6865                 this._map._targets[stamp(targetEl)] = this;
 
6869         removeInteractiveTarget: function (targetEl) {
 
6870                 delete this._map._targets[stamp(targetEl)];
 
6874         // @method getAttribution: String
 
6875         // Used by the `attribution control`, returns the [attribution option](#gridlayer-attribution).
 
6876         getAttribution: function () {
 
6877                 return this.options.attribution;
 
6880         _layerAdd: function (e) {
 
6883                 // check in case layer gets added and then removed before the map is ready
 
6884                 if (!map.hasLayer(this)) { return; }
 
6887                 this._zoomAnimated = map._zoomAnimated;
 
6889                 if (this.getEvents) {
 
6890                         var events = this.getEvents();
 
6891                         map.on(events, this);
 
6892                         this.once('remove', function () {
 
6893                                 map.off(events, this);
 
6900                 map.fire('layeradd', {layer: this});
 
6904   /* @section Extension methods
 
6907    * Every layer should extend from `L.Layer` and (re-)implement the following methods.
 
6909    * @method onAdd(map: Map): this
 
6910    * 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).
 
6912    * @method onRemove(map: Map): this
 
6913    * 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).
 
6915    * @method getEvents(): Object
 
6916    * 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.
 
6918    * @method getAttribution(): String
 
6919    * This optional method should return a string containing HTML to be shown on the `Attribution control` whenever the layer is visible.
 
6921    * @method beforeAdd(map: Map): this
 
6922    * 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.
 
6927    * @section Layer events
 
6929    * @event layeradd: LayerEvent
 
6930    * Fired when a new layer is added to the map.
 
6932    * @event layerremove: LayerEvent
 
6933    * Fired when some layer is removed from the map
 
6935    * @section Methods for Layers and Controls
 
6938         // @method addLayer(layer: Layer): this
 
6939         // Adds the given layer to the map
 
6940         addLayer: function (layer) {
 
6941                 if (!layer._layerAdd) {
 
6942                         throw new Error('The provided object is not a Layer.');
 
6945                 var id = stamp(layer);
 
6946                 if (this._layers[id]) { return this; }
 
6947                 this._layers[id] = layer;
 
6949                 layer._mapToAdd = this;
 
6951                 if (layer.beforeAdd) {
 
6952                         layer.beforeAdd(this);
 
6955                 this.whenReady(layer._layerAdd, layer);
 
6960         // @method removeLayer(layer: Layer): this
 
6961         // Removes the given layer from the map.
 
6962         removeLayer: function (layer) {
 
6963                 var id = stamp(layer);
 
6965                 if (!this._layers[id]) { return this; }
 
6968                         layer.onRemove(this);
 
6971                 delete this._layers[id];
 
6974                         this.fire('layerremove', {layer: layer});
 
6975                         layer.fire('remove');
 
6978                 layer._map = layer._mapToAdd = null;
 
6983         // @method hasLayer(layer: Layer): Boolean
 
6984         // Returns `true` if the given layer is currently added to the map
 
6985         hasLayer: function (layer) {
 
6986                 return stamp(layer) in this._layers;
 
6989         /* @method eachLayer(fn: Function, context?: Object): this
 
6990          * Iterates over the layers of the map, optionally specifying context of the iterator function.
 
6992          * map.eachLayer(function(layer){
 
6993          *     layer.bindPopup('Hello');
 
6997         eachLayer: function (method, context) {
 
6998                 for (var i in this._layers) {
 
6999                         method.call(context, this._layers[i]);
 
7004         _addLayers: function (layers) {
 
7005                 layers = layers ? (isArray(layers) ? layers : [layers]) : [];
 
7007                 for (var i = 0, len = layers.length; i < len; i++) {
 
7008                         this.addLayer(layers[i]);
 
7012         _addZoomLimit: function (layer) {
 
7013                 if (!isNaN(layer.options.maxZoom) || !isNaN(layer.options.minZoom)) {
 
7014                         this._zoomBoundLayers[stamp(layer)] = layer;
 
7015                         this._updateZoomLevels();
 
7019         _removeZoomLimit: function (layer) {
 
7020                 var id = stamp(layer);
 
7022                 if (this._zoomBoundLayers[id]) {
 
7023                         delete this._zoomBoundLayers[id];
 
7024                         this._updateZoomLevels();
 
7028         _updateZoomLevels: function () {
 
7029                 var minZoom = Infinity,
 
7030                     maxZoom = -Infinity,
 
7031                     oldZoomSpan = this._getZoomSpan();
 
7033                 for (var i in this._zoomBoundLayers) {
 
7034                         var options = this._zoomBoundLayers[i].options;
 
7036                         minZoom = options.minZoom === undefined ? minZoom : Math.min(minZoom, options.minZoom);
 
7037                         maxZoom = options.maxZoom === undefined ? maxZoom : Math.max(maxZoom, options.maxZoom);
 
7040                 this._layersMaxZoom = maxZoom === -Infinity ? undefined : maxZoom;
 
7041                 this._layersMinZoom = minZoom === Infinity ? undefined : minZoom;
 
7043                 // @section Map state change events
 
7044                 // @event zoomlevelschange: Event
 
7045                 // Fired when the number of zoomlevels on the map is changed due
 
7046                 // to adding or removing a layer.
 
7047                 if (oldZoomSpan !== this._getZoomSpan()) {
 
7048                         this.fire('zoomlevelschange');
 
7051                 if (this.options.maxZoom === undefined && this._layersMaxZoom && this.getZoom() > this._layersMaxZoom) {
 
7052                         this.setZoom(this._layersMaxZoom);
 
7054                 if (this.options.minZoom === undefined && this._layersMinZoom && this.getZoom() < this._layersMinZoom) {
 
7055                         this.setZoom(this._layersMinZoom);
 
7061    * @class LayerGroup
\r 
7062    * @aka L.LayerGroup
\r 
7063    * @inherits Interactive layer
\r 
7065    * Used to group several layers and handle them as one. If you add it to the map,
\r 
7066    * any layers added or removed from the group will be added/removed on the map as
\r 
7067    * well. Extends `Layer`.
\r 
7072    * L.layerGroup([marker1, marker2])
\r 
7073    *    .addLayer(polyline)
\r 
7078   var LayerGroup = Layer.extend({
\r 
7080         initialize: function (layers, options) {
\r 
7081                 setOptions(this, options);
\r 
7083                 this._layers = {};
\r 
7088                         for (i = 0, len = layers.length; i < len; i++) {
\r 
7089                                 this.addLayer(layers[i]);
\r 
7094         // @method addLayer(layer: Layer): this
\r 
7095         // Adds the given layer to the group.
\r 
7096         addLayer: function (layer) {
\r 
7097                 var id = this.getLayerId(layer);
\r 
7099                 this._layers[id] = layer;
\r 
7102                         this._map.addLayer(layer);
\r 
7108         // @method removeLayer(layer: Layer): this
\r 
7109         // Removes the given layer from the group.
\r 
7111         // @method removeLayer(id: Number): this
\r 
7112         // Removes the layer with the given internal ID from the group.
\r 
7113         removeLayer: function (layer) {
\r 
7114                 var id = layer in this._layers ? layer : this.getLayerId(layer);
\r 
7116                 if (this._map && this._layers[id]) {
\r 
7117                         this._map.removeLayer(this._layers[id]);
\r 
7120                 delete this._layers[id];
\r 
7125         // @method hasLayer(layer: Layer): Boolean
\r 
7126         // Returns `true` if the given layer is currently added to the group.
\r 
7128         // @method hasLayer(id: Number): Boolean
\r 
7129         // Returns `true` if the given internal ID is currently added to the group.
\r 
7130         hasLayer: function (layer) {
\r 
7131                 var layerId = typeof layer === 'number' ? layer : this.getLayerId(layer);
\r 
7132                 return layerId in this._layers;
\r 
7135         // @method clearLayers(): this
\r 
7136         // Removes all the layers from the group.
\r 
7137         clearLayers: function () {
\r 
7138                 return this.eachLayer(this.removeLayer, this);
\r 
7141         // @method invoke(methodName: String, …): this
\r 
7142         // Calls `methodName` on every layer contained in this group, passing any
\r 
7143         // additional parameters. Has no effect if the layers contained do not
\r 
7144         // implement `methodName`.
\r 
7145         invoke: function (methodName) {
\r 
7146                 var args = Array.prototype.slice.call(arguments, 1),
\r 
7149                 for (i in this._layers) {
\r 
7150                         layer = this._layers[i];
\r 
7152                         if (layer[methodName]) {
\r 
7153                                 layer[methodName].apply(layer, args);
\r 
7160         onAdd: function (map) {
\r 
7161                 this.eachLayer(map.addLayer, map);
\r 
7164         onRemove: function (map) {
\r 
7165                 this.eachLayer(map.removeLayer, map);
\r 
7168         // @method eachLayer(fn: Function, context?: Object): this
\r 
7169         // Iterates over the layers of the group, optionally specifying context of the iterator function.
\r 
7171         // group.eachLayer(function (layer) {
\r 
7172         //      layer.bindPopup('Hello');
\r 
7175         eachLayer: function (method, context) {
\r 
7176                 for (var i in this._layers) {
\r 
7177                         method.call(context, this._layers[i]);
\r 
7182         // @method getLayer(id: Number): Layer
\r 
7183         // Returns the layer with the given internal ID.
\r 
7184         getLayer: function (id) {
\r 
7185                 return this._layers[id];
\r 
7188         // @method getLayers(): Layer[]
\r 
7189         // Returns an array of all the layers added to the group.
\r 
7190         getLayers: function () {
\r 
7192                 this.eachLayer(layers.push, layers);
\r 
7196         // @method setZIndex(zIndex: Number): this
\r 
7197         // Calls `setZIndex` on every layer contained in this group, passing the z-index.
\r 
7198         setZIndex: function (zIndex) {
\r 
7199                 return this.invoke('setZIndex', zIndex);
\r 
7202         // @method getLayerId(layer: Layer): Number
\r 
7203         // Returns the internal ID for a layer
\r 
7204         getLayerId: function (layer) {
\r 
7205                 return stamp(layer);
\r 
7210   // @factory L.layerGroup(layers?: Layer[], options?: Object)
\r 
7211   // Create a layer group, optionally given an initial set of layers and an `options` object.
\r 
7212   var layerGroup = function (layers, options) {
\r 
7213         return new LayerGroup(layers, options);
\r 
7217    * @class FeatureGroup
\r 
7218    * @aka L.FeatureGroup
\r 
7219    * @inherits LayerGroup
\r 
7221    * Extended `LayerGroup` that makes it easier to do the same thing to all its member layers:
\r 
7222    *  * [`bindPopup`](#layer-bindpopup) binds a popup to all of the layers at once (likewise with [`bindTooltip`](#layer-bindtooltip))
\r 
7223    *  * Events are propagated to the `FeatureGroup`, so if the group has an event
\r 
7224    * handler, it will handle events from any of the layers. This includes mouse events
\r 
7225    * and custom events.
\r 
7226    *  * Has `layeradd` and `layerremove` events
\r 
7231    * L.featureGroup([marker1, marker2, polyline])
\r 
7232    *    .bindPopup('Hello world!')
\r 
7233    *    .on('click', function() { alert('Clicked on a member of the group!'); })
\r 
7238   var FeatureGroup = LayerGroup.extend({
\r 
7240         addLayer: function (layer) {
\r 
7241                 if (this.hasLayer(layer)) {
\r 
7245                 layer.addEventParent(this);
\r 
7247                 LayerGroup.prototype.addLayer.call(this, layer);
\r 
7249                 // @event layeradd: LayerEvent
\r 
7250                 // Fired when a layer is added to this `FeatureGroup`
\r 
7251                 return this.fire('layeradd', {layer: layer});
\r 
7254         removeLayer: function (layer) {
\r 
7255                 if (!this.hasLayer(layer)) {
\r 
7258                 if (layer in this._layers) {
\r 
7259                         layer = this._layers[layer];
\r 
7262                 layer.removeEventParent(this);
\r 
7264                 LayerGroup.prototype.removeLayer.call(this, layer);
\r 
7266                 // @event layerremove: LayerEvent
\r 
7267                 // Fired when a layer is removed from this `FeatureGroup`
\r 
7268                 return this.fire('layerremove', {layer: layer});
\r 
7271         // @method setStyle(style: Path options): this
\r 
7272         // Sets the given path options to each layer of the group that has a `setStyle` method.
\r 
7273         setStyle: function (style) {
\r 
7274                 return this.invoke('setStyle', style);
\r 
7277         // @method bringToFront(): this
\r 
7278         // Brings the layer group to the top of all other layers
\r 
7279         bringToFront: function () {
\r 
7280                 return this.invoke('bringToFront');
\r 
7283         // @method bringToBack(): this
\r 
7284         // Brings the layer group to the back of all other layers
\r 
7285         bringToBack: function () {
\r 
7286                 return this.invoke('bringToBack');
\r 
7289         // @method getBounds(): LatLngBounds
\r 
7290         // Returns the LatLngBounds of the Feature Group (created from bounds and coordinates of its children).
\r 
7291         getBounds: function () {
\r 
7292                 var bounds = new LatLngBounds();
\r 
7294                 for (var id in this._layers) {
\r 
7295                         var layer = this._layers[id];
\r 
7296                         bounds.extend(layer.getBounds ? layer.getBounds() : layer.getLatLng());
\r 
7302   // @factory L.featureGroup(layers?: Layer[], options?: Object)
\r 
7303   // Create a feature group, optionally given an initial set of layers and an `options` object.
\r 
7304   var featureGroup = function (layers, options) {
\r 
7305         return new FeatureGroup(layers, options);
\r 
7312    * Represents an icon to provide when creating a marker.
\r 
7317    * var myIcon = L.icon({
\r 
7318    *     iconUrl: 'my-icon.png',
\r 
7319    *     iconRetinaUrl: 'my-icon@2x.png',
\r 
7320    *     iconSize: [38, 95],
\r 
7321    *     iconAnchor: [22, 94],
\r 
7322    *     popupAnchor: [-3, -76],
\r 
7323    *     shadowUrl: 'my-icon-shadow.png',
\r 
7324    *     shadowRetinaUrl: 'my-icon-shadow@2x.png',
\r 
7325    *     shadowSize: [68, 95],
\r 
7326    *     shadowAnchor: [22, 94]
\r 
7329    * L.marker([50.505, 30.57], {icon: myIcon}).addTo(map);
\r 
7332    * `L.Icon.Default` extends `L.Icon` and is the blue icon Leaflet uses for markers by default.
\r 
7336   var Icon = Class.extend({
\r 
7339          * @aka Icon options
\r 
7341          * @option iconUrl: String = null
\r 
7342          * **(required)** The URL to the icon image (absolute or relative to your script path).
\r 
7344          * @option iconRetinaUrl: String = null
\r 
7345          * The URL to a retina sized version of the icon image (absolute or relative to your
\r 
7346          * script path). Used for Retina screen devices.
\r 
7348          * @option iconSize: Point = null
\r 
7349          * Size of the icon image in pixels.
\r 
7351          * @option iconAnchor: Point = null
\r 
7352          * The coordinates of the "tip" of the icon (relative to its top left corner). The icon
\r 
7353          * will be aligned so that this point is at the marker's geographical location. Centered
\r 
7354          * by default if size is specified, also can be set in CSS with negative margins.
\r 
7356          * @option popupAnchor: Point = [0, 0]
\r 
7357          * The coordinates of the point from which popups will "open", relative to the icon anchor.
\r 
7359          * @option tooltipAnchor: Point = [0, 0]
\r 
7360          * The coordinates of the point from which tooltips will "open", relative to the icon anchor.
\r 
7362          * @option shadowUrl: String = null
\r 
7363          * The URL to the icon shadow image. If not specified, no shadow image will be created.
\r 
7365          * @option shadowRetinaUrl: String = null
\r 
7367          * @option shadowSize: Point = null
\r 
7368          * Size of the shadow image in pixels.
\r 
7370          * @option shadowAnchor: Point = null
\r 
7371          * The coordinates of the "tip" of the shadow (relative to its top left corner) (the same
\r 
7372          * as iconAnchor if not specified).
\r 
7374          * @option className: String = ''
\r 
7375          * A custom class name to assign to both icon and shadow images. Empty by default.
\r 
7379                 popupAnchor: [0, 0],
\r 
7380                 tooltipAnchor: [0, 0],
\r 
7382                 // @option crossOrigin: Boolean|String = false
\r 
7383                 // Whether the crossOrigin attribute will be added to the tiles.
\r 
7384                 // 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.
\r 
7385                 // Refer to [CORS Settings](https://developer.mozilla.org/en-US/docs/Web/HTML/CORS_settings_attributes) for valid String values.
\r 
7386                 crossOrigin: false
\r 
7389         initialize: function (options) {
\r 
7390                 setOptions(this, options);
\r 
7393         // @method createIcon(oldIcon?: HTMLElement): HTMLElement
\r 
7394         // Called internally when the icon has to be shown, returns a `<img>` HTML element
\r 
7395         // styled according to the options.
\r 
7396         createIcon: function (oldIcon) {
\r 
7397                 return this._createIcon('icon', oldIcon);
\r 
7400         // @method createShadow(oldIcon?: HTMLElement): HTMLElement
\r 
7401         // As `createIcon`, but for the shadow beneath it.
\r 
7402         createShadow: function (oldIcon) {
\r 
7403                 return this._createIcon('shadow', oldIcon);
\r 
7406         _createIcon: function (name, oldIcon) {
\r 
7407                 var src = this._getIconUrl(name);
\r 
7410                         if (name === 'icon') {
\r 
7411                                 throw new Error('iconUrl not set in Icon options (see the docs).');
\r 
7416                 var img = this._createImg(src, oldIcon && oldIcon.tagName === 'IMG' ? oldIcon : null);
\r 
7417                 this._setIconStyles(img, name);
\r 
7419                 if (this.options.crossOrigin || this.options.crossOrigin === '') {
\r 
7420                         img.crossOrigin = this.options.crossOrigin === true ? '' : this.options.crossOrigin;
\r 
7426         _setIconStyles: function (img, name) {
\r 
7427                 var options = this.options;
\r 
7428                 var sizeOption = options[name + 'Size'];
\r 
7430                 if (typeof sizeOption === 'number') {
\r 
7431                         sizeOption = [sizeOption, sizeOption];
\r 
7434                 var size = toPoint(sizeOption),
\r 
7435                     anchor = toPoint(name === 'shadow' && options.shadowAnchor || options.iconAnchor ||
\r 
7436                             size && size.divideBy(2, true));
\r 
7438                 img.className = 'leaflet-marker-' + name + ' ' + (options.className || '');
\r 
7441                         img.style.marginLeft = (-anchor.x) + 'px';
\r 
7442                         img.style.marginTop  = (-anchor.y) + 'px';
\r 
7446                         img.style.width  = size.x + 'px';
\r 
7447                         img.style.height = size.y + 'px';
\r 
7451         _createImg: function (src, el) {
\r 
7452                 el = el || document.createElement('img');
\r 
7457         _getIconUrl: function (name) {
\r 
7458                 return Browser.retina && this.options[name + 'RetinaUrl'] || this.options[name + 'Url'];
\r 
7463   // @factory L.icon(options: Icon options)
\r 
7464   // Creates an icon instance with the given options.
\r 
7465   function icon(options) {
\r 
7466         return new Icon(options);
\r 
7470    * @miniclass Icon.Default (Icon)
 
7471    * @aka L.Icon.Default
 
7474    * A trivial subclass of `Icon`, represents the icon to use in `Marker`s when
 
7475    * no icon is specified. Points to the blue marker image distributed with Leaflet
 
7478    * In order to customize the default icon, just change the properties of `L.Icon.Default.prototype.options`
 
7479    * (which is a set of `Icon options`).
 
7481    * If you want to _completely_ replace the default icon, override the
 
7482    * `L.Marker.prototype.options.icon` with your own icon instead.
 
7485   var IconDefault = Icon.extend({
 
7488                 iconUrl:       'marker-icon.png',
 
7489                 iconRetinaUrl: 'marker-icon-2x.png',
 
7490                 shadowUrl:     'marker-shadow.png',
 
7492                 iconAnchor:  [12, 41],
 
7493                 popupAnchor: [1, -34],
 
7494                 tooltipAnchor: [16, -28],
 
7495                 shadowSize:  [41, 41]
 
7498         _getIconUrl: function (name) {
 
7499                 if (typeof IconDefault.imagePath !== 'string') {        // Deprecated, backwards-compatibility only
 
7500                         IconDefault.imagePath = this._detectIconPath();
 
7503                 // @option imagePath: String
 
7504                 // `Icon.Default` will try to auto-detect the location of the
 
7505                 // blue icon images. If you are placing these images in a non-standard
 
7506                 // way, set this option to point to the right path.
 
7507                 return (this.options.imagePath || IconDefault.imagePath) + Icon.prototype._getIconUrl.call(this, name);
 
7510         _stripUrl: function (path) {    // separate function to use in tests
 
7511                 var strip = function (str, re, idx) {
 
7512                         var match = re.exec(str);
 
7513                         return match && match[idx];
 
7515                 path = strip(path, /^url\((['"])?(.+)\1\)$/, 2);
 
7516                 return path && strip(path, /^(.*)marker-icon\.png$/, 1);
 
7519         _detectIconPath: function () {
 
7520                 var el = create$1('div',  'leaflet-default-icon-path', document.body);
 
7521                 var path = getStyle(el, 'background-image') ||
 
7522                            getStyle(el, 'backgroundImage');     // IE8
 
7524                 document.body.removeChild(el);
 
7525                 path = this._stripUrl(path);
 
7526                 if (path) { return path; }
 
7527                 var link = document.querySelector('link[href$="leaflet.css"]');
 
7528                 if (!link) { return ''; }
 
7529                 return link.href.substring(0, link.href.length - 'leaflet.css'.length - 1);
 
7534    * L.Handler.MarkerDrag is used internally by L.Marker to make the markers draggable.
 
7538   /* @namespace Marker
 
7539    * @section Interaction handlers
 
7541    * 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:
 
7544    * marker.dragging.disable();
 
7547    * @property dragging: Handler
 
7548    * Marker dragging handler (by both mouse and touch). Only valid when the marker is on the map (Otherwise set [`marker.options.draggable`](#marker-draggable)).
 
7551   var MarkerDrag = Handler.extend({
 
7552         initialize: function (marker) {
 
7553                 this._marker = marker;
 
7556         addHooks: function () {
 
7557                 var icon = this._marker._icon;
 
7559                 if (!this._draggable) {
 
7560                         this._draggable = new Draggable(icon, icon, true);
 
7563                 this._draggable.on({
 
7564                         dragstart: this._onDragStart,
 
7565                         predrag: this._onPreDrag,
 
7567                         dragend: this._onDragEnd
 
7570                 addClass(icon, 'leaflet-marker-draggable');
 
7573         removeHooks: function () {
 
7574                 this._draggable.off({
 
7575                         dragstart: this._onDragStart,
 
7576                         predrag: this._onPreDrag,
 
7578                         dragend: this._onDragEnd
 
7581                 if (this._marker._icon) {
 
7582                         removeClass(this._marker._icon, 'leaflet-marker-draggable');
 
7586         moved: function () {
 
7587                 return this._draggable && this._draggable._moved;
 
7590         _adjustPan: function (e) {
 
7591                 var marker = this._marker,
 
7593                     speed = this._marker.options.autoPanSpeed,
 
7594                     padding = this._marker.options.autoPanPadding,
 
7595                     iconPos = getPosition(marker._icon),
 
7596                     bounds = map.getPixelBounds(),
 
7597                     origin = map.getPixelOrigin();
 
7599                 var panBounds = toBounds(
 
7600                         bounds.min._subtract(origin).add(padding),
 
7601                         bounds.max._subtract(origin).subtract(padding)
 
7604                 if (!panBounds.contains(iconPos)) {
 
7605                         // Compute incremental movement
 
7606                         var movement = toPoint(
 
7607                                 (Math.max(panBounds.max.x, iconPos.x) - panBounds.max.x) / (bounds.max.x - panBounds.max.x) -
 
7608                                 (Math.min(panBounds.min.x, iconPos.x) - panBounds.min.x) / (bounds.min.x - panBounds.min.x),
 
7610                                 (Math.max(panBounds.max.y, iconPos.y) - panBounds.max.y) / (bounds.max.y - panBounds.max.y) -
 
7611                                 (Math.min(panBounds.min.y, iconPos.y) - panBounds.min.y) / (bounds.min.y - panBounds.min.y)
 
7612                         ).multiplyBy(speed);
 
7614                         map.panBy(movement, {animate: false});
 
7616                         this._draggable._newPos._add(movement);
 
7617                         this._draggable._startPos._add(movement);
 
7619                         setPosition(marker._icon, this._draggable._newPos);
 
7622                         this._panRequest = requestAnimFrame(this._adjustPan.bind(this, e));
 
7626         _onDragStart: function () {
 
7627                 // @section Dragging events
 
7628                 // @event dragstart: Event
 
7629                 // Fired when the user starts dragging the marker.
 
7631                 // @event movestart: Event
 
7632                 // Fired when the marker starts moving (because of dragging).
 
7634                 this._oldLatLng = this._marker.getLatLng();
 
7636                 // When using ES6 imports it could not be set when `Popup` was not imported as well
 
7637                 this._marker.closePopup && this._marker.closePopup();
 
7644         _onPreDrag: function (e) {
 
7645                 if (this._marker.options.autoPan) {
 
7646                         cancelAnimFrame(this._panRequest);
 
7647                         this._panRequest = requestAnimFrame(this._adjustPan.bind(this, e));
 
7651         _onDrag: function (e) {
 
7652                 var marker = this._marker,
 
7653                     shadow = marker._shadow,
 
7654                     iconPos = getPosition(marker._icon),
 
7655                     latlng = marker._map.layerPointToLatLng(iconPos);
 
7657                 // update shadow position
 
7659                         setPosition(shadow, iconPos);
 
7662                 marker._latlng = latlng;
 
7664                 e.oldLatLng = this._oldLatLng;
 
7666                 // @event drag: Event
 
7667                 // Fired repeatedly while the user drags the marker.
 
7673         _onDragEnd: function (e) {
 
7674                 // @event dragend: DragEndEvent
 
7675                 // Fired when the user stops dragging the marker.
 
7677                  cancelAnimFrame(this._panRequest);
 
7679                 // @event moveend: Event
 
7680                 // Fired when the marker stops moving (because of dragging).
 
7681                 delete this._oldLatLng;
 
7684                     .fire('dragend', e);
 
7690    * @inherits Interactive layer
\r 
7692    * L.Marker is used to display clickable/draggable icons on the map. Extends `Layer`.
\r 
7697    * L.marker([50.5, 30.5]).addTo(map);
\r 
7701   var Marker = Layer.extend({
\r 
7704         // @aka Marker options
\r 
7706                 // @option icon: Icon = *
\r 
7707                 // Icon instance to use for rendering the marker.
\r 
7708                 // See [Icon documentation](#L.Icon) for details on how to customize the marker icon.
\r 
7709                 // If not specified, a common instance of `L.Icon.Default` is used.
\r 
7710                 icon: new IconDefault(),
\r 
7712                 // Option inherited from "Interactive layer" abstract class
\r 
7713                 interactive: true,
\r 
7715                 // @option keyboard: Boolean = true
\r 
7716                 // Whether the marker can be tabbed to with a keyboard and clicked by pressing enter.
\r 
7719                 // @option title: String = ''
\r 
7720                 // Text for the browser tooltip that appear on marker hover (no tooltip by default).
\r 
7721                 // [Useful for accessibility](https://leafletjs.com/examples/accessibility/#markers-must-be-labelled).
\r 
7724                 // @option alt: String = 'Marker'
\r 
7725                 // Text for the `alt` attribute of the icon image.
\r 
7726                 // [Useful for accessibility](https://leafletjs.com/examples/accessibility/#markers-must-be-labelled).
\r 
7729                 // @option zIndexOffset: Number = 0
\r 
7730                 // 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).
\r 
7733                 // @option opacity: Number = 1.0
\r 
7734                 // The opacity of the marker.
\r 
7737                 // @option riseOnHover: Boolean = false
\r 
7738                 // If `true`, the marker will get on top of others when you hover the mouse over it.
\r 
7739                 riseOnHover: false,
\r 
7741                 // @option riseOffset: Number = 250
\r 
7742                 // The z-index offset used for the `riseOnHover` feature.
\r 
7745                 // @option pane: String = 'markerPane'
\r 
7746                 // `Map pane` where the markers icon will be added.
\r 
7747                 pane: 'markerPane',
\r 
7749                 // @option shadowPane: String = 'shadowPane'
\r 
7750                 // `Map pane` where the markers shadow will be added.
\r 
7751                 shadowPane: 'shadowPane',
\r 
7753                 // @option bubblingMouseEvents: Boolean = false
\r 
7754                 // When `true`, a mouse event on this marker will trigger the same event on the map
\r 
7755                 // (unless [`L.DomEvent.stopPropagation`](#domevent-stoppropagation) is used).
\r 
7756                 bubblingMouseEvents: false,
\r 
7758                 // @option autoPanOnFocus: Boolean = true
\r 
7759                 // When `true`, the map will pan whenever the marker is focused (via
\r 
7760                 // e.g. pressing `tab` on the keyboard) to ensure the marker is
\r 
7761                 // visible within the map's bounds
\r 
7762                 autoPanOnFocus: true,
\r 
7764                 // @section Draggable marker options
\r 
7765                 // @option draggable: Boolean = false
\r 
7766                 // Whether the marker is draggable with mouse/touch or not.
\r 
7769                 // @option autoPan: Boolean = false
\r 
7770                 // Whether to pan the map when dragging this marker near its edge or not.
\r 
7773                 // @option autoPanPadding: Point = Point(50, 50)
\r 
7774                 // Distance (in pixels to the left/right and to the top/bottom) of the
\r 
7775                 // map edge to start panning the map.
\r 
7776                 autoPanPadding: [50, 50],
\r 
7778                 // @option autoPanSpeed: Number = 10
\r 
7779                 // Number of pixels the map should pan by.
\r 
7785          * In addition to [shared layer methods](#Layer) like `addTo()` and `remove()` and [popup methods](#Popup) like bindPopup() you can also use the following methods:
\r 
7788         initialize: function (latlng, options) {
\r 
7789                 setOptions(this, options);
\r 
7790                 this._latlng = toLatLng(latlng);
\r 
7793         onAdd: function (map) {
\r 
7794                 this._zoomAnimated = this._zoomAnimated && map.options.markerZoomAnimation;
\r 
7796                 if (this._zoomAnimated) {
\r 
7797                         map.on('zoomanim', this._animateZoom, this);
\r 
7804         onRemove: function (map) {
\r 
7805                 if (this.dragging && this.dragging.enabled()) {
\r 
7806                         this.options.draggable = true;
\r 
7807                         this.dragging.removeHooks();
\r 
7809                 delete this.dragging;
\r 
7811                 if (this._zoomAnimated) {
\r 
7812                         map.off('zoomanim', this._animateZoom, this);
\r 
7815                 this._removeIcon();
\r 
7816                 this._removeShadow();
\r 
7819         getEvents: function () {
\r 
7821                         zoom: this.update,
\r 
7822                         viewreset: this.update
\r 
7826         // @method getLatLng: LatLng
\r 
7827         // Returns the current geographical position of the marker.
\r 
7828         getLatLng: function () {
\r 
7829                 return this._latlng;
\r 
7832         // @method setLatLng(latlng: LatLng): this
\r 
7833         // Changes the marker position to the given point.
\r 
7834         setLatLng: function (latlng) {
\r 
7835                 var oldLatLng = this._latlng;
\r 
7836                 this._latlng = toLatLng(latlng);
\r 
7839                 // @event move: Event
\r 
7840                 // 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`.
\r 
7841                 return this.fire('move', {oldLatLng: oldLatLng, latlng: this._latlng});
\r 
7844         // @method setZIndexOffset(offset: Number): this
\r 
7845         // Changes the [zIndex offset](#marker-zindexoffset) of the marker.
\r 
7846         setZIndexOffset: function (offset) {
\r 
7847                 this.options.zIndexOffset = offset;
\r 
7848                 return this.update();
\r 
7851         // @method getIcon: Icon
\r 
7852         // Returns the current icon used by the marker
\r 
7853         getIcon: function () {
\r 
7854                 return this.options.icon;
\r 
7857         // @method setIcon(icon: Icon): this
\r 
7858         // Changes the marker icon.
\r 
7859         setIcon: function (icon) {
\r 
7861                 this.options.icon = icon;
\r 
7868                 if (this._popup) {
\r 
7869                         this.bindPopup(this._popup, this._popup.options);
\r 
7875         getElement: function () {
\r 
7876                 return this._icon;
\r 
7879         update: function () {
\r 
7881                 if (this._icon && this._map) {
\r 
7882                         var pos = this._map.latLngToLayerPoint(this._latlng).round();
\r 
7883                         this._setPos(pos);
\r 
7889         _initIcon: function () {
\r 
7890                 var options = this.options,
\r 
7891                     classToAdd = 'leaflet-zoom-' + (this._zoomAnimated ? 'animated' : 'hide');
\r 
7893                 var icon = options.icon.createIcon(this._icon),
\r 
7896                 // if we're not reusing the icon, remove the old one and init new one
\r 
7897                 if (icon !== this._icon) {
\r 
7899                                 this._removeIcon();
\r 
7903                         if (options.title) {
\r 
7904                                 icon.title = options.title;
\r 
7907                         if (icon.tagName === 'IMG') {
\r 
7908                                 icon.alt = options.alt || '';
\r 
7912                 addClass(icon, classToAdd);
\r 
7914                 if (options.keyboard) {
\r 
7915                         icon.tabIndex = '0';
\r 
7916                         icon.setAttribute('role', 'button');
\r 
7919                 this._icon = icon;
\r 
7921                 if (options.riseOnHover) {
\r 
7923                                 mouseover: this._bringToFront,
\r 
7924                                 mouseout: this._resetZIndex
\r 
7928                 if (this.options.autoPanOnFocus) {
\r 
7929                         on(icon, 'focus', this._panOnFocus, this);
\r 
7932                 var newShadow = options.icon.createShadow(this._shadow),
\r 
7933                     addShadow = false;
\r 
7935                 if (newShadow !== this._shadow) {
\r 
7936                         this._removeShadow();
\r 
7941                         addClass(newShadow, classToAdd);
\r 
7942                         newShadow.alt = '';
\r 
7944                 this._shadow = newShadow;
\r 
7947                 if (options.opacity < 1) {
\r 
7948                         this._updateOpacity();
\r 
7953                         this.getPane().appendChild(this._icon);
\r 
7955                 this._initInteraction();
\r 
7956                 if (newShadow && addShadow) {
\r 
7957                         this.getPane(options.shadowPane).appendChild(this._shadow);
\r 
7961         _removeIcon: function () {
\r 
7962                 if (this.options.riseOnHover) {
\r 
7964                                 mouseover: this._bringToFront,
\r 
7965                                 mouseout: this._resetZIndex
\r 
7969                 if (this.options.autoPanOnFocus) {
\r 
7970                         off(this._icon, 'focus', this._panOnFocus, this);
\r 
7973                 remove(this._icon);
\r 
7974                 this.removeInteractiveTarget(this._icon);
\r 
7976                 this._icon = null;
\r 
7979         _removeShadow: function () {
\r 
7980                 if (this._shadow) {
\r 
7981                         remove(this._shadow);
\r 
7983                 this._shadow = null;
\r 
7986         _setPos: function (pos) {
\r 
7989                         setPosition(this._icon, pos);
\r 
7992                 if (this._shadow) {
\r 
7993                         setPosition(this._shadow, pos);
\r 
7996                 this._zIndex = pos.y + this.options.zIndexOffset;
\r 
7998                 this._resetZIndex();
\r 
8001         _updateZIndex: function (offset) {
\r 
8003                         this._icon.style.zIndex = this._zIndex + offset;
\r 
8007         _animateZoom: function (opt) {
\r 
8008                 var pos = this._map._latLngToNewLayerPoint(this._latlng, opt.zoom, opt.center).round();
\r 
8010                 this._setPos(pos);
\r 
8013         _initInteraction: function () {
\r 
8015                 if (!this.options.interactive) { return; }
\r 
8017                 addClass(this._icon, 'leaflet-interactive');
\r 
8019                 this.addInteractiveTarget(this._icon);
\r 
8022                         var draggable = this.options.draggable;
\r 
8023                         if (this.dragging) {
\r 
8024                                 draggable = this.dragging.enabled();
\r 
8025                                 this.dragging.disable();
\r 
8028                         this.dragging = new MarkerDrag(this);
\r 
8031                                 this.dragging.enable();
\r 
8036         // @method setOpacity(opacity: Number): this
\r 
8037         // Changes the opacity of the marker.
\r 
8038         setOpacity: function (opacity) {
\r 
8039                 this.options.opacity = opacity;
\r 
8041                         this._updateOpacity();
\r 
8047         _updateOpacity: function () {
\r 
8048                 var opacity = this.options.opacity;
\r 
8051                         setOpacity(this._icon, opacity);
\r 
8054                 if (this._shadow) {
\r 
8055                         setOpacity(this._shadow, opacity);
\r 
8059         _bringToFront: function () {
\r 
8060                 this._updateZIndex(this.options.riseOffset);
\r 
8063         _resetZIndex: function () {
\r 
8064                 this._updateZIndex(0);
\r 
8067         _panOnFocus: function () {
\r 
8068                 var map = this._map;
\r 
8069                 if (!map) { return; }
\r 
8071                 var iconOpts = this.options.icon.options;
\r 
8072                 var size = iconOpts.iconSize ? toPoint(iconOpts.iconSize) : toPoint(0, 0);
\r 
8073                 var anchor = iconOpts.iconAnchor ? toPoint(iconOpts.iconAnchor) : toPoint(0, 0);
\r 
8075                 map.panInside(this._latlng, {
\r 
8076                         paddingTopLeft: anchor,
\r 
8077                         paddingBottomRight: size.subtract(anchor)
\r 
8081         _getPopupAnchor: function () {
\r 
8082                 return this.options.icon.options.popupAnchor;
\r 
8085         _getTooltipAnchor: function () {
\r 
8086                 return this.options.icon.options.tooltipAnchor;
\r 
8091   // factory L.marker(latlng: LatLng, options? : Marker options)
\r 
8093   // @factory L.marker(latlng: LatLng, options? : Marker options)
\r 
8094   // Instantiates a Marker object given a geographical point and optionally an options object.
\r 
8095   function marker(latlng, options) {
\r 
8096         return new Marker(latlng, options);
\r 
8102    * @inherits Interactive layer
 
8104    * An abstract class that contains options and constants shared between vector
 
8105    * overlays (Polygon, Polyline, Circle). Do not use it directly. Extends `Layer`.
 
8108   var Path = Layer.extend({
 
8111         // @aka Path options
 
8113                 // @option stroke: Boolean = true
 
8114                 // Whether to draw stroke along the path. Set it to `false` to disable borders on polygons or circles.
 
8117                 // @option color: String = '#3388ff'
 
8121                 // @option weight: Number = 3
 
8122                 // Stroke width in pixels
 
8125                 // @option opacity: Number = 1.0
 
8129                 // @option lineCap: String= 'round'
 
8130                 // A string that defines [shape to be used at the end](https://developer.mozilla.org/docs/Web/SVG/Attribute/stroke-linecap) of the stroke.
 
8133                 // @option lineJoin: String = 'round'
 
8134                 // A string that defines [shape to be used at the corners](https://developer.mozilla.org/docs/Web/SVG/Attribute/stroke-linejoin) of the stroke.
 
8137                 // @option dashArray: String = null
 
8138                 // 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).
 
8141                 // @option dashOffset: String = null
 
8142                 // 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).
 
8145                 // @option fill: Boolean = depends
 
8146                 // Whether to fill the path with color. Set it to `false` to disable filling on polygons or circles.
 
8149                 // @option fillColor: String = *
 
8150                 // Fill color. Defaults to the value of the [`color`](#path-color) option
 
8153                 // @option fillOpacity: Number = 0.2
 
8157                 // @option fillRule: String = 'evenodd'
 
8158                 // A string that defines [how the inside of a shape](https://developer.mozilla.org/docs/Web/SVG/Attribute/fill-rule) is determined.
 
8159                 fillRule: 'evenodd',
 
8163                 // Option inherited from "Interactive layer" abstract class
 
8166                 // @option bubblingMouseEvents: Boolean = true
 
8167                 // When `true`, a mouse event on this path will trigger the same event on the map
 
8168                 // (unless [`L.DomEvent.stopPropagation`](#domevent-stoppropagation) is used).
 
8169                 bubblingMouseEvents: true
 
8172         beforeAdd: function (map) {
 
8173                 // Renderer is set here because we need to call renderer.getEvents
 
8174                 // before this.getEvents.
 
8175                 this._renderer = map.getRenderer(this);
 
8178         onAdd: function () {
 
8179                 this._renderer._initPath(this);
 
8181                 this._renderer._addPath(this);
 
8184         onRemove: function () {
 
8185                 this._renderer._removePath(this);
 
8188         // @method redraw(): this
 
8189         // Redraws the layer. Sometimes useful after you changed the coordinates that the path uses.
 
8190         redraw: function () {
 
8192                         this._renderer._updatePath(this);
 
8197         // @method setStyle(style: Path options): this
 
8198         // Changes the appearance of a Path based on the options in the `Path options` object.
 
8199         setStyle: function (style) {
 
8200                 setOptions(this, style);
 
8201                 if (this._renderer) {
 
8202                         this._renderer._updateStyle(this);
 
8203                         if (this.options.stroke && style && Object.prototype.hasOwnProperty.call(style, 'weight')) {
 
8204                                 this._updateBounds();
 
8210         // @method bringToFront(): this
 
8211         // Brings the layer to the top of all path layers.
 
8212         bringToFront: function () {
 
8213                 if (this._renderer) {
 
8214                         this._renderer._bringToFront(this);
 
8219         // @method bringToBack(): this
 
8220         // Brings the layer to the bottom of all path layers.
 
8221         bringToBack: function () {
 
8222                 if (this._renderer) {
 
8223                         this._renderer._bringToBack(this);
 
8228         getElement: function () {
 
8232         _reset: function () {
 
8233                 // defined in child classes
 
8238         _clickTolerance: function () {
 
8239                 // used when doing hit detection for Canvas layers
 
8240                 return (this.options.stroke ? this.options.weight / 2 : 0) +
 
8241                   (this._renderer.options.tolerance || 0);
 
8246    * @class CircleMarker
 
8247    * @aka L.CircleMarker
 
8250    * A circle of a fixed size with radius specified in pixels. Extends `Path`.
 
8253   var CircleMarker = Path.extend({
 
8256         // @aka CircleMarker options
 
8260                 // @option radius: Number = 10
 
8261                 // Radius of the circle marker, in pixels
 
8265         initialize: function (latlng, options) {
 
8266                 setOptions(this, options);
 
8267                 this._latlng = toLatLng(latlng);
 
8268                 this._radius = this.options.radius;
 
8271         // @method setLatLng(latLng: LatLng): this
 
8272         // Sets the position of a circle marker to a new location.
 
8273         setLatLng: function (latlng) {
 
8274                 var oldLatLng = this._latlng;
 
8275                 this._latlng = toLatLng(latlng);
 
8278                 // @event move: Event
 
8279                 // Fired when the marker is moved via [`setLatLng`](#circlemarker-setlatlng). Old and new coordinates are included in event arguments as `oldLatLng`, `latlng`.
 
8280                 return this.fire('move', {oldLatLng: oldLatLng, latlng: this._latlng});
 
8283         // @method getLatLng(): LatLng
 
8284         // Returns the current geographical position of the circle marker
 
8285         getLatLng: function () {
 
8286                 return this._latlng;
 
8289         // @method setRadius(radius: Number): this
 
8290         // Sets the radius of a circle marker. Units are in pixels.
 
8291         setRadius: function (radius) {
 
8292                 this.options.radius = this._radius = radius;
 
8293                 return this.redraw();
 
8296         // @method getRadius(): Number
 
8297         // Returns the current radius of the circle
 
8298         getRadius: function () {
 
8299                 return this._radius;
 
8302         setStyle : function (options) {
 
8303                 var radius = options && options.radius || this._radius;
 
8304                 Path.prototype.setStyle.call(this, options);
 
8305                 this.setRadius(radius);
 
8309         _project: function () {
 
8310                 this._point = this._map.latLngToLayerPoint(this._latlng);
 
8311                 this._updateBounds();
 
8314         _updateBounds: function () {
 
8315                 var r = this._radius,
 
8316                     r2 = this._radiusY || r,
 
8317                     w = this._clickTolerance(),
 
8318                     p = [r + w, r2 + w];
 
8319                 this._pxBounds = new Bounds(this._point.subtract(p), this._point.add(p));
 
8322         _update: function () {
 
8328         _updatePath: function () {
 
8329                 this._renderer._updateCircle(this);
 
8332         _empty: function () {
 
8333                 return this._radius && !this._renderer._bounds.intersects(this._pxBounds);
 
8336         // Needed by the `Canvas` renderer for interactivity
 
8337         _containsPoint: function (p) {
 
8338                 return p.distanceTo(this._point) <= this._radius + this._clickTolerance();
 
8343   // @factory L.circleMarker(latlng: LatLng, options?: CircleMarker options)
 
8344   // Instantiates a circle marker object given a geographical point, and an optional options object.
 
8345   function circleMarker(latlng, options) {
 
8346         return new CircleMarker(latlng, options);
 
8352    * @inherits CircleMarker
 
8354    * A class for drawing circle overlays on a map. Extends `CircleMarker`.
 
8356    * It's an approximation and starts to diverge from a real circle closer to poles (due to projection distortion).
 
8361    * L.circle([50.5, 30.5], {radius: 200}).addTo(map);
 
8365   var Circle = CircleMarker.extend({
 
8367         initialize: function (latlng, options, legacyOptions) {
 
8368                 if (typeof options === 'number') {
 
8369                         // Backwards compatibility with 0.7.x factory (latlng, radius, options?)
 
8370                         options = extend({}, legacyOptions, {radius: options});
 
8372                 setOptions(this, options);
 
8373                 this._latlng = toLatLng(latlng);
 
8375                 if (isNaN(this.options.radius)) { throw new Error('Circle radius cannot be NaN'); }
 
8378                 // @aka Circle options
 
8379                 // @option radius: Number; Radius of the circle, in meters.
 
8380                 this._mRadius = this.options.radius;
 
8383         // @method setRadius(radius: Number): this
 
8384         // Sets the radius of a circle. Units are in meters.
 
8385         setRadius: function (radius) {
 
8386                 this._mRadius = radius;
 
8387                 return this.redraw();
 
8390         // @method getRadius(): Number
 
8391         // Returns the current radius of a circle. Units are in meters.
 
8392         getRadius: function () {
 
8393                 return this._mRadius;
 
8396         // @method getBounds(): LatLngBounds
 
8397         // Returns the `LatLngBounds` of the path.
 
8398         getBounds: function () {
 
8399                 var half = [this._radius, this._radiusY || this._radius];
 
8401                 return new LatLngBounds(
 
8402                         this._map.layerPointToLatLng(this._point.subtract(half)),
 
8403                         this._map.layerPointToLatLng(this._point.add(half)));
 
8406         setStyle: Path.prototype.setStyle,
 
8408         _project: function () {
 
8410                 var lng = this._latlng.lng,
 
8411                     lat = this._latlng.lat,
 
8413                     crs = map.options.crs;
 
8415                 if (crs.distance === Earth.distance) {
 
8416                         var d = Math.PI / 180,
 
8417                             latR = (this._mRadius / Earth.R) / d,
 
8418                             top = map.project([lat + latR, lng]),
 
8419                             bottom = map.project([lat - latR, lng]),
 
8420                             p = top.add(bottom).divideBy(2),
 
8421                             lat2 = map.unproject(p).lat,
 
8422                             lngR = Math.acos((Math.cos(latR * d) - Math.sin(lat * d) * Math.sin(lat2 * d)) /
 
8423                                     (Math.cos(lat * d) * Math.cos(lat2 * d))) / d;
 
8425                         if (isNaN(lngR) || lngR === 0) {
 
8426                                 lngR = latR / Math.cos(Math.PI / 180 * lat); // Fallback for edge case, #2425
 
8429                         this._point = p.subtract(map.getPixelOrigin());
 
8430                         this._radius = isNaN(lngR) ? 0 : p.x - map.project([lat2, lng - lngR]).x;
 
8431                         this._radiusY = p.y - top.y;
 
8434                         var latlng2 = crs.unproject(crs.project(this._latlng).subtract([this._mRadius, 0]));
 
8436                         this._point = map.latLngToLayerPoint(this._latlng);
 
8437                         this._radius = this._point.x - map.latLngToLayerPoint(latlng2).x;
 
8440                 this._updateBounds();
 
8444   // @factory L.circle(latlng: LatLng, options?: Circle options)
 
8445   // Instantiates a circle object given a geographical point, and an options object
 
8446   // which contains the circle radius.
 
8448   // @factory L.circle(latlng: LatLng, radius: Number, options?: Circle options)
 
8449   // Obsolete way of instantiating a circle, for compatibility with 0.7.x code.
 
8450   // Do not use in new applications or plugins.
 
8451   function circle(latlng, options, legacyOptions) {
 
8452         return new Circle(latlng, options, legacyOptions);
 
8460    * A class for drawing polyline overlays on a map. Extends `Path`.
 
8465    * // create a red polyline from an array of LatLng points
 
8472    * var polyline = L.polyline(latlngs, {color: 'red'}).addTo(map);
 
8474    * // zoom the map to the polyline
 
8475    * map.fitBounds(polyline.getBounds());
 
8478    * You can also pass a multi-dimensional array to represent a `MultiPolyline` shape:
 
8481    * // create a red polyline from an array of arrays of LatLng points
 
8483    *    [[45.51, -122.68],
 
8494   var Polyline = Path.extend({
 
8497         // @aka Polyline options
 
8499                 // @option smoothFactor: Number = 1.0
 
8500                 // How much to simplify the polyline on each zoom level. More means
 
8501                 // better performance and smoother look, and less means more accurate representation.
 
8504                 // @option noClip: Boolean = false
 
8505                 // Disable polyline clipping.
 
8509         initialize: function (latlngs, options) {
 
8510                 setOptions(this, options);
 
8511                 this._setLatLngs(latlngs);
 
8514         // @method getLatLngs(): LatLng[]
 
8515         // Returns an array of the points in the path, or nested arrays of points in case of multi-polyline.
 
8516         getLatLngs: function () {
 
8517                 return this._latlngs;
 
8520         // @method setLatLngs(latlngs: LatLng[]): this
 
8521         // Replaces all the points in the polyline with the given array of geographical points.
 
8522         setLatLngs: function (latlngs) {
 
8523                 this._setLatLngs(latlngs);
 
8524                 return this.redraw();
 
8527         // @method isEmpty(): Boolean
 
8528         // Returns `true` if the Polyline has no LatLngs.
 
8529         isEmpty: function () {
 
8530                 return !this._latlngs.length;
 
8533         // @method closestLayerPoint(p: Point): Point
 
8534         // Returns the point closest to `p` on the Polyline.
 
8535         closestLayerPoint: function (p) {
 
8536                 var minDistance = Infinity,
 
8538                     closest = _sqClosestPointOnSegment,
 
8541                 for (var j = 0, jLen = this._parts.length; j < jLen; j++) {
 
8542                         var points = this._parts[j];
 
8544                         for (var i = 1, len = points.length; i < len; i++) {
 
8548                                 var sqDist = closest(p, p1, p2, true);
 
8550                                 if (sqDist < minDistance) {
 
8551                                         minDistance = sqDist;
 
8552                                         minPoint = closest(p, p1, p2);
 
8557                         minPoint.distance = Math.sqrt(minDistance);
 
8562         // @method getCenter(): LatLng
 
8563         // Returns the center ([centroid](https://en.wikipedia.org/wiki/Centroid)) of the polyline.
 
8564         getCenter: function () {
 
8565                 // throws error when not yet added to map as this center calculation requires projected coordinates
 
8567                         throw new Error('Must add layer to map before using getCenter()');
 
8569                 return polylineCenter(this._defaultShape(), this._map.options.crs);
 
8572         // @method getBounds(): LatLngBounds
 
8573         // Returns the `LatLngBounds` of the path.
 
8574         getBounds: function () {
 
8575                 return this._bounds;
 
8578         // @method addLatLng(latlng: LatLng, latlngs?: LatLng[]): this
 
8579         // Adds a given point to the polyline. By default, adds to the first ring of
 
8580         // the polyline in case of a multi-polyline, but can be overridden by passing
 
8581         // a specific ring as a LatLng array (that you can earlier access with [`getLatLngs`](#polyline-getlatlngs)).
 
8582         addLatLng: function (latlng, latlngs) {
 
8583                 latlngs = latlngs || this._defaultShape();
 
8584                 latlng = toLatLng(latlng);
 
8585                 latlngs.push(latlng);
 
8586                 this._bounds.extend(latlng);
 
8587                 return this.redraw();
 
8590         _setLatLngs: function (latlngs) {
 
8591                 this._bounds = new LatLngBounds();
 
8592                 this._latlngs = this._convertLatLngs(latlngs);
 
8595         _defaultShape: function () {
 
8596                 return isFlat(this._latlngs) ? this._latlngs : this._latlngs[0];
 
8599         // recursively convert latlngs input into actual LatLng instances; calculate bounds along the way
 
8600         _convertLatLngs: function (latlngs) {
 
8602                     flat = isFlat(latlngs);
 
8604                 for (var i = 0, len = latlngs.length; i < len; i++) {
 
8606                                 result[i] = toLatLng(latlngs[i]);
 
8607                                 this._bounds.extend(result[i]);
 
8609                                 result[i] = this._convertLatLngs(latlngs[i]);
 
8616         _project: function () {
 
8617                 var pxBounds = new Bounds();
 
8619                 this._projectLatlngs(this._latlngs, this._rings, pxBounds);
 
8621                 if (this._bounds.isValid() && pxBounds.isValid()) {
 
8622                         this._rawPxBounds = pxBounds;
 
8623                         this._updateBounds();
 
8627         _updateBounds: function () {
 
8628                 var w = this._clickTolerance(),
 
8629                     p = new Point(w, w);
 
8631                 if (!this._rawPxBounds) {
 
8635                 this._pxBounds = new Bounds([
 
8636                         this._rawPxBounds.min.subtract(p),
 
8637                         this._rawPxBounds.max.add(p)
 
8641         // recursively turns latlngs into a set of rings with projected coordinates
 
8642         _projectLatlngs: function (latlngs, result, projectedBounds) {
 
8643                 var flat = latlngs[0] instanceof LatLng,
 
8644                     len = latlngs.length,
 
8649                         for (i = 0; i < len; i++) {
 
8650                                 ring[i] = this._map.latLngToLayerPoint(latlngs[i]);
 
8651                                 projectedBounds.extend(ring[i]);
 
8655                         for (i = 0; i < len; i++) {
 
8656                                 this._projectLatlngs(latlngs[i], result, projectedBounds);
 
8661         // clip polyline by renderer bounds so that we have less to render for performance
 
8662         _clipPoints: function () {
 
8663                 var bounds = this._renderer._bounds;
 
8666                 if (!this._pxBounds || !this._pxBounds.intersects(bounds)) {
 
8670                 if (this.options.noClip) {
 
8671                         this._parts = this._rings;
 
8675                 var parts = this._parts,
 
8676                     i, j, k, len, len2, segment, points;
 
8678                 for (i = 0, k = 0, len = this._rings.length; i < len; i++) {
 
8679                         points = this._rings[i];
 
8681                         for (j = 0, len2 = points.length; j < len2 - 1; j++) {
 
8682                                 segment = clipSegment(points[j], points[j + 1], bounds, j, true);
 
8684                                 if (!segment) { continue; }
 
8686                                 parts[k] = parts[k] || [];
 
8687                                 parts[k].push(segment[0]);
 
8689                                 // if segment goes out of screen, or it's the last one, it's the end of the line part
 
8690                                 if ((segment[1] !== points[j + 1]) || (j === len2 - 2)) {
 
8691                                         parts[k].push(segment[1]);
 
8698         // simplify each clipped part of the polyline for performance
 
8699         _simplifyPoints: function () {
 
8700                 var parts = this._parts,
 
8701                     tolerance = this.options.smoothFactor;
 
8703                 for (var i = 0, len = parts.length; i < len; i++) {
 
8704                         parts[i] = simplify(parts[i], tolerance);
 
8708         _update: function () {
 
8709                 if (!this._map) { return; }
 
8712                 this._simplifyPoints();
 
8716         _updatePath: function () {
 
8717                 this._renderer._updatePoly(this);
 
8720         // Needed by the `Canvas` renderer for interactivity
 
8721         _containsPoint: function (p, closed) {
 
8722                 var i, j, k, len, len2, part,
 
8723                     w = this._clickTolerance();
 
8725                 if (!this._pxBounds || !this._pxBounds.contains(p)) { return false; }
 
8727                 // hit detection for polylines
 
8728                 for (i = 0, len = this._parts.length; i < len; i++) {
 
8729                         part = this._parts[i];
 
8731                         for (j = 0, len2 = part.length, k = len2 - 1; j < len2; k = j++) {
 
8732                                 if (!closed && (j === 0)) { continue; }
 
8734                                 if (pointToSegmentDistance(p, part[k], part[j]) <= w) {
 
8743   // @factory L.polyline(latlngs: LatLng[], options?: Polyline options)
 
8744   // Instantiates a polyline object given an array of geographical points and
 
8745   // optionally an options object. You can create a `Polyline` object with
 
8746   // multiple separate lines (`MultiPolyline`) by passing an array of arrays
 
8747   // of geographic points.
 
8748   function polyline(latlngs, options) {
 
8749         return new Polyline(latlngs, options);
 
8752   // Retrocompat. Allow plugins to support Leaflet versions before and after 1.1.
 
8753   Polyline._flat = _flat;
 
8758    * @inherits Polyline
 
8760    * A class for drawing polygon overlays on a map. Extends `Polyline`.
 
8762    * 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.
 
8768    * // create a red polygon from an array of LatLng points
 
8769    * var latlngs = [[37, -109.05],[41, -109.03],[41, -102.05],[37, -102.04]];
 
8771    * var polygon = L.polygon(latlngs, {color: 'red'}).addTo(map);
 
8773    * // zoom the map to the polygon
 
8774    * map.fitBounds(polygon.getBounds());
 
8777    * 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:
 
8781    *   [[37, -109.05],[41, -109.03],[41, -102.05],[37, -102.04]], // outer ring
 
8782    *   [[37.29, -108.58],[40.71, -108.58],[40.71, -102.50],[37.29, -102.50]] // hole
 
8786    * Additionally, you can pass a multi-dimensional array to represent a MultiPolygon shape.
 
8790    *   [ // first polygon
 
8791    *     [[37, -109.05],[41, -109.03],[41, -102.05],[37, -102.04]], // outer ring
 
8792    *     [[37.29, -108.58],[40.71, -108.58],[40.71, -102.50],[37.29, -102.50]] // hole
 
8794    *   [ // second polygon
 
8795    *     [[41, -111.03],[45, -111.04],[45, -104.05],[41, -104.05]]
 
8801   var Polygon = Polyline.extend({
 
8807         isEmpty: function () {
 
8808                 return !this._latlngs.length || !this._latlngs[0].length;
 
8811         // @method getCenter(): LatLng
 
8812         // Returns the center ([centroid](http://en.wikipedia.org/wiki/Centroid)) of the Polygon.
 
8813         getCenter: function () {
 
8814                 // throws error when not yet added to map as this center calculation requires projected coordinates
 
8816                         throw new Error('Must add layer to map before using getCenter()');
 
8818                 return polygonCenter(this._defaultShape(), this._map.options.crs);
 
8821         _convertLatLngs: function (latlngs) {
 
8822                 var result = Polyline.prototype._convertLatLngs.call(this, latlngs),
 
8823                     len = result.length;
 
8825                 // remove last point if it equals first one
 
8826                 if (len >= 2 && result[0] instanceof LatLng && result[0].equals(result[len - 1])) {
 
8832         _setLatLngs: function (latlngs) {
 
8833                 Polyline.prototype._setLatLngs.call(this, latlngs);
 
8834                 if (isFlat(this._latlngs)) {
 
8835                         this._latlngs = [this._latlngs];
 
8839         _defaultShape: function () {
 
8840                 return isFlat(this._latlngs[0]) ? this._latlngs[0] : this._latlngs[0][0];
 
8843         _clipPoints: function () {
 
8844                 // polygons need a different clipping algorithm so we redefine that
 
8846                 var bounds = this._renderer._bounds,
 
8847                     w = this.options.weight,
 
8848                     p = new Point(w, w);
 
8850                 // increase clip padding by stroke width to avoid stroke on clip edges
 
8851                 bounds = new Bounds(bounds.min.subtract(p), bounds.max.add(p));
 
8854                 if (!this._pxBounds || !this._pxBounds.intersects(bounds)) {
 
8858                 if (this.options.noClip) {
 
8859                         this._parts = this._rings;
 
8863                 for (var i = 0, len = this._rings.length, clipped; i < len; i++) {
 
8864                         clipped = clipPolygon(this._rings[i], bounds, true);
 
8865                         if (clipped.length) {
 
8866                                 this._parts.push(clipped);
 
8871         _updatePath: function () {
 
8872                 this._renderer._updatePoly(this, true);
 
8875         // Needed by the `Canvas` renderer for interactivity
 
8876         _containsPoint: function (p) {
 
8878                     part, p1, p2, i, j, k, len, len2;
 
8880                 if (!this._pxBounds || !this._pxBounds.contains(p)) { return false; }
 
8882                 // ray casting algorithm for detecting if point is in polygon
 
8883                 for (i = 0, len = this._parts.length; i < len; i++) {
 
8884                         part = this._parts[i];
 
8886                         for (j = 0, len2 = part.length, k = len2 - 1; j < len2; k = j++) {
 
8890                                 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)) {
 
8896                 // also check if it's on polygon stroke
 
8897                 return inside || Polyline.prototype._containsPoint.call(this, p, true);
 
8903   // @factory L.polygon(latlngs: LatLng[], options?: Polyline options)
 
8904   function polygon(latlngs, options) {
 
8905         return new Polygon(latlngs, options);
 
8911    * @inherits FeatureGroup
\r 
8913    * Represents a GeoJSON object or an array of GeoJSON objects. Allows you to parse
\r 
8914    * GeoJSON data and display it on the map. Extends `FeatureGroup`.
\r 
8919    * L.geoJSON(data, {
\r 
8920    *    style: function (feature) {
\r 
8921    *            return {color: feature.properties.color};
\r 
8923    * }).bindPopup(function (layer) {
\r 
8924    *    return layer.feature.properties.description;
\r 
8929   var GeoJSON = FeatureGroup.extend({
\r 
8932          * @aka GeoJSON options
\r 
8934          * @option pointToLayer: Function = *
\r 
8935          * A `Function` defining how GeoJSON points spawn Leaflet layers. It is internally
\r 
8936          * called when data is added, passing the GeoJSON point feature and its `LatLng`.
\r 
8937          * The default is to spawn a default `Marker`:
\r 
8939          * function(geoJsonPoint, latlng) {
\r 
8940          *      return L.marker(latlng);
\r 
8944          * @option style: Function = *
\r 
8945          * A `Function` defining the `Path options` for styling GeoJSON lines and polygons,
\r 
8946          * called internally when data is added.
\r 
8947          * The default value is to not override any defaults:
\r 
8949          * function (geoJsonFeature) {
\r 
8954          * @option onEachFeature: Function = *
\r 
8955          * A `Function` that will be called once for each created `Feature`, after it has
\r 
8956          * been created and styled. Useful for attaching events and popups to features.
\r 
8957          * The default is to do nothing with the newly created layers:
\r 
8959          * function (feature, layer) {}
\r 
8962          * @option filter: Function = *
\r 
8963          * A `Function` that will be used to decide whether to include a feature or not.
\r 
8964          * The default is to include all features:
\r 
8966          * function (geoJsonFeature) {
\r 
8970          * Note: dynamically changing the `filter` option will have effect only on newly
\r 
8971          * added data. It will _not_ re-evaluate already included features.
\r 
8973          * @option coordsToLatLng: Function = *
\r 
8974          * A `Function` that will be used for converting GeoJSON coordinates to `LatLng`s.
\r 
8975          * The default is the `coordsToLatLng` static method.
\r 
8977          * @option markersInheritOptions: Boolean = false
\r 
8978          * Whether default Markers for "Point" type Features inherit from group options.
\r 
8981         initialize: function (geojson, options) {
\r 
8982                 setOptions(this, options);
\r 
8984                 this._layers = {};
\r 
8987                         this.addData(geojson);
\r 
8991         // @method addData( <GeoJSON> data ): this
\r 
8992         // Adds a GeoJSON object to the layer.
\r 
8993         addData: function (geojson) {
\r 
8994                 var features = isArray(geojson) ? geojson : geojson.features,
\r 
8998                         for (i = 0, len = features.length; i < len; i++) {
\r 
8999                                 // only add this if geometry or geometries are set and not null
\r 
9000                                 feature = features[i];
\r 
9001                                 if (feature.geometries || feature.geometry || feature.features || feature.coordinates) {
\r 
9002                                         this.addData(feature);
\r 
9008                 var options = this.options;
\r 
9010                 if (options.filter && !options.filter(geojson)) { return this; }
\r 
9012                 var layer = geometryToLayer(geojson, options);
\r 
9016                 layer.feature = asFeature(geojson);
\r 
9018                 layer.defaultOptions = layer.options;
\r 
9019                 this.resetStyle(layer);
\r 
9021                 if (options.onEachFeature) {
\r 
9022                         options.onEachFeature(geojson, layer);
\r 
9025                 return this.addLayer(layer);
\r 
9028         // @method resetStyle( <Path> layer? ): this
\r 
9029         // Resets the given vector layer's style to the original GeoJSON style, useful for resetting style after hover events.
\r 
9030         // If `layer` is omitted, the style of all features in the current layer is reset.
\r 
9031         resetStyle: function (layer) {
\r 
9032                 if (layer === undefined) {
\r 
9033                         return this.eachLayer(this.resetStyle, this);
\r 
9035                 // reset any custom styles
\r 
9036                 layer.options = extend({}, layer.defaultOptions);
\r 
9037                 this._setLayerStyle(layer, this.options.style);
\r 
9041         // @method setStyle( <Function> style ): this
\r 
9042         // Changes styles of GeoJSON vector layers with the given style function.
\r 
9043         setStyle: function (style) {
\r 
9044                 return this.eachLayer(function (layer) {
\r 
9045                         this._setLayerStyle(layer, style);
\r 
9049         _setLayerStyle: function (layer, style) {
\r 
9050                 if (layer.setStyle) {
\r 
9051                         if (typeof style === 'function') {
\r 
9052                                 style = style(layer.feature);
\r 
9054                         layer.setStyle(style);
\r 
9060   // There are several static functions which can be called without instantiating L.GeoJSON:
\r 
9062   // @function geometryToLayer(featureData: Object, options?: GeoJSON options): Layer
\r 
9063   // Creates a `Layer` from a given GeoJSON feature. Can use a custom
\r 
9064   // [`pointToLayer`](#geojson-pointtolayer) and/or [`coordsToLatLng`](#geojson-coordstolatlng)
\r 
9065   // functions if provided as options.
\r 
9066   function geometryToLayer(geojson, options) {
\r 
9068         var geometry = geojson.type === 'Feature' ? geojson.geometry : geojson,
\r 
9069             coords = geometry ? geometry.coordinates : null,
\r 
9071             pointToLayer = options && options.pointToLayer,
\r 
9072             _coordsToLatLng = options && options.coordsToLatLng || coordsToLatLng,
\r 
9073             latlng, latlngs, i, len;
\r 
9075         if (!coords && !geometry) {
\r 
9079         switch (geometry.type) {
\r 
9081                 latlng = _coordsToLatLng(coords);
\r 
9082                 return _pointToLayer(pointToLayer, geojson, latlng, options);
\r 
9084         case 'MultiPoint':
\r 
9085                 for (i = 0, len = coords.length; i < len; i++) {
\r 
9086                         latlng = _coordsToLatLng(coords[i]);
\r 
9087                         layers.push(_pointToLayer(pointToLayer, geojson, latlng, options));
\r 
9089                 return new FeatureGroup(layers);
\r 
9091         case 'LineString':
\r 
9092         case 'MultiLineString':
\r 
9093                 latlngs = coordsToLatLngs(coords, geometry.type === 'LineString' ? 0 : 1, _coordsToLatLng);
\r 
9094                 return new Polyline(latlngs, options);
\r 
9097         case 'MultiPolygon':
\r 
9098                 latlngs = coordsToLatLngs(coords, geometry.type === 'Polygon' ? 1 : 2, _coordsToLatLng);
\r 
9099                 return new Polygon(latlngs, options);
\r 
9101         case 'GeometryCollection':
\r 
9102                 for (i = 0, len = geometry.geometries.length; i < len; i++) {
\r 
9103                         var geoLayer = geometryToLayer({
\r 
9104                                 geometry: geometry.geometries[i],
\r 
9106                                 properties: geojson.properties
\r 
9110                                 layers.push(geoLayer);
\r 
9113                 return new FeatureGroup(layers);
\r 
9115         case 'FeatureCollection':
\r 
9116                 for (i = 0, len = geometry.features.length; i < len; i++) {
\r 
9117                         var featureLayer = geometryToLayer(geometry.features[i], options);
\r 
9119                         if (featureLayer) {
\r 
9120                                 layers.push(featureLayer);
\r 
9123                 return new FeatureGroup(layers);
\r 
9126                 throw new Error('Invalid GeoJSON object.');
\r 
9130   function _pointToLayer(pointToLayerFn, geojson, latlng, options) {
\r 
9131         return pointToLayerFn ?
\r 
9132                 pointToLayerFn(geojson, latlng) :
\r 
9133                 new Marker(latlng, options && options.markersInheritOptions && options);
\r 
9136   // @function coordsToLatLng(coords: Array): LatLng
\r 
9137   // Creates a `LatLng` object from an array of 2 numbers (longitude, latitude)
\r 
9138   // or 3 numbers (longitude, latitude, altitude) used in GeoJSON for points.
\r 
9139   function coordsToLatLng(coords) {
\r 
9140         return new LatLng(coords[1], coords[0], coords[2]);
\r 
9143   // @function coordsToLatLngs(coords: Array, levelsDeep?: Number, coordsToLatLng?: Function): Array
\r 
9144   // Creates a multidimensional array of `LatLng`s from a GeoJSON coordinates array.
\r 
9145   // `levelsDeep` specifies the nesting level (0 is for an array of points, 1 for an array of arrays of points, etc., 0 by default).
\r 
9146   // Can use a custom [`coordsToLatLng`](#geojson-coordstolatlng) function.
\r 
9147   function coordsToLatLngs(coords, levelsDeep, _coordsToLatLng) {
\r 
9150         for (var i = 0, len = coords.length, latlng; i < len; i++) {
\r 
9151                 latlng = levelsDeep ?
\r 
9152                         coordsToLatLngs(coords[i], levelsDeep - 1, _coordsToLatLng) :
\r 
9153                         (_coordsToLatLng || coordsToLatLng)(coords[i]);
\r 
9155                 latlngs.push(latlng);
\r 
9161   // @function latLngToCoords(latlng: LatLng, precision?: Number|false): Array
\r 
9162   // Reverse of [`coordsToLatLng`](#geojson-coordstolatlng)
\r 
9163   // Coordinates values are rounded with [`formatNum`](#util-formatnum) function.
\r 
9164   function latLngToCoords(latlng, precision) {
\r 
9165         latlng = toLatLng(latlng);
\r 
9166         return latlng.alt !== undefined ?
\r 
9167                 [formatNum(latlng.lng, precision), formatNum(latlng.lat, precision), formatNum(latlng.alt, precision)] :
\r 
9168                 [formatNum(latlng.lng, precision), formatNum(latlng.lat, precision)];
\r 
9171   // @function latLngsToCoords(latlngs: Array, levelsDeep?: Number, closed?: Boolean, precision?: Number|false): Array
\r 
9172   // Reverse of [`coordsToLatLngs`](#geojson-coordstolatlngs)
\r 
9173   // `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.
\r 
9174   // Coordinates values are rounded with [`formatNum`](#util-formatnum) function.
\r 
9175   function latLngsToCoords(latlngs, levelsDeep, closed, precision) {
\r 
9178         for (var i = 0, len = latlngs.length; i < len; i++) {
\r 
9179                 // Check for flat arrays required to ensure unbalanced arrays are correctly converted in recursion
\r 
9180                 coords.push(levelsDeep ?
\r 
9181                         latLngsToCoords(latlngs[i], isFlat(latlngs[i]) ? 0 : levelsDeep - 1, closed, precision) :
\r 
9182                         latLngToCoords(latlngs[i], precision));
\r 
9185         if (!levelsDeep && closed && coords.length > 0) {
\r 
9186                 coords.push(coords[0].slice());
\r 
9192   function getFeature(layer, newGeometry) {
\r 
9193         return layer.feature ?
\r 
9194                 extend({}, layer.feature, {geometry: newGeometry}) :
\r 
9195                 asFeature(newGeometry);
\r 
9198   // @function asFeature(geojson: Object): Object
\r 
9199   // Normalize GeoJSON geometries/features into GeoJSON features.
\r 
9200   function asFeature(geojson) {
\r 
9201         if (geojson.type === 'Feature' || geojson.type === 'FeatureCollection') {
\r 
9212   var PointToGeoJSON = {
\r 
9213         toGeoJSON: function (precision) {
\r 
9214                 return getFeature(this, {
\r 
9216                         coordinates: latLngToCoords(this.getLatLng(), precision)
\r 
9221   // @namespace Marker
\r 
9222   // @section Other methods
\r 
9223   // @method toGeoJSON(precision?: Number|false): Object
\r 
9224   // Coordinates values are rounded with [`formatNum`](#util-formatnum) function with given `precision`.
\r 
9225   // Returns a [`GeoJSON`](https://en.wikipedia.org/wiki/GeoJSON) representation of the marker (as a GeoJSON `Point` Feature).
\r 
9226   Marker.include(PointToGeoJSON);
\r 
9228   // @namespace CircleMarker
\r 
9229   // @method toGeoJSON(precision?: Number|false): Object
\r 
9230   // Coordinates values are rounded with [`formatNum`](#util-formatnum) function with given `precision`.
\r 
9231   // Returns a [`GeoJSON`](https://en.wikipedia.org/wiki/GeoJSON) representation of the circle marker (as a GeoJSON `Point` Feature).
\r 
9232   Circle.include(PointToGeoJSON);
\r 
9233   CircleMarker.include(PointToGeoJSON);
\r 
9236   // @namespace Polyline
\r 
9237   // @method toGeoJSON(precision?: Number|false): Object
\r 
9238   // Coordinates values are rounded with [`formatNum`](#util-formatnum) function with given `precision`.
\r 
9239   // Returns a [`GeoJSON`](https://en.wikipedia.org/wiki/GeoJSON) representation of the polyline (as a GeoJSON `LineString` or `MultiLineString` Feature).
\r 
9240   Polyline.include({
\r 
9241         toGeoJSON: function (precision) {
\r 
9242                 var multi = !isFlat(this._latlngs);
\r 
9244                 var coords = latLngsToCoords(this._latlngs, multi ? 1 : 0, false, precision);
\r 
9246                 return getFeature(this, {
\r 
9247                         type: (multi ? 'Multi' : '') + 'LineString',
\r 
9248                         coordinates: coords
\r 
9253   // @namespace Polygon
\r 
9254   // @method toGeoJSON(precision?: Number|false): Object
\r 
9255   // Coordinates values are rounded with [`formatNum`](#util-formatnum) function with given `precision`.
\r 
9256   // Returns a [`GeoJSON`](https://en.wikipedia.org/wiki/GeoJSON) representation of the polygon (as a GeoJSON `Polygon` or `MultiPolygon` Feature).
\r 
9258         toGeoJSON: function (precision) {
\r 
9259                 var holes = !isFlat(this._latlngs),
\r 
9260                     multi = holes && !isFlat(this._latlngs[0]);
\r 
9262                 var coords = latLngsToCoords(this._latlngs, multi ? 2 : holes ? 1 : 0, true, precision);
\r 
9265                         coords = [coords];
\r 
9268                 return getFeature(this, {
\r 
9269                         type: (multi ? 'Multi' : '') + 'Polygon',
\r 
9270                         coordinates: coords
\r 
9276   // @namespace LayerGroup
\r 
9277   LayerGroup.include({
\r 
9278         toMultiPoint: function (precision) {
\r 
9281                 this.eachLayer(function (layer) {
\r 
9282                         coords.push(layer.toGeoJSON(precision).geometry.coordinates);
\r 
9285                 return getFeature(this, {
\r 
9286                         type: 'MultiPoint',
\r 
9287                         coordinates: coords
\r 
9291         // @method toGeoJSON(precision?: Number|false): Object
\r 
9292         // Coordinates values are rounded with [`formatNum`](#util-formatnum) function with given `precision`.
\r 
9293         // Returns a [`GeoJSON`](https://en.wikipedia.org/wiki/GeoJSON) representation of the layer group (as a GeoJSON `FeatureCollection`, `GeometryCollection`, or `MultiPoint`).
\r 
9294         toGeoJSON: function (precision) {
\r 
9296                 var type = this.feature && this.feature.geometry && this.feature.geometry.type;
\r 
9298                 if (type === 'MultiPoint') {
\r 
9299                         return this.toMultiPoint(precision);
\r 
9302                 var isGeometryCollection = type === 'GeometryCollection',
\r 
9305                 this.eachLayer(function (layer) {
\r 
9306                         if (layer.toGeoJSON) {
\r 
9307                                 var json = layer.toGeoJSON(precision);
\r 
9308                                 if (isGeometryCollection) {
\r 
9309                                         jsons.push(json.geometry);
\r 
9311                                         var feature = asFeature(json);
\r 
9312                                         // Squash nested feature collections
\r 
9313                                         if (feature.type === 'FeatureCollection') {
\r 
9314                                                 jsons.push.apply(jsons, feature.features);
\r 
9316                                                 jsons.push(feature);
\r 
9322                 if (isGeometryCollection) {
\r 
9323                         return getFeature(this, {
\r 
9324                                 geometries: jsons,
\r 
9325                                 type: 'GeometryCollection'
\r 
9330                         type: 'FeatureCollection',
\r 
9336   // @namespace GeoJSON
\r 
9337   // @factory L.geoJSON(geojson?: Object, options?: GeoJSON options)
\r 
9338   // Creates a GeoJSON layer. Optionally accepts an object in
\r 
9339   // [GeoJSON format](https://tools.ietf.org/html/rfc7946) to display on the map
\r 
9340   // (you can alternatively add it later with `addData` method) and an `options` object.
\r 
9341   function geoJSON(geojson, options) {
\r 
9342         return new GeoJSON(geojson, options);
\r 
9345   // Backward compatibility.
\r 
9346   var geoJson = geoJSON;
 
9349    * @class ImageOverlay
\r 
9350    * @aka L.ImageOverlay
\r 
9351    * @inherits Interactive layer
\r 
9353    * Used to load and display a single image over specific bounds of the map. Extends `Layer`.
\r 
9358    * var imageUrl = 'https://maps.lib.utexas.edu/maps/historical/newark_nj_1922.jpg',
\r 
9359    *    imageBounds = [[40.712216, -74.22655], [40.773941, -74.12544]];
\r 
9360    * L.imageOverlay(imageUrl, imageBounds).addTo(map);
\r 
9364   var ImageOverlay = Layer.extend({
\r 
9367         // @aka ImageOverlay options
\r 
9369                 // @option opacity: Number = 1.0
\r 
9370                 // The opacity of the image overlay.
\r 
9373                 // @option alt: String = ''
\r 
9374                 // Text for the `alt` attribute of the image (useful for accessibility).
\r 
9377                 // @option interactive: Boolean = false
\r 
9378                 // If `true`, the image overlay will emit [mouse events](#interactive-layer) when clicked or hovered.
\r 
9379                 interactive: false,
\r 
9381                 // @option crossOrigin: Boolean|String = false
\r 
9382                 // Whether the crossOrigin attribute will be added to the image.
\r 
9383                 // 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.
\r 
9384                 // Refer to [CORS Settings](https://developer.mozilla.org/en-US/docs/Web/HTML/CORS_settings_attributes) for valid String values.
\r 
9385                 crossOrigin: false,
\r 
9387                 // @option errorOverlayUrl: String = ''
\r 
9388                 // URL to the overlay image to show in place of the overlay that failed to load.
\r 
9389                 errorOverlayUrl: '',
\r 
9391                 // @option zIndex: Number = 1
\r 
9392                 // The explicit [zIndex](https://developer.mozilla.org/docs/Web/CSS/CSS_Positioning/Understanding_z_index) of the overlay layer.
\r 
9395                 // @option className: String = ''
\r 
9396                 // A custom class name to assign to the image. Empty by default.
\r 
9400         initialize: function (url, bounds, options) { // (String, LatLngBounds, Object)
\r 
9402                 this._bounds = toLatLngBounds(bounds);
\r 
9404                 setOptions(this, options);
\r 
9407         onAdd: function () {
\r 
9408                 if (!this._image) {
\r 
9409                         this._initImage();
\r 
9411                         if (this.options.opacity < 1) {
\r 
9412                                 this._updateOpacity();
\r 
9416                 if (this.options.interactive) {
\r 
9417                         addClass(this._image, 'leaflet-interactive');
\r 
9418                         this.addInteractiveTarget(this._image);
\r 
9421                 this.getPane().appendChild(this._image);
\r 
9425         onRemove: function () {
\r 
9426                 remove(this._image);
\r 
9427                 if (this.options.interactive) {
\r 
9428                         this.removeInteractiveTarget(this._image);
\r 
9432         // @method setOpacity(opacity: Number): this
\r 
9433         // Sets the opacity of the overlay.
\r 
9434         setOpacity: function (opacity) {
\r 
9435                 this.options.opacity = opacity;
\r 
9437                 if (this._image) {
\r 
9438                         this._updateOpacity();
\r 
9443         setStyle: function (styleOpts) {
\r 
9444                 if (styleOpts.opacity) {
\r 
9445                         this.setOpacity(styleOpts.opacity);
\r 
9450         // @method bringToFront(): this
\r 
9451         // Brings the layer to the top of all overlays.
\r 
9452         bringToFront: function () {
\r 
9454                         toFront(this._image);
\r 
9459         // @method bringToBack(): this
\r 
9460         // Brings the layer to the bottom of all overlays.
\r 
9461         bringToBack: function () {
\r 
9463                         toBack(this._image);
\r 
9468         // @method setUrl(url: String): this
\r 
9469         // Changes the URL of the image.
\r 
9470         setUrl: function (url) {
\r 
9473                 if (this._image) {
\r 
9474                         this._image.src = url;
\r 
9479         // @method setBounds(bounds: LatLngBounds): this
\r 
9480         // Update the bounds that this ImageOverlay covers
\r 
9481         setBounds: function (bounds) {
\r 
9482                 this._bounds = toLatLngBounds(bounds);
\r 
9490         getEvents: function () {
\r 
9492                         zoom: this._reset,
\r 
9493                         viewreset: this._reset
\r 
9496                 if (this._zoomAnimated) {
\r 
9497                         events.zoomanim = this._animateZoom;
\r 
9503         // @method setZIndex(value: Number): this
\r 
9504         // Changes the [zIndex](#imageoverlay-zindex) of the image overlay.
\r 
9505         setZIndex: function (value) {
\r 
9506                 this.options.zIndex = value;
\r 
9507                 this._updateZIndex();
\r 
9511         // @method getBounds(): LatLngBounds
\r 
9512         // Get the bounds that this ImageOverlay covers
\r 
9513         getBounds: function () {
\r 
9514                 return this._bounds;
\r 
9517         // @method getElement(): HTMLElement
\r 
9518         // Returns the instance of [`HTMLImageElement`](https://developer.mozilla.org/docs/Web/API/HTMLImageElement)
\r 
9519         // used by this overlay.
\r 
9520         getElement: function () {
\r 
9521                 return this._image;
\r 
9524         _initImage: function () {
\r 
9525                 var wasElementSupplied = this._url.tagName === 'IMG';
\r 
9526                 var img = this._image = wasElementSupplied ? this._url : create$1('img');
\r 
9528                 addClass(img, 'leaflet-image-layer');
\r 
9529                 if (this._zoomAnimated) { addClass(img, 'leaflet-zoom-animated'); }
\r 
9530                 if (this.options.className) { addClass(img, this.options.className); }
\r 
9532                 img.onselectstart = falseFn;
\r 
9533                 img.onmousemove = falseFn;
\r 
9535                 // @event load: Event
\r 
9536                 // Fired when the ImageOverlay layer has loaded its image
\r 
9537                 img.onload = bind(this.fire, this, 'load');
\r 
9538                 img.onerror = bind(this._overlayOnError, this, 'error');
\r 
9540                 if (this.options.crossOrigin || this.options.crossOrigin === '') {
\r 
9541                         img.crossOrigin = this.options.crossOrigin === true ? '' : this.options.crossOrigin;
\r 
9544                 if (this.options.zIndex) {
\r 
9545                         this._updateZIndex();
\r 
9548                 if (wasElementSupplied) {
\r 
9549                         this._url = img.src;
\r 
9553                 img.src = this._url;
\r 
9554                 img.alt = this.options.alt;
\r 
9557         _animateZoom: function (e) {
\r 
9558                 var scale = this._map.getZoomScale(e.zoom),
\r 
9559                     offset = this._map._latLngBoundsToNewLayerBounds(this._bounds, e.zoom, e.center).min;
\r 
9561                 setTransform(this._image, offset, scale);
\r 
9564         _reset: function () {
\r 
9565                 var image = this._image,
\r 
9566                     bounds = new Bounds(
\r 
9567                         this._map.latLngToLayerPoint(this._bounds.getNorthWest()),
\r 
9568                         this._map.latLngToLayerPoint(this._bounds.getSouthEast())),
\r 
9569                     size = bounds.getSize();
\r 
9571                 setPosition(image, bounds.min);
\r 
9573                 image.style.width  = size.x + 'px';
\r 
9574                 image.style.height = size.y + 'px';
\r 
9577         _updateOpacity: function () {
\r 
9578                 setOpacity(this._image, this.options.opacity);
\r 
9581         _updateZIndex: function () {
\r 
9582                 if (this._image && this.options.zIndex !== undefined && this.options.zIndex !== null) {
\r 
9583                         this._image.style.zIndex = this.options.zIndex;
\r 
9587         _overlayOnError: function () {
\r 
9588                 // @event error: Event
\r 
9589                 // Fired when the ImageOverlay layer fails to load its image
\r 
9590                 this.fire('error');
\r 
9592                 var errorUrl = this.options.errorOverlayUrl;
\r 
9593                 if (errorUrl && this._url !== errorUrl) {
\r 
9594                         this._url = errorUrl;
\r 
9595                         this._image.src = errorUrl;
\r 
9599         // @method getCenter(): LatLng
\r 
9600         // Returns the center of the ImageOverlay.
\r 
9601         getCenter: function () {
\r 
9602                 return this._bounds.getCenter();
\r 
9606   // @factory L.imageOverlay(imageUrl: String, bounds: LatLngBounds, options?: ImageOverlay options)
\r 
9607   // Instantiates an image overlay object given the URL of the image and the
\r 
9608   // geographical bounds it is tied to.
\r 
9609   var imageOverlay = function (url, bounds, options) {
\r 
9610         return new ImageOverlay(url, bounds, options);
\r 
9614    * @class VideoOverlay
\r 
9615    * @aka L.VideoOverlay
\r 
9616    * @inherits ImageOverlay
\r 
9618    * Used to load and display a video player over specific bounds of the map. Extends `ImageOverlay`.
\r 
9620    * A video overlay uses the [`<video>`](https://developer.mozilla.org/docs/Web/HTML/Element/video)
\r 
9626    * var videoUrl = 'https://www.mapbox.com/bites/00188/patricia_nasa.webm',
\r 
9627    *    videoBounds = [[ 32, -130], [ 13, -100]];
\r 
9628    * L.videoOverlay(videoUrl, videoBounds ).addTo(map);
\r 
9632   var VideoOverlay = ImageOverlay.extend({
\r 
9635         // @aka VideoOverlay options
\r 
9637                 // @option autoplay: Boolean = true
\r 
9638                 // Whether the video starts playing automatically when loaded.
\r 
9639                 // On some browsers autoplay will only work with `muted: true`
\r 
9642                 // @option loop: Boolean = true
\r 
9643                 // Whether the video will loop back to the beginning when played.
\r 
9646                 // @option keepAspectRatio: Boolean = true
\r 
9647                 // Whether the video will save aspect ratio after the projection.
\r 
9648                 // Relevant for supported browsers. See [browser compatibility](https://developer.mozilla.org/en-US/docs/Web/CSS/object-fit)
\r 
9649                 keepAspectRatio: true,
\r 
9651                 // @option muted: Boolean = false
\r 
9652                 // Whether the video starts on mute when loaded.
\r 
9655                 // @option playsInline: Boolean = true
\r 
9656                 // Mobile browsers will play the video right where it is instead of open it up in fullscreen mode.
\r 
9660         _initImage: function () {
\r 
9661                 var wasElementSupplied = this._url.tagName === 'VIDEO';
\r 
9662                 var vid = this._image = wasElementSupplied ? this._url : create$1('video');
\r 
9664                 addClass(vid, 'leaflet-image-layer');
\r 
9665                 if (this._zoomAnimated) { addClass(vid, 'leaflet-zoom-animated'); }
\r 
9666                 if (this.options.className) { addClass(vid, this.options.className); }
\r 
9668                 vid.onselectstart = falseFn;
\r 
9669                 vid.onmousemove = falseFn;
\r 
9671                 // @event load: Event
\r 
9672                 // Fired when the video has finished loading the first frame
\r 
9673                 vid.onloadeddata = bind(this.fire, this, 'load');
\r 
9675                 if (wasElementSupplied) {
\r 
9676                         var sourceElements = vid.getElementsByTagName('source');
\r 
9678                         for (var j = 0; j < sourceElements.length; j++) {
\r 
9679                                 sources.push(sourceElements[j].src);
\r 
9682                         this._url = (sourceElements.length > 0) ? sources : [vid.src];
\r 
9686                 if (!isArray(this._url)) { this._url = [this._url]; }
\r 
9688                 if (!this.options.keepAspectRatio && Object.prototype.hasOwnProperty.call(vid.style, 'objectFit')) {
\r 
9689                         vid.style['objectFit'] = 'fill';
\r 
9691                 vid.autoplay = !!this.options.autoplay;
\r 
9692                 vid.loop = !!this.options.loop;
\r 
9693                 vid.muted = !!this.options.muted;
\r 
9694                 vid.playsInline = !!this.options.playsInline;
\r 
9695                 for (var i = 0; i < this._url.length; i++) {
\r 
9696                         var source = create$1('source');
\r 
9697                         source.src = this._url[i];
\r 
9698                         vid.appendChild(source);
\r 
9702         // @method getElement(): HTMLVideoElement
\r 
9703         // Returns the instance of [`HTMLVideoElement`](https://developer.mozilla.org/docs/Web/API/HTMLVideoElement)
\r 
9704         // used by this overlay.
\r 
9708   // @factory L.videoOverlay(video: String|Array|HTMLVideoElement, bounds: LatLngBounds, options?: VideoOverlay options)
\r 
9709   // Instantiates an image overlay object given the URL of the video (or array of URLs, or even a video element) and the
\r 
9710   // geographical bounds it is tied to.
\r 
9712   function videoOverlay(video, bounds, options) {
\r 
9713         return new VideoOverlay(video, bounds, options);
\r 
9719    * @inherits ImageOverlay
 
9721    * Used to load, display and provide DOM access to an SVG file over specific bounds of the map. Extends `ImageOverlay`.
 
9723    * An SVG overlay uses the [`<svg>`](https://developer.mozilla.org/docs/Web/SVG/Element/svg) element.
 
9728    * var svgElement = document.createElementNS("http://www.w3.org/2000/svg", "svg");
 
9729    * svgElement.setAttribute('xmlns', "http://www.w3.org/2000/svg");
 
9730    * svgElement.setAttribute('viewBox', "0 0 200 200");
 
9731    * 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"/>';
 
9732    * var svgElementBounds = [ [ 32, -130 ], [ 13, -100 ] ];
 
9733    * L.svgOverlay(svgElement, svgElementBounds).addTo(map);
 
9737   var SVGOverlay = ImageOverlay.extend({
 
9738         _initImage: function () {
 
9739                 var el = this._image = this._url;
 
9741                 addClass(el, 'leaflet-image-layer');
 
9742                 if (this._zoomAnimated) { addClass(el, 'leaflet-zoom-animated'); }
 
9743                 if (this.options.className) { addClass(el, this.options.className); }
 
9745                 el.onselectstart = falseFn;
 
9746                 el.onmousemove = falseFn;
 
9749         // @method getElement(): SVGElement
 
9750         // Returns the instance of [`SVGElement`](https://developer.mozilla.org/docs/Web/API/SVGElement)
 
9751         // used by this overlay.
 
9755   // @factory L.svgOverlay(svg: String|SVGElement, bounds: LatLngBounds, options?: SVGOverlay options)
 
9756   // Instantiates an image overlay object given an SVG element and the geographical bounds it is tied to.
 
9757   // A viewBox attribute is required on the SVG element to zoom in and out properly.
 
9759   function svgOverlay(el, bounds, options) {
 
9760         return new SVGOverlay(el, bounds, options);
 
9764    * @class DivOverlay
\r 
9765    * @inherits Interactive layer
\r 
9766    * @aka L.DivOverlay
\r 
9767    * Base model for L.Popup and L.Tooltip. Inherit from it for custom overlays like plugins.
\r 
9770   // @namespace DivOverlay
\r 
9771   var DivOverlay = Layer.extend({
\r 
9774         // @aka DivOverlay options
\r 
9776                 // @option interactive: Boolean = false
\r 
9777                 // If true, the popup/tooltip will listen to the mouse events.
\r 
9778                 interactive: false,
\r 
9780                 // @option offset: Point = Point(0, 0)
\r 
9781                 // The offset of the overlay position.
\r 
9784                 // @option className: String = ''
\r 
9785                 // A custom CSS class name to assign to the overlay.
\r 
9788                 // @option pane: String = undefined
\r 
9789                 // `Map pane` where the overlay will be added.
\r 
9792                 // @option content: String|HTMLElement|Function = ''
\r 
9793                 // Sets the HTML content of the overlay while initializing. If a function is passed the source layer will be
\r 
9794                 // passed to the function. The function should return a `String` or `HTMLElement` to be used in the overlay.
\r 
9798         initialize: function (options, source) {
\r 
9799                 if (options && (options instanceof LatLng || isArray(options))) {
\r 
9800                         this._latlng = toLatLng(options);
\r 
9801                         setOptions(this, source);
\r 
9803                         setOptions(this, options);
\r 
9804                         this._source = source;
\r 
9806                 if (this.options.content) {
\r 
9807                         this._content = this.options.content;
\r 
9811         // @method openOn(map: Map): this
\r 
9812         // Adds the overlay to the map.
\r 
9813         // Alternative to `map.openPopup(popup)`/`.openTooltip(tooltip)`.
\r 
9814         openOn: function (map) {
\r 
9815                 map = arguments.length ? map : this._source._map; // experimental, not the part of public api
\r 
9816                 if (!map.hasLayer(this)) {
\r 
9817                         map.addLayer(this);
\r 
9822         // @method close(): this
\r 
9823         // Closes the overlay.
\r 
9824         // Alternative to `map.closePopup(popup)`/`.closeTooltip(tooltip)`
\r 
9825         // and `layer.closePopup()`/`.closeTooltip()`.
\r 
9826         close: function () {
\r 
9828                         this._map.removeLayer(this);
\r 
9833         // @method toggle(layer?: Layer): this
\r 
9834         // Opens or closes the overlay bound to layer depending on its current state.
\r 
9835         // Argument may be omitted only for overlay bound to layer.
\r 
9836         // Alternative to `layer.togglePopup()`/`.toggleTooltip()`.
\r 
9837         toggle: function (layer) {
\r 
9841                         if (arguments.length) {
\r 
9842                                 this._source = layer;
\r 
9844                                 layer = this._source;
\r 
9846                         this._prepareOpen();
\r 
9848                         // open the overlay on the map
\r 
9849                         this.openOn(layer._map);
\r 
9854         onAdd: function (map) {
\r 
9855                 this._zoomAnimated = map._zoomAnimated;
\r 
9857                 if (!this._container) {
\r 
9858                         this._initLayout();
\r 
9861                 if (map._fadeAnimated) {
\r 
9862                         setOpacity(this._container, 0);
\r 
9865                 clearTimeout(this._removeTimeout);
\r 
9866                 this.getPane().appendChild(this._container);
\r 
9869                 if (map._fadeAnimated) {
\r 
9870                         setOpacity(this._container, 1);
\r 
9873                 this.bringToFront();
\r 
9875                 if (this.options.interactive) {
\r 
9876                         addClass(this._container, 'leaflet-interactive');
\r 
9877                         this.addInteractiveTarget(this._container);
\r 
9881         onRemove: function (map) {
\r 
9882                 if (map._fadeAnimated) {
\r 
9883                         setOpacity(this._container, 0);
\r 
9884                         this._removeTimeout = setTimeout(bind(remove, undefined, this._container), 200);
\r 
9886                         remove(this._container);
\r 
9889                 if (this.options.interactive) {
\r 
9890                         removeClass(this._container, 'leaflet-interactive');
\r 
9891                         this.removeInteractiveTarget(this._container);
\r 
9895         // @namespace DivOverlay
\r 
9896         // @method getLatLng: LatLng
\r 
9897         // Returns the geographical point of the overlay.
\r 
9898         getLatLng: function () {
\r 
9899                 return this._latlng;
\r 
9902         // @method setLatLng(latlng: LatLng): this
\r 
9903         // Sets the geographical point where the overlay will open.
\r 
9904         setLatLng: function (latlng) {
\r 
9905                 this._latlng = toLatLng(latlng);
\r 
9907                         this._updatePosition();
\r 
9908                         this._adjustPan();
\r 
9913         // @method getContent: String|HTMLElement
\r 
9914         // Returns the content of the overlay.
\r 
9915         getContent: function () {
\r 
9916                 return this._content;
\r 
9919         // @method setContent(htmlContent: String|HTMLElement|Function): this
\r 
9920         // Sets the HTML content of the overlay. If a function is passed the source layer will be passed to the function.
\r 
9921         // The function should return a `String` or `HTMLElement` to be used in the overlay.
\r 
9922         setContent: function (content) {
\r 
9923                 this._content = content;
\r 
9928         // @method getElement: String|HTMLElement
\r 
9929         // Returns the HTML container of the overlay.
\r 
9930         getElement: function () {
\r 
9931                 return this._container;
\r 
9934         // @method update: null
\r 
9935         // Updates the overlay content, layout and position. Useful for updating the overlay after something inside changed, e.g. image loaded.
\r 
9936         update: function () {
\r 
9937                 if (!this._map) { return; }
\r 
9939                 this._container.style.visibility = 'hidden';
\r 
9941                 this._updateContent();
\r 
9942                 this._updateLayout();
\r 
9943                 this._updatePosition();
\r 
9945                 this._container.style.visibility = '';
\r 
9947                 this._adjustPan();
\r 
9950         getEvents: function () {
\r 
9952                         zoom: this._updatePosition,
\r 
9953                         viewreset: this._updatePosition
\r 
9956                 if (this._zoomAnimated) {
\r 
9957                         events.zoomanim = this._animateZoom;
\r 
9962         // @method isOpen: Boolean
\r 
9963         // Returns `true` when the overlay is visible on the map.
\r 
9964         isOpen: function () {
\r 
9965                 return !!this._map && this._map.hasLayer(this);
\r 
9968         // @method bringToFront: this
\r 
9969         // Brings this overlay in front of other overlays (in the same map pane).
\r 
9970         bringToFront: function () {
\r 
9972                         toFront(this._container);
\r 
9977         // @method bringToBack: this
\r 
9978         // Brings this overlay to the back of other overlays (in the same map pane).
\r 
9979         bringToBack: function () {
\r 
9981                         toBack(this._container);
\r 
9986         // prepare bound overlay to open: update latlng pos / content source (for FeatureGroup)
\r 
9987         _prepareOpen: function (latlng) {
\r 
9988                 var source = this._source;
\r 
9989                 if (!source._map) { return false; }
\r 
9991                 if (source instanceof FeatureGroup) {
\r 
9993                         var layers = this._source._layers;
\r 
9994                         for (var id in layers) {
\r 
9995                                 if (layers[id]._map) {
\r 
9996                                         source = layers[id];
\r 
10000                         if (!source) { return false; } // Unable to get source layer.
\r 
10002                         // set overlay source to this layer
\r 
10003                         this._source = source;
\r 
10007                         if (source.getCenter) {
\r 
10008                                 latlng = source.getCenter();
\r 
10009                         } else if (source.getLatLng) {
\r 
10010                                 latlng = source.getLatLng();
\r 
10011                         } else if (source.getBounds) {
\r 
10012                                 latlng = source.getBounds().getCenter();
\r 
10014                                 throw new Error('Unable to get source layer LatLng.');
\r 
10017                 this.setLatLng(latlng);
\r 
10020                         // update the overlay (content, layout, etc...)
\r 
10027         _updateContent: function () {
\r 
10028                 if (!this._content) { return; }
\r 
10030                 var node = this._contentNode;
\r 
10031                 var content = (typeof this._content === 'function') ? this._content(this._source || this) : this._content;
\r 
10033                 if (typeof content === 'string') {
\r 
10034                         node.innerHTML = content;
\r 
10036                         while (node.hasChildNodes()) {
\r 
10037                                 node.removeChild(node.firstChild);
\r 
10039                         node.appendChild(content);
\r 
10042                 // @namespace DivOverlay
\r 
10043                 // @section DivOverlay events
\r 
10044                 // @event contentupdate: Event
\r 
10045                 // Fired when the content of the overlay is updated
\r 
10046                 this.fire('contentupdate');
\r 
10049         _updatePosition: function () {
\r 
10050                 if (!this._map) { return; }
\r 
10052                 var pos = this._map.latLngToLayerPoint(this._latlng),
\r 
10053                     offset = toPoint(this.options.offset),
\r 
10054                     anchor = this._getAnchor();
\r 
10056                 if (this._zoomAnimated) {
\r 
10057                         setPosition(this._container, pos.add(anchor));
\r 
10059                         offset = offset.add(pos).add(anchor);
\r 
10062                 var bottom = this._containerBottom = -offset.y,
\r 
10063                     left = this._containerLeft = -Math.round(this._containerWidth / 2) + offset.x;
\r 
10065                 // bottom position the overlay in case the height of the overlay changes (images loading etc)
\r 
10066                 this._container.style.bottom = bottom + 'px';
\r 
10067                 this._container.style.left = left + 'px';
\r 
10070         _getAnchor: function () {
\r 
10077         _initOverlay: function (OverlayClass, content, latlng, options) {
\r 
10078                 var overlay = content;
\r 
10079                 if (!(overlay instanceof OverlayClass)) {
\r 
10080                         overlay = new OverlayClass(options).setContent(content);
\r 
10083                         overlay.setLatLng(latlng);
\r 
10091         _initOverlay: function (OverlayClass, old, content, options) {
\r 
10092                 var overlay = content;
\r 
10093                 if (overlay instanceof OverlayClass) {
\r 
10094                         setOptions(overlay, options);
\r 
10095                         overlay._source = this;
\r 
10097                         overlay = (old && !options) ? old : new OverlayClass(options, this);
\r 
10098                         overlay.setContent(content);
\r 
10106    * @inherits DivOverlay
\r 
10108    * Used to open popups in certain places of the map. Use [Map.openPopup](#map-openpopup) to
\r 
10109    * open popups while making sure that only one popup is open at one time
\r 
10110    * (recommended for usability), or use [Map.addLayer](#map-addlayer) to open as many as you want.
\r 
10114    * If you want to just bind a popup to marker click and then open it, it's really easy:
\r 
10117    * marker.bindPopup(popupContent).openPopup();
\r 
10119    * Path overlays like polylines also have a `bindPopup` method.
\r 
10121    * A popup can be also standalone:
\r 
10124    * var popup = L.popup()
\r 
10125    *    .setLatLng(latlng)
\r 
10126    *    .setContent('<p>Hello world!<br />This is a nice popup.</p>')
\r 
10131    * var popup = L.popup(latlng, {content: '<p>Hello world!<br />This is a nice popup.</p>')
\r 
10137   // @namespace Popup
\r 
10138   var Popup = DivOverlay.extend({
\r 
10141         // @aka Popup options
\r 
10143                 // @option pane: String = 'popupPane'
\r 
10144                 // `Map pane` where the popup will be added.
\r 
10145                 pane: 'popupPane',
\r 
10147                 // @option offset: Point = Point(0, 7)
\r 
10148                 // The offset of the popup position.
\r 
10151                 // @option maxWidth: Number = 300
\r 
10152                 // Max width of the popup, in pixels.
\r 
10155                 // @option minWidth: Number = 50
\r 
10156                 // Min width of the popup, in pixels.
\r 
10159                 // @option maxHeight: Number = null
\r 
10160                 // If set, creates a scrollable container of the given height
\r 
10161                 // inside a popup if its content exceeds it.
\r 
10162                 // The scrollable container can be styled using the
\r 
10163                 // `leaflet-popup-scrolled` CSS class selector.
\r 
10166                 // @option autoPan: Boolean = true
\r 
10167                 // Set it to `false` if you don't want the map to do panning animation
\r 
10168                 // to fit the opened popup.
\r 
10171                 // @option autoPanPaddingTopLeft: Point = null
\r 
10172                 // The margin between the popup and the top left corner of the map
\r 
10173                 // view after autopanning was performed.
\r 
10174                 autoPanPaddingTopLeft: null,
\r 
10176                 // @option autoPanPaddingBottomRight: Point = null
\r 
10177                 // The margin between the popup and the bottom right corner of the map
\r 
10178                 // view after autopanning was performed.
\r 
10179                 autoPanPaddingBottomRight: null,
\r 
10181                 // @option autoPanPadding: Point = Point(5, 5)
\r 
10182                 // Equivalent of setting both top left and bottom right autopan padding to the same value.
\r 
10183                 autoPanPadding: [5, 5],
\r 
10185                 // @option keepInView: Boolean = false
\r 
10186                 // Set it to `true` if you want to prevent users from panning the popup
\r 
10187                 // off of the screen while it is open.
\r 
10188                 keepInView: false,
\r 
10190                 // @option closeButton: Boolean = true
\r 
10191                 // Controls the presence of a close button in the popup.
\r 
10192                 closeButton: true,
\r 
10194                 // @option autoClose: Boolean = true
\r 
10195                 // Set it to `false` if you want to override the default behavior of
\r 
10196                 // the popup closing when another popup is opened.
\r 
10199                 // @option closeOnEscapeKey: Boolean = true
\r 
10200                 // Set it to `false` if you want to override the default behavior of
\r 
10201                 // the ESC key for closing of the popup.
\r 
10202                 closeOnEscapeKey: true,
\r 
10204                 // @option closeOnClick: Boolean = *
\r 
10205                 // Set it if you want to override the default behavior of the popup closing when user clicks
\r 
10206                 // on the map. Defaults to the map's [`closePopupOnClick`](#map-closepopuponclick) option.
\r 
10208                 // @option className: String = ''
\r 
10209                 // A custom CSS class name to assign to the popup.
\r 
10213         // @namespace Popup
\r 
10214         // @method openOn(map: Map): this
\r 
10215         // Alternative to `map.openPopup(popup)`.
\r 
10216         // Adds the popup to the map and closes the previous one.
\r 
10217         openOn: function (map) {
\r 
10218                 map = arguments.length ? map : this._source._map; // experimental, not the part of public api
\r 
10220                 if (!map.hasLayer(this) && map._popup && map._popup.options.autoClose) {
\r 
10221                         map.removeLayer(map._popup);
\r 
10223                 map._popup = this;
\r 
10225                 return DivOverlay.prototype.openOn.call(this, map);
\r 
10228         onAdd: function (map) {
\r 
10229                 DivOverlay.prototype.onAdd.call(this, map);
\r 
10231                 // @namespace Map
\r 
10232                 // @section Popup events
\r 
10233                 // @event popupopen: PopupEvent
\r 
10234                 // Fired when a popup is opened in the map
\r 
10235                 map.fire('popupopen', {popup: this});
\r 
10237                 if (this._source) {
\r 
10238                         // @namespace Layer
\r 
10239                         // @section Popup events
\r 
10240                         // @event popupopen: PopupEvent
\r 
10241                         // Fired when a popup bound to this layer is opened
\r 
10242                         this._source.fire('popupopen', {popup: this}, true);
\r 
10243                         // For non-path layers, we toggle the popup when clicking
\r 
10244                         // again the layer, so prevent the map to reopen it.
\r 
10245                         if (!(this._source instanceof Path)) {
\r 
10246                                 this._source.on('preclick', stopPropagation);
\r 
10251         onRemove: function (map) {
\r 
10252                 DivOverlay.prototype.onRemove.call(this, map);
\r 
10254                 // @namespace Map
\r 
10255                 // @section Popup events
\r 
10256                 // @event popupclose: PopupEvent
\r 
10257                 // Fired when a popup in the map is closed
\r 
10258                 map.fire('popupclose', {popup: this});
\r 
10260                 if (this._source) {
\r 
10261                         // @namespace Layer
\r 
10262                         // @section Popup events
\r 
10263                         // @event popupclose: PopupEvent
\r 
10264                         // Fired when a popup bound to this layer is closed
\r 
10265                         this._source.fire('popupclose', {popup: this}, true);
\r 
10266                         if (!(this._source instanceof Path)) {
\r 
10267                                 this._source.off('preclick', stopPropagation);
\r 
10272         getEvents: function () {
\r 
10273                 var events = DivOverlay.prototype.getEvents.call(this);
\r 
10275                 if (this.options.closeOnClick !== undefined ? this.options.closeOnClick : this._map.options.closePopupOnClick) {
\r 
10276                         events.preclick = this.close;
\r 
10279                 if (this.options.keepInView) {
\r 
10280                         events.moveend = this._adjustPan;
\r 
10286         _initLayout: function () {
\r 
10287                 var prefix = 'leaflet-popup',
\r 
10288                     container = this._container = create$1('div',
\r 
10289                         prefix + ' ' + (this.options.className || '') +
\r 
10290                         ' leaflet-zoom-animated');
\r 
10292                 var wrapper = this._wrapper = create$1('div', prefix + '-content-wrapper', container);
\r 
10293                 this._contentNode = create$1('div', prefix + '-content', wrapper);
\r 
10295                 disableClickPropagation(container);
\r 
10296                 disableScrollPropagation(this._contentNode);
\r 
10297                 on(container, 'contextmenu', stopPropagation);
\r 
10299                 this._tipContainer = create$1('div', prefix + '-tip-container', container);
\r 
10300                 this._tip = create$1('div', prefix + '-tip', this._tipContainer);
\r 
10302                 if (this.options.closeButton) {
\r 
10303                         var closeButton = this._closeButton = create$1('a', prefix + '-close-button', container);
\r 
10304                         closeButton.setAttribute('role', 'button'); // overrides the implicit role=link of <a> elements #7399
\r 
10305                         closeButton.setAttribute('aria-label', 'Close popup');
\r 
10306                         closeButton.href = '#close';
\r 
10307                         closeButton.innerHTML = '<span aria-hidden="true">×</span>';
\r 
10309                         on(closeButton, 'click', function (ev) {
\r 
10310                                 preventDefault(ev);
\r 
10316         _updateLayout: function () {
\r 
10317                 var container = this._contentNode,
\r 
10318                     style = container.style;
\r 
10320                 style.width = '';
\r 
10321                 style.whiteSpace = 'nowrap';
\r 
10323                 var width = container.offsetWidth;
\r 
10324                 width = Math.min(width, this.options.maxWidth);
\r 
10325                 width = Math.max(width, this.options.minWidth);
\r 
10327                 style.width = (width + 1) + 'px';
\r 
10328                 style.whiteSpace = '';
\r 
10330                 style.height = '';
\r 
10332                 var height = container.offsetHeight,
\r 
10333                     maxHeight = this.options.maxHeight,
\r 
10334                     scrolledClass = 'leaflet-popup-scrolled';
\r 
10336                 if (maxHeight && height > maxHeight) {
\r 
10337                         style.height = maxHeight + 'px';
\r 
10338                         addClass(container, scrolledClass);
\r 
10340                         removeClass(container, scrolledClass);
\r 
10343                 this._containerWidth = this._container.offsetWidth;
\r 
10346         _animateZoom: function (e) {
\r 
10347                 var pos = this._map._latLngToNewLayerPoint(this._latlng, e.zoom, e.center),
\r 
10348                     anchor = this._getAnchor();
\r 
10349                 setPosition(this._container, pos.add(anchor));
\r 
10352         _adjustPan: function () {
\r 
10353                 if (!this.options.autoPan) { return; }
\r 
10354                 if (this._map._panAnim) { this._map._panAnim.stop(); }
\r 
10356                 // We can endlessly recurse if keepInView is set and the view resets.
\r 
10357                 // Let's guard against that by exiting early if we're responding to our own autopan.
\r 
10358                 if (this._autopanning) {
\r 
10359                         this._autopanning = false;
\r 
10363                 var map = this._map,
\r 
10364                     marginBottom = parseInt(getStyle(this._container, 'marginBottom'), 10) || 0,
\r 
10365                     containerHeight = this._container.offsetHeight + marginBottom,
\r 
10366                     containerWidth = this._containerWidth,
\r 
10367                     layerPos = new Point(this._containerLeft, -containerHeight - this._containerBottom);
\r 
10369                 layerPos._add(getPosition(this._container));
\r 
10371                 var containerPos = map.layerPointToContainerPoint(layerPos),
\r 
10372                     padding = toPoint(this.options.autoPanPadding),
\r 
10373                     paddingTL = toPoint(this.options.autoPanPaddingTopLeft || padding),
\r 
10374                     paddingBR = toPoint(this.options.autoPanPaddingBottomRight || padding),
\r 
10375                     size = map.getSize(),
\r 
10379                 if (containerPos.x + containerWidth + paddingBR.x > size.x) { // right
\r 
10380                         dx = containerPos.x + containerWidth - size.x + paddingBR.x;
\r 
10382                 if (containerPos.x - dx - paddingTL.x < 0) { // left
\r 
10383                         dx = containerPos.x - paddingTL.x;
\r 
10385                 if (containerPos.y + containerHeight + paddingBR.y > size.y) { // bottom
\r 
10386                         dy = containerPos.y + containerHeight - size.y + paddingBR.y;
\r 
10388                 if (containerPos.y - dy - paddingTL.y < 0) { // top
\r 
10389                         dy = containerPos.y - paddingTL.y;
\r 
10392                 // @namespace Map
\r 
10393                 // @section Popup events
\r 
10394                 // @event autopanstart: Event
\r 
10395                 // Fired when the map starts autopanning when opening a popup.
\r 
10397                         // Track that we're autopanning, as this function will be re-ran on moveend
\r 
10398                         if (this.options.keepInView) {
\r 
10399                                 this._autopanning = true;
\r 
10403                             .fire('autopanstart')
\r 
10404                             .panBy([dx, dy]);
\r 
10408         _getAnchor: function () {
\r 
10409                 // Where should we anchor the popup on the source layer?
\r 
10410                 return toPoint(this._source && this._source._getPopupAnchor ? this._source._getPopupAnchor() : [0, 0]);
\r 
10415   // @namespace Popup
\r 
10416   // @factory L.popup(options?: Popup options, source?: Layer)
\r 
10417   // 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.
\r 
10419   // @factory L.popup(latlng: LatLng, options?: Popup options)
\r 
10420   // Instantiates a `Popup` object given `latlng` where the popup will open and an optional `options` object that describes its appearance and location.
\r 
10421   var popup = function (options, source) {
\r 
10422         return new Popup(options, source);
\r 
10426   /* @namespace Map
\r 
10427    * @section Interaction Options
\r 
10428    * @option closePopupOnClick: Boolean = true
\r 
10429    * Set it to `false` if you don't want popups to close when user clicks the map.
\r 
10431   Map.mergeOptions({
\r 
10432         closePopupOnClick: true
\r 
10436   // @namespace Map
\r 
10437   // @section Methods for Layers and Controls
\r 
10439         // @method openPopup(popup: Popup): this
\r 
10440         // Opens the specified popup while closing the previously opened (to make sure only one is opened at one time for usability).
\r 
10442         // @method openPopup(content: String|HTMLElement, latlng: LatLng, options?: Popup options): this
\r 
10443         // Creates a popup with the specified content and options and opens it in the given point on a map.
\r 
10444         openPopup: function (popup, latlng, options) {
\r 
10445                 this._initOverlay(Popup, popup, latlng, options)
\r 
10451         // @method closePopup(popup?: Popup): this
\r 
10452         // Closes the popup previously opened with [openPopup](#map-openpopup) (or the given one).
\r 
10453         closePopup: function (popup) {
\r 
10454                 popup = arguments.length ? popup : this._popup;
\r 
10463    * @namespace Layer
\r 
10464    * @section Popup methods example
\r 
10466    * All layers share a set of methods convenient for binding popups to it.
\r 
10469    * var layer = L.Polygon(latlngs).bindPopup('Hi There!').addTo(map);
\r 
10470    * layer.openPopup();
\r 
10471    * layer.closePopup();
\r 
10474    * 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.
\r 
10477   // @section Popup methods
\r 
10480         // @method bindPopup(content: String|HTMLElement|Function|Popup, options?: Popup options): this
\r 
10481         // Binds a popup to the layer with the passed `content` and sets up the
\r 
10482         // necessary event listeners. If a `Function` is passed it will receive
\r 
10483         // the layer as the first argument and should return a `String` or `HTMLElement`.
\r 
10484         bindPopup: function (content, options) {
\r 
10485                 this._popup = this._initOverlay(Popup, this._popup, content, options);
\r 
10486                 if (!this._popupHandlersAdded) {
\r 
10488                                 click: this._openPopup,
\r 
10489                                 keypress: this._onKeyPress,
\r 
10490                                 remove: this.closePopup,
\r 
10491                                 move: this._movePopup
\r 
10493                         this._popupHandlersAdded = true;
\r 
10499         // @method unbindPopup(): this
\r 
10500         // Removes the popup previously bound with `bindPopup`.
\r 
10501         unbindPopup: function () {
\r 
10502                 if (this._popup) {
\r 
10504                                 click: this._openPopup,
\r 
10505                                 keypress: this._onKeyPress,
\r 
10506                                 remove: this.closePopup,
\r 
10507                                 move: this._movePopup
\r 
10509                         this._popupHandlersAdded = false;
\r 
10510                         this._popup = null;
\r 
10515         // @method openPopup(latlng?: LatLng): this
\r 
10516         // Opens the bound popup at the specified `latlng` or at the default popup anchor if no `latlng` is passed.
\r 
10517         openPopup: function (latlng) {
\r 
10518                 if (this._popup) {
\r 
10519                         if (!(this instanceof FeatureGroup)) {
\r 
10520                                 this._popup._source = this;
\r 
10522                         if (this._popup._prepareOpen(latlng || this._latlng)) {
\r 
10523                                 // open the popup on the map
\r 
10524                                 this._popup.openOn(this._map);
\r 
10530         // @method closePopup(): this
\r 
10531         // Closes the popup bound to this layer if it is open.
\r 
10532         closePopup: function () {
\r 
10533                 if (this._popup) {
\r 
10534                         this._popup.close();
\r 
10539         // @method togglePopup(): this
\r 
10540         // Opens or closes the popup bound to this layer depending on its current state.
\r 
10541         togglePopup: function () {
\r 
10542                 if (this._popup) {
\r 
10543                         this._popup.toggle(this);
\r 
10548         // @method isPopupOpen(): boolean
\r 
10549         // Returns `true` if the popup bound to this layer is currently open.
\r 
10550         isPopupOpen: function () {
\r 
10551                 return (this._popup ? this._popup.isOpen() : false);
\r 
10554         // @method setPopupContent(content: String|HTMLElement|Popup): this
\r 
10555         // Sets the content of the popup bound to this layer.
\r 
10556         setPopupContent: function (content) {
\r 
10557                 if (this._popup) {
\r 
10558                         this._popup.setContent(content);
\r 
10563         // @method getPopup(): Popup
\r 
10564         // Returns the popup bound to this layer.
\r 
10565         getPopup: function () {
\r 
10566                 return this._popup;
\r 
10569         _openPopup: function (e) {
\r 
10570                 if (!this._popup || !this._map) {
\r 
10573                 // prevent map click
\r 
10576                 var target = e.layer || e.target;
\r 
10577                 if (this._popup._source === target && !(target instanceof Path)) {
\r 
10578                         // treat it like a marker and figure out
\r 
10579                         // if we should toggle it open/closed
\r 
10580                         if (this._map.hasLayer(this._popup)) {
\r 
10581                                 this.closePopup();
\r 
10583                                 this.openPopup(e.latlng);
\r 
10587                 this._popup._source = target;
\r 
10588                 this.openPopup(e.latlng);
\r 
10591         _movePopup: function (e) {
\r 
10592                 this._popup.setLatLng(e.latlng);
\r 
10595         _onKeyPress: function (e) {
\r 
10596                 if (e.originalEvent.keyCode === 13) {
\r 
10597                         this._openPopup(e);
\r 
10604    * @inherits DivOverlay
 
10606    * Used to display small texts on top of map layers.
 
10609    * If you want to just bind a tooltip to marker:
 
10612    * marker.bindTooltip("my tooltip text").openTooltip();
 
10614    * Path overlays like polylines also have a `bindTooltip` method.
 
10616    * A tooltip can be also standalone:
 
10619    * var tooltip = L.tooltip()
 
10620    *    .setLatLng(latlng)
 
10621    *    .setContent('Hello world!<br />This is a nice tooltip.')
 
10626    * var tooltip = L.tooltip(latlng, {content: 'Hello world!<br />This is a nice tooltip.'})
 
10631    * Note about tooltip offset. Leaflet takes two options in consideration
 
10632    * for computing tooltip offsetting:
 
10633    * - the `offset` Tooltip option: it defaults to [0, 0], and it's specific to one tooltip.
 
10634    *   Add a positive x offset to move the tooltip to the right, and a positive y offset to
 
10635    *   move it to the bottom. Negatives will move to the left and top.
 
10636    * - the `tooltipAnchor` Icon option: this will only be considered for Marker. You
 
10637    *   should adapt this value if you use a custom icon.
 
10641   // @namespace Tooltip
 
10642   var Tooltip = DivOverlay.extend({
 
10645         // @aka Tooltip options
 
10647                 // @option pane: String = 'tooltipPane'
 
10648                 // `Map pane` where the tooltip will be added.
 
10649                 pane: 'tooltipPane',
 
10651                 // @option offset: Point = Point(0, 0)
 
10652                 // Optional offset of the tooltip position.
 
10655                 // @option direction: String = 'auto'
 
10656                 // Direction where to open the tooltip. Possible values are: `right`, `left`,
 
10657                 // `top`, `bottom`, `center`, `auto`.
 
10658                 // `auto` will dynamically switch between `right` and `left` according to the tooltip
 
10659                 // position on the map.
 
10662                 // @option permanent: Boolean = false
 
10663                 // Whether to open the tooltip permanently or only on mouseover.
 
10666                 // @option sticky: Boolean = false
 
10667                 // If true, the tooltip will follow the mouse instead of being fixed at the feature center.
 
10670                 // @option opacity: Number = 0.9
 
10671                 // Tooltip container opacity.
 
10675         onAdd: function (map) {
 
10676                 DivOverlay.prototype.onAdd.call(this, map);
 
10677                 this.setOpacity(this.options.opacity);
 
10680                 // @section Tooltip events
 
10681                 // @event tooltipopen: TooltipEvent
 
10682                 // Fired when a tooltip is opened in the map.
 
10683                 map.fire('tooltipopen', {tooltip: this});
 
10685                 if (this._source) {
 
10686                         this.addEventParent(this._source);
 
10688                         // @namespace Layer
 
10689                         // @section Tooltip events
 
10690                         // @event tooltipopen: TooltipEvent
 
10691                         // Fired when a tooltip bound to this layer is opened.
 
10692                         this._source.fire('tooltipopen', {tooltip: this}, true);
 
10696         onRemove: function (map) {
 
10697                 DivOverlay.prototype.onRemove.call(this, map);
 
10700                 // @section Tooltip events
 
10701                 // @event tooltipclose: TooltipEvent
 
10702                 // Fired when a tooltip in the map is closed.
 
10703                 map.fire('tooltipclose', {tooltip: this});
 
10705                 if (this._source) {
 
10706                         this.removeEventParent(this._source);
 
10708                         // @namespace Layer
 
10709                         // @section Tooltip events
 
10710                         // @event tooltipclose: TooltipEvent
 
10711                         // Fired when a tooltip bound to this layer is closed.
 
10712                         this._source.fire('tooltipclose', {tooltip: this}, true);
 
10716         getEvents: function () {
 
10717                 var events = DivOverlay.prototype.getEvents.call(this);
 
10719                 if (!this.options.permanent) {
 
10720                         events.preclick = this.close;
 
10726         _initLayout: function () {
 
10727                 var prefix = 'leaflet-tooltip',
 
10728                     className = prefix + ' ' + (this.options.className || '') + ' leaflet-zoom-' + (this._zoomAnimated ? 'animated' : 'hide');
 
10730                 this._contentNode = this._container = create$1('div', className);
 
10732                 this._container.setAttribute('role', 'tooltip');
 
10733                 this._container.setAttribute('id', 'leaflet-tooltip-' + stamp(this));
 
10736         _updateLayout: function () {},
 
10738         _adjustPan: function () {},
 
10740         _setPosition: function (pos) {
 
10743                     container = this._container,
 
10744                     centerPoint = map.latLngToContainerPoint(map.getCenter()),
 
10745                     tooltipPoint = map.layerPointToContainerPoint(pos),
 
10746                     direction = this.options.direction,
 
10747                     tooltipWidth = container.offsetWidth,
 
10748                     tooltipHeight = container.offsetHeight,
 
10749                     offset = toPoint(this.options.offset),
 
10750                     anchor = this._getAnchor();
 
10752                 if (direction === 'top') {
 
10753                         subX = tooltipWidth / 2;
 
10754                         subY = tooltipHeight;
 
10755                 } else if (direction === 'bottom') {
 
10756                         subX = tooltipWidth / 2;
 
10758                 } else if (direction === 'center') {
 
10759                         subX = tooltipWidth / 2;
 
10760                         subY = tooltipHeight / 2;
 
10761                 } else if (direction === 'right') {
 
10763                         subY = tooltipHeight / 2;
 
10764                 } else if (direction === 'left') {
 
10765                         subX = tooltipWidth;
 
10766                         subY = tooltipHeight / 2;
 
10767                 } else if (tooltipPoint.x < centerPoint.x) {
 
10768                         direction = 'right';
 
10770                         subY = tooltipHeight / 2;
 
10772                         direction = 'left';
 
10773                         subX = tooltipWidth + (offset.x + anchor.x) * 2;
 
10774                         subY = tooltipHeight / 2;
 
10777                 pos = pos.subtract(toPoint(subX, subY, true)).add(offset).add(anchor);
 
10779                 removeClass(container, 'leaflet-tooltip-right');
 
10780                 removeClass(container, 'leaflet-tooltip-left');
 
10781                 removeClass(container, 'leaflet-tooltip-top');
 
10782                 removeClass(container, 'leaflet-tooltip-bottom');
 
10783                 addClass(container, 'leaflet-tooltip-' + direction);
 
10784                 setPosition(container, pos);
 
10787         _updatePosition: function () {
 
10788                 var pos = this._map.latLngToLayerPoint(this._latlng);
 
10789                 this._setPosition(pos);
 
10792         setOpacity: function (opacity) {
 
10793                 this.options.opacity = opacity;
 
10795                 if (this._container) {
 
10796                         setOpacity(this._container, opacity);
 
10800         _animateZoom: function (e) {
 
10801                 var pos = this._map._latLngToNewLayerPoint(this._latlng, e.zoom, e.center);
 
10802                 this._setPosition(pos);
 
10805         _getAnchor: function () {
 
10806                 // Where should we anchor the tooltip on the source layer?
 
10807                 return toPoint(this._source && this._source._getTooltipAnchor && !this.options.sticky ? this._source._getTooltipAnchor() : [0, 0]);
 
10812   // @namespace Tooltip
 
10813   // @factory L.tooltip(options?: Tooltip options, source?: Layer)
 
10814   // 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.
 
10816   // @factory L.tooltip(latlng: LatLng, options?: Tooltip options)
 
10817   // Instantiates a `Tooltip` object given `latlng` where the tooltip will open and an optional `options` object that describes its appearance and location.
 
10818   var tooltip = function (options, source) {
 
10819         return new Tooltip(options, source);
 
10823   // @section Methods for Layers and Controls
 
10826         // @method openTooltip(tooltip: Tooltip): this
 
10827         // Opens the specified tooltip.
 
10829         // @method openTooltip(content: String|HTMLElement, latlng: LatLng, options?: Tooltip options): this
 
10830         // Creates a tooltip with the specified content and options and open it.
 
10831         openTooltip: function (tooltip, latlng, options) {
 
10832                 this._initOverlay(Tooltip, tooltip, latlng, options)
 
10838         // @method closeTooltip(tooltip: Tooltip): this
 
10839         // Closes the tooltip given as parameter.
 
10840         closeTooltip: function (tooltip) {
 
10849    * @section Tooltip methods example
 
10851    * All layers share a set of methods convenient for binding tooltips to it.
 
10854    * var layer = L.Polygon(latlngs).bindTooltip('Hi There!').addTo(map);
 
10855    * layer.openTooltip();
 
10856    * layer.closeTooltip();
 
10860   // @section Tooltip methods
 
10863         // @method bindTooltip(content: String|HTMLElement|Function|Tooltip, options?: Tooltip options): this
 
10864         // Binds a tooltip to the layer with the passed `content` and sets up the
 
10865         // necessary event listeners. If a `Function` is passed it will receive
 
10866         // the layer as the first argument and should return a `String` or `HTMLElement`.
 
10867         bindTooltip: function (content, options) {
 
10869                 if (this._tooltip && this.isTooltipOpen()) {
 
10870                         this.unbindTooltip();
 
10873                 this._tooltip = this._initOverlay(Tooltip, this._tooltip, content, options);
 
10874                 this._initTooltipInteractions();
 
10876                 if (this._tooltip.options.permanent && this._map && this._map.hasLayer(this)) {
 
10877                         this.openTooltip();
 
10883         // @method unbindTooltip(): this
 
10884         // Removes the tooltip previously bound with `bindTooltip`.
 
10885         unbindTooltip: function () {
 
10886                 if (this._tooltip) {
 
10887                         this._initTooltipInteractions(true);
 
10888                         this.closeTooltip();
 
10889                         this._tooltip = null;
 
10894         _initTooltipInteractions: function (remove) {
 
10895                 if (!remove && this._tooltipHandlersAdded) { return; }
 
10896                 var onOff = remove ? 'off' : 'on',
 
10898                         remove: this.closeTooltip,
 
10899                         move: this._moveTooltip
 
10901                 if (!this._tooltip.options.permanent) {
 
10902                         events.mouseover = this._openTooltip;
 
10903                         events.mouseout = this.closeTooltip;
 
10904                         events.click = this._openTooltip;
 
10906                                 this._addFocusListeners();
 
10908                                 events.add = this._addFocusListeners;
 
10911                         events.add = this._openTooltip;
 
10913                 if (this._tooltip.options.sticky) {
 
10914                         events.mousemove = this._moveTooltip;
 
10916                 this[onOff](events);
 
10917                 this._tooltipHandlersAdded = !remove;
 
10920         // @method openTooltip(latlng?: LatLng): this
 
10921         // Opens the bound tooltip at the specified `latlng` or at the default tooltip anchor if no `latlng` is passed.
 
10922         openTooltip: function (latlng) {
 
10923                 if (this._tooltip) {
 
10924                         if (!(this instanceof FeatureGroup)) {
 
10925                                 this._tooltip._source = this;
 
10927                         if (this._tooltip._prepareOpen(latlng)) {
 
10928                                 // open the tooltip on the map
 
10929                                 this._tooltip.openOn(this._map);
 
10931                                 if (this.getElement) {
 
10932                                         this._setAriaDescribedByOnLayer(this);
 
10933                                 } else if (this.eachLayer) {
 
10934                                         this.eachLayer(this._setAriaDescribedByOnLayer, this);
 
10941         // @method closeTooltip(): this
 
10942         // Closes the tooltip bound to this layer if it is open.
 
10943         closeTooltip: function () {
 
10944                 if (this._tooltip) {
 
10945                         return this._tooltip.close();
 
10949         // @method toggleTooltip(): this
 
10950         // Opens or closes the tooltip bound to this layer depending on its current state.
 
10951         toggleTooltip: function () {
 
10952                 if (this._tooltip) {
 
10953                         this._tooltip.toggle(this);
 
10958         // @method isTooltipOpen(): boolean
 
10959         // Returns `true` if the tooltip bound to this layer is currently open.
 
10960         isTooltipOpen: function () {
 
10961                 return this._tooltip.isOpen();
 
10964         // @method setTooltipContent(content: String|HTMLElement|Tooltip): this
 
10965         // Sets the content of the tooltip bound to this layer.
 
10966         setTooltipContent: function (content) {
 
10967                 if (this._tooltip) {
 
10968                         this._tooltip.setContent(content);
 
10973         // @method getTooltip(): Tooltip
 
10974         // Returns the tooltip bound to this layer.
 
10975         getTooltip: function () {
 
10976                 return this._tooltip;
 
10979         _addFocusListeners: function () {
 
10980                 if (this.getElement) {
 
10981                         this._addFocusListenersOnLayer(this);
 
10982                 } else if (this.eachLayer) {
 
10983                         this.eachLayer(this._addFocusListenersOnLayer, this);
 
10987         _addFocusListenersOnLayer: function (layer) {
 
10988                 var el = typeof layer.getElement === 'function' && layer.getElement();
 
10990                         on(el, 'focus', function () {
 
10991                                 this._tooltip._source = layer;
 
10992                                 this.openTooltip();
 
10994                         on(el, 'blur', this.closeTooltip, this);
 
10998         _setAriaDescribedByOnLayer: function (layer) {
 
10999                 var el = typeof layer.getElement === 'function' && layer.getElement();
 
11001                         el.setAttribute('aria-describedby', this._tooltip._container.id);
 
11006         _openTooltip: function (e) {
 
11007                 if (!this._tooltip || !this._map) {
 
11011                 // If the map is moving, we will show the tooltip after it's done.
 
11012                 if (this._map.dragging && this._map.dragging.moving() && !this._openOnceFlag) {
 
11013                         this._openOnceFlag = true;
 
11015                         this._map.once('moveend', function () {
 
11016                                 that._openOnceFlag = false;
 
11017                                 that._openTooltip(e);
 
11022                 this._tooltip._source = e.layer || e.target;
 
11024                 this.openTooltip(this._tooltip.options.sticky ? e.latlng : undefined);
 
11027         _moveTooltip: function (e) {
 
11028                 var latlng = e.latlng, containerPoint, layerPoint;
 
11029                 if (this._tooltip.options.sticky && e.originalEvent) {
 
11030                         containerPoint = this._map.mouseEventToContainerPoint(e.originalEvent);
 
11031                         layerPoint = this._map.containerPointToLayerPoint(containerPoint);
 
11032                         latlng = this._map.layerPointToLatLng(layerPoint);
 
11034                 this._tooltip.setLatLng(latlng);
 
11043    * Represents a lightweight icon for markers that uses a simple `<div>`
 
11044    * element instead of an image. Inherits from `Icon` but ignores the `iconUrl` and shadow options.
 
11048    * var myIcon = L.divIcon({className: 'my-div-icon'});
 
11049    * // you can set .my-div-icon styles in CSS
 
11051    * L.marker([50.505, 30.57], {icon: myIcon}).addTo(map);
 
11054    * By default, it has a 'leaflet-div-icon' CSS class and is styled as a little white square with a shadow.
 
11057   var DivIcon = Icon.extend({
 
11060                 // @aka DivIcon options
 
11061                 iconSize: [12, 12], // also can be set through CSS
 
11063                 // iconAnchor: (Point),
 
11064                 // popupAnchor: (Point),
 
11066                 // @option html: String|HTMLElement = ''
 
11067                 // Custom HTML code to put inside the div element, empty by default. Alternatively,
 
11068                 // an instance of `HTMLElement`.
 
11071                 // @option bgPos: Point = [0, 0]
 
11072                 // Optional relative position of the background, in pixels
 
11075                 className: 'leaflet-div-icon'
 
11078         createIcon: function (oldIcon) {
 
11079                 var div = (oldIcon && oldIcon.tagName === 'DIV') ? oldIcon : document.createElement('div'),
 
11080                     options = this.options;
 
11082                 if (options.html instanceof Element) {
 
11084                         div.appendChild(options.html);
 
11086                         div.innerHTML = options.html !== false ? options.html : '';
 
11089                 if (options.bgPos) {
 
11090                         var bgPos = toPoint(options.bgPos);
 
11091                         div.style.backgroundPosition = (-bgPos.x) + 'px ' + (-bgPos.y) + 'px';
 
11093                 this._setIconStyles(div, 'icon');
 
11098         createShadow: function () {
 
11103   // @factory L.divIcon(options: DivIcon options)
 
11104   // Creates a `DivIcon` instance with the given options.
 
11105   function divIcon(options) {
 
11106         return new DivIcon(options);
 
11109   Icon.Default = IconDefault;
 
11116    * Generic class for handling a tiled grid of HTML elements. This is the base class for all tile layers and replaces `TileLayer.Canvas`.
 
11117    * 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.
 
11120    * @section Synchronous usage
 
11123    * 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.
 
11126    * var CanvasLayer = L.GridLayer.extend({
 
11127    *     createTile: function(coords){
 
11128    *         // create a <canvas> element for drawing
 
11129    *         var tile = L.DomUtil.create('canvas', 'leaflet-tile');
 
11131    *         // setup tile width and height according to the options
 
11132    *         var size = this.getTileSize();
 
11133    *         tile.width = size.x;
 
11134    *         tile.height = size.y;
 
11136    *         // get a canvas context and draw something on it using coords.x, coords.y and coords.z
 
11137    *         var ctx = tile.getContext('2d');
 
11139    *         // return the tile so it can be rendered on screen
 
11145    * @section Asynchronous usage
 
11148    * 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.
 
11151    * var CanvasLayer = L.GridLayer.extend({
 
11152    *     createTile: function(coords, done){
 
11155    *         // create a <canvas> element for drawing
 
11156    *         var tile = L.DomUtil.create('canvas', 'leaflet-tile');
 
11158    *         // setup tile width and height according to the options
 
11159    *         var size = this.getTileSize();
 
11160    *         tile.width = size.x;
 
11161    *         tile.height = size.y;
 
11163    *         // draw something asynchronously and pass the tile to the done() callback
 
11164    *         setTimeout(function() {
 
11165    *             done(error, tile);
 
11177   var GridLayer = Layer.extend({
 
11180         // @aka GridLayer options
 
11182                 // @option tileSize: Number|Point = 256
 
11183                 // Width and height of tiles in the grid. Use a number if width and height are equal, or `L.point(width, height)` otherwise.
 
11186                 // @option opacity: Number = 1.0
 
11187                 // Opacity of the tiles. Can be used in the `createTile()` function.
 
11190                 // @option updateWhenIdle: Boolean = (depends)
 
11191                 // Load new tiles only when panning ends.
 
11192                 // `true` by default on mobile browsers, in order to avoid too many requests and keep smooth navigation.
 
11193                 // `false` otherwise in order to display new tiles _during_ panning, since it is easy to pan outside the
 
11194                 // [`keepBuffer`](#gridlayer-keepbuffer) option in desktop browsers.
 
11195                 updateWhenIdle: Browser.mobile,
 
11197                 // @option updateWhenZooming: Boolean = true
 
11198                 // 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.
 
11199                 updateWhenZooming: true,
 
11201                 // @option updateInterval: Number = 200
 
11202                 // Tiles will not update more than once every `updateInterval` milliseconds when panning.
 
11203                 updateInterval: 200,
 
11205                 // @option zIndex: Number = 1
 
11206                 // The explicit zIndex of the tile layer.
 
11209                 // @option bounds: LatLngBounds = undefined
 
11210                 // If set, tiles will only be loaded inside the set `LatLngBounds`.
 
11213                 // @option minZoom: Number = 0
 
11214                 // The minimum zoom level down to which this layer will be displayed (inclusive).
 
11217                 // @option maxZoom: Number = undefined
 
11218                 // The maximum zoom level up to which this layer will be displayed (inclusive).
 
11219                 maxZoom: undefined,
 
11221                 // @option maxNativeZoom: Number = undefined
 
11222                 // Maximum zoom number the tile source has available. If it is specified,
 
11223                 // the tiles on all zoom levels higher than `maxNativeZoom` will be loaded
 
11224                 // from `maxNativeZoom` level and auto-scaled.
 
11225                 maxNativeZoom: undefined,
 
11227                 // @option minNativeZoom: Number = undefined
 
11228                 // Minimum zoom number the tile source has available. If it is specified,
 
11229                 // the tiles on all zoom levels lower than `minNativeZoom` will be loaded
 
11230                 // from `minNativeZoom` level and auto-scaled.
 
11231                 minNativeZoom: undefined,
 
11233                 // @option noWrap: Boolean = false
 
11234                 // Whether the layer is wrapped around the antimeridian. If `true`, the
 
11235                 // GridLayer will only be displayed once at low zoom levels. Has no
 
11236                 // effect when the [map CRS](#map-crs) doesn't wrap around. Can be used
 
11237                 // in combination with [`bounds`](#gridlayer-bounds) to prevent requesting
 
11238                 // tiles outside the CRS limits.
 
11241                 // @option pane: String = 'tilePane'
 
11242                 // `Map pane` where the grid layer will be added.
 
11245                 // @option className: String = ''
 
11246                 // A custom class name to assign to the tile layer. Empty by default.
 
11249                 // @option keepBuffer: Number = 2
 
11250                 // When panning the map, keep this many rows and columns of tiles before unloading them.
 
11254         initialize: function (options) {
 
11255                 setOptions(this, options);
 
11258         onAdd: function () {
 
11259                 this._initContainer();
 
11264                 this._resetView(); // implicit _update() call
 
11267         beforeAdd: function (map) {
 
11268                 map._addZoomLimit(this);
 
11271         onRemove: function (map) {
 
11272                 this._removeAllTiles();
 
11273                 remove(this._container);
 
11274                 map._removeZoomLimit(this);
 
11275                 this._container = null;
 
11276                 this._tileZoom = undefined;
 
11279         // @method bringToFront: this
 
11280         // Brings the tile layer to the top of all tile layers.
 
11281         bringToFront: function () {
 
11283                         toFront(this._container);
 
11284                         this._setAutoZIndex(Math.max);
 
11289         // @method bringToBack: this
 
11290         // Brings the tile layer to the bottom of all tile layers.
 
11291         bringToBack: function () {
 
11293                         toBack(this._container);
 
11294                         this._setAutoZIndex(Math.min);
 
11299         // @method getContainer: HTMLElement
 
11300         // Returns the HTML element that contains the tiles for this layer.
 
11301         getContainer: function () {
 
11302                 return this._container;
 
11305         // @method setOpacity(opacity: Number): this
 
11306         // Changes the [opacity](#gridlayer-opacity) of the grid layer.
 
11307         setOpacity: function (opacity) {
 
11308                 this.options.opacity = opacity;
 
11309                 this._updateOpacity();
 
11313         // @method setZIndex(zIndex: Number): this
 
11314         // Changes the [zIndex](#gridlayer-zindex) of the grid layer.
 
11315         setZIndex: function (zIndex) {
 
11316                 this.options.zIndex = zIndex;
 
11317                 this._updateZIndex();
 
11322         // @method isLoading: Boolean
 
11323         // Returns `true` if any tile in the grid layer has not finished loading.
 
11324         isLoading: function () {
 
11325                 return this._loading;
 
11328         // @method redraw: this
 
11329         // Causes the layer to clear all the tiles and request them again.
 
11330         redraw: function () {
 
11332                         this._removeAllTiles();
 
11333                         var tileZoom = this._clampZoom(this._map.getZoom());
 
11334                         if (tileZoom !== this._tileZoom) {
 
11335                                 this._tileZoom = tileZoom;
 
11336                                 this._updateLevels();
 
11343         getEvents: function () {
 
11345                         viewprereset: this._invalidateAll,
 
11346                         viewreset: this._resetView,
 
11347                         zoom: this._resetView,
 
11348                         moveend: this._onMoveEnd
 
11351                 if (!this.options.updateWhenIdle) {
 
11352                         // update tiles on move, but not more often than once per given interval
 
11353                         if (!this._onMove) {
 
11354                                 this._onMove = throttle(this._onMoveEnd, this.options.updateInterval, this);
 
11357                         events.move = this._onMove;
 
11360                 if (this._zoomAnimated) {
 
11361                         events.zoomanim = this._animateZoom;
 
11367         // @section Extension methods
 
11368         // Layers extending `GridLayer` shall reimplement the following method.
 
11369         // @method createTile(coords: Object, done?: Function): HTMLElement
 
11370         // Called only internally, must be overridden by classes extending `GridLayer`.
 
11371         // Returns the `HTMLElement` corresponding to the given `coords`. If the `done` callback
 
11372         // is specified, it must be called when the tile has finished loading and drawing.
 
11373         createTile: function () {
 
11374                 return document.createElement('div');
 
11378         // @method getTileSize: Point
 
11379         // Normalizes the [tileSize option](#gridlayer-tilesize) into a point. Used by the `createTile()` method.
 
11380         getTileSize: function () {
 
11381                 var s = this.options.tileSize;
 
11382                 return s instanceof Point ? s : new Point(s, s);
 
11385         _updateZIndex: function () {
 
11386                 if (this._container && this.options.zIndex !== undefined && this.options.zIndex !== null) {
 
11387                         this._container.style.zIndex = this.options.zIndex;
 
11391         _setAutoZIndex: function (compare) {
 
11392                 // go through all other layers of the same pane, set zIndex to max + 1 (front) or min - 1 (back)
 
11394                 var layers = this.getPane().children,
 
11395                     edgeZIndex = -compare(-Infinity, Infinity); // -Infinity for max, Infinity for min
 
11397                 for (var i = 0, len = layers.length, zIndex; i < len; i++) {
 
11399                         zIndex = layers[i].style.zIndex;
 
11401                         if (layers[i] !== this._container && zIndex) {
 
11402                                 edgeZIndex = compare(edgeZIndex, +zIndex);
 
11406                 if (isFinite(edgeZIndex)) {
 
11407                         this.options.zIndex = edgeZIndex + compare(-1, 1);
 
11408                         this._updateZIndex();
 
11412         _updateOpacity: function () {
 
11413                 if (!this._map) { return; }
 
11415                 // IE doesn't inherit filter opacity properly, so we're forced to set it on tiles
 
11416                 if (Browser.ielt9) { return; }
 
11418                 setOpacity(this._container, this.options.opacity);
 
11420                 var now = +new Date(),
 
11424                 for (var key in this._tiles) {
 
11425                         var tile = this._tiles[key];
 
11426                         if (!tile.current || !tile.loaded) { continue; }
 
11428                         var fade = Math.min(1, (now - tile.loaded) / 200);
 
11430                         setOpacity(tile.el, fade);
 
11437                                         this._onOpaqueTile(tile);
 
11439                                 tile.active = true;
 
11443                 if (willPrune && !this._noPrune) { this._pruneTiles(); }
 
11446                         cancelAnimFrame(this._fadeFrame);
 
11447                         this._fadeFrame = requestAnimFrame(this._updateOpacity, this);
 
11451         _onOpaqueTile: falseFn,
 
11453         _initContainer: function () {
 
11454                 if (this._container) { return; }
 
11456                 this._container = create$1('div', 'leaflet-layer ' + (this.options.className || ''));
 
11457                 this._updateZIndex();
 
11459                 if (this.options.opacity < 1) {
 
11460                         this._updateOpacity();
 
11463                 this.getPane().appendChild(this._container);
 
11466         _updateLevels: function () {
 
11468                 var zoom = this._tileZoom,
 
11469                     maxZoom = this.options.maxZoom;
 
11471                 if (zoom === undefined) { return undefined; }
 
11473                 for (var z in this._levels) {
 
11475                         if (this._levels[z].el.children.length || z === zoom) {
 
11476                                 this._levels[z].el.style.zIndex = maxZoom - Math.abs(zoom - z);
 
11477                                 this._onUpdateLevel(z);
 
11479                                 remove(this._levels[z].el);
 
11480                                 this._removeTilesAtZoom(z);
 
11481                                 this._onRemoveLevel(z);
 
11482                                 delete this._levels[z];
 
11486                 var level = this._levels[zoom],
 
11490                         level = this._levels[zoom] = {};
 
11492                         level.el = create$1('div', 'leaflet-tile-container leaflet-zoom-animated', this._container);
 
11493                         level.el.style.zIndex = maxZoom;
 
11495                         level.origin = map.project(map.unproject(map.getPixelOrigin()), zoom).round();
 
11498                         this._setZoomTransform(level, map.getCenter(), map.getZoom());
 
11500                         // force the browser to consider the newly added element for transition
 
11501                         falseFn(level.el.offsetWidth);
 
11503                         this._onCreateLevel(level);
 
11506                 this._level = level;
 
11511         _onUpdateLevel: falseFn,
 
11513         _onRemoveLevel: falseFn,
 
11515         _onCreateLevel: falseFn,
 
11517         _pruneTiles: function () {
 
11524                 var zoom = this._map.getZoom();
 
11525                 if (zoom > this.options.maxZoom ||
 
11526                         zoom < this.options.minZoom) {
 
11527                         this._removeAllTiles();
 
11531                 for (key in this._tiles) {
 
11532                         tile = this._tiles[key];
 
11533                         tile.retain = tile.current;
 
11536                 for (key in this._tiles) {
 
11537                         tile = this._tiles[key];
 
11538                         if (tile.current && !tile.active) {
 
11539                                 var coords = tile.coords;
 
11540                                 if (!this._retainParent(coords.x, coords.y, coords.z, coords.z - 5)) {
 
11541                                         this._retainChildren(coords.x, coords.y, coords.z, coords.z + 2);
 
11546                 for (key in this._tiles) {
 
11547                         if (!this._tiles[key].retain) {
 
11548                                 this._removeTile(key);
 
11553         _removeTilesAtZoom: function (zoom) {
 
11554                 for (var key in this._tiles) {
 
11555                         if (this._tiles[key].coords.z !== zoom) {
 
11558                         this._removeTile(key);
 
11562         _removeAllTiles: function () {
 
11563                 for (var key in this._tiles) {
 
11564                         this._removeTile(key);
 
11568         _invalidateAll: function () {
 
11569                 for (var z in this._levels) {
 
11570                         remove(this._levels[z].el);
 
11571                         this._onRemoveLevel(Number(z));
 
11572                         delete this._levels[z];
 
11574                 this._removeAllTiles();
 
11576                 this._tileZoom = undefined;
 
11579         _retainParent: function (x, y, z, minZoom) {
 
11580                 var x2 = Math.floor(x / 2),
 
11581                     y2 = Math.floor(y / 2),
 
11583                     coords2 = new Point(+x2, +y2);
 
11586                 var key = this._tileCoordsToKey(coords2),
 
11587                     tile = this._tiles[key];
 
11589                 if (tile && tile.active) {
 
11590                         tile.retain = true;
 
11593                 } else if (tile && tile.loaded) {
 
11594                         tile.retain = true;
 
11597                 if (z2 > minZoom) {
 
11598                         return this._retainParent(x2, y2, z2, minZoom);
 
11604         _retainChildren: function (x, y, z, maxZoom) {
 
11606                 for (var i = 2 * x; i < 2 * x + 2; i++) {
 
11607                         for (var j = 2 * y; j < 2 * y + 2; j++) {
 
11609                                 var coords = new Point(i, j);
 
11612                                 var key = this._tileCoordsToKey(coords),
 
11613                                     tile = this._tiles[key];
 
11615                                 if (tile && tile.active) {
 
11616                                         tile.retain = true;
 
11619                                 } else if (tile && tile.loaded) {
 
11620                                         tile.retain = true;
 
11623                                 if (z + 1 < maxZoom) {
 
11624                                         this._retainChildren(i, j, z + 1, maxZoom);
 
11630         _resetView: function (e) {
 
11631                 var animating = e && (e.pinch || e.flyTo);
 
11632                 this._setView(this._map.getCenter(), this._map.getZoom(), animating, animating);
 
11635         _animateZoom: function (e) {
 
11636                 this._setView(e.center, e.zoom, true, e.noUpdate);
 
11639         _clampZoom: function (zoom) {
 
11640                 var options = this.options;
 
11642                 if (undefined !== options.minNativeZoom && zoom < options.minNativeZoom) {
 
11643                         return options.minNativeZoom;
 
11646                 if (undefined !== options.maxNativeZoom && options.maxNativeZoom < zoom) {
 
11647                         return options.maxNativeZoom;
 
11653         _setView: function (center, zoom, noPrune, noUpdate) {
 
11654                 var tileZoom = Math.round(zoom);
 
11655                 if ((this.options.maxZoom !== undefined && tileZoom > this.options.maxZoom) ||
 
11656                     (this.options.minZoom !== undefined && tileZoom < this.options.minZoom)) {
 
11657                         tileZoom = undefined;
 
11659                         tileZoom = this._clampZoom(tileZoom);
 
11662                 var tileZoomChanged = this.options.updateWhenZooming && (tileZoom !== this._tileZoom);
 
11664                 if (!noUpdate || tileZoomChanged) {
 
11666                         this._tileZoom = tileZoom;
 
11668                         if (this._abortLoading) {
 
11669                                 this._abortLoading();
 
11672                         this._updateLevels();
 
11675                         if (tileZoom !== undefined) {
 
11676                                 this._update(center);
 
11680                                 this._pruneTiles();
 
11683                         // Flag to prevent _updateOpacity from pruning tiles during
 
11684                         // a zoom anim or a pinch gesture
 
11685                         this._noPrune = !!noPrune;
 
11688                 this._setZoomTransforms(center, zoom);
 
11691         _setZoomTransforms: function (center, zoom) {
 
11692                 for (var i in this._levels) {
 
11693                         this._setZoomTransform(this._levels[i], center, zoom);
 
11697         _setZoomTransform: function (level, center, zoom) {
 
11698                 var scale = this._map.getZoomScale(zoom, level.zoom),
 
11699                     translate = level.origin.multiplyBy(scale)
 
11700                         .subtract(this._map._getNewPixelOrigin(center, zoom)).round();
 
11702                 if (Browser.any3d) {
 
11703                         setTransform(level.el, translate, scale);
 
11705                         setPosition(level.el, translate);
 
11709         _resetGrid: function () {
 
11710                 var map = this._map,
 
11711                     crs = map.options.crs,
 
11712                     tileSize = this._tileSize = this.getTileSize(),
 
11713                     tileZoom = this._tileZoom;
 
11715                 var bounds = this._map.getPixelWorldBounds(this._tileZoom);
 
11717                         this._globalTileRange = this._pxBoundsToTileRange(bounds);
 
11720                 this._wrapX = crs.wrapLng && !this.options.noWrap && [
 
11721                         Math.floor(map.project([0, crs.wrapLng[0]], tileZoom).x / tileSize.x),
 
11722                         Math.ceil(map.project([0, crs.wrapLng[1]], tileZoom).x / tileSize.y)
 
11724                 this._wrapY = crs.wrapLat && !this.options.noWrap && [
 
11725                         Math.floor(map.project([crs.wrapLat[0], 0], tileZoom).y / tileSize.x),
 
11726                         Math.ceil(map.project([crs.wrapLat[1], 0], tileZoom).y / tileSize.y)
 
11730         _onMoveEnd: function () {
 
11731                 if (!this._map || this._map._animatingZoom) { return; }
 
11736         _getTiledPixelBounds: function (center) {
 
11737                 var map = this._map,
 
11738                     mapZoom = map._animatingZoom ? Math.max(map._animateToZoom, map.getZoom()) : map.getZoom(),
 
11739                     scale = map.getZoomScale(mapZoom, this._tileZoom),
 
11740                     pixelCenter = map.project(center, this._tileZoom).floor(),
 
11741                     halfSize = map.getSize().divideBy(scale * 2);
 
11743                 return new Bounds(pixelCenter.subtract(halfSize), pixelCenter.add(halfSize));
 
11746         // Private method to load tiles in the grid's active zoom level according to map bounds
 
11747         _update: function (center) {
 
11748                 var map = this._map;
 
11749                 if (!map) { return; }
 
11750                 var zoom = this._clampZoom(map.getZoom());
 
11752                 if (center === undefined) { center = map.getCenter(); }
 
11753                 if (this._tileZoom === undefined) { return; }   // if out of minzoom/maxzoom
 
11755                 var pixelBounds = this._getTiledPixelBounds(center),
 
11756                     tileRange = this._pxBoundsToTileRange(pixelBounds),
 
11757                     tileCenter = tileRange.getCenter(),
 
11759                     margin = this.options.keepBuffer,
 
11760                     noPruneRange = new Bounds(tileRange.getBottomLeft().subtract([margin, -margin]),
 
11761                                               tileRange.getTopRight().add([margin, -margin]));
 
11763                 // Sanity check: panic if the tile range contains Infinity somewhere.
 
11764                 if (!(isFinite(tileRange.min.x) &&
 
11765                       isFinite(tileRange.min.y) &&
 
11766                       isFinite(tileRange.max.x) &&
 
11767                       isFinite(tileRange.max.y))) { throw new Error('Attempted to load an infinite number of tiles'); }
 
11769                 for (var key in this._tiles) {
 
11770                         var c = this._tiles[key].coords;
 
11771                         if (c.z !== this._tileZoom || !noPruneRange.contains(new Point(c.x, c.y))) {
 
11772                                 this._tiles[key].current = false;
 
11776                 // _update just loads more tiles. If the tile zoom level differs too much
 
11777                 // from the map's, let _setView reset levels and prune old tiles.
 
11778                 if (Math.abs(zoom - this._tileZoom) > 1) { this._setView(center, zoom); return; }
 
11780                 // create a queue of coordinates to load tiles from
 
11781                 for (var j = tileRange.min.y; j <= tileRange.max.y; j++) {
 
11782                         for (var i = tileRange.min.x; i <= tileRange.max.x; i++) {
 
11783                                 var coords = new Point(i, j);
 
11784                                 coords.z = this._tileZoom;
 
11786                                 if (!this._isValidTile(coords)) { continue; }
 
11788                                 var tile = this._tiles[this._tileCoordsToKey(coords)];
 
11790                                         tile.current = true;
 
11792                                         queue.push(coords);
 
11797                 // sort tile queue to load tiles in order of their distance to center
 
11798                 queue.sort(function (a, b) {
 
11799                         return a.distanceTo(tileCenter) - b.distanceTo(tileCenter);
 
11802                 if (queue.length !== 0) {
 
11803                         // if it's the first batch of tiles to load
 
11804                         if (!this._loading) {
 
11805                                 this._loading = true;
 
11806                                 // @event loading: Event
 
11807                                 // Fired when the grid layer starts loading tiles.
 
11808                                 this.fire('loading');
 
11811                         // create DOM fragment to append tiles in one batch
 
11812                         var fragment = document.createDocumentFragment();
 
11814                         for (i = 0; i < queue.length; i++) {
 
11815                                 this._addTile(queue[i], fragment);
 
11818                         this._level.el.appendChild(fragment);
 
11822         _isValidTile: function (coords) {
 
11823                 var crs = this._map.options.crs;
 
11825                 if (!crs.infinite) {
 
11826                         // don't load tile if it's out of bounds and not wrapped
 
11827                         var bounds = this._globalTileRange;
 
11828                         if ((!crs.wrapLng && (coords.x < bounds.min.x || coords.x > bounds.max.x)) ||
 
11829                             (!crs.wrapLat && (coords.y < bounds.min.y || coords.y > bounds.max.y))) { return false; }
 
11832                 if (!this.options.bounds) { return true; }
 
11834                 // don't load tile if it doesn't intersect the bounds in options
 
11835                 var tileBounds = this._tileCoordsToBounds(coords);
 
11836                 return toLatLngBounds(this.options.bounds).overlaps(tileBounds);
 
11839         _keyToBounds: function (key) {
 
11840                 return this._tileCoordsToBounds(this._keyToTileCoords(key));
 
11843         _tileCoordsToNwSe: function (coords) {
 
11844                 var map = this._map,
 
11845                     tileSize = this.getTileSize(),
 
11846                     nwPoint = coords.scaleBy(tileSize),
 
11847                     sePoint = nwPoint.add(tileSize),
 
11848                     nw = map.unproject(nwPoint, coords.z),
 
11849                     se = map.unproject(sePoint, coords.z);
 
11853         // converts tile coordinates to its geographical bounds
 
11854         _tileCoordsToBounds: function (coords) {
 
11855                 var bp = this._tileCoordsToNwSe(coords),
 
11856                     bounds = new LatLngBounds(bp[0], bp[1]);
 
11858                 if (!this.options.noWrap) {
 
11859                         bounds = this._map.wrapLatLngBounds(bounds);
 
11863         // converts tile coordinates to key for the tile cache
 
11864         _tileCoordsToKey: function (coords) {
 
11865                 return coords.x + ':' + coords.y + ':' + coords.z;
 
11868         // converts tile cache key to coordinates
 
11869         _keyToTileCoords: function (key) {
 
11870                 var k = key.split(':'),
 
11871                     coords = new Point(+k[0], +k[1]);
 
11876         _removeTile: function (key) {
 
11877                 var tile = this._tiles[key];
 
11878                 if (!tile) { return; }
 
11882                 delete this._tiles[key];
 
11884                 // @event tileunload: TileEvent
 
11885                 // Fired when a tile is removed (e.g. when a tile goes off the screen).
 
11886                 this.fire('tileunload', {
 
11888                         coords: this._keyToTileCoords(key)
 
11892         _initTile: function (tile) {
 
11893                 addClass(tile, 'leaflet-tile');
 
11895                 var tileSize = this.getTileSize();
 
11896                 tile.style.width = tileSize.x + 'px';
 
11897                 tile.style.height = tileSize.y + 'px';
 
11899                 tile.onselectstart = falseFn;
 
11900                 tile.onmousemove = falseFn;
 
11902                 // update opacity on tiles in IE7-8 because of filter inheritance problems
 
11903                 if (Browser.ielt9 && this.options.opacity < 1) {
 
11904                         setOpacity(tile, this.options.opacity);
 
11908         _addTile: function (coords, container) {
 
11909                 var tilePos = this._getTilePos(coords),
 
11910                     key = this._tileCoordsToKey(coords);
 
11912                 var tile = this.createTile(this._wrapCoords(coords), bind(this._tileReady, this, coords));
 
11914                 this._initTile(tile);
 
11916                 // if createTile is defined with a second argument ("done" callback),
 
11917                 // we know that tile is async and will be ready later; otherwise
 
11918                 if (this.createTile.length < 2) {
 
11919                         // mark tile as ready, but delay one frame for opacity animation to happen
 
11920                         requestAnimFrame(bind(this._tileReady, this, coords, null, tile));
 
11923                 setPosition(tile, tilePos);
 
11925                 // save tile in cache
 
11926                 this._tiles[key] = {
 
11932                 container.appendChild(tile);
 
11933                 // @event tileloadstart: TileEvent
 
11934                 // Fired when a tile is requested and starts loading.
 
11935                 this.fire('tileloadstart', {
 
11941         _tileReady: function (coords, err, tile) {
 
11943                         // @event tileerror: TileErrorEvent
 
11944                         // Fired when there is an error loading a tile.
 
11945                         this.fire('tileerror', {
 
11952                 var key = this._tileCoordsToKey(coords);
 
11954                 tile = this._tiles[key];
 
11955                 if (!tile) { return; }
 
11957                 tile.loaded = +new Date();
 
11958                 if (this._map._fadeAnimated) {
 
11959                         setOpacity(tile.el, 0);
 
11960                         cancelAnimFrame(this._fadeFrame);
 
11961                         this._fadeFrame = requestAnimFrame(this._updateOpacity, this);
 
11963                         tile.active = true;
 
11964                         this._pruneTiles();
 
11968                         addClass(tile.el, 'leaflet-tile-loaded');
 
11970                         // @event tileload: TileEvent
 
11971                         // Fired when a tile loads.
 
11972                         this.fire('tileload', {
 
11978                 if (this._noTilesToLoad()) {
 
11979                         this._loading = false;
 
11980                         // @event load: Event
 
11981                         // Fired when the grid layer loaded all visible tiles.
 
11984                         if (Browser.ielt9 || !this._map._fadeAnimated) {
 
11985                                 requestAnimFrame(this._pruneTiles, this);
 
11987                                 // Wait a bit more than 0.2 secs (the duration of the tile fade-in)
 
11988                                 // to trigger a pruning.
 
11989                                 setTimeout(bind(this._pruneTiles, this), 250);
 
11994         _getTilePos: function (coords) {
 
11995                 return coords.scaleBy(this.getTileSize()).subtract(this._level.origin);
 
11998         _wrapCoords: function (coords) {
 
11999                 var newCoords = new Point(
 
12000                         this._wrapX ? wrapNum(coords.x, this._wrapX) : coords.x,
 
12001                         this._wrapY ? wrapNum(coords.y, this._wrapY) : coords.y);
 
12002                 newCoords.z = coords.z;
 
12006         _pxBoundsToTileRange: function (bounds) {
 
12007                 var tileSize = this.getTileSize();
 
12009                         bounds.min.unscaleBy(tileSize).floor(),
 
12010                         bounds.max.unscaleBy(tileSize).ceil().subtract([1, 1]));
 
12013         _noTilesToLoad: function () {
 
12014                 for (var key in this._tiles) {
 
12015                         if (!this._tiles[key].loaded) { return false; }
 
12021   // @factory L.gridLayer(options?: GridLayer options)
 
12022   // Creates a new instance of GridLayer with the supplied options.
 
12023   function gridLayer(options) {
 
12024         return new GridLayer(options);
 
12028    * @class TileLayer
\r 
12029    * @inherits GridLayer
\r 
12030    * @aka L.TileLayer
\r 
12031    * 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`.
\r 
12036    * L.tileLayer('https://tile.openstreetmap.org/{z}/{x}/{y}.png?{foo}', {foo: 'bar', attribution: '© <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors'}).addTo(map);
 
12039    * @section URL template
\r 
12042    * A string of the following form:
\r 
12045    * 'https://{s}.somedomain.com/blabla/{z}/{x}/{y}{r}.png'
\r 
12048    * `{s}` means one of the available subdomains (used sequentially to help with browser parallel requests per domain limitation; subdomain values are specified in options; `a`, `b` or `c` by default, can be omitted), `{z}` — zoom level, `{x}` and `{y}` — tile coordinates. `{r}` can be used to add "@2x" to the URL to load retina tiles.
\r 
12050    * You can use custom keys in the template, which will be [evaluated](#util-template) from TileLayer options, like this:
\r 
12053    * L.tileLayer('https://{s}.somedomain.com/{foo}/{z}/{x}/{y}.png', {foo: 'bar'});
\r 
12058   var TileLayer = GridLayer.extend({
\r 
12061         // @aka TileLayer options
\r 
12063                 // @option minZoom: Number = 0
\r 
12064                 // The minimum zoom level down to which this layer will be displayed (inclusive).
\r 
12067                 // @option maxZoom: Number = 18
\r 
12068                 // The maximum zoom level up to which this layer will be displayed (inclusive).
\r 
12071                 // @option subdomains: String|String[] = 'abc'
\r 
12072                 // 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.
\r 
12073                 subdomains: 'abc',
\r 
12075                 // @option errorTileUrl: String = ''
\r 
12076                 // URL to the tile image to show in place of the tile that failed to load.
\r 
12077                 errorTileUrl: '',
\r 
12079                 // @option zoomOffset: Number = 0
\r 
12080                 // The zoom number used in tile URLs will be offset with this value.
\r 
12083                 // @option tms: Boolean = false
\r 
12084                 // If `true`, inverses Y axis numbering for tiles (turn this on for [TMS](https://en.wikipedia.org/wiki/Tile_Map_Service) services).
\r 
12087                 // @option zoomReverse: Boolean = false
\r 
12088                 // If set to true, the zoom number used in tile URLs will be reversed (`maxZoom - zoom` instead of `zoom`)
\r 
12089                 zoomReverse: false,
\r 
12091                 // @option detectRetina: Boolean = false
\r 
12092                 // 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.
\r 
12093                 detectRetina: false,
\r 
12095                 // @option crossOrigin: Boolean|String = false
\r 
12096                 // Whether the crossOrigin attribute will be added to the tiles.
\r 
12097                 // 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.
\r 
12098                 // Refer to [CORS Settings](https://developer.mozilla.org/en-US/docs/Web/HTML/CORS_settings_attributes) for valid String values.
\r 
12099                 crossOrigin: false,
\r 
12101                 // @option referrerPolicy: Boolean|String = false
\r 
12102                 // Whether the referrerPolicy attribute will be added to the tiles.
\r 
12103                 // If a String is provided, all tiles will have their referrerPolicy attribute set to the String provided.
\r 
12104                 // This may be needed if your map's rendering context has a strict default but your tile provider expects a valid referrer
\r 
12105                 // (e.g. to validate an API token).
\r 
12106                 // Refer to [HTMLImageElement.referrerPolicy](https://developer.mozilla.org/en-US/docs/Web/API/HTMLImageElement/referrerPolicy) for valid String values.
\r 
12107                 referrerPolicy: false
\r 
12110         initialize: function (url, options) {
\r 
12114                 options = setOptions(this, options);
\r 
12116                 // detecting retina displays, adjusting tileSize and zoom levels
\r 
12117                 if (options.detectRetina && Browser.retina && options.maxZoom > 0) {
\r 
12119                         options.tileSize = Math.floor(options.tileSize / 2);
\r 
12121                         if (!options.zoomReverse) {
\r 
12122                                 options.zoomOffset++;
\r 
12123                                 options.maxZoom = Math.max(options.minZoom, options.maxZoom - 1);
\r 
12125                                 options.zoomOffset--;
\r 
12126                                 options.minZoom = Math.min(options.maxZoom, options.minZoom + 1);
\r 
12129                         options.minZoom = Math.max(0, options.minZoom);
\r 
12130                 } else if (!options.zoomReverse) {
\r 
12131                         // make sure maxZoom is gte minZoom
\r 
12132                         options.maxZoom = Math.max(options.minZoom, options.maxZoom);
\r 
12134                         // make sure minZoom is lte maxZoom
\r 
12135                         options.minZoom = Math.min(options.maxZoom, options.minZoom);
\r 
12138                 if (typeof options.subdomains === 'string') {
\r 
12139                         options.subdomains = options.subdomains.split('');
\r 
12142                 this.on('tileunload', this._onTileRemove);
\r 
12145         // @method setUrl(url: String, noRedraw?: Boolean): this
\r 
12146         // Updates the layer's URL template and redraws it (unless `noRedraw` is set to `true`).
\r 
12147         // If the URL does not change, the layer will not be redrawn unless
\r 
12148         // the noRedraw parameter is set to false.
\r 
12149         setUrl: function (url, noRedraw) {
\r 
12150                 if (this._url === url && noRedraw === undefined) {
\r 
12162         // @method createTile(coords: Object, done?: Function): HTMLElement
\r 
12163         // Called only internally, overrides GridLayer's [`createTile()`](#gridlayer-createtile)
\r 
12164         // to return an `<img>` HTML element with the appropriate image URL given `coords`. The `done`
\r 
12165         // callback is called when the tile has been loaded.
\r 
12166         createTile: function (coords, done) {
\r 
12167                 var tile = document.createElement('img');
\r 
12169                 on(tile, 'load', bind(this._tileOnLoad, this, done, tile));
\r 
12170                 on(tile, 'error', bind(this._tileOnError, this, done, tile));
\r 
12172                 if (this.options.crossOrigin || this.options.crossOrigin === '') {
\r 
12173                         tile.crossOrigin = this.options.crossOrigin === true ? '' : this.options.crossOrigin;
\r 
12176                 // for this new option we follow the documented behavior
\r 
12177                 // more closely by only setting the property when string
\r 
12178                 if (typeof this.options.referrerPolicy === 'string') {
\r 
12179                         tile.referrerPolicy = this.options.referrerPolicy;
\r 
12182                 // The alt attribute is set to the empty string,
\r 
12183                 // allowing screen readers to ignore the decorative image tiles.
\r 
12184                 // https://www.w3.org/WAI/tutorials/images/decorative/
\r 
12185                 // https://www.w3.org/TR/html-aria/#el-img-empty-alt
\r 
12188                 tile.src = this.getTileUrl(coords);
\r 
12193         // @section Extension methods
\r 
12194         // @uninheritable
\r 
12195         // Layers extending `TileLayer` might reimplement the following method.
\r 
12196         // @method getTileUrl(coords: Object): String
\r 
12197         // Called only internally, returns the URL for a tile given its coordinates.
\r 
12198         // Classes extending `TileLayer` can override this function to provide custom tile URL naming schemes.
\r 
12199         getTileUrl: function (coords) {
\r 
12201                         r: Browser.retina ? '@2x' : '',
\r 
12202                         s: this._getSubdomain(coords),
\r 
12205                         z: this._getZoomForUrl()
\r 
12207                 if (this._map && !this._map.options.crs.infinite) {
\r 
12208                         var invertedY = this._globalTileRange.max.y - coords.y;
\r 
12209                         if (this.options.tms) {
\r 
12210                                 data['y'] = invertedY;
\r 
12212                         data['-y'] = invertedY;
\r 
12215                 return template(this._url, extend(data, this.options));
\r 
12218         _tileOnLoad: function (done, tile) {
\r 
12219                 // For https://github.com/Leaflet/Leaflet/issues/3332
\r 
12220                 if (Browser.ielt9) {
\r 
12221                         setTimeout(bind(done, this, null, tile), 0);
\r 
12223                         done(null, tile);
\r 
12227         _tileOnError: function (done, tile, e) {
\r 
12228                 var errorUrl = this.options.errorTileUrl;
\r 
12229                 if (errorUrl && tile.getAttribute('src') !== errorUrl) {
\r 
12230                         tile.src = errorUrl;
\r 
12235         _onTileRemove: function (e) {
\r 
12236                 e.tile.onload = null;
\r 
12239         _getZoomForUrl: function () {
\r 
12240                 var zoom = this._tileZoom,
\r 
12241                 maxZoom = this.options.maxZoom,
\r 
12242                 zoomReverse = this.options.zoomReverse,
\r 
12243                 zoomOffset = this.options.zoomOffset;
\r 
12245                 if (zoomReverse) {
\r 
12246                         zoom = maxZoom - zoom;
\r 
12249                 return zoom + zoomOffset;
\r 
12252         _getSubdomain: function (tilePoint) {
\r 
12253                 var index = Math.abs(tilePoint.x + tilePoint.y) % this.options.subdomains.length;
\r 
12254                 return this.options.subdomains[index];
\r 
12257         // stops loading all tiles in the background layer
\r 
12258         _abortLoading: function () {
\r 
12260                 for (i in this._tiles) {
\r 
12261                         if (this._tiles[i].coords.z !== this._tileZoom) {
\r 
12262                                 tile = this._tiles[i].el;
\r 
12264                                 tile.onload = falseFn;
\r 
12265                                 tile.onerror = falseFn;
\r 
12267                                 if (!tile.complete) {
\r 
12268                                         tile.src = emptyImageUrl;
\r 
12269                                         var coords = this._tiles[i].coords;
\r 
12271                                         delete this._tiles[i];
\r 
12272                                         // @event tileabort: TileEvent
\r 
12273                                         // Fired when a tile was loading but is now not wanted.
\r 
12274                                         this.fire('tileabort', {
\r 
12283         _removeTile: function (key) {
\r 
12284                 var tile = this._tiles[key];
\r 
12285                 if (!tile) { return; }
\r 
12287                 // Cancels any pending http requests associated with the tile
\r 
12288                 tile.el.setAttribute('src', emptyImageUrl);
\r 
12290                 return GridLayer.prototype._removeTile.call(this, key);
\r 
12293         _tileReady: function (coords, err, tile) {
\r 
12294                 if (!this._map || (tile && tile.getAttribute('src') === emptyImageUrl)) {
\r 
12298                 return GridLayer.prototype._tileReady.call(this, coords, err, tile);
\r 
12303   // @factory L.tilelayer(urlTemplate: String, options?: TileLayer options)
\r 
12304   // Instantiates a tile layer object given a `URL template` and optionally an options object.
\r 
12306   function tileLayer(url, options) {
\r 
12307         return new TileLayer(url, options);
\r 
12311    * @class TileLayer.WMS
\r 
12312    * @inherits TileLayer
\r 
12313    * @aka L.TileLayer.WMS
\r 
12314    * Used to display [WMS](https://en.wikipedia.org/wiki/Web_Map_Service) services as tile layers on the map. Extends `TileLayer`.
\r 
12319    * var nexrad = L.tileLayer.wms("http://mesonet.agron.iastate.edu/cgi-bin/wms/nexrad/n0r.cgi", {
\r 
12320    *    layers: 'nexrad-n0r-900913',
\r 
12321    *    format: 'image/png',
\r 
12322    *    transparent: true,
\r 
12323    *    attribution: "Weather data © 2012 IEM Nexrad"
\r 
12328   var TileLayerWMS = TileLayer.extend({
\r 
12331         // @aka TileLayer.WMS options
\r 
12332         // If any custom options not documented here are used, they will be sent to the
\r 
12333         // WMS server as extra parameters in each request URL. This can be useful for
\r 
12334         // [non-standard vendor WMS parameters](https://docs.geoserver.org/stable/en/user/services/wms/vendor.html).
\r 
12335         defaultWmsParams: {
\r 
12337                 request: 'GetMap',
\r 
12339                 // @option layers: String = ''
\r 
12340                 // **(required)** Comma-separated list of WMS layers to show.
\r 
12343                 // @option styles: String = ''
\r 
12344                 // Comma-separated list of WMS styles.
\r 
12347                 // @option format: String = 'image/jpeg'
\r 
12348                 // WMS image format (use `'image/png'` for layers with transparency).
\r 
12349                 format: 'image/jpeg',
\r 
12351                 // @option transparent: Boolean = false
\r 
12352                 // If `true`, the WMS service will return images with transparency.
\r 
12353                 transparent: false,
\r 
12355                 // @option version: String = '1.1.1'
\r 
12356                 // Version of the WMS service to use
\r 
12361                 // @option crs: CRS = null
\r 
12362                 // Coordinate Reference System to use for the WMS requests, defaults to
\r 
12363                 // map CRS. Don't change this if you're not sure what it means.
\r 
12366                 // @option uppercase: Boolean = false
\r 
12367                 // If `true`, WMS request parameter keys will be uppercase.
\r 
12371         initialize: function (url, options) {
\r 
12375                 var wmsParams = extend({}, this.defaultWmsParams);
\r 
12377                 // all keys that are not TileLayer options go to WMS params
\r 
12378                 for (var i in options) {
\r 
12379                         if (!(i in this.options)) {
\r 
12380                                 wmsParams[i] = options[i];
\r 
12384                 options = setOptions(this, options);
\r 
12386                 var realRetina = options.detectRetina && Browser.retina ? 2 : 1;
\r 
12387                 var tileSize = this.getTileSize();
\r 
12388                 wmsParams.width = tileSize.x * realRetina;
\r 
12389                 wmsParams.height = tileSize.y * realRetina;
\r 
12391                 this.wmsParams = wmsParams;
\r 
12394         onAdd: function (map) {
\r 
12396                 this._crs = this.options.crs || map.options.crs;
\r 
12397                 this._wmsVersion = parseFloat(this.wmsParams.version);
\r 
12399                 var projectionKey = this._wmsVersion >= 1.3 ? 'crs' : 'srs';
\r 
12400                 this.wmsParams[projectionKey] = this._crs.code;
\r 
12402                 TileLayer.prototype.onAdd.call(this, map);
\r 
12405         getTileUrl: function (coords) {
\r 
12407                 var tileBounds = this._tileCoordsToNwSe(coords),
\r 
12409                     bounds = toBounds(crs.project(tileBounds[0]), crs.project(tileBounds[1])),
\r 
12410                     min = bounds.min,
\r 
12411                     max = bounds.max,
\r 
12412                     bbox = (this._wmsVersion >= 1.3 && this._crs === EPSG4326 ?
\r 
12413                     [min.y, min.x, max.y, max.x] :
\r 
12414                     [min.x, min.y, max.x, max.y]).join(','),
\r 
12415                     url = TileLayer.prototype.getTileUrl.call(this, coords);
\r 
12417                         getParamString(this.wmsParams, url, this.options.uppercase) +
\r 
12418                         (this.options.uppercase ? '&BBOX=' : '&bbox=') + bbox;
\r 
12421         // @method setParams(params: Object, noRedraw?: Boolean): this
\r 
12422         // Merges an object with the new parameters and re-requests tiles on the current screen (unless `noRedraw` was set to true).
\r 
12423         setParams: function (params, noRedraw) {
\r 
12425                 extend(this.wmsParams, params);
\r 
12436   // @factory L.tileLayer.wms(baseUrl: String, options: TileLayer.WMS options)
\r 
12437   // Instantiates a WMS tile layer object given a base URL of the WMS service and a WMS parameters/options object.
\r 
12438   function tileLayerWMS(url, options) {
\r 
12439         return new TileLayerWMS(url, options);
\r 
12442   TileLayer.WMS = TileLayerWMS;
 
12443   tileLayer.wms = tileLayerWMS;
 
12450    * Base class for vector renderer implementations (`SVG`, `Canvas`). Handles the
 
12451    * DOM container of the renderer, its bounds, and its zoom animation.
 
12453    * A `Renderer` works as an implicit layer group for all `Path`s - the renderer
 
12454    * itself can be added or removed to the map. All paths use a renderer, which can
 
12455    * be implicit (the map will decide the type of renderer and use it automatically)
 
12456    * or explicit (using the [`renderer`](#path-renderer) option of the path).
 
12458    * Do not use this class directly, use `SVG` and `Canvas` instead.
 
12460    * @event update: Event
 
12461    * Fired when the renderer updates its bounds, center and zoom, for example when
 
12462    * its map has moved
 
12465   var Renderer = Layer.extend({
 
12468         // @aka Renderer options
 
12470                 // @option padding: Number = 0.1
 
12471                 // How much to extend the clip area around the map view (relative to its size)
 
12472                 // e.g. 0.1 would be 10% of map view in each direction
 
12476         initialize: function (options) {
 
12477                 setOptions(this, options);
 
12479                 this._layers = this._layers || {};
 
12482         onAdd: function () {
 
12483                 if (!this._container) {
 
12484                         this._initContainer(); // defined by renderer implementations
 
12486                         // always keep transform-origin as 0 0
 
12487                         addClass(this._container, 'leaflet-zoom-animated');
 
12490                 this.getPane().appendChild(this._container);
 
12492                 this.on('update', this._updatePaths, this);
 
12495         onRemove: function () {
 
12496                 this.off('update', this._updatePaths, this);
 
12497                 this._destroyContainer();
 
12500         getEvents: function () {
 
12502                         viewreset: this._reset,
 
12503                         zoom: this._onZoom,
 
12504                         moveend: this._update,
 
12505                         zoomend: this._onZoomEnd
 
12507                 if (this._zoomAnimated) {
 
12508                         events.zoomanim = this._onAnimZoom;
 
12513         _onAnimZoom: function (ev) {
 
12514                 this._updateTransform(ev.center, ev.zoom);
 
12517         _onZoom: function () {
 
12518                 this._updateTransform(this._map.getCenter(), this._map.getZoom());
 
12521         _updateTransform: function (center, zoom) {
 
12522                 var scale = this._map.getZoomScale(zoom, this._zoom),
 
12523                     viewHalf = this._map.getSize().multiplyBy(0.5 + this.options.padding),
 
12524                     currentCenterPoint = this._map.project(this._center, zoom),
 
12526                     topLeftOffset = viewHalf.multiplyBy(-scale).add(currentCenterPoint)
 
12527                                   .subtract(this._map._getNewPixelOrigin(center, zoom));
 
12529                 if (Browser.any3d) {
 
12530                         setTransform(this._container, topLeftOffset, scale);
 
12532                         setPosition(this._container, topLeftOffset);
 
12536         _reset: function () {
 
12538                 this._updateTransform(this._center, this._zoom);
 
12540                 for (var id in this._layers) {
 
12541                         this._layers[id]._reset();
 
12545         _onZoomEnd: function () {
 
12546                 for (var id in this._layers) {
 
12547                         this._layers[id]._project();
 
12551         _updatePaths: function () {
 
12552                 for (var id in this._layers) {
 
12553                         this._layers[id]._update();
 
12557         _update: function () {
 
12558                 // Update pixel bounds of renderer container (for positioning/sizing/clipping later)
 
12559                 // Subclasses are responsible of firing the 'update' event.
 
12560                 var p = this.options.padding,
 
12561                     size = this._map.getSize(),
 
12562                     min = this._map.containerPointToLayerPoint(size.multiplyBy(-p)).round();
 
12564                 this._bounds = new Bounds(min, min.add(size.multiplyBy(1 + p * 2)).round());
 
12566                 this._center = this._map.getCenter();
 
12567                 this._zoom = this._map.getZoom();
 
12573    * @inherits Renderer
 
12576    * Allows vector layers to be displayed with [`<canvas>`](https://developer.mozilla.org/docs/Web/API/Canvas_API).
 
12577    * Inherits `Renderer`.
 
12579    * Due to [technical limitations](https://caniuse.com/canvas), Canvas is not
 
12580    * available in all web browsers, notably IE8, and overlapping geometries might
 
12581    * not display properly in some edge cases.
 
12585    * Use Canvas by default for all paths in the map:
 
12588    * var map = L.map('map', {
 
12589    *    renderer: L.canvas()
 
12593    * Use a Canvas renderer with extra padding for specific vector geometries:
 
12596    * var map = L.map('map');
 
12597    * var myRenderer = L.canvas({ padding: 0.5 });
 
12598    * var line = L.polyline( coordinates, { renderer: myRenderer } );
 
12599    * var circle = L.circle( center, { renderer: myRenderer } );
 
12603   var Canvas = Renderer.extend({
 
12606         // @aka Canvas options
 
12608                 // @option tolerance: Number = 0
 
12609                 // How much to extend the click tolerance around a path/object on the map.
 
12613         getEvents: function () {
 
12614                 var events = Renderer.prototype.getEvents.call(this);
 
12615                 events.viewprereset = this._onViewPreReset;
 
12619         _onViewPreReset: function () {
 
12620                 // Set a flag so that a viewprereset+moveend+viewreset only updates&redraws once
 
12621                 this._postponeUpdatePaths = true;
 
12624         onAdd: function () {
 
12625                 Renderer.prototype.onAdd.call(this);
 
12627                 // Redraw vectors since canvas is cleared upon removal,
 
12628                 // in case of removing the renderer itself from the map.
 
12632         _initContainer: function () {
 
12633                 var container = this._container = document.createElement('canvas');
 
12635                 on(container, 'mousemove', this._onMouseMove, this);
 
12636                 on(container, 'click dblclick mousedown mouseup contextmenu', this._onClick, this);
 
12637                 on(container, 'mouseout', this._handleMouseOut, this);
 
12638                 container['_leaflet_disable_events'] = true;
 
12640                 this._ctx = container.getContext('2d');
 
12643         _destroyContainer: function () {
 
12644                 cancelAnimFrame(this._redrawRequest);
 
12646                 remove(this._container);
 
12647                 off(this._container);
 
12648                 delete this._container;
 
12651         _updatePaths: function () {
 
12652                 if (this._postponeUpdatePaths) { return; }
 
12655                 this._redrawBounds = null;
 
12656                 for (var id in this._layers) {
 
12657                         layer = this._layers[id];
 
12663         _update: function () {
 
12664                 if (this._map._animatingZoom && this._bounds) { return; }
 
12666                 Renderer.prototype._update.call(this);
 
12668                 var b = this._bounds,
 
12669                     container = this._container,
 
12670                     size = b.getSize(),
 
12671                     m = Browser.retina ? 2 : 1;
 
12673                 setPosition(container, b.min);
 
12675                 // set canvas size (also clearing it); use double size on retina
 
12676                 container.width = m * size.x;
 
12677                 container.height = m * size.y;
 
12678                 container.style.width = size.x + 'px';
 
12679                 container.style.height = size.y + 'px';
 
12681                 if (Browser.retina) {
 
12682                         this._ctx.scale(2, 2);
 
12685                 // translate so we use the same path coordinates after canvas element moves
 
12686                 this._ctx.translate(-b.min.x, -b.min.y);
 
12688                 // Tell paths to redraw themselves
 
12689                 this.fire('update');
 
12692         _reset: function () {
 
12693                 Renderer.prototype._reset.call(this);
 
12695                 if (this._postponeUpdatePaths) {
 
12696                         this._postponeUpdatePaths = false;
 
12697                         this._updatePaths();
 
12701         _initPath: function (layer) {
 
12702                 this._updateDashArray(layer);
 
12703                 this._layers[stamp(layer)] = layer;
 
12705                 var order = layer._order = {
 
12707                         prev: this._drawLast,
 
12710                 if (this._drawLast) { this._drawLast.next = order; }
 
12711                 this._drawLast = order;
 
12712                 this._drawFirst = this._drawFirst || this._drawLast;
 
12715         _addPath: function (layer) {
 
12716                 this._requestRedraw(layer);
 
12719         _removePath: function (layer) {
 
12720                 var order = layer._order;
 
12721                 var next = order.next;
 
12722                 var prev = order.prev;
 
12727                         this._drawLast = prev;
 
12732                         this._drawFirst = next;
 
12735                 delete layer._order;
 
12737                 delete this._layers[stamp(layer)];
 
12739                 this._requestRedraw(layer);
 
12742         _updatePath: function (layer) {
 
12743                 // Redraw the union of the layer's old pixel
 
12744                 // bounds and the new pixel bounds.
 
12745                 this._extendRedrawBounds(layer);
 
12748                 // The redraw will extend the redraw bounds
 
12749                 // with the new pixel bounds.
 
12750                 this._requestRedraw(layer);
 
12753         _updateStyle: function (layer) {
 
12754                 this._updateDashArray(layer);
 
12755                 this._requestRedraw(layer);
 
12758         _updateDashArray: function (layer) {
 
12759                 if (typeof layer.options.dashArray === 'string') {
 
12760                         var parts = layer.options.dashArray.split(/[, ]+/),
 
12764                         for (i = 0; i < parts.length; i++) {
 
12765                                 dashValue = Number(parts[i]);
 
12766                                 // Ignore dash array containing invalid lengths
 
12767                                 if (isNaN(dashValue)) { return; }
 
12768                                 dashArray.push(dashValue);
 
12770                         layer.options._dashArray = dashArray;
 
12772                         layer.options._dashArray = layer.options.dashArray;
 
12776         _requestRedraw: function (layer) {
 
12777                 if (!this._map) { return; }
 
12779                 this._extendRedrawBounds(layer);
 
12780                 this._redrawRequest = this._redrawRequest || requestAnimFrame(this._redraw, this);
 
12783         _extendRedrawBounds: function (layer) {
 
12784                 if (layer._pxBounds) {
 
12785                         var padding = (layer.options.weight || 0) + 1;
 
12786                         this._redrawBounds = this._redrawBounds || new Bounds();
 
12787                         this._redrawBounds.extend(layer._pxBounds.min.subtract([padding, padding]));
 
12788                         this._redrawBounds.extend(layer._pxBounds.max.add([padding, padding]));
 
12792         _redraw: function () {
 
12793                 this._redrawRequest = null;
 
12795                 if (this._redrawBounds) {
 
12796                         this._redrawBounds.min._floor();
 
12797                         this._redrawBounds.max._ceil();
 
12800                 this._clear(); // clear layers in redraw bounds
 
12801                 this._draw(); // draw layers
 
12803                 this._redrawBounds = null;
 
12806         _clear: function () {
 
12807                 var bounds = this._redrawBounds;
 
12809                         var size = bounds.getSize();
 
12810                         this._ctx.clearRect(bounds.min.x, bounds.min.y, size.x, size.y);
 
12813                         this._ctx.setTransform(1, 0, 0, 1, 0, 0);
 
12814                         this._ctx.clearRect(0, 0, this._container.width, this._container.height);
 
12815                         this._ctx.restore();
 
12819         _draw: function () {
 
12820                 var layer, bounds = this._redrawBounds;
 
12823                         var size = bounds.getSize();
 
12824                         this._ctx.beginPath();
 
12825                         this._ctx.rect(bounds.min.x, bounds.min.y, size.x, size.y);
 
12829                 this._drawing = true;
 
12831                 for (var order = this._drawFirst; order; order = order.next) {
 
12832                         layer = order.layer;
 
12833                         if (!bounds || (layer._pxBounds && layer._pxBounds.intersects(bounds))) {
 
12834                                 layer._updatePath();
 
12838                 this._drawing = false;
 
12840                 this._ctx.restore();  // Restore state before clipping.
 
12843         _updatePoly: function (layer, closed) {
 
12844                 if (!this._drawing) { return; }
 
12847                     parts = layer._parts,
 
12848                     len = parts.length,
 
12851                 if (!len) { return; }
 
12855                 for (i = 0; i < len; i++) {
 
12856                         for (j = 0, len2 = parts[i].length; j < len2; j++) {
 
12858                                 ctx[j ? 'lineTo' : 'moveTo'](p.x, p.y);
 
12865                 this._fillStroke(ctx, layer);
 
12867                 // TODO optimization: 1 fill/stroke for all features with equal style instead of 1 for each feature
 
12870         _updateCircle: function (layer) {
 
12872                 if (!this._drawing || layer._empty()) { return; }
 
12874                 var p = layer._point,
 
12876                     r = Math.max(Math.round(layer._radius), 1),
 
12877                     s = (Math.max(Math.round(layer._radiusY), 1) || r) / r;
 
12885                 ctx.arc(p.x, p.y / s, r, 0, Math.PI * 2, false);
 
12891                 this._fillStroke(ctx, layer);
 
12894         _fillStroke: function (ctx, layer) {
 
12895                 var options = layer.options;
 
12897                 if (options.fill) {
 
12898                         ctx.globalAlpha = options.fillOpacity;
 
12899                         ctx.fillStyle = options.fillColor || options.color;
 
12900                         ctx.fill(options.fillRule || 'evenodd');
 
12903                 if (options.stroke && options.weight !== 0) {
 
12904                         if (ctx.setLineDash) {
 
12905                                 ctx.setLineDash(layer.options && layer.options._dashArray || []);
 
12907                         ctx.globalAlpha = options.opacity;
 
12908                         ctx.lineWidth = options.weight;
 
12909                         ctx.strokeStyle = options.color;
 
12910                         ctx.lineCap = options.lineCap;
 
12911                         ctx.lineJoin = options.lineJoin;
 
12916         // Canvas obviously doesn't have mouse events for individual drawn objects,
 
12917         // so we emulate that by calculating what's under the mouse on mousemove/click manually
 
12919         _onClick: function (e) {
 
12920                 var point = this._map.mouseEventToLayerPoint(e), layer, clickedLayer;
 
12922                 for (var order = this._drawFirst; order; order = order.next) {
 
12923                         layer = order.layer;
 
12924                         if (layer.options.interactive && layer._containsPoint(point)) {
 
12925                                 if (!(e.type === 'click' || e.type === 'preclick') || !this._map._draggableMoved(layer)) {
 
12926                                         clickedLayer = layer;
 
12930                 this._fireEvent(clickedLayer ? [clickedLayer] : false, e);
 
12933         _onMouseMove: function (e) {
 
12934                 if (!this._map || this._map.dragging.moving() || this._map._animatingZoom) { return; }
 
12936                 var point = this._map.mouseEventToLayerPoint(e);
 
12937                 this._handleMouseHover(e, point);
 
12941         _handleMouseOut: function (e) {
 
12942                 var layer = this._hoveredLayer;
 
12944                         // if we're leaving the layer, fire mouseout
 
12945                         removeClass(this._container, 'leaflet-interactive');
 
12946                         this._fireEvent([layer], e, 'mouseout');
 
12947                         this._hoveredLayer = null;
 
12948                         this._mouseHoverThrottled = false;
 
12952         _handleMouseHover: function (e, point) {
 
12953                 if (this._mouseHoverThrottled) {
 
12957                 var layer, candidateHoveredLayer;
 
12959                 for (var order = this._drawFirst; order; order = order.next) {
 
12960                         layer = order.layer;
 
12961                         if (layer.options.interactive && layer._containsPoint(point)) {
 
12962                                 candidateHoveredLayer = layer;
 
12966                 if (candidateHoveredLayer !== this._hoveredLayer) {
 
12967                         this._handleMouseOut(e);
 
12969                         if (candidateHoveredLayer) {
 
12970                                 addClass(this._container, 'leaflet-interactive'); // change cursor
 
12971                                 this._fireEvent([candidateHoveredLayer], e, 'mouseover');
 
12972                                 this._hoveredLayer = candidateHoveredLayer;
 
12976                 this._fireEvent(this._hoveredLayer ? [this._hoveredLayer] : false, e);
 
12978                 this._mouseHoverThrottled = true;
 
12979                 setTimeout(bind(function () {
 
12980                         this._mouseHoverThrottled = false;
 
12984         _fireEvent: function (layers, e, type) {
 
12985                 this._map._fireDOMEvent(e, type || e.type, layers);
 
12988         _bringToFront: function (layer) {
 
12989                 var order = layer._order;
 
12991                 if (!order) { return; }
 
12993                 var next = order.next;
 
12994                 var prev = order.prev;
 
13005                         // Update first entry unless this is the
 
13007                         this._drawFirst = next;
 
13010                 order.prev = this._drawLast;
 
13011                 this._drawLast.next = order;
 
13014                 this._drawLast = order;
 
13016                 this._requestRedraw(layer);
 
13019         _bringToBack: function (layer) {
 
13020                 var order = layer._order;
 
13022                 if (!order) { return; }
 
13024                 var next = order.next;
 
13025                 var prev = order.prev;
 
13036                         // Update last entry unless this is the
 
13038                         this._drawLast = prev;
 
13043                 order.next = this._drawFirst;
 
13044                 this._drawFirst.prev = order;
 
13045                 this._drawFirst = order;
 
13047                 this._requestRedraw(layer);
 
13051   // @factory L.canvas(options?: Renderer options)
 
13052   // Creates a Canvas renderer with the given options.
 
13053   function canvas(options) {
 
13054         return Browser.canvas ? new Canvas(options) : null;
 
13058    * Thanks to Dmitry Baranovsky and his Raphael library for inspiration!
 
13062   var vmlCreate = (function () {
 
13064                 document.namespaces.add('lvml', 'urn:schemas-microsoft-com:vml');
 
13065                 return function (name) {
 
13066                         return document.createElement('<lvml:' + name + ' class="lvml">');
 
13069                 // Do not return fn from catch block so `e` can be garbage collected
 
13070                 // See https://github.com/Leaflet/Leaflet/pull/7279
 
13072         return function (name) {
 
13073                 return document.createElement('<' + name + ' xmlns="urn:schemas-microsoft.com:vml" class="lvml">');
 
13082    * VML was deprecated in 2012, which means VML functionality exists only for backwards compatibility
 
13083    * with old versions of Internet Explorer.
 
13086   // mixin to redefine some SVG methods to handle VML syntax which is similar but with some differences
 
13089         _initContainer: function () {
 
13090                 this._container = create$1('div', 'leaflet-vml-container');
 
13093         _update: function () {
 
13094                 if (this._map._animatingZoom) { return; }
 
13095                 Renderer.prototype._update.call(this);
 
13096                 this.fire('update');
 
13099         _initPath: function (layer) {
 
13100                 var container = layer._container = vmlCreate('shape');
 
13102                 addClass(container, 'leaflet-vml-shape ' + (this.options.className || ''));
 
13104                 container.coordsize = '1 1';
 
13106                 layer._path = vmlCreate('path');
 
13107                 container.appendChild(layer._path);
 
13109                 this._updateStyle(layer);
 
13110                 this._layers[stamp(layer)] = layer;
 
13113         _addPath: function (layer) {
 
13114                 var container = layer._container;
 
13115                 this._container.appendChild(container);
 
13117                 if (layer.options.interactive) {
 
13118                         layer.addInteractiveTarget(container);
 
13122         _removePath: function (layer) {
 
13123                 var container = layer._container;
 
13125                 layer.removeInteractiveTarget(container);
 
13126                 delete this._layers[stamp(layer)];
 
13129         _updateStyle: function (layer) {
 
13130                 var stroke = layer._stroke,
 
13131                     fill = layer._fill,
 
13132                     options = layer.options,
 
13133                     container = layer._container;
 
13135                 container.stroked = !!options.stroke;
 
13136                 container.filled = !!options.fill;
 
13138                 if (options.stroke) {
 
13140                                 stroke = layer._stroke = vmlCreate('stroke');
 
13142                         container.appendChild(stroke);
 
13143                         stroke.weight = options.weight + 'px';
 
13144                         stroke.color = options.color;
 
13145                         stroke.opacity = options.opacity;
 
13147                         if (options.dashArray) {
 
13148                                 stroke.dashStyle = isArray(options.dashArray) ?
 
13149                                     options.dashArray.join(' ') :
 
13150                                     options.dashArray.replace(/( *, *)/g, ' ');
 
13152                                 stroke.dashStyle = '';
 
13154                         stroke.endcap = options.lineCap.replace('butt', 'flat');
 
13155                         stroke.joinstyle = options.lineJoin;
 
13157                 } else if (stroke) {
 
13158                         container.removeChild(stroke);
 
13159                         layer._stroke = null;
 
13162                 if (options.fill) {
 
13164                                 fill = layer._fill = vmlCreate('fill');
 
13166                         container.appendChild(fill);
 
13167                         fill.color = options.fillColor || options.color;
 
13168                         fill.opacity = options.fillOpacity;
 
13171                         container.removeChild(fill);
 
13172                         layer._fill = null;
 
13176         _updateCircle: function (layer) {
 
13177                 var p = layer._point.round(),
 
13178                     r = Math.round(layer._radius),
 
13179                     r2 = Math.round(layer._radiusY || r);
 
13181                 this._setPath(layer, layer._empty() ? 'M0 0' :
 
13182                         'AL ' + p.x + ',' + p.y + ' ' + r + ',' + r2 + ' 0,' + (65535 * 360));
 
13185         _setPath: function (layer, path) {
 
13186                 layer._path.v = path;
 
13189         _bringToFront: function (layer) {
 
13190                 toFront(layer._container);
 
13193         _bringToBack: function (layer) {
 
13194                 toBack(layer._container);
 
13198   var create = Browser.vml ? vmlCreate : svgCreate;
 
13202    * @inherits Renderer
 
13205    * Allows vector layers to be displayed with [SVG](https://developer.mozilla.org/docs/Web/SVG).
 
13206    * Inherits `Renderer`.
 
13208    * Due to [technical limitations](https://caniuse.com/svg), SVG is not
 
13209    * available in all web browsers, notably Android 2.x and 3.x.
 
13211    * Although SVG is not available on IE7 and IE8, these browsers support
 
13212    * [VML](https://en.wikipedia.org/wiki/Vector_Markup_Language)
 
13213    * (a now deprecated technology), and the SVG renderer will fall back to VML in
 
13218    * Use SVG by default for all paths in the map:
 
13221    * var map = L.map('map', {
 
13222    *    renderer: L.svg()
 
13226    * Use a SVG renderer with extra padding for specific vector geometries:
 
13229    * var map = L.map('map');
 
13230    * var myRenderer = L.svg({ padding: 0.5 });
 
13231    * var line = L.polyline( coordinates, { renderer: myRenderer } );
 
13232    * var circle = L.circle( center, { renderer: myRenderer } );
 
13236   var SVG = Renderer.extend({
 
13238         _initContainer: function () {
 
13239                 this._container = create('svg');
 
13241                 // makes it possible to click through svg root; we'll reset it back in individual paths
 
13242                 this._container.setAttribute('pointer-events', 'none');
 
13244                 this._rootGroup = create('g');
 
13245                 this._container.appendChild(this._rootGroup);
 
13248         _destroyContainer: function () {
 
13249                 remove(this._container);
 
13250                 off(this._container);
 
13251                 delete this._container;
 
13252                 delete this._rootGroup;
 
13253                 delete this._svgSize;
 
13256         _update: function () {
 
13257                 if (this._map._animatingZoom && this._bounds) { return; }
 
13259                 Renderer.prototype._update.call(this);
 
13261                 var b = this._bounds,
 
13262                     size = b.getSize(),
 
13263                     container = this._container;
 
13265                 // set size of svg-container if changed
 
13266                 if (!this._svgSize || !this._svgSize.equals(size)) {
 
13267                         this._svgSize = size;
 
13268                         container.setAttribute('width', size.x);
 
13269                         container.setAttribute('height', size.y);
 
13272                 // movement: update container viewBox so that we don't have to change coordinates of individual layers
 
13273                 setPosition(container, b.min);
 
13274                 container.setAttribute('viewBox', [b.min.x, b.min.y, size.x, size.y].join(' '));
 
13276                 this.fire('update');
 
13279         // methods below are called by vector layers implementations
 
13281         _initPath: function (layer) {
 
13282                 var path = layer._path = create('path');
 
13285                 // @option className: String = null
 
13286                 // Custom class name set on an element. Only for SVG renderer.
 
13287                 if (layer.options.className) {
 
13288                         addClass(path, layer.options.className);
 
13291                 if (layer.options.interactive) {
 
13292                         addClass(path, 'leaflet-interactive');
 
13295                 this._updateStyle(layer);
 
13296                 this._layers[stamp(layer)] = layer;
 
13299         _addPath: function (layer) {
 
13300                 if (!this._rootGroup) { this._initContainer(); }
 
13301                 this._rootGroup.appendChild(layer._path);
 
13302                 layer.addInteractiveTarget(layer._path);
 
13305         _removePath: function (layer) {
 
13306                 remove(layer._path);
 
13307                 layer.removeInteractiveTarget(layer._path);
 
13308                 delete this._layers[stamp(layer)];
 
13311         _updatePath: function (layer) {
 
13316         _updateStyle: function (layer) {
 
13317                 var path = layer._path,
 
13318                     options = layer.options;
 
13320                 if (!path) { return; }
 
13322                 if (options.stroke) {
 
13323                         path.setAttribute('stroke', options.color);
 
13324                         path.setAttribute('stroke-opacity', options.opacity);
 
13325                         path.setAttribute('stroke-width', options.weight);
 
13326                         path.setAttribute('stroke-linecap', options.lineCap);
 
13327                         path.setAttribute('stroke-linejoin', options.lineJoin);
 
13329                         if (options.dashArray) {
 
13330                                 path.setAttribute('stroke-dasharray', options.dashArray);
 
13332                                 path.removeAttribute('stroke-dasharray');
 
13335                         if (options.dashOffset) {
 
13336                                 path.setAttribute('stroke-dashoffset', options.dashOffset);
 
13338                                 path.removeAttribute('stroke-dashoffset');
 
13341                         path.setAttribute('stroke', 'none');
 
13344                 if (options.fill) {
 
13345                         path.setAttribute('fill', options.fillColor || options.color);
 
13346                         path.setAttribute('fill-opacity', options.fillOpacity);
 
13347                         path.setAttribute('fill-rule', options.fillRule || 'evenodd');
 
13349                         path.setAttribute('fill', 'none');
 
13353         _updatePoly: function (layer, closed) {
 
13354                 this._setPath(layer, pointsToPath(layer._parts, closed));
 
13357         _updateCircle: function (layer) {
 
13358                 var p = layer._point,
 
13359                     r = Math.max(Math.round(layer._radius), 1),
 
13360                     r2 = Math.max(Math.round(layer._radiusY), 1) || r,
 
13361                     arc = 'a' + r + ',' + r2 + ' 0 1,0 ';
 
13363                 // drawing a circle with two half-arcs
 
13364                 var d = layer._empty() ? 'M0 0' :
 
13365                         'M' + (p.x - r) + ',' + p.y +
 
13366                         arc + (r * 2) + ',0 ' +
 
13367                         arc + (-r * 2) + ',0 ';
 
13369                 this._setPath(layer, d);
 
13372         _setPath: function (layer, path) {
 
13373                 layer._path.setAttribute('d', path);
 
13376         // SVG does not have the concept of zIndex so we resort to changing the DOM order of elements
 
13377         _bringToFront: function (layer) {
 
13378                 toFront(layer._path);
 
13381         _bringToBack: function (layer) {
 
13382                 toBack(layer._path);
 
13387         SVG.include(vmlMixin);
 
13391   // @factory L.svg(options?: Renderer options)
 
13392   // Creates a SVG renderer with the given options.
 
13393   function svg(options) {
 
13394         return Browser.svg || Browser.vml ? new SVG(options) : null;
 
13398         // @namespace Map; @method getRenderer(layer: Path): Renderer
 
13399         // Returns the instance of `Renderer` that should be used to render the given
 
13400         // `Path`. It will ensure that the `renderer` options of the map and paths
 
13401         // are respected, and that the renderers do exist on the map.
 
13402         getRenderer: function (layer) {
 
13403                 // @namespace Path; @option renderer: Renderer
 
13404                 // Use this specific instance of `Renderer` for this path. Takes
 
13405                 // precedence over the map's [default renderer](#map-renderer).
 
13406                 var renderer = layer.options.renderer || this._getPaneRenderer(layer.options.pane) || this.options.renderer || this._renderer;
 
13409                         renderer = this._renderer = this._createRenderer();
 
13412                 if (!this.hasLayer(renderer)) {
 
13413                         this.addLayer(renderer);
 
13418         _getPaneRenderer: function (name) {
 
13419                 if (name === 'overlayPane' || name === undefined) {
 
13423                 var renderer = this._paneRenderers[name];
 
13424                 if (renderer === undefined) {
 
13425                         renderer = this._createRenderer({pane: name});
 
13426                         this._paneRenderers[name] = renderer;
 
13431         _createRenderer: function (options) {
 
13432                 // @namespace Map; @option preferCanvas: Boolean = false
 
13433                 // Whether `Path`s should be rendered on a `Canvas` renderer.
 
13434                 // By default, all `Path`s are rendered in a `SVG` renderer.
 
13435                 return (this.options.preferCanvas && canvas(options)) || svg(options);
 
13440    * L.Rectangle extends Polygon and creates a rectangle when passed a LatLngBounds object.
 
13446    * @inherits Polygon
 
13448    * A class for drawing rectangle overlays on a map. Extends `Polygon`.
 
13453    * // define rectangle geographical bounds
 
13454    * var bounds = [[54.559322, -5.767822], [56.1210604, -3.021240]];
 
13456    * // create an orange rectangle
 
13457    * L.rectangle(bounds, {color: "#ff7800", weight: 1}).addTo(map);
 
13459    * // zoom the map to the rectangle bounds
 
13460    * map.fitBounds(bounds);
 
13466   var Rectangle = Polygon.extend({
 
13467         initialize: function (latLngBounds, options) {
 
13468                 Polygon.prototype.initialize.call(this, this._boundsToLatLngs(latLngBounds), options);
 
13471         // @method setBounds(latLngBounds: LatLngBounds): this
 
13472         // Redraws the rectangle with the passed bounds.
 
13473         setBounds: function (latLngBounds) {
 
13474                 return this.setLatLngs(this._boundsToLatLngs(latLngBounds));
 
13477         _boundsToLatLngs: function (latLngBounds) {
 
13478                 latLngBounds = toLatLngBounds(latLngBounds);
 
13480                         latLngBounds.getSouthWest(),
 
13481                         latLngBounds.getNorthWest(),
 
13482                         latLngBounds.getNorthEast(),
 
13483                         latLngBounds.getSouthEast()
 
13489   // @factory L.rectangle(latLngBounds: LatLngBounds, options?: Polyline options)
 
13490   function rectangle(latLngBounds, options) {
 
13491         return new Rectangle(latLngBounds, options);
 
13494   SVG.create = create;
 
13495   SVG.pointsToPath = pointsToPath;
 
13497   GeoJSON.geometryToLayer = geometryToLayer;
 
13498   GeoJSON.coordsToLatLng = coordsToLatLng;
 
13499   GeoJSON.coordsToLatLngs = coordsToLatLngs;
 
13500   GeoJSON.latLngToCoords = latLngToCoords;
 
13501   GeoJSON.latLngsToCoords = latLngsToCoords;
 
13502   GeoJSON.getFeature = getFeature;
 
13503   GeoJSON.asFeature = asFeature;
 
13506    * L.Handler.BoxZoom is used to add shift-drag zoom interaction to the map
 
13507    * (zoom to a selected bounding box), enabled by default.
 
13511   // @section Interaction Options
 
13513         // @option boxZoom: Boolean = true
 
13514         // Whether the map can be zoomed to a rectangular area specified by
 
13515         // dragging the mouse while pressing the shift key.
 
13519   var BoxZoom = Handler.extend({
 
13520         initialize: function (map) {
 
13522                 this._container = map._container;
 
13523                 this._pane = map._panes.overlayPane;
 
13524                 this._resetStateTimeout = 0;
 
13525                 map.on('unload', this._destroy, this);
 
13528         addHooks: function () {
 
13529                 on(this._container, 'mousedown', this._onMouseDown, this);
 
13532         removeHooks: function () {
 
13533                 off(this._container, 'mousedown', this._onMouseDown, this);
 
13536         moved: function () {
 
13537                 return this._moved;
 
13540         _destroy: function () {
 
13541                 remove(this._pane);
 
13545         _resetState: function () {
 
13546                 this._resetStateTimeout = 0;
 
13547                 this._moved = false;
 
13550         _clearDeferredResetState: function () {
 
13551                 if (this._resetStateTimeout !== 0) {
 
13552                         clearTimeout(this._resetStateTimeout);
 
13553                         this._resetStateTimeout = 0;
 
13557         _onMouseDown: function (e) {
 
13558                 if (!e.shiftKey || ((e.which !== 1) && (e.button !== 1))) { return false; }
 
13560                 // Clear the deferred resetState if it hasn't executed yet, otherwise it
 
13561                 // will interrupt the interaction and orphan a box element in the container.
 
13562                 this._clearDeferredResetState();
 
13563                 this._resetState();
 
13565                 disableTextSelection();
 
13566                 disableImageDrag();
 
13568                 this._startPoint = this._map.mouseEventToContainerPoint(e);
 
13572                         mousemove: this._onMouseMove,
 
13573                         mouseup: this._onMouseUp,
 
13574                         keydown: this._onKeyDown
 
13578         _onMouseMove: function (e) {
 
13579                 if (!this._moved) {
 
13580                         this._moved = true;
 
13582                         this._box = create$1('div', 'leaflet-zoom-box', this._container);
 
13583                         addClass(this._container, 'leaflet-crosshair');
 
13585                         this._map.fire('boxzoomstart');
 
13588                 this._point = this._map.mouseEventToContainerPoint(e);
 
13590                 var bounds = new Bounds(this._point, this._startPoint),
 
13591                     size = bounds.getSize();
 
13593                 setPosition(this._box, bounds.min);
 
13595                 this._box.style.width  = size.x + 'px';
 
13596                 this._box.style.height = size.y + 'px';
 
13599         _finish: function () {
 
13602                         removeClass(this._container, 'leaflet-crosshair');
 
13605                 enableTextSelection();
 
13610                         mousemove: this._onMouseMove,
 
13611                         mouseup: this._onMouseUp,
 
13612                         keydown: this._onKeyDown
 
13616         _onMouseUp: function (e) {
 
13617                 if ((e.which !== 1) && (e.button !== 1)) { return; }
 
13621                 if (!this._moved) { return; }
 
13622                 // Postpone to next JS tick so internal click event handling
 
13623                 // still see it as "moved".
 
13624                 this._clearDeferredResetState();
 
13625                 this._resetStateTimeout = setTimeout(bind(this._resetState, this), 0);
 
13627                 var bounds = new LatLngBounds(
 
13628                         this._map.containerPointToLatLng(this._startPoint),
 
13629                         this._map.containerPointToLatLng(this._point));
 
13633                         .fire('boxzoomend', {boxZoomBounds: bounds});
 
13636         _onKeyDown: function (e) {
 
13637                 if (e.keyCode === 27) {
 
13639                         this._clearDeferredResetState();
 
13640                         this._resetState();
 
13645   // @section Handlers
 
13646   // @property boxZoom: Handler
 
13647   // Box (shift-drag with mouse) zoom handler.
 
13648   Map.addInitHook('addHandler', 'boxZoom', BoxZoom);
 
13651    * L.Handler.DoubleClickZoom is used to handle double-click zoom on the map, enabled by default.
 
13655   // @section Interaction Options
 
13658         // @option doubleClickZoom: Boolean|String = true
 
13659         // Whether the map can be zoomed in by double clicking on it and
 
13660         // zoomed out by double clicking while holding shift. If passed
 
13661         // `'center'`, double-click zoom will zoom to the center of the
 
13662         //  view regardless of where the mouse was.
 
13663         doubleClickZoom: true
 
13666   var DoubleClickZoom = Handler.extend({
 
13667         addHooks: function () {
 
13668                 this._map.on('dblclick', this._onDoubleClick, this);
 
13671         removeHooks: function () {
 
13672                 this._map.off('dblclick', this._onDoubleClick, this);
 
13675         _onDoubleClick: function (e) {
 
13676                 var map = this._map,
 
13677                     oldZoom = map.getZoom(),
 
13678                     delta = map.options.zoomDelta,
 
13679                     zoom = e.originalEvent.shiftKey ? oldZoom - delta : oldZoom + delta;
 
13681                 if (map.options.doubleClickZoom === 'center') {
 
13684                         map.setZoomAround(e.containerPoint, zoom);
 
13689   // @section Handlers
 
13691   // Map properties include interaction handlers that allow you to control
 
13692   // interaction behavior in runtime, enabling or disabling certain features such
 
13693   // as dragging or touch zoom (see `Handler` methods). For example:
 
13696   // map.doubleClickZoom.disable();
 
13699   // @property doubleClickZoom: Handler
 
13700   // Double click zoom handler.
 
13701   Map.addInitHook('addHandler', 'doubleClickZoom', DoubleClickZoom);
 
13704    * L.Handler.MapDrag is used to make the map draggable (with panning inertia), enabled by default.
 
13708   // @section Interaction Options
 
13710         // @option dragging: Boolean = true
 
13711         // Whether the map is draggable with mouse/touch or not.
 
13714         // @section Panning Inertia Options
 
13715         // @option inertia: Boolean = *
 
13716         // If enabled, panning of the map will have an inertia effect where
 
13717         // the map builds momentum while dragging and continues moving in
 
13718         // the same direction for some time. Feels especially nice on touch
 
13719         // devices. Enabled by default.
 
13722         // @option inertiaDeceleration: Number = 3000
 
13723         // The rate with which the inertial movement slows down, in pixels/second².
 
13724         inertiaDeceleration: 3400, // px/s^2
 
13726         // @option inertiaMaxSpeed: Number = Infinity
 
13727         // Max speed of the inertial movement, in pixels/second.
 
13728         inertiaMaxSpeed: Infinity, // px/s
 
13730         // @option easeLinearity: Number = 0.2
 
13731         easeLinearity: 0.2,
 
13733         // TODO refactor, move to CRS
 
13734         // @option worldCopyJump: Boolean = false
 
13735         // With this option enabled, the map tracks when you pan to another "copy"
 
13736         // of the world and seamlessly jumps to the original one so that all overlays
 
13737         // like markers and vector layers are still visible.
 
13738         worldCopyJump: false,
 
13740         // @option maxBoundsViscosity: Number = 0.0
 
13741         // If `maxBounds` is set, this option will control how solid the bounds
 
13742         // are when dragging the map around. The default value of `0.0` allows the
 
13743         // user to drag outside the bounds at normal speed, higher values will
 
13744         // slow down map dragging outside bounds, and `1.0` makes the bounds fully
 
13745         // solid, preventing the user from dragging outside the bounds.
 
13746         maxBoundsViscosity: 0.0
 
13749   var Drag = Handler.extend({
 
13750         addHooks: function () {
 
13751                 if (!this._draggable) {
 
13752                         var map = this._map;
 
13754                         this._draggable = new Draggable(map._mapPane, map._container);
 
13756                         this._draggable.on({
 
13757                                 dragstart: this._onDragStart,
 
13758                                 drag: this._onDrag,
 
13759                                 dragend: this._onDragEnd
 
13762                         this._draggable.on('predrag', this._onPreDragLimit, this);
 
13763                         if (map.options.worldCopyJump) {
 
13764                                 this._draggable.on('predrag', this._onPreDragWrap, this);
 
13765                                 map.on('zoomend', this._onZoomEnd, this);
 
13767                                 map.whenReady(this._onZoomEnd, this);
 
13770                 addClass(this._map._container, 'leaflet-grab leaflet-touch-drag');
 
13771                 this._draggable.enable();
 
13772                 this._positions = [];
 
13776         removeHooks: function () {
 
13777                 removeClass(this._map._container, 'leaflet-grab');
 
13778                 removeClass(this._map._container, 'leaflet-touch-drag');
 
13779                 this._draggable.disable();
 
13782         moved: function () {
 
13783                 return this._draggable && this._draggable._moved;
 
13786         moving: function () {
 
13787                 return this._draggable && this._draggable._moving;
 
13790         _onDragStart: function () {
 
13791                 var map = this._map;
 
13794                 if (this._map.options.maxBounds && this._map.options.maxBoundsViscosity) {
 
13795                         var bounds = toLatLngBounds(this._map.options.maxBounds);
 
13797                         this._offsetLimit = toBounds(
 
13798                                 this._map.latLngToContainerPoint(bounds.getNorthWest()).multiplyBy(-1),
 
13799                                 this._map.latLngToContainerPoint(bounds.getSouthEast()).multiplyBy(-1)
 
13800                                         .add(this._map.getSize()));
 
13802                         this._viscosity = Math.min(1.0, Math.max(0.0, this._map.options.maxBoundsViscosity));
 
13804                         this._offsetLimit = null;
 
13809                     .fire('dragstart');
 
13811                 if (map.options.inertia) {
 
13812                         this._positions = [];
 
13817         _onDrag: function (e) {
 
13818                 if (this._map.options.inertia) {
 
13819                         var time = this._lastTime = +new Date(),
 
13820                             pos = this._lastPos = this._draggable._absPos || this._draggable._newPos;
 
13822                         this._positions.push(pos);
 
13823                         this._times.push(time);
 
13825                         this._prunePositions(time);
 
13833         _prunePositions: function (time) {
 
13834                 while (this._positions.length > 1 && time - this._times[0] > 50) {
 
13835                         this._positions.shift();
 
13836                         this._times.shift();
 
13840         _onZoomEnd: function () {
 
13841                 var pxCenter = this._map.getSize().divideBy(2),
 
13842                     pxWorldCenter = this._map.latLngToLayerPoint([0, 0]);
 
13844                 this._initialWorldOffset = pxWorldCenter.subtract(pxCenter).x;
 
13845                 this._worldWidth = this._map.getPixelWorldBounds().getSize().x;
 
13848         _viscousLimit: function (value, threshold) {
 
13849                 return value - (value - threshold) * this._viscosity;
 
13852         _onPreDragLimit: function () {
 
13853                 if (!this._viscosity || !this._offsetLimit) { return; }
 
13855                 var offset = this._draggable._newPos.subtract(this._draggable._startPos);
 
13857                 var limit = this._offsetLimit;
 
13858                 if (offset.x < limit.min.x) { offset.x = this._viscousLimit(offset.x, limit.min.x); }
 
13859                 if (offset.y < limit.min.y) { offset.y = this._viscousLimit(offset.y, limit.min.y); }
 
13860                 if (offset.x > limit.max.x) { offset.x = this._viscousLimit(offset.x, limit.max.x); }
 
13861                 if (offset.y > limit.max.y) { offset.y = this._viscousLimit(offset.y, limit.max.y); }
 
13863                 this._draggable._newPos = this._draggable._startPos.add(offset);
 
13866         _onPreDragWrap: function () {
 
13867                 // TODO refactor to be able to adjust map pane position after zoom
 
13868                 var worldWidth = this._worldWidth,
 
13869                     halfWidth = Math.round(worldWidth / 2),
 
13870                     dx = this._initialWorldOffset,
 
13871                     x = this._draggable._newPos.x,
 
13872                     newX1 = (x - halfWidth + dx) % worldWidth + halfWidth - dx,
 
13873                     newX2 = (x + halfWidth + dx) % worldWidth - halfWidth - dx,
 
13874                     newX = Math.abs(newX1 + dx) < Math.abs(newX2 + dx) ? newX1 : newX2;
 
13876                 this._draggable._absPos = this._draggable._newPos.clone();
 
13877                 this._draggable._newPos.x = newX;
 
13880         _onDragEnd: function (e) {
 
13881                 var map = this._map,
 
13882                     options = map.options,
 
13884                     noInertia = !options.inertia || e.noInertia || this._times.length < 2;
 
13886                 map.fire('dragend', e);
 
13889                         map.fire('moveend');
 
13892                         this._prunePositions(+new Date());
 
13894                         var direction = this._lastPos.subtract(this._positions[0]),
 
13895                             duration = (this._lastTime - this._times[0]) / 1000,
 
13896                             ease = options.easeLinearity,
 
13898                             speedVector = direction.multiplyBy(ease / duration),
 
13899                             speed = speedVector.distanceTo([0, 0]),
 
13901                             limitedSpeed = Math.min(options.inertiaMaxSpeed, speed),
 
13902                             limitedSpeedVector = speedVector.multiplyBy(limitedSpeed / speed),
 
13904                             decelerationDuration = limitedSpeed / (options.inertiaDeceleration * ease),
 
13905                             offset = limitedSpeedVector.multiplyBy(-decelerationDuration / 2).round();
 
13907                         if (!offset.x && !offset.y) {
 
13908                                 map.fire('moveend');
 
13911                                 offset = map._limitOffset(offset, map.options.maxBounds);
 
13913                                 requestAnimFrame(function () {
 
13914                                         map.panBy(offset, {
 
13915                                                 duration: decelerationDuration,
 
13916                                                 easeLinearity: ease,
 
13926   // @section Handlers
 
13927   // @property dragging: Handler
 
13928   // Map dragging handler (by both mouse and touch).
 
13929   Map.addInitHook('addHandler', 'dragging', Drag);
 
13932    * L.Map.Keyboard is handling keyboard interaction with the map, enabled by default.
 
13936   // @section Keyboard Navigation Options
 
13938         // @option keyboard: Boolean = true
 
13939         // Makes the map focusable and allows users to navigate the map with keyboard
 
13940         // arrows and `+`/`-` keys.
 
13943         // @option keyboardPanDelta: Number = 80
 
13944         // Amount of pixels to pan when pressing an arrow key.
 
13945         keyboardPanDelta: 80
 
13948   var Keyboard = Handler.extend({
 
13955                 zoomIn:  [187, 107, 61, 171],
 
13956                 zoomOut: [189, 109, 54, 173]
 
13959         initialize: function (map) {
 
13962                 this._setPanDelta(map.options.keyboardPanDelta);
 
13963                 this._setZoomDelta(map.options.zoomDelta);
 
13966         addHooks: function () {
 
13967                 var container = this._map._container;
 
13969                 // make the container focusable by tabbing
 
13970                 if (container.tabIndex <= 0) {
 
13971                         container.tabIndex = '0';
 
13975                         focus: this._onFocus,
 
13976                         blur: this._onBlur,
 
13977                         mousedown: this._onMouseDown
 
13981                         focus: this._addHooks,
 
13982                         blur: this._removeHooks
 
13986         removeHooks: function () {
 
13987                 this._removeHooks();
 
13989                 off(this._map._container, {
 
13990                         focus: this._onFocus,
 
13991                         blur: this._onBlur,
 
13992                         mousedown: this._onMouseDown
 
13996                         focus: this._addHooks,
 
13997                         blur: this._removeHooks
 
14001         _onMouseDown: function () {
 
14002                 if (this._focused) { return; }
 
14004                 var body = document.body,
 
14005                     docEl = document.documentElement,
 
14006                     top = body.scrollTop || docEl.scrollTop,
 
14007                     left = body.scrollLeft || docEl.scrollLeft;
 
14009                 this._map._container.focus();
 
14011                 window.scrollTo(left, top);
 
14014         _onFocus: function () {
 
14015                 this._focused = true;
 
14016                 this._map.fire('focus');
 
14019         _onBlur: function () {
 
14020                 this._focused = false;
 
14021                 this._map.fire('blur');
 
14024         _setPanDelta: function (panDelta) {
 
14025                 var keys = this._panKeys = {},
 
14026                     codes = this.keyCodes,
 
14029                 for (i = 0, len = codes.left.length; i < len; i++) {
 
14030                         keys[codes.left[i]] = [-1 * panDelta, 0];
 
14032                 for (i = 0, len = codes.right.length; i < len; i++) {
 
14033                         keys[codes.right[i]] = [panDelta, 0];
 
14035                 for (i = 0, len = codes.down.length; i < len; i++) {
 
14036                         keys[codes.down[i]] = [0, panDelta];
 
14038                 for (i = 0, len = codes.up.length; i < len; i++) {
 
14039                         keys[codes.up[i]] = [0, -1 * panDelta];
 
14043         _setZoomDelta: function (zoomDelta) {
 
14044                 var keys = this._zoomKeys = {},
 
14045                     codes = this.keyCodes,
 
14048                 for (i = 0, len = codes.zoomIn.length; i < len; i++) {
 
14049                         keys[codes.zoomIn[i]] = zoomDelta;
 
14051                 for (i = 0, len = codes.zoomOut.length; i < len; i++) {
 
14052                         keys[codes.zoomOut[i]] = -zoomDelta;
 
14056         _addHooks: function () {
 
14057                 on(document, 'keydown', this._onKeyDown, this);
 
14060         _removeHooks: function () {
 
14061                 off(document, 'keydown', this._onKeyDown, this);
 
14064         _onKeyDown: function (e) {
 
14065                 if (e.altKey || e.ctrlKey || e.metaKey) { return; }
 
14067                 var key = e.keyCode,
 
14071                 if (key in this._panKeys) {
 
14072                         if (!map._panAnim || !map._panAnim._inProgress) {
 
14073                                 offset = this._panKeys[key];
 
14075                                         offset = toPoint(offset).multiplyBy(3);
 
14078                                 if (map.options.maxBounds) {
 
14079                                         offset = map._limitOffset(toPoint(offset), map.options.maxBounds);
 
14082                                 if (map.options.worldCopyJump) {
 
14083                                         var newLatLng = map.wrapLatLng(map.unproject(map.project(map.getCenter()).add(offset)));
 
14084                                         map.panTo(newLatLng);
 
14089                 } else if (key in this._zoomKeys) {
 
14090                         map.setZoom(map.getZoom() + (e.shiftKey ? 3 : 1) * this._zoomKeys[key]);
 
14092                 } else if (key === 27 && map._popup && map._popup.options.closeOnEscapeKey) {
 
14103   // @section Handlers
 
14104   // @section Handlers
 
14105   // @property keyboard: Handler
 
14106   // Keyboard navigation handler.
 
14107   Map.addInitHook('addHandler', 'keyboard', Keyboard);
 
14110    * L.Handler.ScrollWheelZoom is used by L.Map to enable mouse scroll wheel zoom on the map.
 
14114   // @section Interaction Options
 
14116         // @section Mouse wheel options
 
14117         // @option scrollWheelZoom: Boolean|String = true
 
14118         // Whether the map can be zoomed by using the mouse wheel. If passed `'center'`,
 
14119         // it will zoom to the center of the view regardless of where the mouse was.
 
14120         scrollWheelZoom: true,
 
14122         // @option wheelDebounceTime: Number = 40
 
14123         // Limits the rate at which a wheel can fire (in milliseconds). By default
 
14124         // user can't zoom via wheel more often than once per 40 ms.
 
14125         wheelDebounceTime: 40,
 
14127         // @option wheelPxPerZoomLevel: Number = 60
 
14128         // How many scroll pixels (as reported by [L.DomEvent.getWheelDelta](#domevent-getwheeldelta))
 
14129         // mean a change of one full zoom level. Smaller values will make wheel-zooming
 
14130         // faster (and vice versa).
 
14131         wheelPxPerZoomLevel: 60
 
14134   var ScrollWheelZoom = Handler.extend({
 
14135         addHooks: function () {
 
14136                 on(this._map._container, 'wheel', this._onWheelScroll, this);
 
14141         removeHooks: function () {
 
14142                 off(this._map._container, 'wheel', this._onWheelScroll, this);
 
14145         _onWheelScroll: function (e) {
 
14146                 var delta = getWheelDelta(e);
 
14148                 var debounce = this._map.options.wheelDebounceTime;
 
14150                 this._delta += delta;
 
14151                 this._lastMousePos = this._map.mouseEventToContainerPoint(e);
 
14153                 if (!this._startTime) {
 
14154                         this._startTime = +new Date();
 
14157                 var left = Math.max(debounce - (+new Date() - this._startTime), 0);
 
14159                 clearTimeout(this._timer);
 
14160                 this._timer = setTimeout(bind(this._performZoom, this), left);
 
14165         _performZoom: function () {
 
14166                 var map = this._map,
 
14167                     zoom = map.getZoom(),
 
14168                     snap = this._map.options.zoomSnap || 0;
 
14170                 map._stop(); // stop panning and fly animations if any
 
14172                 // map the delta with a sigmoid function to -4..4 range leaning on -1..1
 
14173                 var d2 = this._delta / (this._map.options.wheelPxPerZoomLevel * 4),
 
14174                     d3 = 4 * Math.log(2 / (1 + Math.exp(-Math.abs(d2)))) / Math.LN2,
 
14175                     d4 = snap ? Math.ceil(d3 / snap) * snap : d3,
 
14176                     delta = map._limitZoom(zoom + (this._delta > 0 ? d4 : -d4)) - zoom;
 
14179                 this._startTime = null;
 
14181                 if (!delta) { return; }
 
14183                 if (map.options.scrollWheelZoom === 'center') {
 
14184                         map.setZoom(zoom + delta);
 
14186                         map.setZoomAround(this._lastMousePos, zoom + delta);
 
14191   // @section Handlers
 
14192   // @property scrollWheelZoom: Handler
 
14193   // Scroll wheel zoom handler.
 
14194   Map.addInitHook('addHandler', 'scrollWheelZoom', ScrollWheelZoom);
 
14197    * L.Map.TapHold is used to simulate `contextmenu` event on long hold,
 
14198    * which otherwise is not fired by mobile Safari.
 
14201   var tapHoldDelay = 600;
 
14204   // @section Interaction Options
 
14206         // @section Touch interaction options
 
14207         // @option tapHold: Boolean
 
14208         // Enables simulation of `contextmenu` event, default is `true` for mobile Safari.
 
14209         tapHold: Browser.touchNative && Browser.safari && Browser.mobile,
 
14211         // @option tapTolerance: Number = 15
 
14212         // The max number of pixels a user can shift his finger during touch
 
14213         // for it to be considered a valid tap.
 
14217   var TapHold = Handler.extend({
 
14218         addHooks: function () {
 
14219                 on(this._map._container, 'touchstart', this._onDown, this);
 
14222         removeHooks: function () {
 
14223                 off(this._map._container, 'touchstart', this._onDown, this);
 
14226         _onDown: function (e) {
 
14227                 clearTimeout(this._holdTimeout);
 
14228                 if (e.touches.length !== 1) { return; }
 
14230                 var first = e.touches[0];
 
14231                 this._startPos = this._newPos = new Point(first.clientX, first.clientY);
 
14233                 this._holdTimeout = setTimeout(bind(function () {
 
14235                         if (!this._isTapValid()) { return; }
 
14237                         // prevent simulated mouse events https://w3c.github.io/touch-events/#mouse-events
 
14238                         on(document, 'touchend', preventDefault);
 
14239                         on(document, 'touchend touchcancel', this._cancelClickPrevent);
 
14240                         this._simulateEvent('contextmenu', first);
 
14241                 }, this), tapHoldDelay);
 
14243                 on(document, 'touchend touchcancel contextmenu', this._cancel, this);
 
14244                 on(document, 'touchmove', this._onMove, this);
 
14247         _cancelClickPrevent: function cancelClickPrevent() {
 
14248                 off(document, 'touchend', preventDefault);
 
14249                 off(document, 'touchend touchcancel', cancelClickPrevent);
 
14252         _cancel: function () {
 
14253                 clearTimeout(this._holdTimeout);
 
14254                 off(document, 'touchend touchcancel contextmenu', this._cancel, this);
 
14255                 off(document, 'touchmove', this._onMove, this);
 
14258         _onMove: function (e) {
 
14259                 var first = e.touches[0];
 
14260                 this._newPos = new Point(first.clientX, first.clientY);
 
14263         _isTapValid: function () {
 
14264                 return this._newPos.distanceTo(this._startPos) <= this._map.options.tapTolerance;
 
14267         _simulateEvent: function (type, e) {
 
14268                 var simulatedEvent = new MouseEvent(type, {
 
14273                         screenX: e.screenX,
 
14274                         screenY: e.screenY,
 
14275                         clientX: e.clientX,
 
14276                         clientY: e.clientY,
 
14281                 simulatedEvent._simulated = true;
 
14283                 e.target.dispatchEvent(simulatedEvent);
 
14287   // @section Handlers
 
14288   // @property tapHold: Handler
 
14289   // Long tap handler to simulate `contextmenu` event (useful in mobile Safari).
 
14290   Map.addInitHook('addHandler', 'tapHold', TapHold);
 
14293    * L.Handler.TouchZoom is used by L.Map to add pinch zoom on supported mobile browsers.
 
14297   // @section Interaction Options
 
14299         // @section Touch interaction options
 
14300         // @option touchZoom: Boolean|String = *
 
14301         // Whether the map can be zoomed by touch-dragging with two fingers. If
 
14302         // passed `'center'`, it will zoom to the center of the view regardless of
 
14303         // where the touch events (fingers) were. Enabled for touch-capable web
 
14305         touchZoom: Browser.touch,
 
14307         // @option bounceAtZoomLimits: Boolean = true
 
14308         // Set it to false if you don't want the map to zoom beyond min/max zoom
 
14309         // and then bounce back when pinch-zooming.
 
14310         bounceAtZoomLimits: true
 
14313   var TouchZoom = Handler.extend({
 
14314         addHooks: function () {
 
14315                 addClass(this._map._container, 'leaflet-touch-zoom');
 
14316                 on(this._map._container, 'touchstart', this._onTouchStart, this);
 
14319         removeHooks: function () {
 
14320                 removeClass(this._map._container, 'leaflet-touch-zoom');
 
14321                 off(this._map._container, 'touchstart', this._onTouchStart, this);
 
14324         _onTouchStart: function (e) {
 
14325                 var map = this._map;
 
14326                 if (!e.touches || e.touches.length !== 2 || map._animatingZoom || this._zooming) { return; }
 
14328                 var p1 = map.mouseEventToContainerPoint(e.touches[0]),
 
14329                     p2 = map.mouseEventToContainerPoint(e.touches[1]);
 
14331                 this._centerPoint = map.getSize()._divideBy(2);
 
14332                 this._startLatLng = map.containerPointToLatLng(this._centerPoint);
 
14333                 if (map.options.touchZoom !== 'center') {
 
14334                         this._pinchStartLatLng = map.containerPointToLatLng(p1.add(p2)._divideBy(2));
 
14337                 this._startDist = p1.distanceTo(p2);
 
14338                 this._startZoom = map.getZoom();
 
14340                 this._moved = false;
 
14341                 this._zooming = true;
 
14345                 on(document, 'touchmove', this._onTouchMove, this);
 
14346                 on(document, 'touchend touchcancel', this._onTouchEnd, this);
 
14351         _onTouchMove: function (e) {
 
14352                 if (!e.touches || e.touches.length !== 2 || !this._zooming) { return; }
 
14354                 var map = this._map,
 
14355                     p1 = map.mouseEventToContainerPoint(e.touches[0]),
 
14356                     p2 = map.mouseEventToContainerPoint(e.touches[1]),
 
14357                     scale = p1.distanceTo(p2) / this._startDist;
 
14359                 this._zoom = map.getScaleZoom(scale, this._startZoom);
 
14361                 if (!map.options.bounceAtZoomLimits && (
 
14362                         (this._zoom < map.getMinZoom() && scale < 1) ||
 
14363                         (this._zoom > map.getMaxZoom() && scale > 1))) {
 
14364                         this._zoom = map._limitZoom(this._zoom);
 
14367                 if (map.options.touchZoom === 'center') {
 
14368                         this._center = this._startLatLng;
 
14369                         if (scale === 1) { return; }
 
14371                         // Get delta from pinch to center, so centerLatLng is delta applied to initial pinchLatLng
 
14372                         var delta = p1._add(p2)._divideBy(2)._subtract(this._centerPoint);
 
14373                         if (scale === 1 && delta.x === 0 && delta.y === 0) { return; }
 
14374                         this._center = map.unproject(map.project(this._pinchStartLatLng, this._zoom).subtract(delta), this._zoom);
 
14377                 if (!this._moved) {
 
14378                         map._moveStart(true, false);
 
14379                         this._moved = true;
 
14382                 cancelAnimFrame(this._animRequest);
 
14384                 var moveFn = bind(map._move, map, this._center, this._zoom, {pinch: true, round: false}, undefined);
 
14385                 this._animRequest = requestAnimFrame(moveFn, this, true);
 
14390         _onTouchEnd: function () {
 
14391                 if (!this._moved || !this._zooming) {
 
14392                         this._zooming = false;
 
14396                 this._zooming = false;
 
14397                 cancelAnimFrame(this._animRequest);
 
14399                 off(document, 'touchmove', this._onTouchMove, this);
 
14400                 off(document, 'touchend touchcancel', this._onTouchEnd, this);
 
14402                 // Pinch updates GridLayers' levels only when zoomSnap is off, so zoomSnap becomes noUpdate.
 
14403                 if (this._map.options.zoomAnimation) {
 
14404                         this._map._animateZoom(this._center, this._map._limitZoom(this._zoom), true, this._map.options.zoomSnap);
 
14406                         this._map._resetView(this._center, this._map._limitZoom(this._zoom));
 
14411   // @section Handlers
 
14412   // @property touchZoom: Handler
 
14413   // Touch zoom handler.
 
14414   Map.addInitHook('addHandler', 'touchZoom', TouchZoom);
 
14416   Map.BoxZoom = BoxZoom;
 
14417   Map.DoubleClickZoom = DoubleClickZoom;
 
14419   Map.Keyboard = Keyboard;
 
14420   Map.ScrollWheelZoom = ScrollWheelZoom;
 
14421   Map.TapHold = TapHold;
 
14422   Map.TouchZoom = TouchZoom;
 
14424   exports.Bounds = Bounds;
 
14425   exports.Browser = Browser;
 
14427   exports.Canvas = Canvas;
 
14428   exports.Circle = Circle;
 
14429   exports.CircleMarker = CircleMarker;
 
14430   exports.Class = Class;
 
14431   exports.Control = Control;
 
14432   exports.DivIcon = DivIcon;
 
14433   exports.DivOverlay = DivOverlay;
 
14434   exports.DomEvent = DomEvent;
 
14435   exports.DomUtil = DomUtil;
 
14436   exports.Draggable = Draggable;
 
14437   exports.Evented = Evented;
 
14438   exports.FeatureGroup = FeatureGroup;
 
14439   exports.GeoJSON = GeoJSON;
 
14440   exports.GridLayer = GridLayer;
 
14441   exports.Handler = Handler;
 
14442   exports.Icon = Icon;
 
14443   exports.ImageOverlay = ImageOverlay;
 
14444   exports.LatLng = LatLng;
 
14445   exports.LatLngBounds = LatLngBounds;
 
14446   exports.Layer = Layer;
 
14447   exports.LayerGroup = LayerGroup;
 
14448   exports.LineUtil = LineUtil;
 
14450   exports.Marker = Marker;
 
14451   exports.Mixin = Mixin;
 
14452   exports.Path = Path;
 
14453   exports.Point = Point;
 
14454   exports.PolyUtil = PolyUtil;
 
14455   exports.Polygon = Polygon;
 
14456   exports.Polyline = Polyline;
 
14457   exports.Popup = Popup;
 
14458   exports.PosAnimation = PosAnimation;
 
14459   exports.Projection = index;
 
14460   exports.Rectangle = Rectangle;
 
14461   exports.Renderer = Renderer;
 
14463   exports.SVGOverlay = SVGOverlay;
 
14464   exports.TileLayer = TileLayer;
 
14465   exports.Tooltip = Tooltip;
 
14466   exports.Transformation = Transformation;
 
14467   exports.Util = Util;
 
14468   exports.VideoOverlay = VideoOverlay;
 
14469   exports.bind = bind;
 
14470   exports.bounds = toBounds;
 
14471   exports.canvas = canvas;
 
14472   exports.circle = circle;
 
14473   exports.circleMarker = circleMarker;
 
14474   exports.control = control;
 
14475   exports.divIcon = divIcon;
 
14476   exports.extend = extend;
 
14477   exports.featureGroup = featureGroup;
 
14478   exports.geoJSON = geoJSON;
 
14479   exports.geoJson = geoJson;
 
14480   exports.gridLayer = gridLayer;
 
14481   exports.icon = icon;
 
14482   exports.imageOverlay = imageOverlay;
 
14483   exports.latLng = toLatLng;
 
14484   exports.latLngBounds = toLatLngBounds;
 
14485   exports.layerGroup = layerGroup;
 
14486   exports.map = createMap;
 
14487   exports.marker = marker;
 
14488   exports.point = toPoint;
 
14489   exports.polygon = polygon;
 
14490   exports.polyline = polyline;
 
14491   exports.popup = popup;
 
14492   exports.rectangle = rectangle;
 
14493   exports.setOptions = setOptions;
 
14494   exports.stamp = stamp;
 
14496   exports.svgOverlay = svgOverlay;
 
14497   exports.tileLayer = tileLayer;
 
14498   exports.tooltip = tooltip;
 
14499   exports.transformation = toTransformation;
 
14500   exports.version = version;
 
14501   exports.videoOverlay = videoOverlay;
 
14503   var oldL = window.L;
 
14504   exports.noConflict = function() {
 
14508   // Always export us to window global (see #2364)
 
14509   window.L = exports;
 
14512 //# sourceMappingURL=leaflet-src.js.map