[ Index ]

MailPress 7.2

[ Index ]     [ Classes ]     [ Functions ]     [ Variables ]     [ Constants ]     [ Statistics ]    

title

Body

[close]

/mp-includes/class/options/map/leaflet/ -> leaflet-src.js (source)

   1  /* @preserve
   2   * Leaflet 1.6.0+Detached: 0c81bdf904d864fd12a286e3d1979f47aba17991.0c81bdf, a JS library for interactive maps. http://leafletjs.com
   3   * (c) 2010-2019 Vladimir Agafonkin, (c) 2010-2011 CloudMade
   4   */
   5  
   6  (function (global, factory) {
   7      typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) :
   8      typeof define === 'function' && define.amd ? define(['exports'], factory) :
   9      (factory((global.L = {})));
  10  }(this, (function (exports) { 'use strict';
  11  
  12  var version = "1.6.0+HEAD.0c81bdf";
  13  
  14  /*
  15   * @namespace Util
  16   *
  17   * Various utility functions, used by Leaflet internally.
  18   */
  19  
  20  var freeze = Object.freeze;
  21  Object.freeze = function (obj) { return obj; };
  22  
  23  // @function extend(dest: Object, src?: Object): Object
  24  // Merges the properties of the `src` object (or multiple objects) into `dest` object and returns the latter. Has an `L.extend` shortcut.
  25  function extend(dest) {
  26      var i, j, len, src;
  27  
  28      for (j = 1, len = arguments.length; j < len; j++) {
  29          src = arguments[j];
  30          for (i in src) {
  31              dest[i] = src[i];
  32          }
  33      }
  34      return dest;
  35  }
  36  
  37  // @function create(proto: Object, properties?: Object): Object
  38  // Compatibility polyfill for [Object.create](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Object/create)
  39  var create = Object.create || (function () {
  40      function F() {}
  41      return function (proto) {
  42          F.prototype = proto;
  43          return new F();
  44      };
  45  })();
  46  
  47  // @function bind(fn: Function, …): Function
  48  // Returns a new function bound to the arguments passed, like [Function.prototype.bind](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Function/bind).
  49  // Has a `L.bind()` shortcut.
  50  function bind(fn, obj) {
  51      var slice = Array.prototype.slice;
  52  
  53      if (fn.bind) {
  54          return fn.bind.apply(fn, slice.call(arguments, 1));
  55      }
  56  
  57      var args = slice.call(arguments, 2);
  58  
  59      return function () {
  60          return fn.apply(obj, args.length ? args.concat(slice.call(arguments)) : arguments);
  61      };
  62  }
  63  
  64  // @property lastId: Number
  65  // Last unique ID used by [`stamp()`](#util-stamp)
  66  var lastId = 0;
  67  
  68  // @function stamp(obj: Object): Number
  69  // Returns the unique ID of an object, assigning it one if it doesn't have it.
  70  function stamp(obj) {
  71      /*eslint-disable */
  72      obj._leaflet_id = obj._leaflet_id || ++lastId;
  73      return obj._leaflet_id;
  74      /* eslint-enable */
  75  }
  76  
  77  // @function throttle(fn: Function, time: Number, context: Object): Function
  78  // Returns a function which executes function `fn` with the given scope `context`
  79  // (so that the `this` keyword refers to `context` inside `fn`'s code). The function
  80  // `fn` will be called no more than one time per given amount of `time`. The arguments
  81  // received by the bound function will be any arguments passed when binding the
  82  // function, followed by any arguments passed when invoking the bound function.
  83  // Has an `L.throttle` shortcut.
  84  function throttle(fn, time, context) {
  85      var lock, args, wrapperFn, later;
  86  
  87      later = function () {
  88          // reset lock and call if queued
  89          lock = false;
  90          if (args) {
  91              wrapperFn.apply(context, args);
  92              args = false;
  93          }
  94      };
  95  
  96      wrapperFn = function () {
  97          if (lock) {
  98              // called too soon, queue to call later
  99              args = arguments;
 100  
 101          } else {
 102              // call and lock until later
 103              fn.apply(context, arguments);
 104              setTimeout(later, time);
 105              lock = true;
 106          }
 107      };
 108  
 109      return wrapperFn;
 110  }
 111  
 112  // @function wrapNum(num: Number, range: Number[], includeMax?: Boolean): Number
 113  // Returns the number `num` modulo `range` in such a way so it lies within
 114  // `range[0]` and `range[1]`. The returned value will be always smaller than
 115  // `range[1]` unless `includeMax` is set to `true`.
 116  function wrapNum(x, range, includeMax) {
 117      var max = range[1],
 118          min = range[0],
 119          d = max - min;
 120      return x === max && includeMax ? x : ((x - min) % d + d) % d + min;
 121  }
 122  
 123  // @function falseFn(): Function
 124  // Returns a function which always returns `false`.
 125  function falseFn() { return false; }
 126  
 127  // @function formatNum(num: Number, digits?: Number): Number
 128  // Returns the number `num` rounded to `digits` decimals, or to 6 decimals by default.
 129  function formatNum(num, digits) {
 130      var pow = Math.pow(10, (digits === undefined ? 6 : digits));
 131      return Math.round(num * pow) / pow;
 132  }
 133  
 134  // @function trim(str: String): String
 135  // Compatibility polyfill for [String.prototype.trim](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String/Trim)
 136  function trim(str) {
 137      return str.trim ? str.trim() : str.replace(/^\s+|\s+$/g, '');
 138  }
 139  
 140  // @function splitWords(str: String): String[]
 141  // Trims and splits the string on whitespace and returns the array of parts.
 142  function splitWords(str) {
 143      return trim(str).split(/\s+/);
 144  }
 145  
 146  // @function setOptions(obj: Object, options: Object): Object
 147  // Merges the given properties to the `options` of the `obj` object, returning the resulting options. See `Class options`. Has an `L.setOptions` shortcut.
 148  function setOptions(obj, options) {
 149      if (!obj.hasOwnProperty('options')) {
 150          obj.options = obj.options ? create(obj.options) : {};
 151      }
 152      for (var i in options) {
 153          obj.options[i] = options[i];
 154      }
 155      return obj.options;
 156  }
 157  
 158  // @function getParamString(obj: Object, existingUrl?: String, uppercase?: Boolean): String
 159  // Converts an object into a parameter URL string, e.g. `{a: "foo", b: "bar"}`
 160  // translates to `'?a=foo&b=bar'`. If `existingUrl` is set, the parameters will
 161  // be appended at the end. If `uppercase` is `true`, the parameter names will
 162  // be uppercased (e.g. `'?A=foo&B=bar'`)
 163  function getParamString(obj, existingUrl, uppercase) {
 164      var params = [];
 165      for (var i in obj) {
 166          params.push(encodeURIComponent(uppercase ? i.toUpperCase() : i) + '=' + encodeURIComponent(obj[i]));
 167      }
 168      return ((!existingUrl || existingUrl.indexOf('?') === -1) ? '?' : '&') + params.join('&');
 169  }
 170  
 171  var templateRe = /\{ *([\w_-]+) *\}/g;
 172  
 173  // @function template(str: String, data: Object): String
 174  // Simple templating facility, accepts a template string of the form `'Hello {a}, {b}'`
 175  // and a data object like `{a: 'foo', b: 'bar'}`, returns evaluated string
 176  // `('Hello foo, bar')`. You can also specify functions instead of strings for
 177  // data values — they will be evaluated passing `data` as an argument.
 178  function template(str, data) {
 179      return str.replace(templateRe, function (str, key) {
 180          var value = data[key];
 181  
 182          if (value === undefined) {
 183              throw new Error('No value provided for variable ' + str);
 184  
 185          } else if (typeof value === 'function') {
 186              value = value(data);
 187          }
 188          return value;
 189      });
 190  }
 191  
 192  // @function isArray(obj): Boolean
 193  // Compatibility polyfill for [Array.isArray](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Array/isArray)
 194  var isArray = Array.isArray || function (obj) {
 195      return (Object.prototype.toString.call(obj) === '[object Array]');
 196  };
 197  
 198  // @function indexOf(array: Array, el: Object): Number
 199  // Compatibility polyfill for [Array.prototype.indexOf](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Array/indexOf)
 200  function indexOf(array, el) {
 201      for (var i = 0; i < array.length; i++) {
 202          if (array[i] === el) { return i; }
 203      }
 204      return -1;
 205  }
 206  
 207  // @property emptyImageUrl: String
 208  // Data URI string containing a base64-encoded empty GIF image.
 209  // Used as a hack to free memory from unused images on WebKit-powered
 210  // mobile devices (by setting image `src` to this string).
 211  var emptyImageUrl = 'data:image/gif;base64,R0lGODlhAQABAAD/ACwAAAAAAQABAAACADs=';
 212  
 213  // inspired by http://paulirish.com/2011/requestanimationframe-for-smart-animating/
 214  
 215  function getPrefixed(name) {
 216      return window['webkit' + name] || window['moz' + name] || window['ms' + name];
 217  }
 218  
 219  var lastTime = 0;
 220  
 221  // fallback for IE 7-8
 222  function timeoutDefer(fn) {
 223      var time = +new Date(),
 224          timeToCall = Math.max(0, 16 - (time - lastTime));
 225  
 226      lastTime = time + timeToCall;
 227      return window.setTimeout(fn, timeToCall);
 228  }
 229  
 230  var requestFn = window.requestAnimationFrame || getPrefixed('RequestAnimationFrame') || timeoutDefer;
 231  var cancelFn = window.cancelAnimationFrame || getPrefixed('CancelAnimationFrame') ||
 232          getPrefixed('CancelRequestAnimationFrame') || function (id) { window.clearTimeout(id); };
 233  
 234  // @function requestAnimFrame(fn: Function, context?: Object, immediate?: Boolean): Number
 235  // Schedules `fn` to be executed when the browser repaints. `fn` is bound to
 236  // `context` if given. When `immediate` is set, `fn` is called immediately if
 237  // the browser doesn't have native support for
 238  // [`window.requestAnimationFrame`](https://developer.mozilla.org/docs/Web/API/window/requestAnimationFrame),
 239  // otherwise it's delayed. Returns a request ID that can be used to cancel the request.
 240  function requestAnimFrame(fn, context, immediate) {
 241      if (immediate && requestFn === timeoutDefer) {
 242          fn.call(context);
 243      } else {
 244          return requestFn.call(window, bind(fn, context));
 245      }
 246  }
 247  
 248  // @function cancelAnimFrame(id: Number): undefined
 249  // Cancels a previous `requestAnimFrame`. See also [window.cancelAnimationFrame](https://developer.mozilla.org/docs/Web/API/window/cancelAnimationFrame).
 250  function cancelAnimFrame(id) {
 251      if (id) {
 252          cancelFn.call(window, id);
 253      }
 254  }
 255  
 256  
 257  var Util = (Object.freeze || Object)({
 258      freeze: freeze,
 259      extend: extend,
 260      create: create,
 261      bind: bind,
 262      lastId: lastId,
 263      stamp: stamp,
 264      throttle: throttle,
 265      wrapNum: wrapNum,
 266      falseFn: falseFn,
 267      formatNum: formatNum,
 268      trim: trim,
 269      splitWords: splitWords,
 270      setOptions: setOptions,
 271      getParamString: getParamString,
 272      template: template,
 273      isArray: isArray,
 274      indexOf: indexOf,
 275      emptyImageUrl: emptyImageUrl,
 276      requestFn: requestFn,
 277      cancelFn: cancelFn,
 278      requestAnimFrame: requestAnimFrame,
 279      cancelAnimFrame: cancelAnimFrame
 280  });
 281  
 282  // @class Class
 283  // @aka L.Class
 284  
 285  // @section
 286  // @uninheritable
 287  
 288  // Thanks to John Resig and Dean Edwards for inspiration!
 289  
 290  function Class() {}
 291  
 292  Class.extend = function (props) {
 293  
 294      // @function extend(props: Object): Function
 295      // [Extends the current class](#class-inheritance) given the properties to be included.
 296      // Returns a Javascript function that is a class constructor (to be called with `new`).
 297      var NewClass = function () {
 298  
 299          // call the constructor
 300          if (this.initialize) {
 301              this.initialize.apply(this, arguments);
 302          }
 303  
 304          // call all constructor hooks
 305          this.callInitHooks();
 306      };
 307  
 308      var parentProto = NewClass.__super__ = this.prototype;
 309  
 310      var proto = create(parentProto);
 311      proto.constructor = NewClass;
 312  
 313      NewClass.prototype = proto;
 314  
 315      // inherit parent's statics
 316      for (var i in this) {
 317          if (this.hasOwnProperty(i) && i !== 'prototype' && i !== '__super__') {
 318              NewClass[i] = this[i];
 319          }
 320      }
 321  
 322      // mix static properties into the class
 323      if (props.statics) {
 324          extend(NewClass, props.statics);
 325          delete props.statics;
 326      }
 327  
 328      // mix includes into the prototype
 329      if (props.includes) {
 330          checkDeprecatedMixinEvents(props.includes);
 331          extend.apply(null, [proto].concat(props.includes));
 332          delete props.includes;
 333      }
 334  
 335      // merge options
 336      if (proto.options) {
 337          props.options = extend(create(proto.options), props.options);
 338      }
 339  
 340      // mix given properties into the prototype
 341      extend(proto, props);
 342  
 343      proto._initHooks = [];
 344  
 345      // add method for calling all hooks
 346      proto.callInitHooks = function () {
 347  
 348          if (this._initHooksCalled) { return; }
 349  
 350          if (parentProto.callInitHooks) {
 351              parentProto.callInitHooks.call(this);
 352          }
 353  
 354          this._initHooksCalled = true;
 355  
 356          for (var i = 0, len = proto._initHooks.length; i < len; i++) {
 357              proto._initHooks[i].call(this);
 358          }
 359      };
 360  
 361      return NewClass;
 362  };
 363  
 364  
 365  // @function include(properties: Object): this
 366  // [Includes a mixin](#class-includes) into the current class.
 367  Class.include = function (props) {
 368      extend(this.prototype, props);
 369      return this;
 370  };
 371  
 372  // @function mergeOptions(options: Object): this
 373  // [Merges `options`](#class-options) into the defaults of the class.
 374  Class.mergeOptions = function (options) {
 375      extend(this.prototype.options, options);
 376      return this;
 377  };
 378  
 379  // @function addInitHook(fn: Function): this
 380  // Adds a [constructor hook](#class-constructor-hooks) to the class.
 381  Class.addInitHook = function (fn) { // (Function) || (String, args...)
 382      var args = Array.prototype.slice.call(arguments, 1);
 383  
 384      var init = typeof fn === 'function' ? fn : function () {
 385          this[fn].apply(this, args);
 386      };
 387  
 388      this.prototype._initHooks = this.prototype._initHooks || [];
 389      this.prototype._initHooks.push(init);
 390      return this;
 391  };
 392  
 393  function checkDeprecatedMixinEvents(includes) {
 394      if (typeof L === 'undefined' || !L || !L.Mixin) { return; }
 395  
 396      includes = isArray(includes) ? includes : [includes];
 397  
 398      for (var i = 0; i < includes.length; i++) {
 399          if (includes[i] === L.Mixin.Events) {
 400              console.warn('Deprecated include of L.Mixin.Events: ' +
 401                  'this property will be removed in future releases, ' +
 402                  'please inherit from L.Evented instead.', new Error().stack);
 403          }
 404      }
 405  }
 406  
 407  /*
 408   * @class Evented
 409   * @aka L.Evented
 410   * @inherits Class
 411   *
 412   * A set of methods shared between event-powered classes (like `Map` and `Marker`). Generally, events allow you to execute some function when something happens with an object (e.g. the user clicks on the map, causing the map to fire `'click'` event).
 413   *
 414   * @example
 415   *
 416   * ```js
 417   * map.on('click', function(e) {
 418   *     alert(e.latlng);
 419   * } );
 420   * ```
 421   *
 422   * Leaflet deals with event listeners by reference, so if you want to add a listener and then remove it, define it as a function:
 423   *
 424   * ```js
 425   * function onClick(e) { ... }
 426   *
 427   * map.on('click', onClick);
 428   * map.off('click', onClick);
 429   * ```
 430   */
 431  
 432  var Events = {
 433      /* @method on(type: String, fn: Function, context?: Object): this
 434       * Adds a listener function (`fn`) to a particular event type of the object. You can optionally specify the context of the listener (object the this keyword will point to). You can also pass several space-separated types (e.g. `'click dblclick'`).
 435       *
 436       * @alternative
 437       * @method on(eventMap: Object): this
 438       * Adds a set of type/listener pairs, e.g. `{click: onClick, mousemove: onMouseMove}`
 439       */
 440      on: function (types, fn, context) {
 441  
 442          // types can be a map of types/handlers
 443          if (typeof types === 'object') {
 444              for (var type in types) {
 445                  // we don't process space-separated events here for performance;
 446                  // it's a hot path since Layer uses the on(obj) syntax
 447                  this._on(type, types[type], fn);
 448              }
 449  
 450          } else {
 451              // types can be a string of space-separated words
 452              types = splitWords(types);
 453  
 454              for (var i = 0, len = types.length; i < len; i++) {
 455                  this._on(types[i], fn, context);
 456              }
 457          }
 458  
 459          return this;
 460      },
 461  
 462      /* @method off(type: String, fn?: Function, context?: Object): this
 463       * Removes a previously added listener function. If no function is specified, it will remove all the listeners of that particular event from the object. Note that if you passed a custom context to `on`, you must pass the same context to `off` in order to remove the listener.
 464       *
 465       * @alternative
 466       * @method off(eventMap: Object): this
 467       * Removes a set of type/listener pairs.
 468       *
 469       * @alternative
 470       * @method off: this
 471       * Removes all listeners to all events on the object. This includes implicitly attached events.
 472       */
 473      off: function (types, fn, context) {
 474  
 475          if (!types) {
 476              // clear all listeners if called without arguments
 477              delete this._events;
 478  
 479          } else if (typeof types === 'object') {
 480              for (var type in types) {
 481                  this._off(type, types[type], fn);
 482              }
 483  
 484          } else {
 485              types = splitWords(types);
 486  
 487              for (var i = 0, len = types.length; i < len; i++) {
 488                  this._off(types[i], fn, context);
 489              }
 490          }
 491  
 492          return this;
 493      },
 494  
 495      // attach listener (without syntactic sugar now)
 496      _on: function (type, fn, context) {
 497          this._events = this._events || {};
 498  
 499          /* get/init listeners for type */
 500          var typeListeners = this._events[type];
 501          if (!typeListeners) {
 502              typeListeners = [];
 503              this._events[type] = typeListeners;
 504          }
 505  
 506          if (context === this) {
 507              // Less memory footprint.
 508              context = undefined;
 509          }
 510          var newListener = {fn: fn, ctx: context},
 511              listeners = typeListeners;
 512  
 513          // check if fn already there
 514          for (var i = 0, len = listeners.length; i < len; i++) {
 515              if (listeners[i].fn === fn && listeners[i].ctx === context) {
 516                  return;
 517              }
 518          }
 519  
 520          listeners.push(newListener);
 521      },
 522  
 523      _off: function (type, fn, context) {
 524          var listeners,
 525              i,
 526              len;
 527  
 528          if (!this._events) { return; }
 529  
 530          listeners = this._events[type];
 531  
 532          if (!listeners) {
 533              return;
 534          }
 535  
 536          if (!fn) {
 537              // Set all removed listeners to noop so they are not called if remove happens in fire
 538              for (i = 0, len = listeners.length; i < len; i++) {
 539                  listeners[i].fn = falseFn;
 540              }
 541              // clear all listeners for a type if function isn't specified
 542              delete this._events[type];
 543              return;
 544          }
 545  
 546          if (context === this) {
 547              context = undefined;
 548          }
 549  
 550          if (listeners) {
 551  
 552              // find fn and remove it
 553              for (i = 0, len = listeners.length; i < len; i++) {
 554                  var l = listeners[i];
 555                  if (l.ctx !== context) { continue; }
 556                  if (l.fn === fn) {
 557  
 558                      // set the removed listener to noop so that's not called if remove happens in fire
 559                      l.fn = falseFn;
 560  
 561                      if (this._firingCount) {
 562                          /* copy array in case events are being fired */
 563                          this._events[type] = listeners = listeners.slice();
 564                      }
 565                      listeners.splice(i, 1);
 566  
 567                      return;
 568                  }
 569              }
 570          }
 571      },
 572  
 573      // @method fire(type: String, data?: Object, propagate?: Boolean): this
 574      // Fires an event of the specified type. You can optionally provide an data
 575      // object — the first argument of the listener function will contain its
 576      // properties. The event can optionally be propagated to event parents.
 577      fire: function (type, data, propagate) {
 578          if (!this.listens(type, propagate)) { return this; }
 579  
 580          var event = extend({}, data, {
 581              type: type,
 582              target: this,
 583              sourceTarget: data && data.sourceTarget || this
 584          });
 585  
 586          if (this._events) {
 587              var listeners = this._events[type];
 588  
 589              if (listeners) {
 590                  this._firingCount = (this._firingCount + 1) || 1;
 591                  for (var i = 0, len = listeners.length; i < len; i++) {
 592                      var l = listeners[i];
 593                      l.fn.call(l.ctx || this, event);
 594                  }
 595  
 596                  this._firingCount--;
 597              }
 598          }
 599  
 600          if (propagate) {
 601              // propagate the event to parents (set with addEventParent)
 602              this._propagateEvent(event);
 603          }
 604  
 605          return this;
 606      },
 607  
 608      // @method listens(type: String): Boolean
 609      // Returns `true` if a particular event type has any listeners attached to it.
 610      listens: function (type, propagate) {
 611          var listeners = this._events && this._events[type];
 612          if (listeners && listeners.length) { return true; }
 613  
 614          if (propagate) {
 615              // also check parents for listeners if event propagates
 616              for (var id in this._eventParents) {
 617                  if (this._eventParents[id].listens(type, propagate)) { return true; }
 618              }
 619          }
 620          return false;
 621      },
 622  
 623      // @method once(…): this
 624      // Behaves as [`on(…)`](#evented-on), except the listener will only get fired once and then removed.
 625      once: function (types, fn, context) {
 626  
 627          if (typeof types === 'object') {
 628              for (var type in types) {
 629                  this.once(type, types[type], fn);
 630              }
 631              return this;
 632          }
 633  
 634          var handler = bind(function () {
 635              this
 636                  .off(types, fn, context)
 637                  .off(types, handler, context);
 638          }, this);
 639  
 640          // add a listener that's executed once and removed after that
 641          return this
 642              .on(types, fn, context)
 643              .on(types, handler, context);
 644      },
 645  
 646      // @method addEventParent(obj: Evented): this
 647      // Adds an event parent - an `Evented` that will receive propagated events
 648      addEventParent: function (obj) {
 649          this._eventParents = this._eventParents || {};
 650          this._eventParents[stamp(obj)] = obj;
 651          return this;
 652      },
 653  
 654      // @method removeEventParent(obj: Evented): this
 655      // Removes an event parent, so it will stop receiving propagated events
 656      removeEventParent: function (obj) {
 657          if (this._eventParents) {
 658              delete this._eventParents[stamp(obj)];
 659          }
 660          return this;
 661      },
 662  
 663      _propagateEvent: function (e) {
 664          for (var id in this._eventParents) {
 665              this._eventParents[id].fire(e.type, extend({
 666                  layer: e.target,
 667                  propagatedFrom: e.target
 668              }, e), true);
 669          }
 670      }
 671  };
 672  
 673  // aliases; we should ditch those eventually
 674  
 675  // @method addEventListener(…): this
 676  // Alias to [`on(…)`](#evented-on)
 677  Events.addEventListener = Events.on;
 678  
 679  // @method removeEventListener(…): this
 680  // Alias to [`off(…)`](#evented-off)
 681  
 682  // @method clearAllEventListeners(…): this
 683  // Alias to [`off()`](#evented-off)
 684  Events.removeEventListener = Events.clearAllEventListeners = Events.off;
 685  
 686  // @method addOneTimeEventListener(…): this
 687  // Alias to [`once(…)`](#evented-once)
 688  Events.addOneTimeEventListener = Events.once;
 689  
 690  // @method fireEvent(…): this
 691  // Alias to [`fire(…)`](#evented-fire)
 692  Events.fireEvent = Events.fire;
 693  
 694  // @method hasEventListeners(…): Boolean
 695  // Alias to [`listens(…)`](#evented-listens)
 696  Events.hasEventListeners = Events.listens;
 697  
 698  var Evented = Class.extend(Events);
 699  
 700  /*
 701   * @class Point
 702   * @aka L.Point
 703   *
 704   * Represents a point with `x` and `y` coordinates in pixels.
 705   *
 706   * @example
 707   *
 708   * ```js
 709   * var point = L.point(200, 300);
 710   * ```
 711   *
 712   * All Leaflet methods and options that accept `Point` objects also accept them in a simple Array form (unless noted otherwise), so these lines are equivalent:
 713   *
 714   * ```js
 715   * map.panBy([200, 300]);
 716   * map.panBy(L.point(200, 300));
 717   * ```
 718   *
 719   * Note that `Point` does not inherit from Leafet's `Class` object,
 720   * which means new classes can't inherit from it, and new methods
 721   * can't be added to it with the `include` function.
 722   */
 723  
 724  function Point(x, y, round) {
 725      // @property x: Number; The `x` coordinate of the point
 726      this.x = (round ? Math.round(x) : x);
 727      // @property y: Number; The `y` coordinate of the point
 728      this.y = (round ? Math.round(y) : y);
 729  }
 730  
 731  var trunc = Math.trunc || function (v) {
 732      return v > 0 ? Math.floor(v) : Math.ceil(v);
 733  };
 734  
 735  Point.prototype = {
 736  
 737      // @method clone(): Point
 738      // Returns a copy of the current point.
 739      clone: function () {
 740          return new Point(this.x, this.y);
 741      },
 742  
 743      // @method add(otherPoint: Point): Point
 744      // Returns the result of addition of the current and the given points.
 745      add: function (point) {
 746          // non-destructive, returns a new point
 747          return this.clone()._add(toPoint(point));
 748      },
 749  
 750      _add: function (point) {
 751          // destructive, used directly for performance in situations where it's safe to modify existing point
 752          this.x += point.x;
 753          this.y += point.y;
 754          return this;
 755      },
 756  
 757      // @method subtract(otherPoint: Point): Point
 758      // Returns the result of subtraction of the given point from the current.
 759      subtract: function (point) {
 760          return this.clone()._subtract(toPoint(point));
 761      },
 762  
 763      _subtract: function (point) {
 764          this.x -= point.x;
 765          this.y -= point.y;
 766          return this;
 767      },
 768  
 769      // @method divideBy(num: Number): Point
 770      // Returns the result of division of the current point by the given number.
 771      divideBy: function (num) {
 772          return this.clone()._divideBy(num);
 773      },
 774  
 775      _divideBy: function (num) {
 776          this.x /= num;
 777          this.y /= num;
 778          return this;
 779      },
 780  
 781      // @method multiplyBy(num: Number): Point
 782      // Returns the result of multiplication of the current point by the given number.
 783      multiplyBy: function (num) {
 784          return this.clone()._multiplyBy(num);
 785      },
 786  
 787      _multiplyBy: function (num) {
 788          this.x *= num;
 789          this.y *= num;
 790          return this;
 791      },
 792  
 793      // @method scaleBy(scale: Point): Point
 794      // Multiply each coordinate of the current point by each coordinate of
 795      // `scale`. In linear algebra terms, multiply the point by the
 796      // [scaling matrix](https://en.wikipedia.org/wiki/Scaling_%28geometry%29#Matrix_representation)
 797      // defined by `scale`.
 798      scaleBy: function (point) {
 799          return new Point(this.x * point.x, this.y * point.y);
 800      },
 801  
 802      // @method unscaleBy(scale: Point): Point
 803      // Inverse of `scaleBy`. Divide each coordinate of the current point by
 804      // each coordinate of `scale`.
 805      unscaleBy: function (point) {
 806          return new Point(this.x / point.x, this.y / point.y);
 807      },
 808  
 809      // @method round(): Point
 810      // Returns a copy of the current point with rounded coordinates.
 811      round: function () {
 812          return this.clone()._round();
 813      },
 814  
 815      _round: function () {
 816          this.x = Math.round(this.x);
 817          this.y = Math.round(this.y);
 818          return this;
 819      },
 820  
 821      // @method floor(): Point
 822      // Returns a copy of the current point with floored coordinates (rounded down).
 823      floor: function () {
 824          return this.clone()._floor();
 825      },
 826  
 827      _floor: function () {
 828          this.x = Math.floor(this.x);
 829          this.y = Math.floor(this.y);
 830          return this;
 831      },
 832  
 833      // @method ceil(): Point
 834      // Returns a copy of the current point with ceiled coordinates (rounded up).
 835      ceil: function () {
 836          return this.clone()._ceil();
 837      },
 838  
 839      _ceil: function () {
 840          this.x = Math.ceil(this.x);
 841          this.y = Math.ceil(this.y);
 842          return this;
 843      },
 844  
 845      // @method trunc(): Point
 846      // Returns a copy of the current point with truncated coordinates (rounded towards zero).
 847      trunc: function () {
 848          return this.clone()._trunc();
 849      },
 850  
 851      _trunc: function () {
 852          this.x = trunc(this.x);
 853          this.y = trunc(this.y);
 854          return this;
 855      },
 856  
 857      // @method distanceTo(otherPoint: Point): Number
 858      // Returns the cartesian distance between the current and the given points.
 859      distanceTo: function (point) {
 860          point = toPoint(point);
 861  
 862          var x = point.x - this.x,
 863              y = point.y - this.y;
 864  
 865          return Math.sqrt(x * x + y * y);
 866      },
 867  
 868      // @method equals(otherPoint: Point): Boolean
 869      // Returns `true` if the given point has the same coordinates.
 870      equals: function (point) {
 871          point = toPoint(point);
 872  
 873          return point.x === this.x &&
 874                 point.y === this.y;
 875      },
 876  
 877      // @method contains(otherPoint: Point): Boolean
 878      // Returns `true` if both coordinates of the given point are less than the corresponding current point coordinates (in absolute values).
 879      contains: function (point) {
 880          point = toPoint(point);
 881  
 882          return Math.abs(point.x) <= Math.abs(this.x) &&
 883                 Math.abs(point.y) <= Math.abs(this.y);
 884      },
 885  
 886      // @method toString(): String
 887      // Returns a string representation of the point for debugging purposes.
 888      toString: function () {
 889          return 'Point(' +
 890                  formatNum(this.x) + ', ' +
 891                  formatNum(this.y) + ')';
 892      }
 893  };
 894  
 895  // @factory L.point(x: Number, y: Number, round?: Boolean)
 896  // Creates a Point object with the given `x` and `y` coordinates. If optional `round` is set to true, rounds the `x` and `y` values.
 897  
 898  // @alternative
 899  // @factory L.point(coords: Number[])
 900  // Expects an array of the form `[x, y]` instead.
 901  
 902  // @alternative
 903  // @factory L.point(coords: Object)
 904  // Expects a plain object of the form `{x: Number, y: Number}` instead.
 905  function toPoint(x, y, round) {
 906      if (x instanceof Point) {
 907          return x;
 908      }
 909      if (isArray(x)) {
 910          return new Point(x[0], x[1]);
 911      }
 912      if (x === undefined || x === null) {
 913          return x;
 914      }
 915      if (typeof x === 'object' && 'x' in x && 'y' in x) {
 916          return new Point(x.x, x.y);
 917      }
 918      return new Point(x, y, round);
 919  }
 920  
 921  /*
 922   * @class Bounds
 923   * @aka L.Bounds
 924   *
 925   * Represents a rectangular area in pixel coordinates.
 926   *
 927   * @example
 928   *
 929   * ```js
 930   * var p1 = L.point(10, 10),
 931   * p2 = L.point(40, 60),
 932   * bounds = L.bounds(p1, p2);
 933   * ```
 934   *
 935   * All Leaflet methods that accept `Bounds` objects also accept them in a simple Array form (unless noted otherwise), so the bounds example above can be passed like this:
 936   *
 937   * ```js
 938   * otherBounds.intersects([[10, 10], [40, 60]]);
 939   * ```
 940   *
 941   * Note that `Bounds` does not inherit from Leafet's `Class` object,
 942   * which means new classes can't inherit from it, and new methods
 943   * can't be added to it with the `include` function.
 944   */
 945  
 946  function Bounds(a, b) {
 947      if (!a) { return; }
 948  
 949      var points = b ? [a, b] : a;
 950  
 951      for (var i = 0, len = points.length; i < len; i++) {
 952          this.extend(points[i]);
 953      }
 954  }
 955  
 956  Bounds.prototype = {
 957      // @method extend(point: Point): this
 958      // Extends the bounds to contain the given point.
 959      extend: function (point) { // (Point)
 960          point = toPoint(point);
 961  
 962          // @property min: Point
 963          // The top left corner of the rectangle.
 964          // @property max: Point
 965          // The bottom right corner of the rectangle.
 966          if (!this.min && !this.max) {
 967              this.min = point.clone();
 968              this.max = point.clone();
 969          } else {
 970              this.min.x = Math.min(point.x, this.min.x);
 971              this.max.x = Math.max(point.x, this.max.x);
 972              this.min.y = Math.min(point.y, this.min.y);
 973              this.max.y = Math.max(point.y, this.max.y);
 974          }
 975          return this;
 976      },
 977  
 978      // @method getCenter(round?: Boolean): Point
 979      // Returns the center point of the bounds.
 980      getCenter: function (round) {
 981          return new Point(
 982                  (this.min.x + this.max.x) / 2,
 983                  (this.min.y + this.max.y) / 2, round);
 984      },
 985  
 986      // @method getBottomLeft(): Point
 987      // Returns the bottom-left point of the bounds.
 988      getBottomLeft: function () {
 989          return new Point(this.min.x, this.max.y);
 990      },
 991  
 992      // @method getTopRight(): Point
 993      // Returns the top-right point of the bounds.
 994      getTopRight: function () { // -> Point
 995          return new Point(this.max.x, this.min.y);
 996      },
 997  
 998      // @method getTopLeft(): Point
 999      // Returns the top-left point of the bounds (i.e. [`this.min`](#bounds-min)).
1000      getTopLeft: function () {
1001          return this.min; // left, top
1002      },
1003  
1004      // @method getBottomRight(): Point
1005      // Returns the bottom-right point of the bounds (i.e. [`this.max`](#bounds-max)).
1006      getBottomRight: function () {
1007          return this.max; // right, bottom
1008      },
1009  
1010      // @method getSize(): Point
1011      // Returns the size of the given bounds
1012      getSize: function () {
1013          return this.max.subtract(this.min);
1014      },
1015  
1016      // @method contains(otherBounds: Bounds): Boolean
1017      // Returns `true` if the rectangle contains the given one.
1018      // @alternative
1019      // @method contains(point: Point): Boolean
1020      // Returns `true` if the rectangle contains the given point.
1021      contains: function (obj) {
1022          var min, max;
1023  
1024          if (typeof obj[0] === 'number' || obj instanceof Point) {
1025              obj = toPoint(obj);
1026          } else {
1027              obj = toBounds(obj);
1028          }
1029  
1030          if (obj instanceof Bounds) {
1031              min = obj.min;
1032              max = obj.max;
1033          } else {
1034              min = max = obj;
1035          }
1036  
1037          return (min.x >= this.min.x) &&
1038                 (max.x <= this.max.x) &&
1039                 (min.y >= this.min.y) &&
1040                 (max.y <= this.max.y);
1041      },
1042  
1043      // @method intersects(otherBounds: Bounds): Boolean
1044      // Returns `true` if the rectangle intersects the given bounds. Two bounds
1045      // intersect if they have at least one point in common.
1046      intersects: function (bounds) { // (Bounds) -> Boolean
1047          bounds = toBounds(bounds);
1048  
1049          var min = this.min,
1050              max = this.max,
1051              min2 = bounds.min,
1052              max2 = bounds.max,
1053              xIntersects = (max2.x >= min.x) && (min2.x <= max.x),
1054              yIntersects = (max2.y >= min.y) && (min2.y <= max.y);
1055  
1056          return xIntersects && yIntersects;
1057      },
1058  
1059      // @method overlaps(otherBounds: Bounds): Boolean
1060      // Returns `true` if the rectangle overlaps the given bounds. Two bounds
1061      // overlap if their intersection is an area.
1062      overlaps: function (bounds) { // (Bounds) -> Boolean
1063          bounds = toBounds(bounds);
1064  
1065          var min = this.min,
1066              max = this.max,
1067              min2 = bounds.min,
1068              max2 = bounds.max,
1069              xOverlaps = (max2.x > min.x) && (min2.x < max.x),
1070              yOverlaps = (max2.y > min.y) && (min2.y < max.y);
1071  
1072          return xOverlaps && yOverlaps;
1073      },
1074  
1075      isValid: function () {
1076          return !!(this.min && this.max);
1077      }
1078  };
1079  
1080  
1081  // @factory L.bounds(corner1: Point, corner2: Point)
1082  // Creates a Bounds object from two corners coordinate pairs.
1083  // @alternative
1084  // @factory L.bounds(points: Point[])
1085  // Creates a Bounds object from the given array of points.
1086  function toBounds(a, b) {
1087      if (!a || a instanceof Bounds) {
1088          return a;
1089      }
1090      return new Bounds(a, b);
1091  }
1092  
1093  /*
1094   * @class LatLngBounds
1095   * @aka L.LatLngBounds
1096   *
1097   * Represents a rectangular geographical area on a map.
1098   *
1099   * @example
1100   *
1101   * ```js
1102   * var corner1 = L.latLng(40.712, -74.227),
1103   * corner2 = L.latLng(40.774, -74.125),
1104   * bounds = L.latLngBounds(corner1, corner2);
1105   * ```
1106   *
1107   * All Leaflet methods that accept LatLngBounds objects also accept them in a simple Array form (unless noted otherwise), so the bounds example above can be passed like this:
1108   *
1109   * ```js
1110   * map.fitBounds([
1111   *     [40.712, -74.227],
1112   *     [40.774, -74.125]
1113   * ]);
1114   * ```
1115   *
1116   * Caution: if the area crosses the antimeridian (often confused with the International Date Line), you must specify corners _outside_ the [-180, 180] degrees longitude range.
1117   *
1118   * Note that `LatLngBounds` does not inherit from Leafet's `Class` object,
1119   * which means new classes can't inherit from it, and new methods
1120   * can't be added to it with the `include` function.
1121   */
1122  
1123  function LatLngBounds(corner1, corner2) { // (LatLng, LatLng) or (LatLng[])
1124      if (!corner1) { return; }
1125  
1126      var latlngs = corner2 ? [corner1, corner2] : corner1;
1127  
1128      for (var i = 0, len = latlngs.length; i < len; i++) {
1129          this.extend(latlngs[i]);
1130      }
1131  }
1132  
1133  LatLngBounds.prototype = {
1134  
1135      // @method extend(latlng: LatLng): this
1136      // Extend the bounds to contain the given point
1137  
1138      // @alternative
1139      // @method extend(otherBounds: LatLngBounds): this
1140      // Extend the bounds to contain the given bounds
1141      extend: function (obj) {
1142          var sw = this._southWest,
1143              ne = this._northEast,
1144              sw2, ne2;
1145  
1146          if (obj instanceof LatLng) {
1147              sw2 = obj;
1148              ne2 = obj;
1149  
1150          } else if (obj instanceof LatLngBounds) {
1151              sw2 = obj._southWest;
1152              ne2 = obj._northEast;
1153  
1154              if (!sw2 || !ne2) { return this; }
1155  
1156          } else {
1157              return obj ? this.extend(toLatLng(obj) || toLatLngBounds(obj)) : this;
1158          }
1159  
1160          if (!sw && !ne) {
1161              this._southWest = new LatLng(sw2.lat, sw2.lng);
1162              this._northEast = new LatLng(ne2.lat, ne2.lng);
1163          } else {
1164              sw.lat = Math.min(sw2.lat, sw.lat);
1165              sw.lng = Math.min(sw2.lng, sw.lng);
1166              ne.lat = Math.max(ne2.lat, ne.lat);
1167              ne.lng = Math.max(ne2.lng, ne.lng);
1168          }
1169  
1170          return this;
1171      },
1172  
1173      // @method pad(bufferRatio: Number): LatLngBounds
1174      // Returns bounds created by extending or retracting the current bounds by a given ratio in each direction.
1175      // For example, a ratio of 0.5 extends the bounds by 50% in each direction.
1176      // Negative values will retract the bounds.
1177      pad: function (bufferRatio) {
1178          var sw = this._southWest,
1179              ne = this._northEast,
1180              heightBuffer = Math.abs(sw.lat - ne.lat) * bufferRatio,
1181              widthBuffer = Math.abs(sw.lng - ne.lng) * bufferRatio;
1182  
1183          return new LatLngBounds(
1184                  new LatLng(sw.lat - heightBuffer, sw.lng - widthBuffer),
1185                  new LatLng(ne.lat + heightBuffer, ne.lng + widthBuffer));
1186      },
1187  
1188      // @method getCenter(): LatLng
1189      // Returns the center point of the bounds.
1190      getCenter: function () {
1191          return new LatLng(
1192                  (this._southWest.lat + this._northEast.lat) / 2,
1193                  (this._southWest.lng + this._northEast.lng) / 2);
1194      },
1195  
1196      // @method getSouthWest(): LatLng
1197      // Returns the south-west point of the bounds.
1198      getSouthWest: function () {
1199          return this._southWest;
1200      },
1201  
1202      // @method getNorthEast(): LatLng
1203      // Returns the north-east point of the bounds.
1204      getNorthEast: function () {
1205          return this._northEast;
1206      },
1207  
1208      // @method getNorthWest(): LatLng
1209      // Returns the north-west point of the bounds.
1210      getNorthWest: function () {
1211          return new LatLng(this.getNorth(), this.getWest());
1212      },
1213  
1214      // @method getSouthEast(): LatLng
1215      // Returns the south-east point of the bounds.
1216      getSouthEast: function () {
1217          return new LatLng(this.getSouth(), this.getEast());
1218      },
1219  
1220      // @method getWest(): Number
1221      // Returns the west longitude of the bounds
1222      getWest: function () {
1223          return this._southWest.lng;
1224      },
1225  
1226      // @method getSouth(): Number
1227      // Returns the south latitude of the bounds
1228      getSouth: function () {
1229          return this._southWest.lat;
1230      },
1231  
1232      // @method getEast(): Number
1233      // Returns the east longitude of the bounds
1234      getEast: function () {
1235          return this._northEast.lng;
1236      },
1237  
1238      // @method getNorth(): Number
1239      // Returns the north latitude of the bounds
1240      getNorth: function () {
1241          return this._northEast.lat;
1242      },
1243  
1244      // @method contains(otherBounds: LatLngBounds): Boolean
1245      // Returns `true` if the rectangle contains the given one.
1246  
1247      // @alternative
1248      // @method contains (latlng: LatLng): Boolean
1249      // Returns `true` if the rectangle contains the given point.
1250      contains: function (obj) { // (LatLngBounds) or (LatLng) -> Boolean
1251          if (typeof obj[0] === 'number' || obj instanceof LatLng || 'lat' in obj) {
1252              obj = toLatLng(obj);
1253          } else {
1254              obj = toLatLngBounds(obj);
1255          }
1256  
1257          var sw = this._southWest,
1258              ne = this._northEast,
1259              sw2, ne2;
1260  
1261          if (obj instanceof LatLngBounds) {
1262              sw2 = obj.getSouthWest();
1263              ne2 = obj.getNorthEast();
1264          } else {
1265              sw2 = ne2 = obj;
1266          }
1267  
1268          return (sw2.lat >= sw.lat) && (ne2.lat <= ne.lat) &&
1269                 (sw2.lng >= sw.lng) && (ne2.lng <= ne.lng);
1270      },
1271  
1272      // @method intersects(otherBounds: LatLngBounds): Boolean
1273      // Returns `true` if the rectangle intersects the given bounds. Two bounds intersect if they have at least one point in common.
1274      intersects: function (bounds) {
1275          bounds = toLatLngBounds(bounds);
1276  
1277          var sw = this._southWest,
1278              ne = this._northEast,
1279              sw2 = bounds.getSouthWest(),
1280              ne2 = bounds.getNorthEast(),
1281  
1282              latIntersects = (ne2.lat >= sw.lat) && (sw2.lat <= ne.lat),
1283              lngIntersects = (ne2.lng >= sw.lng) && (sw2.lng <= ne.lng);
1284  
1285          return latIntersects && lngIntersects;
1286      },
1287  
1288      // @method overlaps(otherBounds: Bounds): Boolean
1289      // Returns `true` if the rectangle overlaps the given bounds. Two bounds overlap if their intersection is an area.
1290      overlaps: function (bounds) {
1291          bounds = toLatLngBounds(bounds);
1292  
1293          var sw = this._southWest,
1294              ne = this._northEast,
1295              sw2 = bounds.getSouthWest(),
1296              ne2 = bounds.getNorthEast(),
1297  
1298              latOverlaps = (ne2.lat > sw.lat) && (sw2.lat < ne.lat),
1299              lngOverlaps = (ne2.lng > sw.lng) && (sw2.lng < ne.lng);
1300  
1301          return latOverlaps && lngOverlaps;
1302      },
1303  
1304      // @method toBBoxString(): String
1305      // Returns a string with bounding box coordinates in a 'southwest_lng,southwest_lat,northeast_lng,northeast_lat' format. Useful for sending requests to web services that return geo data.
1306      toBBoxString: function () {
1307          return [this.getWest(), this.getSouth(), this.getEast(), this.getNorth()].join(',');
1308      },
1309  
1310      // @method equals(otherBounds: LatLngBounds, maxMargin?: Number): Boolean
1311      // Returns `true` if the rectangle is equivalent (within a small margin of error) to the given bounds. The margin of error can be overridden by setting `maxMargin` to a small number.
1312      equals: function (bounds, maxMargin) {
1313          if (!bounds) { return false; }
1314  
1315          bounds = toLatLngBounds(bounds);
1316  
1317          return this._southWest.equals(bounds.getSouthWest(), maxMargin) &&
1318                 this._northEast.equals(bounds.getNorthEast(), maxMargin);
1319      },
1320  
1321      // @method isValid(): Boolean
1322      // Returns `true` if the bounds are properly initialized.
1323      isValid: function () {
1324          return !!(this._southWest && this._northEast);
1325      }
1326  };
1327  
1328  // TODO International date line?
1329  
1330  // @factory L.latLngBounds(corner1: LatLng, corner2: LatLng)
1331  // Creates a `LatLngBounds` object by defining two diagonally opposite corners of the rectangle.
1332  
1333  // @alternative
1334  // @factory L.latLngBounds(latlngs: LatLng[])
1335  // Creates a `LatLngBounds` object defined by the geographical points it contains. Very useful for zooming the map to fit a particular set of locations with [`fitBounds`](#map-fitbounds).
1336  function toLatLngBounds(a, b) {
1337      if (a instanceof LatLngBounds) {
1338          return a;
1339      }
1340      return new LatLngBounds(a, b);
1341  }
1342  
1343  /* @class LatLng
1344   * @aka L.LatLng
1345   *
1346   * Represents a geographical point with a certain latitude and longitude.
1347   *
1348   * @example
1349   *
1350   * ```
1351   * var latlng = L.latLng(50.5, 30.5);
1352   * ```
1353   *
1354   * All Leaflet methods that accept LatLng objects also accept them in a simple Array form and simple object form (unless noted otherwise), so these lines are equivalent:
1355   *
1356   * ```
1357   * map.panTo([50, 30]);
1358   * map.panTo({lon: 30, lat: 50});
1359   * map.panTo({lat: 50, lng: 30});
1360   * map.panTo(L.latLng(50, 30));
1361   * ```
1362   *
1363   * Note that `LatLng` does not inherit from Leaflet's `Class` object,
1364   * which means new classes can't inherit from it, and new methods
1365   * can't be added to it with the `include` function.
1366   */
1367  
1368  function LatLng(lat, lng, alt) {
1369      if (isNaN(lat) || isNaN(lng)) {
1370          throw new Error('Invalid LatLng object: (' + lat + ', ' + lng + ')');
1371      }
1372  
1373      // @property lat: Number
1374      // Latitude in degrees
1375      this.lat = +lat;
1376  
1377      // @property lng: Number
1378      // Longitude in degrees
1379      this.lng = +lng;
1380  
1381      // @property alt: Number
1382      // Altitude in meters (optional)
1383      if (alt !== undefined) {
1384          this.alt = +alt;
1385      }
1386  }
1387  
1388  LatLng.prototype = {
1389      // @method equals(otherLatLng: LatLng, maxMargin?: Number): Boolean
1390      // Returns `true` if the given `LatLng` point is at the same position (within a small margin of error). The margin of error can be overridden by setting `maxMargin` to a small number.
1391      equals: function (obj, maxMargin) {
1392          if (!obj) { return false; }
1393  
1394          obj = toLatLng(obj);
1395  
1396          var margin = Math.max(
1397                  Math.abs(this.lat - obj.lat),
1398                  Math.abs(this.lng - obj.lng));
1399  
1400          return margin <= (maxMargin === undefined ? 1.0E-9 : maxMargin);
1401      },
1402  
1403      // @method toString(): String
1404      // Returns a string representation of the point (for debugging purposes).
1405      toString: function (precision) {
1406          return 'LatLng(' +
1407                  formatNum(this.lat, precision) + ', ' +
1408                  formatNum(this.lng, precision) + ')';
1409      },
1410  
1411      // @method distanceTo(otherLatLng: LatLng): Number
1412      // Returns the distance (in meters) to the given `LatLng` calculated using the [Spherical Law of Cosines](https://en.wikipedia.org/wiki/Spherical_law_of_cosines).
1413      distanceTo: function (other) {
1414          return Earth.distance(this, toLatLng(other));
1415      },
1416  
1417      // @method wrap(): LatLng
1418      // Returns a new `LatLng` object with the longitude wrapped so it's always between -180 and +180 degrees.
1419      wrap: function () {
1420          return Earth.wrapLatLng(this);
1421      },
1422  
1423      // @method toBounds(sizeInMeters: Number): LatLngBounds
1424      // Returns a new `LatLngBounds` object in which each boundary is `sizeInMeters/2` meters apart from the `LatLng`.
1425      toBounds: function (sizeInMeters) {
1426          var latAccuracy = 180 * sizeInMeters / 40075017,
1427              lngAccuracy = latAccuracy / Math.cos((Math.PI / 180) * this.lat);
1428  
1429          return toLatLngBounds(
1430                  [this.lat - latAccuracy, this.lng - lngAccuracy],
1431                  [this.lat + latAccuracy, this.lng + lngAccuracy]);
1432      },
1433  
1434      clone: function () {
1435          return new LatLng(this.lat, this.lng, this.alt);
1436      }
1437  };
1438  
1439  
1440  
1441  // @factory L.latLng(latitude: Number, longitude: Number, altitude?: Number): LatLng
1442  // Creates an object representing a geographical point with the given latitude and longitude (and optionally altitude).
1443  
1444  // @alternative
1445  // @factory L.latLng(coords: Array): LatLng
1446  // Expects an array of the form `[Number, Number]` or `[Number, Number, Number]` instead.
1447  
1448  // @alternative
1449  // @factory L.latLng(coords: Object): LatLng
1450  // Expects an plain object of the form `{lat: Number, lng: Number}` or `{lat: Number, lng: Number, alt: Number}` instead.
1451  
1452  function toLatLng(a, b, c) {
1453      if (a instanceof LatLng) {
1454          return a;
1455      }
1456      if (isArray(a) && typeof a[0] !== 'object') {
1457          if (a.length === 3) {
1458              return new LatLng(a[0], a[1], a[2]);
1459          }
1460          if (a.length === 2) {
1461              return new LatLng(a[0], a[1]);
1462          }
1463          return null;
1464      }
1465      if (a === undefined || a === null) {
1466          return a;
1467      }
1468      if (typeof a === 'object' && 'lat' in a) {
1469          return new LatLng(a.lat, 'lng' in a ? a.lng : a.lon, a.alt);
1470      }
1471      if (b === undefined) {
1472          return null;
1473      }
1474      return new LatLng(a, b, c);
1475  }
1476  
1477  /*
1478   * @namespace CRS
1479   * @crs L.CRS.Base
1480   * Object that defines coordinate reference systems for projecting
1481   * geographical points into pixel (screen) coordinates and back (and to
1482   * coordinates in other units for [WMS](https://en.wikipedia.org/wiki/Web_Map_Service) services). See
1483   * [spatial reference system](http://en.wikipedia.org/wiki/Coordinate_reference_system).
1484   *
1485   * Leaflet defines the most usual CRSs by default. If you want to use a
1486   * CRS not defined by default, take a look at the
1487   * [Proj4Leaflet](https://github.com/kartena/Proj4Leaflet) plugin.
1488   *
1489   * Note that the CRS instances do not inherit from Leafet's `Class` object,
1490   * and can't be instantiated. Also, new classes can't inherit from them,
1491   * and methods can't be added to them with the `include` function.
1492   */
1493  
1494  var CRS = {
1495      // @method latLngToPoint(latlng: LatLng, zoom: Number): Point
1496      // Projects geographical coordinates into pixel coordinates for a given zoom.
1497      latLngToPoint: function (latlng, zoom) {
1498          var projectedPoint = this.projection.project(latlng),
1499              scale = this.scale(zoom);
1500  
1501          return this.transformation._transform(projectedPoint, scale);
1502      },
1503  
1504      // @method pointToLatLng(point: Point, zoom: Number): LatLng
1505      // The inverse of `latLngToPoint`. Projects pixel coordinates on a given
1506      // zoom into geographical coordinates.
1507      pointToLatLng: function (point, zoom) {
1508          var scale = this.scale(zoom),
1509              untransformedPoint = this.transformation.untransform(point, scale);
1510  
1511          return this.projection.unproject(untransformedPoint);
1512      },
1513  
1514      // @method project(latlng: LatLng): Point
1515      // Projects geographical coordinates into coordinates in units accepted for
1516      // this CRS (e.g. meters for EPSG:3857, for passing it to WMS services).
1517      project: function (latlng) {
1518          return this.projection.project(latlng);
1519      },
1520  
1521      // @method unproject(point: Point): LatLng
1522      // Given a projected coordinate returns the corresponding LatLng.
1523      // The inverse of `project`.
1524      unproject: function (point) {
1525          return this.projection.unproject(point);
1526      },
1527  
1528      // @method scale(zoom: Number): Number
1529      // Returns the scale used when transforming projected coordinates into
1530      // pixel coordinates for a particular zoom. For example, it returns
1531      // `256 * 2^zoom` for Mercator-based CRS.
1532      scale: function (zoom) {
1533          return 256 * Math.pow(2, zoom);
1534      },
1535  
1536      // @method zoom(scale: Number): Number
1537      // Inverse of `scale()`, returns the zoom level corresponding to a scale
1538      // factor of `scale`.
1539      zoom: function (scale) {
1540          return Math.log(scale / 256) / Math.LN2;
1541      },
1542  
1543      // @method getProjectedBounds(zoom: Number): Bounds
1544      // Returns the projection's bounds scaled and transformed for the provided `zoom`.
1545      getProjectedBounds: function (zoom) {
1546          if (this.infinite) { return null; }
1547  
1548          var b = this.projection.bounds,
1549              s = this.scale(zoom),
1550              min = this.transformation.transform(b.min, s),
1551              max = this.transformation.transform(b.max, s);
1552  
1553          return new Bounds(min, max);
1554      },
1555  
1556      // @method distance(latlng1: LatLng, latlng2: LatLng): Number
1557      // Returns the distance between two geographical coordinates.
1558  
1559      // @property code: String
1560      // Standard code name of the CRS passed into WMS services (e.g. `'EPSG:3857'`)
1561      //
1562      // @property wrapLng: Number[]
1563      // An array of two numbers defining whether the longitude (horizontal) coordinate
1564      // axis wraps around a given range and how. Defaults to `[-180, 180]` in most
1565      // geographical CRSs. If `undefined`, the longitude axis does not wrap around.
1566      //
1567      // @property wrapLat: Number[]
1568      // Like `wrapLng`, but for the latitude (vertical) axis.
1569  
1570      // wrapLng: [min, max],
1571      // wrapLat: [min, max],
1572  
1573      // @property infinite: Boolean
1574      // If true, the coordinate space will be unbounded (infinite in both axes)
1575      infinite: false,
1576  
1577      // @method wrapLatLng(latlng: LatLng): LatLng
1578      // Returns a `LatLng` where lat and lng has been wrapped according to the
1579      // CRS's `wrapLat` and `wrapLng` properties, if they are outside the CRS's bounds.
1580      wrapLatLng: function (latlng) {
1581          var lng = this.wrapLng ? wrapNum(latlng.lng, this.wrapLng, true) : latlng.lng,
1582              lat = this.wrapLat ? wrapNum(latlng.lat, this.wrapLat, true) : latlng.lat,
1583              alt = latlng.alt;
1584  
1585          return new LatLng(lat, lng, alt);
1586      },
1587  
1588      // @method wrapLatLngBounds(bounds: LatLngBounds): LatLngBounds
1589      // Returns a `LatLngBounds` with the same size as the given one, ensuring
1590      // that its center is within the CRS's bounds.
1591      // Only accepts actual `L.LatLngBounds` instances, not arrays.
1592      wrapLatLngBounds: function (bounds) {
1593          var center = bounds.getCenter(),
1594              newCenter = this.wrapLatLng(center),
1595              latShift = center.lat - newCenter.lat,
1596              lngShift = center.lng - newCenter.lng;
1597  
1598          if (latShift === 0 && lngShift === 0) {
1599              return bounds;
1600          }
1601  
1602          var sw = bounds.getSouthWest(),
1603              ne = bounds.getNorthEast(),
1604              newSw = new LatLng(sw.lat - latShift, sw.lng - lngShift),
1605              newNe = new LatLng(ne.lat - latShift, ne.lng - lngShift);
1606  
1607          return new LatLngBounds(newSw, newNe);
1608      }
1609  };
1610  
1611  /*
1612   * @namespace CRS
1613   * @crs L.CRS.Earth
1614   *
1615   * Serves as the base for CRS that are global such that they cover the earth.
1616   * Can only be used as the base for other CRS and cannot be used directly,
1617   * since it does not have a `code`, `projection` or `transformation`. `distance()` returns
1618   * meters.
1619   */
1620  
1621  var Earth = extend({}, CRS, {
1622      wrapLng: [-180, 180],
1623  
1624      // Mean Earth Radius, as recommended for use by
1625      // the International Union of Geodesy and Geophysics,
1626      // see http://rosettacode.org/wiki/Haversine_formula
1627      R: 6371000,
1628  
1629      // distance between two geographical points using spherical law of cosines approximation
1630      distance: function (latlng1, latlng2) {
1631          var rad = Math.PI / 180,
1632              lat1 = latlng1.lat * rad,
1633              lat2 = latlng2.lat * rad,
1634              sinDLat = Math.sin((latlng2.lat - latlng1.lat) * rad / 2),
1635              sinDLon = Math.sin((latlng2.lng - latlng1.lng) * rad / 2),
1636              a = sinDLat * sinDLat + Math.cos(lat1) * Math.cos(lat2) * sinDLon * sinDLon,
1637              c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
1638          return this.R * c;
1639      }
1640  });
1641  
1642  /*
1643   * @namespace Projection
1644   * @projection L.Projection.SphericalMercator
1645   *
1646   * Spherical Mercator projection — the most common projection for online maps,
1647   * used by almost all free and commercial tile providers. Assumes that Earth is
1648   * a sphere. Used by the `EPSG:3857` CRS.
1649   */
1650  
1651  var earthRadius = 6378137;
1652  
1653  var SphericalMercator = {
1654  
1655      R: earthRadius,
1656      MAX_LATITUDE: 85.0511287798,
1657  
1658      project: function (latlng) {
1659          var d = Math.PI / 180,
1660              max = this.MAX_LATITUDE,
1661              lat = Math.max(Math.min(max, latlng.lat), -max),
1662              sin = Math.sin(lat * d);
1663  
1664          return new Point(
1665              this.R * latlng.lng * d,
1666              this.R * Math.log((1 + sin) / (1 - sin)) / 2);
1667      },
1668  
1669      unproject: function (point) {
1670          var d = 180 / Math.PI;
1671  
1672          return new LatLng(
1673              (2 * Math.atan(Math.exp(point.y / this.R)) - (Math.PI / 2)) * d,
1674              point.x * d / this.R);
1675      },
1676  
1677      bounds: (function () {
1678          var d = earthRadius * Math.PI;
1679          return new Bounds([-d, -d], [d, d]);
1680      })()
1681  };
1682  
1683  /*
1684   * @class Transformation
1685   * @aka L.Transformation
1686   *
1687   * Represents an affine transformation: a set of coefficients `a`, `b`, `c`, `d`
1688   * for transforming a point of a form `(x, y)` into `(a*x + b, c*y + d)` and doing
1689   * the reverse. Used by Leaflet in its projections code.
1690   *
1691   * @example
1692   *
1693   * ```js
1694   * var transformation = L.transformation(2, 5, -1, 10),
1695   *     p = L.point(1, 2),
1696   *     p2 = transformation.transform(p), //  L.point(7, 8)
1697   *     p3 = transformation.untransform(p2); //  L.point(1, 2)
1698   * ```
1699   */
1700  
1701  
1702  // factory new L.Transformation(a: Number, b: Number, c: Number, d: Number)
1703  // Creates a `Transformation` object with the given coefficients.
1704  function Transformation(a, b, c, d) {
1705      if (isArray(a)) {
1706          // use array properties
1707          this._a = a[0];
1708          this._b = a[1];
1709          this._c = a[2];
1710          this._d = a[3];
1711          return;
1712      }
1713      this._a = a;
1714      this._b = b;
1715      this._c = c;
1716      this._d = d;
1717  }
1718  
1719  Transformation.prototype = {
1720      // @method transform(point: Point, scale?: Number): Point
1721      // Returns a transformed point, optionally multiplied by the given scale.
1722      // Only accepts actual `L.Point` instances, not arrays.
1723      transform: function (point, scale) { // (Point, Number) -> Point
1724          return this._transform(point.clone(), scale);
1725      },
1726  
1727      // destructive transform (faster)
1728      _transform: function (point, scale) {
1729          scale = scale || 1;
1730          point.x = scale * (this._a * point.x + this._b);
1731          point.y = scale * (this._c * point.y + this._d);
1732          return point;
1733      },
1734  
1735      // @method untransform(point: Point, scale?: Number): Point
1736      // Returns the reverse transformation of the given point, optionally divided
1737      // by the given scale. Only accepts actual `L.Point` instances, not arrays.
1738      untransform: function (point, scale) {
1739          scale = scale || 1;
1740          return new Point(
1741                  (point.x / scale - this._b) / this._a,
1742                  (point.y / scale - this._d) / this._c);
1743      }
1744  };
1745  
1746  // factory L.transformation(a: Number, b: Number, c: Number, d: Number)
1747  
1748  // @factory L.transformation(a: Number, b: Number, c: Number, d: Number)
1749  // Instantiates a Transformation object with the given coefficients.
1750  
1751  // @alternative
1752  // @factory L.transformation(coefficients: Array): Transformation
1753  // Expects an coefficients array of the form
1754  // `[a: Number, b: Number, c: Number, d: Number]`.
1755  
1756  function toTransformation(a, b, c, d) {
1757      return new Transformation(a, b, c, d);
1758  }
1759  
1760  /*
1761   * @namespace CRS
1762   * @crs L.CRS.EPSG3857
1763   *
1764   * The most common CRS for online maps, used by almost all free and commercial
1765   * tile providers. Uses Spherical Mercator projection. Set in by default in
1766   * Map's `crs` option.
1767   */
1768  
1769  var EPSG3857 = extend({}, Earth, {
1770      code: 'EPSG:3857',
1771      projection: SphericalMercator,
1772  
1773      transformation: (function () {
1774          var scale = 0.5 / (Math.PI * SphericalMercator.R);
1775          return toTransformation(scale, 0.5, -scale, 0.5);
1776      }())
1777  });
1778  
1779  var EPSG900913 = extend({}, EPSG3857, {
1780      code: 'EPSG:900913'
1781  });
1782  
1783  // @namespace SVG; @section
1784  // There are several static functions which can be called without instantiating L.SVG:
1785  
1786  // @function create(name: String): SVGElement
1787  // Returns a instance of [SVGElement](https://developer.mozilla.org/docs/Web/API/SVGElement),
1788  // corresponding to the class name passed. For example, using 'line' will return
1789  // an instance of [SVGLineElement](https://developer.mozilla.org/docs/Web/API/SVGLineElement).
1790  function svgCreate(name) {
1791      return document.createElementNS('http://www.w3.org/2000/svg', name);
1792  }
1793  
1794  // @function pointsToPath(rings: Point[], closed: Boolean): String
1795  // Generates a SVG path string for multiple rings, with each ring turning
1796  // into "M..L..L.." instructions
1797  function pointsToPath(rings, closed) {
1798      var str = '',
1799      i, j, len, len2, points, p;
1800  
1801      for (i = 0, len = rings.length; i < len; i++) {
1802          points = rings[i];
1803  
1804          for (j = 0, len2 = points.length; j < len2; j++) {
1805              p = points[j];
1806              str += (j ? 'L' : 'M') + p.x + ' ' + p.y;
1807          }
1808  
1809          // closes the ring for polygons; "x" is VML syntax
1810          str += closed ? (svg ? 'z' : 'x') : '';
1811      }
1812  
1813      // SVG complains about empty path strings
1814      return str || 'M0 0';
1815  }
1816  
1817  /*
1818   * @namespace Browser
1819   * @aka L.Browser
1820   *
1821   * A namespace with static properties for browser/feature detection used by Leaflet internally.
1822   *
1823   * @example
1824   *
1825   * ```js
1826   * if (L.Browser.ielt9) {
1827   *   alert('Upgrade your browser, dude!');
1828   * }
1829   * ```
1830   */
1831  
1832  var style$1 = document.documentElement.style;
1833  
1834  // @property ie: Boolean; `true` for all Internet Explorer versions (not Edge).
1835  var ie = 'ActiveXObject' in window;
1836  
1837  // @property ielt9: Boolean; `true` for Internet Explorer versions less than 9.
1838  var ielt9 = ie && !document.addEventListener;
1839  
1840  // @property edge: Boolean; `true` for the Edge web browser.
1841  var edge = 'msLaunchUri' in navigator && !('documentMode' in document);
1842  
1843  // @property webkit: Boolean;
1844  // `true` for webkit-based browsers like Chrome and Safari (including mobile versions).
1845  var webkit = userAgentContains('webkit');
1846  
1847  // @property android: Boolean
1848  // `true` for any browser running on an Android platform.
1849  var android = userAgentContains('android');
1850  
1851  // @property android23: Boolean; `true` for browsers running on Android 2 or Android 3.
1852  var android23 = userAgentContains('android 2') || userAgentContains('android 3');
1853  
1854  /* See https://stackoverflow.com/a/17961266 for details on detecting stock Android */
1855  var webkitVer = parseInt(/WebKit\/([0-9]+)|$/.exec(navigator.userAgent)[1], 10); // also matches AppleWebKit
1856  // @property androidStock: Boolean; `true` for the Android stock browser (i.e. not Chrome)
1857  var androidStock = android && userAgentContains('Google') && webkitVer < 537 && !('AudioNode' in window);
1858  
1859  // @property opera: Boolean; `true` for the Opera browser
1860  var opera = !!window.opera;
1861  
1862  // @property chrome: Boolean; `true` for the Chrome browser.
1863  var chrome = userAgentContains('chrome');
1864  
1865  // @property gecko: Boolean; `true` for gecko-based browsers like Firefox.
1866  var gecko = userAgentContains('gecko') && !webkit && !opera && !ie;
1867  
1868  // @property safari: Boolean; `true` for the Safari browser.
1869  var safari = !chrome && userAgentContains('safari');
1870  
1871  var phantom = userAgentContains('phantom');
1872  
1873  // @property opera12: Boolean
1874  // `true` for the Opera browser supporting CSS transforms (version 12 or later).
1875  var opera12 = 'OTransition' in style$1;
1876  
1877  // @property win: Boolean; `true` when the browser is running in a Windows platform
1878  var win = navigator.platform.indexOf('Win') === 0;
1879  
1880  // @property ie3d: Boolean; `true` for all Internet Explorer versions supporting CSS transforms.
1881  var ie3d = ie && ('transition' in style$1);
1882  
1883  // @property webkit3d: Boolean; `true` for webkit-based browsers supporting CSS transforms.
1884  var webkit3d = ('WebKitCSSMatrix' in window) && ('m11' in new window.WebKitCSSMatrix()) && !android23;
1885  
1886  // @property gecko3d: Boolean; `true` for gecko-based browsers supporting CSS transforms.
1887  var gecko3d = 'MozPerspective' in style$1;
1888  
1889  // @property any3d: Boolean
1890  // `true` for all browsers supporting CSS transforms.
1891  var any3d = !window.L_DISABLE_3D && (ie3d || webkit3d || gecko3d) && !opera12 && !phantom;
1892  
1893  // @property mobile: Boolean; `true` for all browsers running in a mobile device.
1894  var mobile = typeof orientation !== 'undefined' || userAgentContains('mobile');
1895  
1896  // @property mobileWebkit: Boolean; `true` for all webkit-based browsers in a mobile device.
1897  var mobileWebkit = mobile && webkit;
1898  
1899  // @property mobileWebkit3d: Boolean
1900  // `true` for all webkit-based browsers in a mobile device supporting CSS transforms.
1901  var mobileWebkit3d = mobile && webkit3d;
1902  
1903  // @property msPointer: Boolean
1904  // `true` for browsers implementing the Microsoft touch events model (notably IE10).
1905  var msPointer = !window.PointerEvent && window.MSPointerEvent;
1906  
1907  // @property pointer: Boolean
1908  // `true` for all browsers supporting [pointer events](https://msdn.microsoft.com/en-us/library/dn433244%28v=vs.85%29.aspx).
1909  var pointer = !webkit && !!(window.PointerEvent || msPointer);
1910  
1911  // @property touch: Boolean
1912  // `true` for all browsers supporting [touch events](https://developer.mozilla.org/docs/Web/API/Touch_events).
1913  // This does not necessarily mean that the browser is running in a computer with
1914  // a touchscreen, it only means that the browser is capable of understanding
1915  // touch events.
1916  var touch = !window.L_NO_TOUCH && (pointer || 'ontouchstart' in window ||
1917          (window.DocumentTouch && document instanceof window.DocumentTouch));
1918  
1919  // @property mobileOpera: Boolean; `true` for the Opera browser in a mobile device.
1920  var mobileOpera = mobile && opera;
1921  
1922  // @property mobileGecko: Boolean
1923  // `true` for gecko-based browsers running in a mobile device.
1924  var mobileGecko = mobile && gecko;
1925  
1926  // @property retina: Boolean
1927  // `true` for browsers on a high-resolution "retina" screen or on any screen when browser's display zoom is more than 100%.
1928  var retina = (window.devicePixelRatio || (window.screen.deviceXDPI / window.screen.logicalXDPI)) > 1;
1929  
1930  // @property passiveEvents: Boolean
1931  // `true` for browsers that support passive events.
1932  var passiveEvents = (function () {
1933      var supportsPassiveOption = false;
1934      try {
1935          var opts = Object.defineProperty({}, 'passive', {
1936              get: function () {
1937                  supportsPassiveOption = true;
1938              }
1939          });
1940          window.addEventListener('testPassiveEventSupport', falseFn, opts);
1941          window.removeEventListener('testPassiveEventSupport', falseFn, opts);
1942      } catch (e) {
1943          // Errors can safely be ignored since this is only a browser support test.
1944      }
1945      return supportsPassiveOption;
1946  });
1947  
1948  // @property canvas: Boolean
1949  // `true` when the browser supports [`<canvas>`](https://developer.mozilla.org/docs/Web/API/Canvas_API).
1950  var canvas = (function () {
1951      return !!document.createElement('canvas').getContext;
1952  }());
1953  
1954  // @property svg: Boolean
1955  // `true` when the browser supports [SVG](https://developer.mozilla.org/docs/Web/SVG).
1956  var svg = !!(document.createElementNS && svgCreate('svg').createSVGRect);
1957  
1958  // @property vml: Boolean
1959  // `true` if the browser supports [VML](https://en.wikipedia.org/wiki/Vector_Markup_Language).
1960  var vml = !svg && (function () {
1961      try {
1962          var div = document.createElement('div');
1963          div.innerHTML = '<v:shape adj="1"/>';
1964  
1965          var shape = div.firstChild;
1966          shape.style.behavior = 'url(#default#VML)';
1967  
1968          return shape && (typeof shape.adj === 'object');
1969  
1970      } catch (e) {
1971          return false;
1972      }
1973  }());
1974  
1975  
1976  function userAgentContains(str) {
1977      return navigator.userAgent.toLowerCase().indexOf(str) >= 0;
1978  }
1979  
1980  
1981  var Browser = (Object.freeze || Object)({
1982      ie: ie,
1983      ielt9: ielt9,
1984      edge: edge,
1985      webkit: webkit,
1986      android: android,
1987      android23: android23,
1988      androidStock: androidStock,
1989      opera: opera,
1990      chrome: chrome,
1991      gecko: gecko,
1992      safari: safari,
1993      phantom: phantom,
1994      opera12: opera12,
1995      win: win,
1996      ie3d: ie3d,
1997      webkit3d: webkit3d,
1998      gecko3d: gecko3d,
1999      any3d: any3d,
2000      mobile: mobile,
2001      mobileWebkit: mobileWebkit,
2002      mobileWebkit3d: mobileWebkit3d,
2003      msPointer: msPointer,
2004      pointer: pointer,
2005      touch: touch,
2006      mobileOpera: mobileOpera,
2007      mobileGecko: mobileGecko,
2008      retina: retina,
2009      passiveEvents: passiveEvents,
2010      canvas: canvas,
2011      svg: svg,
2012      vml: vml
2013  });
2014  
2015  /*
2016   * Extends L.DomEvent to provide touch support for Internet Explorer and Windows-based devices.
2017   */
2018  
2019  
2020  var POINTER_DOWN =   msPointer ? 'MSPointerDown'   : 'pointerdown';
2021  var POINTER_MOVE =   msPointer ? 'MSPointerMove'   : 'pointermove';
2022  var POINTER_UP =     msPointer ? 'MSPointerUp'     : 'pointerup';
2023  var POINTER_CANCEL = msPointer ? 'MSPointerCancel' : 'pointercancel';
2024  var TAG_WHITE_LIST = ['INPUT', 'SELECT', 'OPTION'];
2025  
2026  var _pointers = {};
2027  var _pointerDocListener = false;
2028  
2029  // DomEvent.DoubleTap needs to know about this
2030  var _pointersCount = 0;
2031  
2032  // Provides a touch events wrapper for (ms)pointer events.
2033  // ref http://www.w3.org/TR/pointerevents/ https://www.w3.org/Bugs/Public/show_bug.cgi?id=22890
2034  
2035  function addPointerListener(obj, type, handler, id) {
2036      if (type === 'touchstart') {
2037          _addPointerStart(obj, handler, id);
2038  
2039      } else if (type === 'touchmove') {
2040          _addPointerMove(obj, handler, id);
2041  
2042      } else if (type === 'touchend') {
2043          _addPointerEnd(obj, handler, id);
2044      }
2045  
2046      return this;
2047  }
2048  
2049  function removePointerListener(obj, type, id) {
2050      var handler = obj['_leaflet_' + type + id];
2051  
2052      if (type === 'touchstart') {
2053          obj.removeEventListener(POINTER_DOWN, handler, false);
2054  
2055      } else if (type === 'touchmove') {
2056          obj.removeEventListener(POINTER_MOVE, handler, false);
2057  
2058      } else if (type === 'touchend') {
2059          obj.removeEventListener(POINTER_UP, handler, false);
2060          obj.removeEventListener(POINTER_CANCEL, handler, false);
2061      }
2062  
2063      return this;
2064  }
2065  
2066  function _addPointerStart(obj, handler, id) {
2067      var onDown = bind(function (e) {
2068          if (e.pointerType !== 'mouse' && e.MSPOINTER_TYPE_MOUSE && e.pointerType !== e.MSPOINTER_TYPE_MOUSE) {
2069              // In IE11, some touch events needs to fire for form controls, or
2070              // the controls will stop working. We keep a whitelist of tag names that
2071              // need these events. For other target tags, we prevent default on the event.
2072              if (TAG_WHITE_LIST.indexOf(e.target.tagName) < 0) {
2073                  preventDefault(e);
2074              } else {
2075                  return;
2076              }
2077          }
2078  
2079          _handlePointer(e, handler);
2080      });
2081  
2082      obj['_leaflet_touchstart' + id] = onDown;
2083      obj.addEventListener(POINTER_DOWN, onDown, false);
2084  
2085      // need to keep track of what pointers and how many are active to provide e.touches emulation
2086      if (!_pointerDocListener) {
2087          // we listen documentElement as any drags that end by moving the touch off the screen get fired there
2088          document.documentElement.addEventListener(POINTER_DOWN, _globalPointerDown, true);
2089          document.documentElement.addEventListener(POINTER_MOVE, _globalPointerMove, true);
2090          document.documentElement.addEventListener(POINTER_UP, _globalPointerUp, true);
2091          document.documentElement.addEventListener(POINTER_CANCEL, _globalPointerUp, true);
2092  
2093          _pointerDocListener = true;
2094      }
2095  }
2096  
2097  function _globalPointerDown(e) {
2098      _pointers[e.pointerId] = e;
2099      _pointersCount++;
2100  }
2101  
2102  function _globalPointerMove(e) {
2103      if (_pointers[e.pointerId]) {
2104          _pointers[e.pointerId] = e;
2105      }
2106  }
2107  
2108  function _globalPointerUp(e) {
2109      delete _pointers[e.pointerId];
2110      _pointersCount--;
2111  }
2112  
2113  function _handlePointer(e, handler) {
2114      e.touches = [];
2115      for (var i in _pointers) {
2116          e.touches.push(_pointers[i]);
2117      }
2118      e.changedTouches = [e];
2119  
2120      handler(e);
2121  }
2122  
2123  function _addPointerMove(obj, handler, id) {
2124      var onMove = function (e) {
2125          // don't fire touch moves when mouse isn't down
2126          if ((e.pointerType === e.MSPOINTER_TYPE_MOUSE || e.pointerType === 'mouse') && e.buttons === 0) { return; }
2127  
2128          _handlePointer(e, handler);
2129      };
2130  
2131      obj['_leaflet_touchmove' + id] = onMove;
2132      obj.addEventListener(POINTER_MOVE, onMove, false);
2133  }
2134  
2135  function _addPointerEnd(obj, handler, id) {
2136      var onUp = function (e) {
2137          _handlePointer(e, handler);
2138      };
2139  
2140      obj['_leaflet_touchend' + id] = onUp;
2141      obj.addEventListener(POINTER_UP, onUp, false);
2142      obj.addEventListener(POINTER_CANCEL, onUp, false);
2143  }
2144  
2145  /*
2146   * Extends the event handling code with double tap support for mobile browsers.
2147   */
2148  
2149  var _touchstart = msPointer ? 'MSPointerDown' : pointer ? 'pointerdown' : 'touchstart';
2150  var _touchend = msPointer ? 'MSPointerUp' : pointer ? 'pointerup' : 'touchend';
2151  var _pre = '_leaflet_';
2152  
2153  // inspired by Zepto touch code by Thomas Fuchs
2154  function addDoubleTapListener(obj, handler, id) {
2155      var last, touch$$1,
2156          doubleTap = false,
2157          delay = 250;
2158  
2159  	function onTouchStart(e) {
2160          var count;
2161  
2162          if (pointer) {
2163              if ((!edge) || e.pointerType === 'mouse') { return; }
2164              count = _pointersCount;
2165          } else {
2166              count = e.touches.length;
2167          }
2168  
2169          if (count > 1) { return; }
2170  
2171          var now = Date.now(),
2172              delta = now - (last || now);
2173  
2174          touch$$1 = e.touches ? e.touches[0] : e;
2175          doubleTap = (delta > 0 && delta <= delay);
2176          last = now;
2177      }
2178  
2179  	function onTouchEnd(e) {
2180          if (doubleTap && !touch$$1.cancelBubble) {
2181              if (pointer) {
2182                  if ((!edge) || e.pointerType === 'mouse') { return; }
2183                  // work around .type being readonly with MSPointer* events
2184                  var newTouch = {},
2185                      prop, i;
2186  
2187                  for (i in touch$$1) {
2188                      prop = touch$$1[i];
2189                      newTouch[i] = prop && prop.bind ? prop.bind(touch$$1) : prop;
2190                  }
2191                  touch$$1 = newTouch;
2192              }
2193              touch$$1.type = 'dblclick';
2194              touch$$1.button = 0;
2195              handler(touch$$1);
2196              last = null;
2197          }
2198      }
2199  
2200      obj[_pre + _touchstart + id] = onTouchStart;
2201      obj[_pre + _touchend + id] = onTouchEnd;
2202      obj[_pre + 'dblclick' + id] = handler;
2203  
2204      obj.addEventListener(_touchstart, onTouchStart, passiveEvents ? {passive: false} : false);
2205      obj.addEventListener(_touchend, onTouchEnd, passiveEvents ? {passive: false} : false);
2206  
2207      // On some platforms (notably, chrome<55 on win10 + touchscreen + mouse),
2208      // the browser doesn't fire touchend/pointerup events but does fire
2209      // native dblclicks. See #4127.
2210      // Edge 14 also fires native dblclicks, but only for pointerType mouse, see #5180.
2211      obj.addEventListener('dblclick', handler, false);
2212  
2213      return this;
2214  }
2215  
2216  function removeDoubleTapListener(obj, id) {
2217      var touchstart = obj[_pre + _touchstart + id],
2218          touchend = obj[_pre + _touchend + id],
2219          dblclick = obj[_pre + 'dblclick' + id];
2220  
2221      obj.removeEventListener(_touchstart, touchstart, passiveEvents ? {passive: false} : false);
2222      obj.removeEventListener(_touchend, touchend, passiveEvents ? {passive: false} : false);
2223      if (!edge) {
2224          obj.removeEventListener('dblclick', dblclick, false);
2225      }
2226  
2227      return this;
2228  }
2229  
2230  /*
2231   * @namespace DomUtil
2232   *
2233   * Utility functions to work with the [DOM](https://developer.mozilla.org/docs/Web/API/Document_Object_Model)
2234   * tree, used by Leaflet internally.
2235   *
2236   * Most functions expecting or returning a `HTMLElement` also work for
2237   * SVG elements. The only difference is that classes refer to CSS classes
2238   * in HTML and SVG classes in SVG.
2239   */
2240  
2241  
2242  // @property TRANSFORM: String
2243  // Vendor-prefixed transform style name (e.g. `'webkitTransform'` for WebKit).
2244  var TRANSFORM = testProp(
2245      ['transform', 'webkitTransform', 'OTransform', 'MozTransform', 'msTransform']);
2246  
2247  // webkitTransition comes first because some browser versions that drop vendor prefix don't do
2248  // the same for the transitionend event, in particular the Android 4.1 stock browser
2249  
2250  // @property TRANSITION: String
2251  // Vendor-prefixed transition style name.
2252  var TRANSITION = testProp(
2253      ['webkitTransition', 'transition', 'OTransition', 'MozTransition', 'msTransition']);
2254  
2255  // @property TRANSITION_END: String
2256  // Vendor-prefixed transitionend event name.
2257  var TRANSITION_END =
2258      TRANSITION === 'webkitTransition' || TRANSITION === 'OTransition' ? TRANSITION + 'End' : 'transitionend';
2259  
2260  
2261  // @function get(id: String|HTMLElement): HTMLElement
2262  // Returns an element given its DOM id, or returns the element itself
2263  // if it was passed directly.
2264  function get(id) {
2265      return typeof id === 'string' ? document.getElementById(id) : id;
2266  }
2267  
2268  // @function getStyle(el: HTMLElement, styleAttrib: String): String
2269  // Returns the value for a certain style attribute on an element,
2270  // including computed values or values set through CSS.
2271  function getStyle(el, style) {
2272      var value = el.style[style] || (el.currentStyle && el.currentStyle[style]);
2273  
2274      if ((!value || value === 'auto') && document.defaultView) {
2275          var css = document.defaultView.getComputedStyle(el, null);
2276          value = css ? css[style] : null;
2277      }
2278      return value === 'auto' ? null : value;
2279  }
2280  
2281  // @function create(tagName: String, className?: String, container?: HTMLElement): HTMLElement
2282  // Creates an HTML element with `tagName`, sets its class to `className`, and optionally appends it to `container` element.
2283  function create$1(tagName, className, container) {
2284      var el = document.createElement(tagName);
2285      el.className = className || '';
2286  
2287      if (container) {
2288          container.appendChild(el);
2289      }
2290      return el;
2291  }
2292  
2293  // @function remove(el: HTMLElement)
2294  // Removes `el` from its parent element
2295  function remove(el) {
2296      var parent = el.parentNode;
2297      if (parent) {
2298          parent.removeChild(el);
2299      }
2300  }
2301  
2302  // @function empty(el: HTMLElement)
2303  // Removes all of `el`'s children elements from `el`
2304  function empty(el) {
2305      while (el.firstChild) {
2306          el.removeChild(el.firstChild);
2307      }
2308  }
2309  
2310  // @function toFront(el: HTMLElement)
2311  // Makes `el` the last child of its parent, so it renders in front of the other children.
2312  function toFront(el) {
2313      var parent = el.parentNode;
2314      if (parent && parent.lastChild !== el) {
2315          parent.appendChild(el);
2316      }
2317  }
2318  
2319  // @function toBack(el: HTMLElement)
2320  // Makes `el` the first child of its parent, so it renders behind the other children.
2321  function toBack(el) {
2322      var parent = el.parentNode;
2323      if (parent && parent.firstChild !== el) {
2324          parent.insertBefore(el, parent.firstChild);
2325      }
2326  }
2327  
2328  // @function hasClass(el: HTMLElement, name: String): Boolean
2329  // Returns `true` if the element's class attribute contains `name`.
2330  function hasClass(el, name) {
2331      if (el.classList !== undefined) {
2332          return el.classList.contains(name);
2333      }
2334      var className = getClass(el);
2335      return className.length > 0 && new RegExp('(^|\\s)' + name + '(\\s|$)').test(className);
2336  }
2337  
2338  // @function addClass(el: HTMLElement, name: String)
2339  // Adds `name` to the element's class attribute.
2340  function addClass(el, name) {
2341      if (el.classList !== undefined) {
2342          var classes = splitWords(name);
2343          for (var i = 0, len = classes.length; i < len; i++) {
2344              el.classList.add(classes[i]);
2345          }
2346      } else if (!hasClass(el, name)) {
2347          var className = getClass(el);
2348          setClass(el, (className ? className + ' ' : '') + name);
2349      }
2350  }
2351  
2352  // @function removeClass(el: HTMLElement, name: String)
2353  // Removes `name` from the element's class attribute.
2354  function removeClass(el, name) {
2355      if (el.classList !== undefined) {
2356          el.classList.remove(name);
2357      } else {
2358          setClass(el, trim((' ' + getClass(el) + ' ').replace(' ' + name + ' ', ' ')));
2359      }
2360  }
2361  
2362  // @function setClass(el: HTMLElement, name: String)
2363  // Sets the element's class.
2364  function setClass(el, name) {
2365      if (el.className.baseVal === undefined) {
2366          el.className = name;
2367      } else {
2368          // in case of SVG element
2369          el.className.baseVal = name;
2370      }
2371  }
2372  
2373  // @function getClass(el: HTMLElement): String
2374  // Returns the element's class.
2375  function getClass(el) {
2376      // Check if the element is an SVGElementInstance and use the correspondingElement instead
2377      // (Required for linked SVG elements in IE11.)
2378      if (el.correspondingElement) {
2379          el = el.correspondingElement;
2380      }
2381      return el.className.baseVal === undefined ? el.className : el.className.baseVal;
2382  }
2383  
2384  // @function setOpacity(el: HTMLElement, opacity: Number)
2385  // Set the opacity of an element (including old IE support).
2386  // `opacity` must be a number from `0` to `1`.
2387  function setOpacity(el, value) {
2388      if ('opacity' in el.style) {
2389          el.style.opacity = value;
2390      } else if ('filter' in el.style) {
2391          _setOpacityIE(el, value);
2392      }
2393  }
2394  
2395  function _setOpacityIE(el, value) {
2396      var filter = false,
2397          filterName = 'DXImageTransform.Microsoft.Alpha';
2398  
2399      // filters collection throws an error if we try to retrieve a filter that doesn't exist
2400      try {
2401          filter = el.filters.item(filterName);
2402      } catch (e) {
2403          // don't set opacity to 1 if we haven't already set an opacity,
2404          // it isn't needed and breaks transparent pngs.
2405          if (value === 1) { return; }
2406      }
2407  
2408      value = Math.round(value * 100);
2409  
2410      if (filter) {
2411          filter.Enabled = (value !== 100);
2412          filter.Opacity = value;
2413      } else {
2414          el.style.filter += ' progid:' + filterName + '(opacity=' + value + ')';
2415      }
2416  }
2417  
2418  // @function testProp(props: String[]): String|false
2419  // Goes through the array of style names and returns the first name
2420  // that is a valid style name for an element. If no such name is found,
2421  // it returns false. Useful for vendor-prefixed styles like `transform`.
2422  function testProp(props) {
2423      var style = document.documentElement.style;
2424  
2425      for (var i = 0; i < props.length; i++) {
2426          if (props[i] in style) {
2427              return props[i];
2428          }
2429      }
2430      return false;
2431  }
2432  
2433  // @function setTransform(el: HTMLElement, offset: Point, scale?: Number)
2434  // Resets the 3D CSS transform of `el` so it is translated by `offset` pixels
2435  // and optionally scaled by `scale`. Does not have an effect if the
2436  // browser doesn't support 3D CSS transforms.
2437  function setTransform(el, offset, scale) {
2438      var pos = offset || new Point(0, 0);
2439  
2440      el.style[TRANSFORM] =
2441          (ie3d ?
2442              'translate(' + pos.x + 'px,' + pos.y + 'px)' :
2443              'translate3d(' + pos.x + 'px,' + pos.y + 'px,0)') +
2444          (scale ? ' scale(' + scale + ')' : '');
2445  }
2446  
2447  // @function setPosition(el: HTMLElement, position: Point)
2448  // Sets the position of `el` to coordinates specified by `position`,
2449  // using CSS translate or top/left positioning depending on the browser
2450  // (used by Leaflet internally to position its layers).
2451  function setPosition(el, point) {
2452  
2453      /*eslint-disable */
2454      el._leaflet_pos = point;
2455      /* eslint-enable */
2456  
2457      if (any3d) {
2458          setTransform(el, point);
2459      } else {
2460          el.style.left = point.x + 'px';
2461          el.style.top = point.y + 'px';
2462      }
2463  }
2464  
2465  // @function getPosition(el: HTMLElement): Point
2466  // Returns the coordinates of an element previously positioned with setPosition.
2467  function getPosition(el) {
2468      // this method is only used for elements previously positioned using setPosition,
2469      // so it's safe to cache the position for performance
2470  
2471      return el._leaflet_pos || new Point(0, 0);
2472  }
2473  
2474  // @function disableTextSelection()
2475  // Prevents the user from generating `selectstart` DOM events, usually generated
2476  // when the user drags the mouse through a page with text. Used internally
2477  // by Leaflet to override the behaviour of any click-and-drag interaction on
2478  // the map. Affects drag interactions on the whole document.
2479  
2480  // @function enableTextSelection()
2481  // Cancels the effects of a previous [`L.DomUtil.disableTextSelection`](#domutil-disabletextselection).
2482  var disableTextSelection;
2483  var enableTextSelection;
2484  var _userSelect;
2485  if ('onselectstart' in document) {
2486      disableTextSelection = function () {
2487          on(window, 'selectstart', preventDefault);
2488      };
2489      enableTextSelection = function () {
2490          off(window, 'selectstart', preventDefault);
2491      };
2492  } else {
2493      var userSelectProperty = testProp(
2494          ['userSelect', 'WebkitUserSelect', 'OUserSelect', 'MozUserSelect', 'msUserSelect']);
2495  
2496      disableTextSelection = function () {
2497          if (userSelectProperty) {
2498              var style = document.documentElement.style;
2499              _userSelect = style[userSelectProperty];
2500              style[userSelectProperty] = 'none';
2501          }
2502      };
2503      enableTextSelection = function () {
2504          if (userSelectProperty) {
2505              document.documentElement.style[userSelectProperty] = _userSelect;
2506              _userSelect = undefined;
2507          }
2508      };
2509  }
2510  
2511  // @function disableImageDrag()
2512  // As [`L.DomUtil.disableTextSelection`](#domutil-disabletextselection), but
2513  // for `dragstart` DOM events, usually generated when the user drags an image.
2514  function disableImageDrag() {
2515      on(window, 'dragstart', preventDefault);
2516  }
2517  
2518  // @function enableImageDrag()
2519  // Cancels the effects of a previous [`L.DomUtil.disableImageDrag`](#domutil-disabletextselection).
2520  function enableImageDrag() {
2521      off(window, 'dragstart', preventDefault);
2522  }
2523  
2524  var _outlineElement;
2525  var _outlineStyle;
2526  // @function preventOutline(el: HTMLElement)
2527  // Makes the [outline](https://developer.mozilla.org/docs/Web/CSS/outline)
2528  // of the element `el` invisible. Used internally by Leaflet to prevent
2529  // focusable elements from displaying an outline when the user performs a
2530  // drag interaction on them.
2531  function preventOutline(element) {
2532      while (element.tabIndex === -1) {
2533          element = element.parentNode;
2534      }
2535      if (!element.style) { return; }
2536      restoreOutline();
2537      _outlineElement = element;
2538      _outlineStyle = element.style.outline;
2539      element.style.outline = 'none';
2540      on(window, 'keydown', restoreOutline);
2541  }
2542  
2543  // @function restoreOutline()
2544  // Cancels the effects of a previous [`L.DomUtil.preventOutline`]().
2545  function restoreOutline() {
2546      if (!_outlineElement) { return; }
2547      _outlineElement.style.outline = _outlineStyle;
2548      _outlineElement = undefined;
2549      _outlineStyle = undefined;
2550      off(window, 'keydown', restoreOutline);
2551  }
2552  
2553  // @function getSizedParentNode(el: HTMLElement): HTMLElement
2554  // Finds the closest parent node which size (width and height) is not null.
2555  function getSizedParentNode(element) {
2556      do {
2557          element = element.parentNode;
2558      } while ((!element.offsetWidth || !element.offsetHeight) && element !== document.body);
2559      return element;
2560  }
2561  
2562  // @function getScale(el: HTMLElement): Object
2563  // Computes the CSS scale currently applied on the element.
2564  // Returns an object with `x` and `y` members as horizontal and vertical scales respectively,
2565  // and `boundingClientRect` as the result of [`getBoundingClientRect()`](https://developer.mozilla.org/en-US/docs/Web/API/Element/getBoundingClientRect).
2566  function getScale(element) {
2567      var rect = element.getBoundingClientRect(); // Read-only in old browsers.
2568  
2569      return {
2570          x: rect.width / element.offsetWidth || 1,
2571          y: rect.height / element.offsetHeight || 1,
2572          boundingClientRect: rect
2573      };
2574  }
2575  
2576  
2577  var DomUtil = (Object.freeze || Object)({
2578      TRANSFORM: TRANSFORM,
2579      TRANSITION: TRANSITION,
2580      TRANSITION_END: TRANSITION_END,
2581      get: get,
2582      getStyle: getStyle,
2583      create: create$1,
2584      remove: remove,
2585      empty: empty,
2586      toFront: toFront,
2587      toBack: toBack,
2588      hasClass: hasClass,
2589      addClass: addClass,
2590      removeClass: removeClass,
2591      setClass: setClass,
2592      getClass: getClass,
2593      setOpacity: setOpacity,
2594      testProp: testProp,
2595      setTransform: setTransform,
2596      setPosition: setPosition,
2597      getPosition: getPosition,
2598      disableTextSelection: disableTextSelection,
2599      enableTextSelection: enableTextSelection,
2600      disableImageDrag: disableImageDrag,
2601      enableImageDrag: enableImageDrag,
2602      preventOutline: preventOutline,
2603      restoreOutline: restoreOutline,
2604      getSizedParentNode: getSizedParentNode,
2605      getScale: getScale
2606  });
2607  
2608  /*
2609   * @namespace DomEvent
2610   * Utility functions to work with the [DOM events](https://developer.mozilla.org/docs/Web/API/Event), used by Leaflet internally.
2611   */
2612  
2613  // Inspired by John Resig, Dean Edwards and YUI addEvent implementations.
2614  
2615  // @function on(el: HTMLElement, types: String, fn: Function, context?: Object): this
2616  // Adds a listener function (`fn`) to a particular DOM event type of the
2617  // element `el`. You can optionally specify the context of the listener
2618  // (object the `this` keyword will point to). You can also pass several
2619  // space-separated types (e.g. `'click dblclick'`).
2620  
2621  // @alternative
2622  // @function on(el: HTMLElement, eventMap: Object, context?: Object): this
2623  // Adds a set of type/listener pairs, e.g. `{click: onClick, mousemove: onMouseMove}`
2624  function on(obj, types, fn, context) {
2625  
2626      if (typeof types === 'object') {
2627          for (var type in types) {
2628              addOne(obj, type, types[type], fn);
2629          }
2630      } else {
2631          types = splitWords(types);
2632  
2633          for (var i = 0, len = types.length; i < len; i++) {
2634              addOne(obj, types[i], fn, context);
2635          }
2636      }
2637  
2638      return this;
2639  }
2640  
2641  var eventsKey = '_leaflet_events';
2642  
2643  // @function off(el: HTMLElement, types: String, fn: Function, context?: Object): this
2644  // Removes a previously added listener function.
2645  // Note that if you passed a custom context to on, you must pass the same
2646  // context to `off` in order to remove the listener.
2647  
2648  // @alternative
2649  // @function off(el: HTMLElement, eventMap: Object, context?: Object): this
2650  // Removes a set of type/listener pairs, e.g. `{click: onClick, mousemove: onMouseMove}`
2651  function off(obj, types, fn, context) {
2652  
2653      if (typeof types === 'object') {
2654          for (var type in types) {
2655              removeOne(obj, type, types[type], fn);
2656          }
2657      } else if (types) {
2658          types = splitWords(types);
2659  
2660          for (var i = 0, len = types.length; i < len; i++) {
2661              removeOne(obj, types[i], fn, context);
2662          }
2663      } else {
2664          for (var j in obj[eventsKey]) {
2665              removeOne(obj, j, obj[eventsKey][j]);
2666          }
2667          delete obj[eventsKey];
2668      }
2669  
2670      return this;
2671  }
2672  
2673  function addOne(obj, type, fn, context) {
2674      var id = type + stamp(fn) + (context ? '_' + stamp(context) : '');
2675  
2676      if (obj[eventsKey] && obj[eventsKey][id]) { return this; }
2677  
2678      var handler = function (e) {
2679          return fn.call(context || obj, e || window.event);
2680      };
2681  
2682      var originalHandler = handler;
2683  
2684      if (pointer && type.indexOf('touch') === 0) {
2685          // Needs DomEvent.Pointer.js
2686          addPointerListener(obj, type, handler, id);
2687  
2688      } else if (touch && (type === 'dblclick') && addDoubleTapListener &&
2689                 !(pointer && chrome)) {
2690          // Chrome >55 does not need the synthetic dblclicks from addDoubleTapListener
2691          // See #5180
2692          addDoubleTapListener(obj, handler, id);
2693  
2694      } else if ('addEventListener' in obj) {
2695  
2696          if (type === 'mousewheel') {
2697              obj.addEventListener('onwheel' in obj ? 'wheel' : 'mousewheel', handler, passiveEvents ? {passive: false} : false);
2698  
2699          } else if ((type === 'mouseenter') || (type === 'mouseleave')) {
2700              handler = function (e) {
2701                  e = e || window.event;
2702                  if (isExternalTarget(obj, e)) {
2703                      originalHandler(e);
2704                  }
2705              };
2706              obj.addEventListener(type === 'mouseenter' ? 'mouseover' : 'mouseout', handler, false);
2707  
2708          } else {
2709              if (type === 'click' && android) {
2710                  handler = function (e) {
2711                      filterClick(e, originalHandler);
2712                  };
2713              }
2714              obj.addEventListener(type, handler, false);
2715          }
2716  
2717      } else if ('attachEvent' in obj) {
2718          obj.attachEvent('on' + type, handler);
2719      }
2720  
2721      obj[eventsKey] = obj[eventsKey] || {};
2722      obj[eventsKey][id] = handler;
2723  }
2724  
2725  function removeOne(obj, type, fn, context) {
2726  
2727      var id = type + stamp(fn) + (context ? '_' + stamp(context) : ''),
2728          handler = obj[eventsKey] && obj[eventsKey][id];
2729  
2730      if (!handler) { return this; }
2731  
2732      if (pointer && type.indexOf('touch') === 0) {
2733          removePointerListener(obj, type, id);
2734  
2735      } else if (touch && (type === 'dblclick') && removeDoubleTapListener &&
2736                 !(pointer && chrome)) {
2737          removeDoubleTapListener(obj, id);
2738  
2739      } else if ('removeEventListener' in obj) {
2740  
2741          if (type === 'mousewheel') {
2742              obj.removeEventListener('onwheel' in obj ? 'wheel' : 'mousewheel', handler, passiveEvents ? {passive: false} : false);
2743  
2744          } else {
2745              obj.removeEventListener(
2746                  type === 'mouseenter' ? 'mouseover' :
2747                  type === 'mouseleave' ? 'mouseout' : type, handler, false);
2748          }
2749  
2750      } else if ('detachEvent' in obj) {
2751          obj.detachEvent('on' + type, handler);
2752      }
2753  
2754      obj[eventsKey][id] = null;
2755  }
2756  
2757  // @function stopPropagation(ev: DOMEvent): this
2758  // Stop the given event from propagation to parent elements. Used inside the listener functions:
2759  // ```js
2760  // L.DomEvent.on(div, 'click', function (ev) {
2761  //     L.DomEvent.stopPropagation(ev);
2762  // });
2763  // ```
2764  function stopPropagation(e) {
2765  
2766      if (e.stopPropagation) {
2767          e.stopPropagation();
2768      } else if (e.originalEvent) {  // In case of Leaflet event.
2769          e.originalEvent._stopped = true;
2770      } else {
2771          e.cancelBubble = true;
2772      }
2773      skipped(e);
2774  
2775      return this;
2776  }
2777  
2778  // @function disableScrollPropagation(el: HTMLElement): this
2779  // Adds `stopPropagation` to the element's `'mousewheel'` events (plus browser variants).
2780  function disableScrollPropagation(el) {
2781      addOne(el, 'mousewheel', stopPropagation);
2782      return this;
2783  }
2784  
2785  // @function disableClickPropagation(el: HTMLElement): this
2786  // Adds `stopPropagation` to the element's `'click'`, `'doubleclick'`,
2787  // `'mousedown'` and `'touchstart'` events (plus browser variants).
2788  function disableClickPropagation(el) {
2789      on(el, 'mousedown touchstart dblclick', stopPropagation);
2790      addOne(el, 'click', fakeStop);
2791      return this;
2792  }
2793  
2794  // @function preventDefault(ev: DOMEvent): this
2795  // Prevents the default action of the DOM Event `ev` from happening (such as
2796  // following a link in the href of the a element, or doing a POST request
2797  // with page reload when a `<form>` is submitted).
2798  // Use it inside listener functions.
2799  function preventDefault(e) {
2800      if (e.preventDefault) {
2801          e.preventDefault();
2802      } else {
2803          e.returnValue = false;
2804      }
2805      return this;
2806  }
2807  
2808  // @function stop(ev: DOMEvent): this
2809  // Does `stopPropagation` and `preventDefault` at the same time.
2810  function stop(e) {
2811      preventDefault(e);
2812      stopPropagation(e);
2813      return this;
2814  }
2815  
2816  // @function getMousePosition(ev: DOMEvent, container?: HTMLElement): Point
2817  // Gets normalized mouse position from a DOM event relative to the
2818  // `container` (border excluded) or to the whole page if not specified.
2819  function getMousePosition(e, container) {
2820      if (!container) {
2821          return new Point(e.clientX, e.clientY);
2822      }
2823  
2824      var scale = getScale(container),
2825          offset = scale.boundingClientRect; // left and top  values are in page scale (like the event clientX/Y)
2826  
2827      return new Point(
2828          // offset.left/top values are in page scale (like clientX/Y),
2829          // whereas clientLeft/Top (border width) values are the original values (before CSS scale applies).
2830          (e.clientX - offset.left) / scale.x - container.clientLeft,
2831          (e.clientY - offset.top) / scale.y - container.clientTop
2832      );
2833  }
2834  
2835  // Chrome on Win scrolls double the pixels as in other platforms (see #4538),
2836  // and Firefox scrolls device pixels, not CSS pixels
2837  var wheelPxFactor =
2838      (win && chrome) ? 2 * window.devicePixelRatio :
2839      gecko ? window.devicePixelRatio : 1;
2840  
2841  // @function getWheelDelta(ev: DOMEvent): Number
2842  // Gets normalized wheel delta from a mousewheel DOM event, in vertical
2843  // pixels scrolled (negative if scrolling down).
2844  // Events from pointing devices without precise scrolling are mapped to
2845  // a best guess of 60 pixels.
2846  function getWheelDelta(e) {
2847      return (edge) ? e.wheelDeltaY / 2 : // Don't trust window-geometry-based delta
2848             (e.deltaY && e.deltaMode === 0) ? -e.deltaY / wheelPxFactor : // Pixels
2849             (e.deltaY && e.deltaMode === 1) ? -e.deltaY * 20 : // Lines
2850             (e.deltaY && e.deltaMode === 2) ? -e.deltaY * 60 : // Pages
2851             (e.deltaX || e.deltaZ) ? 0 :    // Skip horizontal/depth wheel events
2852             e.wheelDelta ? (e.wheelDeltaY || e.wheelDelta) / 2 : // Legacy IE pixels
2853             (e.detail && Math.abs(e.detail) < 32765) ? -e.detail * 20 : // Legacy Moz lines
2854             e.detail ? e.detail / -32765 * 60 : // Legacy Moz pages
2855             0;
2856  }
2857  
2858  var skipEvents = {};
2859  
2860  function fakeStop(e) {
2861      // fakes stopPropagation by setting a special event flag, checked/reset with skipped(e)
2862      skipEvents[e.type] = true;
2863  }
2864  
2865  function skipped(e) {
2866      var events = skipEvents[e.type];
2867      // reset when checking, as it's only used in map container and propagates outside of the map
2868      skipEvents[e.type] = false;
2869      return events;
2870  }
2871  
2872  // check if element really left/entered the event target (for mouseenter/mouseleave)
2873  function isExternalTarget(el, e) {
2874  
2875      var related = e.relatedTarget;
2876  
2877      if (!related) { return true; }
2878  
2879      try {
2880          while (related && (related !== el)) {
2881              related = related.parentNode;
2882          }
2883      } catch (err) {
2884          return false;
2885      }
2886      return (related !== el);
2887  }
2888  
2889  var lastClick;
2890  
2891  // this is a horrible workaround for a bug in Android where a single touch triggers two click events
2892  function filterClick(e, handler) {
2893      var timeStamp = (e.timeStamp || (e.originalEvent && e.originalEvent.timeStamp)),
2894          elapsed = lastClick && (timeStamp - lastClick);
2895  
2896      // are they closer together than 500ms yet more than 100ms?
2897      // Android typically triggers them ~300ms apart while multiple listeners
2898      // on the same event should be triggered far faster;
2899      // or check if click is simulated on the element, and if it is, reject any non-simulated events
2900  
2901      if ((elapsed && elapsed > 100 && elapsed < 500) || (e.target._simulatedClick && !e._simulated)) {
2902          stop(e);
2903          return;
2904      }
2905      lastClick = timeStamp;
2906  
2907      handler(e);
2908  }
2909  
2910  
2911  
2912  
2913  var DomEvent = (Object.freeze || Object)({
2914      on: on,
2915      off: off,
2916      stopPropagation: stopPropagation,
2917      disableScrollPropagation: disableScrollPropagation,
2918      disableClickPropagation: disableClickPropagation,
2919      preventDefault: preventDefault,
2920      stop: stop,
2921      getMousePosition: getMousePosition,
2922      getWheelDelta: getWheelDelta,
2923      fakeStop: fakeStop,
2924      skipped: skipped,
2925      isExternalTarget: isExternalTarget,
2926      addListener: on,
2927      removeListener: off
2928  });
2929  
2930  /*
2931   * @class PosAnimation
2932   * @aka L.PosAnimation
2933   * @inherits Evented
2934   * Used internally for panning animations, utilizing CSS3 Transitions for modern browsers and a timer fallback for IE6-9.
2935   *
2936   * @example
2937   * ```js
2938   * var fx = new L.PosAnimation();
2939   * fx.run(el, [300, 500], 0.5);
2940   * ```
2941   *
2942   * @constructor L.PosAnimation()
2943   * Creates a `PosAnimation` object.
2944   *
2945   */
2946  
2947  var PosAnimation = Evented.extend({
2948  
2949      // @method run(el: HTMLElement, newPos: Point, duration?: Number, easeLinearity?: Number)
2950      // Run an animation of a given element to a new position, optionally setting
2951      // duration in seconds (`0.25` by default) and easing linearity factor (3rd
2952      // argument of the [cubic bezier curve](http://cubic-bezier.com/#0,0,.5,1),
2953      // `0.5` by default).
2954      run: function (el, newPos, duration, easeLinearity) {
2955          this.stop();
2956  
2957          this._el = el;
2958          this._inProgress = true;
2959          this._duration = duration || 0.25;
2960          this._easeOutPower = 1 / Math.max(easeLinearity || 0.5, 0.2);
2961  
2962          this._startPos = getPosition(el);
2963          this._offset = newPos.subtract(this._startPos);
2964          this._startTime = +new Date();
2965  
2966          // @event start: Event
2967          // Fired when the animation starts
2968          this.fire('start');
2969  
2970          this._animate();
2971      },
2972  
2973      // @method stop()
2974      // Stops the animation (if currently running).
2975      stop: function () {
2976          if (!this._inProgress) { return; }
2977  
2978          this._step(true);
2979          this._complete();
2980      },
2981  
2982      _animate: function () {
2983          // animation loop
2984          this._animId = requestAnimFrame(this._animate, this);
2985          this._step();
2986      },
2987  
2988      _step: function (round) {
2989          var elapsed = (+new Date()) - this._startTime,
2990              duration = this._duration * 1000;
2991  
2992          if (elapsed < duration) {
2993              this._runFrame(this._easeOut(elapsed / duration), round);
2994          } else {
2995              this._runFrame(1);
2996              this._complete();
2997          }
2998      },
2999  
3000      _runFrame: function (progress, round) {
3001          var pos = this._startPos.add(this._offset.multiplyBy(progress));
3002          if (round) {
3003              pos._round();
3004          }
3005          setPosition(this._el, pos);
3006  
3007          // @event step: Event
3008          // Fired continuously during the animation.
3009          this.fire('step');
3010      },
3011  
3012      _complete: function () {
3013          cancelAnimFrame(this._animId);
3014  
3015          this._inProgress = false;
3016          // @event end: Event
3017          // Fired when the animation ends.
3018          this.fire('end');
3019      },
3020  
3021      _easeOut: function (t) {
3022          return 1 - Math.pow(1 - t, this._easeOutPower);
3023      }
3024  });
3025  
3026  /*
3027   * @class Map
3028   * @aka L.Map
3029   * @inherits Evented
3030   *
3031   * The central class of the API — it is used to create a map on a page and manipulate it.
3032   *
3033   * @example
3034   *
3035   * ```js
3036   * // initialize the map on the "map" div with a given center and zoom
3037   * var map = L.map('map', {
3038   *     center: [51.505, -0.09],
3039   *     zoom: 13
3040   * });
3041   * ```
3042   *
3043   */
3044  
3045  var Map = Evented.extend({
3046  
3047      options: {
3048          // @section Map State Options
3049          // @option crs: CRS = L.CRS.EPSG3857
3050          // The [Coordinate Reference System](#crs) to use. Don't change this if you're not
3051          // sure what it means.
3052          crs: EPSG3857,
3053  
3054          // @option center: LatLng = undefined
3055          // Initial geographic center of the map
3056          center: undefined,
3057  
3058          // @option zoom: Number = undefined
3059          // Initial map zoom level
3060          zoom: undefined,
3061  
3062          // @option minZoom: Number = *
3063          // Minimum zoom level of the map.
3064          // If not specified and at least one `GridLayer` or `TileLayer` is in the map,
3065          // the lowest of their `minZoom` options will be used instead.
3066          minZoom: undefined,
3067  
3068          // @option maxZoom: Number = *
3069          // Maximum zoom level of the map.
3070          // If not specified and at least one `GridLayer` or `TileLayer` is in the map,
3071          // the highest of their `maxZoom` options will be used instead.
3072          maxZoom: undefined,
3073  
3074          // @option layers: Layer[] = []
3075          // Array of layers that will be added to the map initially
3076          layers: [],
3077  
3078          // @option maxBounds: LatLngBounds = null
3079          // When this option is set, the map restricts the view to the given
3080          // geographical bounds, bouncing the user back if the user tries to pan
3081          // outside the view. To set the restriction dynamically, use
3082          // [`setMaxBounds`](#map-setmaxbounds) method.
3083          maxBounds: undefined,
3084  
3085          // @option renderer: Renderer = *
3086          // The default method for drawing vector layers on the map. `L.SVG`
3087          // or `L.Canvas` by default depending on browser support.
3088          renderer: undefined,
3089  
3090  
3091          // @section Animation Options
3092          // @option zoomAnimation: Boolean = true
3093          // Whether the map zoom animation is enabled. By default it's enabled
3094          // in all browsers that support CSS3 Transitions except Android.
3095          zoomAnimation: true,
3096  
3097          // @option zoomAnimationThreshold: Number = 4
3098          // Won't animate zoom if the zoom difference exceeds this value.
3099          zoomAnimationThreshold: 4,
3100  
3101          // @option fadeAnimation: Boolean = true
3102          // Whether the tile fade animation is enabled. By default it's enabled
3103          // in all browsers that support CSS3 Transitions except Android.
3104          fadeAnimation: true,
3105  
3106          // @option markerZoomAnimation: Boolean = true
3107          // Whether markers animate their zoom with the zoom animation, if disabled
3108          // they will disappear for the length of the animation. By default it's
3109          // enabled in all browsers that support CSS3 Transitions except Android.
3110          markerZoomAnimation: true,
3111  
3112          // @option transform3DLimit: Number = 2^23
3113          // Defines the maximum size of a CSS translation transform. The default
3114          // value should not be changed unless a web browser positions layers in
3115          // the wrong place after doing a large `panBy`.
3116          transform3DLimit: 8388608, // Precision limit of a 32-bit float
3117  
3118          // @section Interaction Options
3119          // @option zoomSnap: Number = 1
3120          // Forces the map's zoom level to always be a multiple of this, particularly
3121          // right after a [`fitBounds()`](#map-fitbounds) or a pinch-zoom.
3122          // By default, the zoom level snaps to the nearest integer; lower values
3123          // (e.g. `0.5` or `0.1`) allow for greater granularity. A value of `0`
3124          // means the zoom level will not be snapped after `fitBounds` or a pinch-zoom.
3125          zoomSnap: 1,
3126  
3127          // @option zoomDelta: Number = 1
3128          // Controls how much the map's zoom level will change after a
3129          // [`zoomIn()`](#map-zoomin), [`zoomOut()`](#map-zoomout), pressing `+`
3130          // or `-` on the keyboard, or using the [zoom controls](#control-zoom).
3131          // Values smaller than `1` (e.g. `0.5`) allow for greater granularity.
3132          zoomDelta: 1,
3133  
3134          // @option trackResize: Boolean = true
3135          // Whether the map automatically handles browser window resize to update itself.
3136          trackResize: true
3137      },
3138  
3139      initialize: function (id, options) { // (HTMLElement or String, Object)
3140          options = setOptions(this, options);
3141  
3142          // Make sure to assign internal flags at the beginning,
3143          // to avoid inconsistent state in some edge cases.
3144          this._handlers = [];
3145          this._layers = {};
3146          this._zoomBoundLayers = {};
3147          this._sizeChanged = true;
3148  
3149          this._initContainer(id);
3150          this._initLayout();
3151  
3152          // hack for https://github.com/Leaflet/Leaflet/issues/1980
3153          this._onResize = bind(this._onResize, this);
3154  
3155          this._initEvents();
3156  
3157          if (options.maxBounds) {
3158              this.setMaxBounds(options.maxBounds);
3159          }
3160  
3161          if (options.zoom !== undefined) {
3162              this._zoom = this._limitZoom(options.zoom);
3163          }
3164  
3165          if (options.center && options.zoom !== undefined) {
3166              this.setView(toLatLng(options.center), options.zoom, {reset: true});
3167          }
3168  
3169          this.callInitHooks();
3170  
3171          // don't animate on browsers without hardware-accelerated transitions or old Android/Opera
3172          this._zoomAnimated = TRANSITION && any3d && !mobileOpera &&
3173                  this.options.zoomAnimation;
3174  
3175          // zoom transitions run with the same duration for all layers, so if one of transitionend events
3176          // happens after starting zoom animation (propagating to the map pane), we know that it ended globally
3177          if (this._zoomAnimated) {
3178              this._createAnimProxy();
3179              on(this._proxy, TRANSITION_END, this._catchTransitionEnd, this);
3180          }
3181  
3182          this._addLayers(this.options.layers);
3183      },
3184  
3185  
3186      // @section Methods for modifying map state
3187  
3188      // @method setView(center: LatLng, zoom: Number, options?: Zoom/pan options): this
3189      // Sets the view of the map (geographical center and zoom) with the given
3190      // animation options.
3191      setView: function (center, zoom, options) {
3192  
3193          zoom = zoom === undefined ? this._zoom : this._limitZoom(zoom);
3194          center = this._limitCenter(toLatLng(center), zoom, this.options.maxBounds);
3195          options = options || {};
3196  
3197          this._stop();
3198  
3199          if (this._loaded && !options.reset && options !== true) {
3200  
3201              if (options.animate !== undefined) {
3202                  options.zoom = extend({animate: options.animate}, options.zoom);
3203                  options.pan = extend({animate: options.animate, duration: options.duration}, options.pan);
3204              }
3205  
3206              // try animating pan or zoom
3207              var moved = (this._zoom !== zoom) ?
3208                  this._tryAnimatedZoom && this._tryAnimatedZoom(center, zoom, options.zoom) :
3209                  this._tryAnimatedPan(center, options.pan);
3210  
3211              if (moved) {
3212                  // prevent resize handler call, the view will refresh after animation anyway
3213                  clearTimeout(this._sizeTimer);
3214                  return this;
3215              }
3216          }
3217  
3218          // animation didn't start, just reset the map view
3219          this._resetView(center, zoom);
3220  
3221          return this;
3222      },
3223  
3224      // @method setZoom(zoom: Number, options?: Zoom/pan options): this
3225      // Sets the zoom of the map.
3226      setZoom: function (zoom, options) {
3227          if (!this._loaded) {
3228              this._zoom = zoom;
3229              return this;
3230          }
3231          return this.setView(this.getCenter(), zoom, {zoom: options});
3232      },
3233  
3234      // @method zoomIn(delta?: Number, options?: Zoom options): this
3235      // Increases the zoom of the map by `delta` ([`zoomDelta`](#map-zoomdelta) by default).
3236      zoomIn: function (delta, options) {
3237          delta = delta || (any3d ? this.options.zoomDelta : 1);
3238          return this.setZoom(this._zoom + delta, options);
3239      },
3240  
3241      // @method zoomOut(delta?: Number, options?: Zoom options): this
3242      // Decreases the zoom of the map by `delta` ([`zoomDelta`](#map-zoomdelta) by default).
3243      zoomOut: function (delta, options) {
3244          delta = delta || (any3d ? this.options.zoomDelta : 1);
3245          return this.setZoom(this._zoom - delta, options);
3246      },
3247  
3248      // @method setZoomAround(latlng: LatLng, zoom: Number, options: Zoom options): this
3249      // Zooms the map while keeping a specified geographical point on the map
3250      // stationary (e.g. used internally for scroll zoom and double-click zoom).
3251      // @alternative
3252      // @method setZoomAround(offset: Point, zoom: Number, options: Zoom options): this
3253      // Zooms the map while keeping a specified pixel on the map (relative to the top-left corner) stationary.
3254      setZoomAround: function (latlng, zoom, options) {
3255          var scale = this.getZoomScale(zoom),
3256              viewHalf = this.getSize().divideBy(2),
3257              containerPoint = latlng instanceof Point ? latlng : this.latLngToContainerPoint(latlng),
3258  
3259              centerOffset = containerPoint.subtract(viewHalf).multiplyBy(1 - 1 / scale),
3260              newCenter = this.containerPointToLatLng(viewHalf.add(centerOffset));
3261  
3262          return this.setView(newCenter, zoom, {zoom: options});
3263      },
3264  
3265      _getBoundsCenterZoom: function (bounds, options) {
3266  
3267          options = options || {};
3268          bounds = bounds.getBounds ? bounds.getBounds() : toLatLngBounds(bounds);
3269  
3270          var paddingTL = toPoint(options.paddingTopLeft || options.padding || [0, 0]),
3271              paddingBR = toPoint(options.paddingBottomRight || options.padding || [0, 0]),
3272  
3273              zoom = this.getBoundsZoom(bounds, false, paddingTL.add(paddingBR));
3274  
3275          zoom = (typeof options.maxZoom === 'number') ? Math.min(options.maxZoom, zoom) : zoom;
3276  
3277          if (zoom === Infinity) {
3278              return {
3279                  center: bounds.getCenter(),
3280                  zoom: zoom
3281              };
3282          }
3283  
3284          var paddingOffset = paddingBR.subtract(paddingTL).divideBy(2),
3285  
3286              swPoint = this.project(bounds.getSouthWest(), zoom),
3287              nePoint = this.project(bounds.getNorthEast(), zoom),
3288              center = this.unproject(swPoint.add(nePoint).divideBy(2).add(paddingOffset), zoom);
3289  
3290          return {
3291              center: center,
3292              zoom: zoom
3293          };
3294      },
3295  
3296      // @method fitBounds(bounds: LatLngBounds, options?: fitBounds options): this
3297      // Sets a map view that contains the given geographical bounds with the
3298      // maximum zoom level possible.
3299      fitBounds: function (bounds, options) {
3300  
3301          bounds = toLatLngBounds(bounds);
3302  
3303          if (!bounds.isValid()) {
3304              throw new Error('Bounds are not valid.');
3305          }
3306  
3307          var target = this._getBoundsCenterZoom(bounds, options);
3308          return this.setView(target.center, target.zoom, options);
3309      },
3310  
3311      // @method fitWorld(options?: fitBounds options): this
3312      // Sets a map view that mostly contains the whole world with the maximum
3313      // zoom level possible.
3314      fitWorld: function (options) {
3315          return this.fitBounds([[-90, -180], [90, 180]], options);
3316      },
3317  
3318      // @method panTo(latlng: LatLng, options?: Pan options): this
3319      // Pans the map to a given center.
3320      panTo: function (center, options) { // (LatLng)
3321          return this.setView(center, this._zoom, {pan: options});
3322      },
3323  
3324      // @method panBy(offset: Point, options?: Pan options): this
3325      // Pans the map by a given number of pixels (animated).
3326      panBy: function (offset, options) {
3327          offset = toPoint(offset).round();
3328          options = options || {};
3329  
3330          if (!offset.x && !offset.y) {
3331              return this.fire('moveend');
3332          }
3333          // If we pan too far, Chrome gets issues with tiles
3334          // and makes them disappear or appear in the wrong place (slightly offset) #2602
3335          if (options.animate !== true && !this.getSize().contains(offset)) {
3336              this._resetView(this.unproject(this.project(this.getCenter()).add(offset)), this.getZoom());
3337              return this;
3338          }
3339  
3340          if (!this._panAnim) {
3341              this._panAnim = new PosAnimation();
3342  
3343              this._panAnim.on({
3344                  'step': this._onPanTransitionStep,
3345                  'end': this._onPanTransitionEnd
3346              }, this);
3347          }
3348  
3349          // don't fire movestart if animating inertia
3350          if (!options.noMoveStart) {
3351              this.fire('movestart');
3352          }
3353  
3354          // animate pan unless animate: false specified
3355          if (options.animate !== false) {
3356              addClass(this._mapPane, 'leaflet-pan-anim');
3357  
3358              var newPos = this._getMapPanePos().subtract(offset).round();
3359              this._panAnim.run(this._mapPane, newPos, options.duration || 0.25, options.easeLinearity);
3360          } else {
3361              this._rawPanBy(offset);
3362              this.fire('move').fire('moveend');
3363          }
3364  
3365          return this;
3366      },
3367  
3368      // @method flyTo(latlng: LatLng, zoom?: Number, options?: Zoom/pan options): this
3369      // Sets the view of the map (geographical center and zoom) performing a smooth
3370      // pan-zoom animation.
3371      flyTo: function (targetCenter, targetZoom, options) {
3372  
3373          options = options || {};
3374          if (options.animate === false || !any3d) {
3375              return this.setView(targetCenter, targetZoom, options);
3376          }
3377  
3378          this._stop();
3379  
3380          var from = this.project(this.getCenter()),
3381              to = this.project(targetCenter),
3382              size = this.getSize(),
3383              startZoom = this._zoom;
3384  
3385          targetCenter = toLatLng(targetCenter);
3386          targetZoom = targetZoom === undefined ? startZoom : targetZoom;
3387  
3388          var w0 = Math.max(size.x, size.y),
3389              w1 = w0 * this.getZoomScale(startZoom, targetZoom),
3390              u1 = (to.distanceTo(from)) || 1,
3391              rho = 1.42,
3392              rho2 = rho * rho;
3393  
3394          function r(i) {
3395              var s1 = i ? -1 : 1,
3396                  s2 = i ? w1 : w0,
3397                  t1 = w1 * w1 - w0 * w0 + s1 * rho2 * rho2 * u1 * u1,
3398                  b1 = 2 * s2 * rho2 * u1,
3399                  b = t1 / b1,
3400                  sq = Math.sqrt(b * b + 1) - b;
3401  
3402                  // workaround for floating point precision bug when sq = 0, log = -Infinite,
3403                  // thus triggering an infinite loop in flyTo
3404                  var log = sq < 0.000000001 ? -18 : Math.log(sq);
3405  
3406              return log;
3407          }
3408  
3409  		function sinh(n) { return (Math.exp(n) - Math.exp(-n)) / 2; }
3410  		function cosh(n) { return (Math.exp(n) + Math.exp(-n)) / 2; }
3411  		function tanh(n) { return sinh(n) / cosh(n); }
3412  
3413          var r0 = r(0);
3414  
3415          function w(s) { return w0 * (cosh(r0) / cosh(r0 + rho * s)); }
3416          function u(s) { return w0 * (cosh(r0) * tanh(r0 + rho * s) - sinh(r0)) / rho2; }
3417  
3418  		function easeOut(t) { return 1 - Math.pow(1 - t, 1.5); }
3419  
3420          var start = Date.now(),
3421              S = (r(1) - r0) / rho,
3422              duration = options.duration ? 1000 * options.duration : 1000 * S * 0.8;
3423  
3424  		function frame() {
3425              var t = (Date.now() - start) / duration,
3426                  s = easeOut(t) * S;
3427  
3428              if (t <= 1) {
3429                  this._flyToFrame = requestAnimFrame(frame, this);
3430  
3431                  this._move(
3432                      this.unproject(from.add(to.subtract(from).multiplyBy(u(s) / u1)), startZoom),
3433                      this.getScaleZoom(w0 / w(s), startZoom),
3434                      {flyTo: true});
3435  
3436              } else {
3437                  this
3438                      ._move(targetCenter, targetZoom)
3439                      ._moveEnd(true);
3440              }
3441          }
3442  
3443          this._moveStart(true, options.noMoveStart);
3444  
3445          frame.call(this);
3446          return this;
3447      },
3448  
3449      // @method flyToBounds(bounds: LatLngBounds, options?: fitBounds options): this
3450      // Sets the view of the map with a smooth animation like [`flyTo`](#map-flyto),
3451      // but takes a bounds parameter like [`fitBounds`](#map-fitbounds).
3452      flyToBounds: function (bounds, options) {
3453          var target = this._getBoundsCenterZoom(bounds, options);
3454          return this.flyTo(target.center, target.zoom, options);
3455      },
3456  
3457      // @method setMaxBounds(bounds: Bounds): this
3458      // Restricts the map view to the given bounds (see the [maxBounds](#map-maxbounds) option).
3459      setMaxBounds: function (bounds) {
3460          bounds = toLatLngBounds(bounds);
3461  
3462          if (!bounds.isValid()) {
3463              this.options.maxBounds = null;
3464              return this.off('moveend', this._panInsideMaxBounds);
3465          } else if (this.options.maxBounds) {
3466              this.off('moveend', this._panInsideMaxBounds);
3467          }
3468  
3469          this.options.maxBounds = bounds;
3470  
3471          if (this._loaded) {
3472              this._panInsideMaxBounds();
3473          }
3474  
3475          return this.on('moveend', this._panInsideMaxBounds);
3476      },
3477  
3478      // @method setMinZoom(zoom: Number): this
3479      // Sets the lower limit for the available zoom levels (see the [minZoom](#map-minzoom) option).
3480      setMinZoom: function (zoom) {
3481          var oldZoom = this.options.minZoom;
3482          this.options.minZoom = zoom;
3483  
3484          if (this._loaded && oldZoom !== zoom) {
3485              this.fire('zoomlevelschange');
3486  
3487              if (this.getZoom() < this.options.minZoom) {
3488                  return this.setZoom(zoom);
3489              }
3490          }
3491  
3492          return this;
3493      },
3494  
3495      // @method setMaxZoom(zoom: Number): this
3496      // Sets the upper limit for the available zoom levels (see the [maxZoom](#map-maxzoom) option).
3497      setMaxZoom: function (zoom) {
3498          var oldZoom = this.options.maxZoom;
3499          this.options.maxZoom = zoom;
3500  
3501          if (this._loaded && oldZoom !== zoom) {
3502              this.fire('zoomlevelschange');
3503  
3504              if (this.getZoom() > this.options.maxZoom) {
3505                  return this.setZoom(zoom);
3506              }
3507          }
3508  
3509          return this;
3510      },
3511  
3512      // @method panInsideBounds(bounds: LatLngBounds, options?: Pan options): this
3513      // 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.
3514      panInsideBounds: function (bounds, options) {
3515          this._enforcingBounds = true;
3516          var center = this.getCenter(),
3517              newCenter = this._limitCenter(center, this._zoom, toLatLngBounds(bounds));
3518  
3519          if (!center.equals(newCenter)) {
3520              this.panTo(newCenter, options);
3521          }
3522  
3523          this._enforcingBounds = false;
3524          return this;
3525      },
3526  
3527      // @method panInside(latlng: LatLng, options?: options): this
3528      // Pans the map the minimum amount to make the `latlng` visible. Use
3529      // `padding`, `paddingTopLeft` and `paddingTopRight` options to fit
3530      // the display to more restricted bounds, like [`fitBounds`](#map-fitbounds).
3531      // If `latlng` is already within the (optionally padded) display bounds,
3532      // the map will not be panned.
3533      panInside: function (latlng, options) {
3534          options = options || {};
3535  
3536          var paddingTL = toPoint(options.paddingTopLeft || options.padding || [0, 0]),
3537              paddingBR = toPoint(options.paddingBottomRight || options.padding || [0, 0]),
3538              center = this.getCenter(),
3539              pixelCenter = this.project(center),
3540              pixelPoint = this.project(latlng),
3541              pixelBounds = this.getPixelBounds(),
3542              halfPixelBounds = pixelBounds.getSize().divideBy(2),
3543              paddedBounds = toBounds([pixelBounds.min.add(paddingTL), pixelBounds.max.subtract(paddingBR)]);
3544  
3545          if (!paddedBounds.contains(pixelPoint)) {
3546              this._enforcingBounds = true;
3547              var diff = pixelCenter.subtract(pixelPoint),
3548                  newCenter = toPoint(pixelPoint.x + diff.x, pixelPoint.y + diff.y);
3549  
3550              if (pixelPoint.x < paddedBounds.min.x || pixelPoint.x > paddedBounds.max.x) {
3551                  newCenter.x = pixelCenter.x - diff.x;
3552                  if (diff.x > 0) {
3553                      newCenter.x += halfPixelBounds.x - paddingTL.x;
3554                  } else {
3555                      newCenter.x -= halfPixelBounds.x - paddingBR.x;
3556                  }
3557              }
3558              if (pixelPoint.y < paddedBounds.min.y || pixelPoint.y > paddedBounds.max.y) {
3559                  newCenter.y = pixelCenter.y - diff.y;
3560                  if (diff.y > 0) {
3561                      newCenter.y += halfPixelBounds.y - paddingTL.y;
3562                  } else {
3563                      newCenter.y -= halfPixelBounds.y - paddingBR.y;
3564                  }
3565              }
3566              this.panTo(this.unproject(newCenter), options);
3567              this._enforcingBounds = false;
3568          }
3569          return this;
3570      },
3571  
3572      // @method invalidateSize(options: Zoom/pan options): this
3573      // Checks if the map container size changed and updates the map if so —
3574      // call it after you've changed the map size dynamically, also animating
3575      // pan by default. If `options.pan` is `false`, panning will not occur.
3576      // If `options.debounceMoveend` is `true`, it will delay `moveend` event so
3577      // that it doesn't happen often even if the method is called many
3578      // times in a row.
3579  
3580      // @alternative
3581      // @method invalidateSize(animate: Boolean): this
3582      // Checks if the map container size changed and updates the map if so —
3583      // call it after you've changed the map size dynamically, also animating
3584      // pan by default.
3585      invalidateSize: function (options) {
3586          if (!this._loaded) { return this; }
3587  
3588          options = extend({
3589              animate: false,
3590              pan: true
3591          }, options === true ? {animate: true} : options);
3592  
3593          var oldSize = this.getSize();
3594          this._sizeChanged = true;
3595          this._lastCenter = null;
3596  
3597          var newSize = this.getSize(),
3598              oldCenter = oldSize.divideBy(2).round(),
3599              newCenter = newSize.divideBy(2).round(),
3600              offset = oldCenter.subtract(newCenter);
3601  
3602          if (!offset.x && !offset.y) { return this; }
3603  
3604          if (options.animate && options.pan) {
3605              this.panBy(offset);
3606  
3607          } else {
3608              if (options.pan) {
3609                  this._rawPanBy(offset);
3610              }
3611  
3612              this.fire('move');
3613  
3614              if (options.debounceMoveend) {
3615                  clearTimeout(this._sizeTimer);
3616                  this._sizeTimer = setTimeout(bind(this.fire, this, 'moveend'), 200);
3617              } else {
3618                  this.fire('moveend');
3619              }
3620          }
3621  
3622          // @section Map state change events
3623          // @event resize: ResizeEvent
3624          // Fired when the map is resized.
3625          return this.fire('resize', {
3626              oldSize: oldSize,
3627              newSize: newSize
3628          });
3629      },
3630  
3631      // @section Methods for modifying map state
3632      // @method stop(): this
3633      // Stops the currently running `panTo` or `flyTo` animation, if any.
3634      stop: function () {
3635          this.setZoom(this._limitZoom(this._zoom));
3636          if (!this.options.zoomSnap) {
3637              this.fire('viewreset');
3638          }
3639          return this._stop();
3640      },
3641  
3642      // @section Geolocation methods
3643      // @method locate(options?: Locate options): this
3644      // Tries to locate the user using the Geolocation API, firing a [`locationfound`](#map-locationfound)
3645      // event with location data on success or a [`locationerror`](#map-locationerror) event on failure,
3646      // and optionally sets the map view to the user's location with respect to
3647      // detection accuracy (or to the world view if geolocation failed).
3648      // Note that, if your page doesn't use HTTPS, this method will fail in
3649      // modern browsers ([Chrome 50 and newer](https://sites.google.com/a/chromium.org/dev/Home/chromium-security/deprecating-powerful-features-on-insecure-origins))
3650      // See `Locate options` for more details.
3651      locate: function (options) {
3652  
3653          options = this._locateOptions = extend({
3654              timeout: 10000,
3655              watch: false
3656              // setView: false
3657              // maxZoom: <Number>
3658              // maximumAge: 0
3659              // enableHighAccuracy: false
3660          }, options);
3661  
3662          if (!('geolocation' in navigator)) {
3663              this._handleGeolocationError({
3664                  code: 0,
3665                  message: 'Geolocation not supported.'
3666              });
3667              return this;
3668          }
3669  
3670          var onResponse = bind(this._handleGeolocationResponse, this),
3671              onError = bind(this._handleGeolocationError, this);
3672  
3673          if (options.watch) {
3674              this._locationWatchId =
3675                      navigator.geolocation.watchPosition(onResponse, onError, options);
3676          } else {
3677              navigator.geolocation.getCurrentPosition(onResponse, onError, options);
3678          }
3679          return this;
3680      },
3681  
3682      // @method stopLocate(): this
3683      // Stops watching location previously initiated by `map.locate({watch: true})`
3684      // and aborts resetting the map view if map.locate was called with
3685      // `{setView: true}`.
3686      stopLocate: function () {
3687          if (navigator.geolocation && navigator.geolocation.clearWatch) {
3688              navigator.geolocation.clearWatch(this._locationWatchId);
3689          }
3690          if (this._locateOptions) {
3691              this._locateOptions.setView = false;
3692          }
3693          return this;
3694      },
3695  
3696      _handleGeolocationError: function (error) {
3697          var c = error.code,
3698              message = error.message ||
3699                      (c === 1 ? 'permission denied' :
3700                      (c === 2 ? 'position unavailable' : 'timeout'));
3701  
3702          if (this._locateOptions.setView && !this._loaded) {
3703              this.fitWorld();
3704          }
3705  
3706          // @section Location events
3707          // @event locationerror: ErrorEvent
3708          // Fired when geolocation (using the [`locate`](#map-locate) method) failed.
3709          this.fire('locationerror', {
3710              code: c,
3711              message: 'Geolocation error: ' + message + '.'
3712          });
3713      },
3714  
3715      _handleGeolocationResponse: function (pos) {
3716          var lat = pos.coords.latitude,
3717              lng = pos.coords.longitude,
3718              latlng = new LatLng(lat, lng),
3719              bounds = latlng.toBounds(pos.coords.accuracy * 2),
3720              options = this._locateOptions;
3721  
3722          if (options.setView) {
3723              var zoom = this.getBoundsZoom(bounds);
3724              this.setView(latlng, options.maxZoom ? Math.min(zoom, options.maxZoom) : zoom);
3725          }
3726  
3727          var data = {
3728              latlng: latlng,
3729              bounds: bounds,
3730              timestamp: pos.timestamp
3731          };
3732  
3733          for (var i in pos.coords) {
3734              if (typeof pos.coords[i] === 'number') {
3735                  data[i] = pos.coords[i];
3736              }
3737          }
3738  
3739          // @event locationfound: LocationEvent
3740          // Fired when geolocation (using the [`locate`](#map-locate) method)
3741          // went successfully.
3742          this.fire('locationfound', data);
3743      },
3744  
3745      // TODO Appropriate docs section?
3746      // @section Other Methods
3747      // @method addHandler(name: String, HandlerClass: Function): this
3748      // Adds a new `Handler` to the map, given its name and constructor function.
3749      addHandler: function (name, HandlerClass) {
3750          if (!HandlerClass) { return this; }
3751  
3752          var handler = this[name] = new HandlerClass(this);
3753  
3754          this._handlers.push(handler);
3755  
3756          if (this.options[name]) {
3757              handler.enable();
3758          }
3759  
3760          return this;
3761      },
3762  
3763      // @method remove(): this
3764      // Destroys the map and clears all related event listeners.
3765      remove: function () {
3766  
3767          this._initEvents(true);
3768  
3769          if (this._containerId !== this._container._leaflet_id) {
3770              throw new Error('Map container is being reused by another instance');
3771          }
3772  
3773          try {
3774              // throws error in IE6-8
3775              delete this._container._leaflet_id;
3776              delete this._containerId;
3777          } catch (e) {
3778              /*eslint-disable */
3779              this._container._leaflet_id = undefined;
3780              /* eslint-enable */
3781              this._containerId = undefined;
3782          }
3783  
3784          if (this._locationWatchId !== undefined) {
3785              this.stopLocate();
3786          }
3787  
3788          this._stop();
3789  
3790          remove(this._mapPane);
3791  
3792          if (this._clearControlPos) {
3793              this._clearControlPos();
3794          }
3795          if (this._resizeRequest) {
3796              cancelAnimFrame(this._resizeRequest);
3797              this._resizeRequest = null;
3798          }
3799  
3800          this._clearHandlers();
3801  
3802          if (this._loaded) {
3803              // @section Map state change events
3804              // @event unload: Event
3805              // Fired when the map is destroyed with [remove](#map-remove) method.
3806              this.fire('unload');
3807          }
3808  
3809          var i;
3810          for (i in this._layers) {
3811              this._layers[i].remove();
3812          }
3813          for (i in this._panes) {
3814              remove(this._panes[i]);
3815          }
3816  
3817          this._layers = [];
3818          this._panes = [];
3819          delete this._mapPane;
3820          delete this._renderer;
3821  
3822          return this;
3823      },
3824  
3825      // @section Other Methods
3826      // @method createPane(name: String, container?: HTMLElement): HTMLElement
3827      // Creates a new [map pane](#map-pane) with the given name if it doesn't exist already,
3828      // then returns it. The pane is created as a child of `container`, or
3829      // as a child of the main map pane if not set.
3830      createPane: function (name, container) {
3831          var className = 'leaflet-pane' + (name ? ' leaflet-' + name.replace('Pane', '') + '-pane' : ''),
3832              pane = create$1('div', className, container || this._mapPane);
3833  
3834          if (name) {
3835              this._panes[name] = pane;
3836          }
3837          return pane;
3838      },
3839  
3840      // @section Methods for Getting Map State
3841  
3842      // @method getCenter(): LatLng
3843      // Returns the geographical center of the map view
3844      getCenter: function () {
3845          this._checkIfLoaded();
3846  
3847          if (this._lastCenter && !this._moved()) {
3848              return this._lastCenter;
3849          }
3850          return this.layerPointToLatLng(this._getCenterLayerPoint());
3851      },
3852  
3853      // @method getZoom(): Number
3854      // Returns the current zoom level of the map view
3855      getZoom: function () {
3856          return this._zoom;
3857      },
3858  
3859      // @method getBounds(): LatLngBounds
3860      // Returns the geographical bounds visible in the current map view
3861      getBounds: function () {
3862          var bounds = this.getPixelBounds(),
3863              sw = this.unproject(bounds.getBottomLeft()),
3864              ne = this.unproject(bounds.getTopRight());
3865  
3866          return new LatLngBounds(sw, ne);
3867      },
3868  
3869      // @method getMinZoom(): Number
3870      // 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.
3871      getMinZoom: function () {
3872          return this.options.minZoom === undefined ? this._layersMinZoom || 0 : this.options.minZoom;
3873      },
3874  
3875      // @method getMaxZoom(): Number
3876      // Returns the maximum zoom level of the map (if set in the `maxZoom` option of the map or of any layers).
3877      getMaxZoom: function () {
3878          return this.options.maxZoom === undefined ?
3879              (this._layersMaxZoom === undefined ? Infinity : this._layersMaxZoom) :
3880              this.options.maxZoom;
3881      },
3882  
3883      // @method getBoundsZoom(bounds: LatLngBounds, inside?: Boolean, padding?: Point): Number
3884      // Returns the maximum zoom level on which the given bounds fit to the map
3885      // view in its entirety. If `inside` (optional) is set to `true`, the method
3886      // instead returns the minimum zoom level on which the map view fits into
3887      // the given bounds in its entirety.
3888      getBoundsZoom: function (bounds, inside, padding) { // (LatLngBounds[, Boolean, Point]) -> Number
3889          bounds = toLatLngBounds(bounds);
3890          padding = toPoint(padding || [0, 0]);
3891  
3892          var zoom = this.getZoom() || 0,
3893              min = this.getMinZoom(),
3894              max = this.getMaxZoom(),
3895              nw = bounds.getNorthWest(),
3896              se = bounds.getSouthEast(),
3897              size = this.getSize().subtract(padding),
3898              boundsSize = toBounds(this.project(se, zoom), this.project(nw, zoom)).getSize(),
3899              snap = any3d ? this.options.zoomSnap : 1,
3900              scalex = size.x / boundsSize.x,
3901              scaley = size.y / boundsSize.y,
3902              scale = inside ? Math.max(scalex, scaley) : Math.min(scalex, scaley);
3903  
3904          zoom = this.getScaleZoom(scale, zoom);
3905  
3906          if (snap) {
3907              zoom = Math.round(zoom / (snap / 100)) * (snap / 100); // don't jump if within 1% of a snap level
3908              zoom = inside ? Math.ceil(zoom / snap) * snap : Math.floor(zoom / snap) * snap;
3909          }
3910  
3911          return Math.max(min, Math.min(max, zoom));
3912      },
3913  
3914      // @method getSize(): Point
3915      // Returns the current size of the map container (in pixels).
3916      getSize: function () {
3917          if (!this._size || this._sizeChanged) {
3918              this._size = new Point(
3919                  this._container.clientWidth || 0,
3920                  this._container.clientHeight || 0);
3921  
3922              this._sizeChanged = false;
3923          }
3924          return this._size.clone();
3925      },
3926  
3927      // @method getPixelBounds(): Bounds
3928      // Returns the bounds of the current map view in projected pixel
3929      // coordinates (sometimes useful in layer and overlay implementations).
3930      getPixelBounds: function (center, zoom) {
3931          var topLeftPoint = this._getTopLeftPoint(center, zoom);
3932          return new Bounds(topLeftPoint, topLeftPoint.add(this.getSize()));
3933      },
3934  
3935      // TODO: Check semantics - isn't the pixel origin the 0,0 coord relative to
3936      // the map pane? "left point of the map layer" can be confusing, specially
3937      // since there can be negative offsets.
3938      // @method getPixelOrigin(): Point
3939      // Returns the projected pixel coordinates of the top left point of
3940      // the map layer (useful in custom layer and overlay implementations).
3941      getPixelOrigin: function () {
3942          this._checkIfLoaded();
3943          return this._pixelOrigin;
3944      },
3945  
3946      // @method getPixelWorldBounds(zoom?: Number): Bounds
3947      // Returns the world's bounds in pixel coordinates for zoom level `zoom`.
3948      // If `zoom` is omitted, the map's current zoom level is used.
3949      getPixelWorldBounds: function (zoom) {
3950          return this.options.crs.getProjectedBounds(zoom === undefined ? this.getZoom() : zoom);
3951      },
3952  
3953      // @section Other Methods
3954  
3955      // @method getPane(pane: String|HTMLElement): HTMLElement
3956      // Returns a [map pane](#map-pane), given its name or its HTML element (its identity).
3957      getPane: function (pane) {
3958          return typeof pane === 'string' ? this._panes[pane] : pane;
3959      },
3960  
3961      // @method getPanes(): Object
3962      // Returns a plain object containing the names of all [panes](#map-pane) as keys and
3963      // the panes as values.
3964      getPanes: function () {
3965          return this._panes;
3966      },
3967  
3968      // @method getContainer: HTMLElement
3969      // Returns the HTML element that contains the map.
3970      getContainer: function () {
3971          return this._container;
3972      },
3973  
3974  
3975      // @section Conversion Methods
3976  
3977      // @method getZoomScale(toZoom: Number, fromZoom: Number): Number
3978      // Returns the scale factor to be applied to a map transition from zoom level
3979      // `fromZoom` to `toZoom`. Used internally to help with zoom animations.
3980      getZoomScale: function (toZoom, fromZoom) {
3981          // TODO replace with universal implementation after refactoring projections
3982          var crs = this.options.crs;
3983          fromZoom = fromZoom === undefined ? this._zoom : fromZoom;
3984          return crs.scale(toZoom) / crs.scale(fromZoom);
3985      },
3986  
3987      // @method getScaleZoom(scale: Number, fromZoom: Number): Number
3988      // Returns the zoom level that the map would end up at, if it is at `fromZoom`
3989      // level and everything is scaled by a factor of `scale`. Inverse of
3990      // [`getZoomScale`](#map-getZoomScale).
3991      getScaleZoom: function (scale, fromZoom) {
3992          var crs = this.options.crs;
3993          fromZoom = fromZoom === undefined ? this._zoom : fromZoom;
3994          var zoom = crs.zoom(scale * crs.scale(fromZoom));
3995          return isNaN(zoom) ? Infinity : zoom;
3996      },
3997  
3998      // @method project(latlng: LatLng, zoom: Number): Point
3999      // Projects a geographical coordinate `LatLng` according to the projection
4000      // of the map's CRS, then scales it according to `zoom` and the CRS's
4001      // `Transformation`. The result is pixel coordinate relative to
4002      // the CRS origin.
4003      project: function (latlng, zoom) {
4004          zoom = zoom === undefined ? this._zoom : zoom;
4005          return this.options.crs.latLngToPoint(toLatLng(latlng), zoom);
4006      },
4007  
4008      // @method unproject(point: Point, zoom: Number): LatLng
4009      // Inverse of [`project`](#map-project).
4010      unproject: function (point, zoom) {
4011          zoom = zoom === undefined ? this._zoom : zoom;
4012          return this.options.crs.pointToLatLng(toPoint(point), zoom);
4013      },
4014  
4015      // @method layerPointToLatLng(point: Point): LatLng
4016      // Given a pixel coordinate relative to the [origin pixel](#map-getpixelorigin),
4017      // returns the corresponding geographical coordinate (for the current zoom level).
4018      layerPointToLatLng: function (point) {
4019          var projectedPoint = toPoint(point).add(this.getPixelOrigin());
4020          return this.unproject(projectedPoint);
4021      },
4022  
4023      // @method latLngToLayerPoint(latlng: LatLng): Point
4024      // Given a geographical coordinate, returns the corresponding pixel coordinate
4025      // relative to the [origin pixel](#map-getpixelorigin).
4026      latLngToLayerPoint: function (latlng) {
4027          var projectedPoint = this.project(toLatLng(latlng))._round();
4028          return projectedPoint._subtract(this.getPixelOrigin());
4029      },
4030  
4031      // @method wrapLatLng(latlng: LatLng): LatLng
4032      // Returns a `LatLng` where `lat` and `lng` has been wrapped according to the
4033      // map's CRS's `wrapLat` and `wrapLng` properties, if they are outside the
4034      // CRS's bounds.
4035      // By default this means longitude is wrapped around the dateline so its
4036      // value is between -180 and +180 degrees.
4037      wrapLatLng: function (latlng) {
4038          return this.options.crs.wrapLatLng(toLatLng(latlng));
4039      },
4040  
4041      // @method wrapLatLngBounds(bounds: LatLngBounds): LatLngBounds
4042      // Returns a `LatLngBounds` with the same size as the given one, ensuring that
4043      // its center is within the CRS's bounds.
4044      // By default this means the center longitude is wrapped around the dateline so its
4045      // value is between -180 and +180 degrees, and the majority of the bounds
4046      // overlaps the CRS's bounds.
4047      wrapLatLngBounds: function (latlng) {
4048          return this.options.crs.wrapLatLngBounds(toLatLngBounds(latlng));
4049      },
4050  
4051      // @method distance(latlng1: LatLng, latlng2: LatLng): Number
4052      // Returns the distance between two geographical coordinates according to
4053      // the map's CRS. By default this measures distance in meters.
4054      distance: function (latlng1, latlng2) {
4055          return this.options.crs.distance(toLatLng(latlng1), toLatLng(latlng2));
4056      },
4057  
4058      // @method containerPointToLayerPoint(point: Point): Point
4059      // Given a pixel coordinate relative to the map container, returns the corresponding
4060      // pixel coordinate relative to the [origin pixel](#map-getpixelorigin).
4061      containerPointToLayerPoint: function (point) { // (Point)
4062          return toPoint(point).subtract(this._getMapPanePos());
4063      },
4064  
4065      // @method layerPointToContainerPoint(point: Point): Point
4066      // Given a pixel coordinate relative to the [origin pixel](#map-getpixelorigin),
4067      // returns the corresponding pixel coordinate relative to the map container.
4068      layerPointToContainerPoint: function (point) { // (Point)
4069          return toPoint(point).add(this._getMapPanePos());
4070      },
4071  
4072      // @method containerPointToLatLng(point: Point): LatLng
4073      // Given a pixel coordinate relative to the map container, returns
4074      // the corresponding geographical coordinate (for the current zoom level).
4075      containerPointToLatLng: function (point) {
4076          var layerPoint = this.containerPointToLayerPoint(toPoint(point));
4077          return this.layerPointToLatLng(layerPoint);
4078      },
4079  
4080      // @method latLngToContainerPoint(latlng: LatLng): Point
4081      // Given a geographical coordinate, returns the corresponding pixel coordinate
4082      // relative to the map container.
4083      latLngToContainerPoint: function (latlng) {
4084          return this.layerPointToContainerPoint(this.latLngToLayerPoint(toLatLng(latlng)));
4085      },
4086  
4087      // @method mouseEventToContainerPoint(ev: MouseEvent): Point
4088      // Given a MouseEvent object, returns the pixel coordinate relative to the
4089      // map container where the event took place.
4090      mouseEventToContainerPoint: function (e) {
4091          return getMousePosition(e, this._container);
4092      },
4093  
4094      // @method mouseEventToLayerPoint(ev: MouseEvent): Point
4095      // Given a MouseEvent object, returns the pixel coordinate relative to
4096      // the [origin pixel](#map-getpixelorigin) where the event took place.
4097      mouseEventToLayerPoint: function (e) {
4098          return this.containerPointToLayerPoint(this.mouseEventToContainerPoint(e));
4099      },
4100  
4101      // @method mouseEventToLatLng(ev: MouseEvent): LatLng
4102      // Given a MouseEvent object, returns geographical coordinate where the
4103      // event took place.
4104      mouseEventToLatLng: function (e) { // (MouseEvent)
4105          return this.layerPointToLatLng(this.mouseEventToLayerPoint(e));
4106      },
4107  
4108  
4109      // map initialization methods
4110  
4111      _initContainer: function (id) {
4112          var container = this._container = get(id);
4113  
4114          if (!container) {
4115              throw new Error('Map container not found.');
4116          } else if (container._leaflet_id) {
4117              throw new Error('Map container is already initialized.');
4118          }
4119  
4120          on(container, 'scroll', this._onScroll, this);
4121          this._containerId = stamp(container);
4122      },
4123  
4124      _initLayout: function () {
4125          var container = this._container;
4126  
4127          this._fadeAnimated = this.options.fadeAnimation && any3d;
4128  
4129          addClass(container, 'leaflet-container' +
4130              (touch ? ' leaflet-touch' : '') +
4131              (retina ? ' leaflet-retina' : '') +
4132              (ielt9 ? ' leaflet-oldie' : '') +
4133              (safari ? ' leaflet-safari' : '') +
4134              (this._fadeAnimated ? ' leaflet-fade-anim' : ''));
4135  
4136          var position = getStyle(container, 'position');
4137  
4138          if (position !== 'absolute' && position !== 'relative' && position !== 'fixed') {
4139              container.style.position = 'relative';
4140          }
4141  
4142          this._initPanes();
4143  
4144          if (this._initControlPos) {
4145              this._initControlPos();
4146          }
4147      },
4148  
4149      _initPanes: function () {
4150          var panes = this._panes = {};
4151          this._paneRenderers = {};
4152  
4153          // @section
4154          //
4155          // Panes are DOM elements used to control the ordering of layers on the map. You
4156          // can access panes with [`map.getPane`](#map-getpane) or
4157          // [`map.getPanes`](#map-getpanes) methods. New panes can be created with the
4158          // [`map.createPane`](#map-createpane) method.
4159          //
4160          // Every map has the following default panes that differ only in zIndex.
4161          //
4162          // @pane mapPane: HTMLElement = 'auto'
4163          // Pane that contains all other map panes
4164  
4165          this._mapPane = this.createPane('mapPane', this._container);
4166          setPosition(this._mapPane, new Point(0, 0));
4167  
4168          // @pane tilePane: HTMLElement = 200
4169          // Pane for `GridLayer`s and `TileLayer`s
4170          this.createPane('tilePane');
4171          // @pane overlayPane: HTMLElement = 400
4172          // Pane for vectors (`Path`s, like `Polyline`s and `Polygon`s), `ImageOverlay`s and `VideoOverlay`s
4173          this.createPane('shadowPane');
4174          // @pane shadowPane: HTMLElement = 500
4175          // Pane for overlay shadows (e.g. `Marker` shadows)
4176          this.createPane('overlayPane');
4177          // @pane markerPane: HTMLElement = 600
4178          // Pane for `Icon`s of `Marker`s
4179          this.createPane('markerPane');
4180          // @pane tooltipPane: HTMLElement = 650
4181          // Pane for `Tooltip`s.
4182          this.createPane('tooltipPane');
4183          // @pane popupPane: HTMLElement = 700
4184          // Pane for `Popup`s.
4185          this.createPane('popupPane');
4186  
4187          if (!this.options.markerZoomAnimation) {
4188              addClass(panes.markerPane, 'leaflet-zoom-hide');
4189              addClass(panes.shadowPane, 'leaflet-zoom-hide');
4190          }
4191      },
4192  
4193  
4194      // private methods that modify map state
4195  
4196      // @section Map state change events
4197      _resetView: function (center, zoom) {
4198          setPosition(this._mapPane, new Point(0, 0));
4199  
4200          var loading = !this._loaded;
4201          this._loaded = true;
4202          zoom = this._limitZoom(zoom);
4203  
4204          this.fire('viewprereset');
4205  
4206          var zoomChanged = this._zoom !== zoom;
4207          this
4208              ._moveStart(zoomChanged, false)
4209              ._move(center, zoom)
4210              ._moveEnd(zoomChanged);
4211  
4212          // @event viewreset: Event
4213          // Fired when the map needs to redraw its content (this usually happens
4214          // on map zoom or load). Very useful for creating custom overlays.
4215          this.fire('viewreset');
4216  
4217          // @event load: Event
4218          // Fired when the map is initialized (when its center and zoom are set
4219          // for the first time).
4220          if (loading) {
4221              this.fire('load');
4222          }
4223      },
4224  
4225      _moveStart: function (zoomChanged, noMoveStart) {
4226          // @event zoomstart: Event
4227          // Fired when the map zoom is about to change (e.g. before zoom animation).
4228          // @event movestart: Event
4229          // Fired when the view of the map starts changing (e.g. user starts dragging the map).
4230          if (zoomChanged) {
4231              this.fire('zoomstart');
4232          }
4233          if (!noMoveStart) {
4234              this.fire('movestart');
4235          }
4236          return this;
4237      },
4238  
4239      _move: function (center, zoom, data) {
4240          if (zoom === undefined) {
4241              zoom = this._zoom;
4242          }
4243          var zoomChanged = this._zoom !== zoom;
4244  
4245          this._zoom = zoom;
4246          this._lastCenter = center;
4247          this._pixelOrigin = this._getNewPixelOrigin(center);
4248  
4249          // @event zoom: Event
4250          // Fired repeatedly during any change in zoom level, including zoom
4251          // and fly animations.
4252          if (zoomChanged || (data && data.pinch)) {    // Always fire 'zoom' if pinching because #3530
4253              this.fire('zoom', data);
4254          }
4255  
4256          // @event move: Event
4257          // Fired repeatedly during any movement of the map, including pan and
4258          // fly animations.
4259          return this.fire('move', data);
4260      },
4261  
4262      _moveEnd: function (zoomChanged) {
4263          // @event zoomend: Event
4264          // Fired when the map has changed, after any animations.
4265          if (zoomChanged) {
4266              this.fire('zoomend');
4267          }
4268  
4269          // @event moveend: Event
4270          // Fired when the center of the map stops changing (e.g. user stopped
4271          // dragging the map).
4272          return this.fire('moveend');
4273      },
4274  
4275      _stop: function () {
4276          cancelAnimFrame(this._flyToFrame);
4277          if (this._panAnim) {
4278              this._panAnim.stop();
4279          }
4280          return this;
4281      },
4282  
4283      _rawPanBy: function (offset) {
4284          setPosition(this._mapPane, this._getMapPanePos().subtract(offset));
4285      },
4286  
4287      _getZoomSpan: function () {
4288          return this.getMaxZoom() - this.getMinZoom();
4289      },
4290  
4291      _panInsideMaxBounds: function () {
4292          if (!this._enforcingBounds) {
4293              this.panInsideBounds(this.options.maxBounds);
4294          }
4295      },
4296  
4297      _checkIfLoaded: function () {
4298          if (!this._loaded) {
4299              throw new Error('Set map center and zoom first.');
4300          }
4301      },
4302  
4303      // DOM event handling
4304  
4305      // @section Interaction events
4306      _initEvents: function (remove$$1) {
4307          this._targets = {};
4308          this._targets[stamp(this._container)] = this;
4309  
4310          var onOff = remove$$1 ? off : on;
4311  
4312          // @event click: MouseEvent
4313          // Fired when the user clicks (or taps) the map.
4314          // @event dblclick: MouseEvent
4315          // Fired when the user double-clicks (or double-taps) the map.
4316          // @event mousedown: MouseEvent
4317          // Fired when the user pushes the mouse button on the map.
4318          // @event mouseup: MouseEvent
4319          // Fired when the user releases the mouse button on the map.
4320          // @event mouseover: MouseEvent
4321          // Fired when the mouse enters the map.
4322          // @event mouseout: MouseEvent
4323          // Fired when the mouse leaves the map.
4324          // @event mousemove: MouseEvent
4325          // Fired while the mouse moves over the map.
4326          // @event contextmenu: MouseEvent
4327          // Fired when the user pushes the right mouse button on the map, prevents
4328          // default browser context menu from showing if there are listeners on
4329          // this event. Also fired on mobile when the user holds a single touch
4330          // for a second (also called long press).
4331          // @event keypress: KeyboardEvent
4332          // Fired when the user presses a key from the keyboard that produces a character value while the map is focused.
4333          // @event keydown: KeyboardEvent
4334          // Fired when the user presses a key from the keyboard while the map is focused. Unlike the `keypress` event,
4335          // the `keydown` event is fired for keys that produce a character value and for keys
4336          // that do not produce a character value.
4337          // @event keyup: KeyboardEvent
4338          // Fired when the user releases a key from the keyboard while the map is focused.
4339          onOff(this._container, 'click dblclick mousedown mouseup ' +
4340              'mouseover mouseout mousemove contextmenu keypress keydown keyup', this._handleDOMEvent, this);
4341  
4342          if (this.options.trackResize) {
4343              onOff(window, 'resize', this._onResize, this);
4344          }
4345  
4346          if (any3d && this.options.transform3DLimit) {
4347              (remove$$1 ? this.off : this.on).call(this, 'moveend', this._onMoveEnd);
4348          }
4349      },
4350  
4351      _onResize: function () {
4352          cancelAnimFrame(this._resizeRequest);
4353          this._resizeRequest = requestAnimFrame(
4354                  function () { this.invalidateSize({debounceMoveend: true}); }, this);
4355      },
4356  
4357      _onScroll: function () {
4358          this._container.scrollTop  = 0;
4359          this._container.scrollLeft = 0;
4360      },
4361  
4362      _onMoveEnd: function () {
4363          var pos = this._getMapPanePos();
4364          if (Math.max(Math.abs(pos.x), Math.abs(pos.y)) >= this.options.transform3DLimit) {
4365              // https://bugzilla.mozilla.org/show_bug.cgi?id=1203873 but Webkit also have
4366              // a pixel offset on very high values, see: http://jsfiddle.net/dg6r5hhb/
4367              this._resetView(this.getCenter(), this.getZoom());
4368          }
4369      },
4370  
4371      _findEventTargets: function (e, type) {
4372          var targets = [],
4373              target,
4374              isHover = type === 'mouseout' || type === 'mouseover',
4375              src = e.target || e.srcElement,
4376              dragging = false;
4377  
4378          while (src) {
4379              target = this._targets[stamp(src)];
4380              if (target && (type === 'click' || type === 'preclick') && !e._simulated && this._draggableMoved(target)) {
4381                  // Prevent firing click after you just dragged an object.
4382                  dragging = true;
4383                  break;
4384              }
4385              if (target && target.listens(type, true)) {
4386                  if (isHover && !isExternalTarget(src, e)) { break; }
4387                  targets.push(target);
4388                  if (isHover) { break; }
4389              }
4390              if (src === this._container) { break; }
4391              src = src.parentNode;
4392          }
4393          if (!targets.length && !dragging && !isHover && isExternalTarget(src, e)) {
4394              targets = [this];
4395          }
4396          return targets;
4397      },
4398  
4399      _handleDOMEvent: function (e) {
4400          if (!this._loaded || skipped(e)) { return; }
4401  
4402          var type = e.type;
4403  
4404          if (type === 'mousedown' || type === 'keypress' || type === 'keyup' || type === 'keydown') {
4405              // prevents outline when clicking on keyboard-focusable element
4406              preventOutline(e.target || e.srcElement);
4407          }
4408  
4409          this._fireDOMEvent(e, type);
4410      },
4411  
4412      _mouseEvents: ['click', 'dblclick', 'mouseover', 'mouseout', 'contextmenu'],
4413  
4414      _fireDOMEvent: function (e, type, targets) {
4415  
4416          if (e.type === 'click') {
4417              // Fire a synthetic 'preclick' event which propagates up (mainly for closing popups).
4418              // @event preclick: MouseEvent
4419              // Fired before mouse click on the map (sometimes useful when you
4420              // want something to happen on click before any existing click
4421              // handlers start running).
4422              var synth = extend({}, e);
4423              synth.type = 'preclick';
4424              this._fireDOMEvent(synth, synth.type, targets);
4425          }
4426  
4427          if (e._stopped) { return; }
4428  
4429          // Find the layer the event is propagating from and its parents.
4430          targets = (targets || []).concat(this._findEventTargets(e, type));
4431  
4432          if (!targets.length) { return; }
4433  
4434          var target = targets[0];
4435          if (type === 'contextmenu' && target.listens(type, true)) {
4436              preventDefault(e);
4437          }
4438  
4439          var data = {
4440              originalEvent: e
4441          };
4442  
4443          if (e.type !== 'keypress' && e.type !== 'keydown' && e.type !== 'keyup') {
4444              var isMarker = target.getLatLng && (!target._radius || target._radius <= 10);
4445              data.containerPoint = isMarker ?
4446                  this.latLngToContainerPoint(target.getLatLng()) : this.mouseEventToContainerPoint(e);
4447              data.layerPoint = this.containerPointToLayerPoint(data.containerPoint);
4448              data.latlng = isMarker ? target.getLatLng() : this.layerPointToLatLng(data.layerPoint);
4449          }
4450  
4451          for (var i = 0; i < targets.length; i++) {
4452              targets[i].fire(type, data, true);
4453              if (data.originalEvent._stopped ||
4454                  (targets[i].options.bubblingMouseEvents === false && indexOf(this._mouseEvents, type) !== -1)) { return; }
4455          }
4456      },
4457  
4458      _draggableMoved: function (obj) {
4459          obj = obj.dragging && obj.dragging.enabled() ? obj : this;
4460          return (obj.dragging && obj.dragging.moved()) || (this.boxZoom && this.boxZoom.moved());
4461      },
4462  
4463      _clearHandlers: function () {
4464          for (var i = 0, len = this._handlers.length; i < len; i++) {
4465              this._handlers[i].disable();
4466          }
4467      },
4468  
4469      // @section Other Methods
4470  
4471      // @method whenReady(fn: Function, context?: Object): this
4472      // Runs the given function `fn` when the map gets initialized with
4473      // a view (center and zoom) and at least one layer, or immediately
4474      // if it's already initialized, optionally passing a function context.
4475      whenReady: function (callback, context) {
4476          if (this._loaded) {
4477              callback.call(context || this, {target: this});
4478          } else {
4479              this.on('load', callback, context);
4480          }
4481          return this;
4482      },
4483  
4484  
4485      // private methods for getting map state
4486  
4487      _getMapPanePos: function () {
4488          return getPosition(this._mapPane) || new Point(0, 0);
4489      },
4490  
4491      _moved: function () {
4492          var pos = this._getMapPanePos();
4493          return pos && !pos.equals([0, 0]);
4494      },
4495  
4496      _getTopLeftPoint: function (center, zoom) {
4497          var pixelOrigin = center && zoom !== undefined ?
4498              this._getNewPixelOrigin(center, zoom) :
4499              this.getPixelOrigin();
4500          return pixelOrigin.subtract(this._getMapPanePos());
4501      },
4502  
4503      _getNewPixelOrigin: function (center, zoom) {
4504          var viewHalf = this.getSize()._divideBy(2);
4505          return this.project(center, zoom)._subtract(viewHalf)._add(this._getMapPanePos())._round();
4506      },
4507  
4508      _latLngToNewLayerPoint: function (latlng, zoom, center) {
4509          var topLeft = this._getNewPixelOrigin(center, zoom);
4510          return this.project(latlng, zoom)._subtract(topLeft);
4511      },
4512  
4513      _latLngBoundsToNewLayerBounds: function (latLngBounds, zoom, center) {
4514          var topLeft = this._getNewPixelOrigin(center, zoom);
4515          return toBounds([
4516              this.project(latLngBounds.getSouthWest(), zoom)._subtract(topLeft),
4517              this.project(latLngBounds.getNorthWest(), zoom)._subtract(topLeft),
4518              this.project(latLngBounds.getSouthEast(), zoom)._subtract(topLeft),
4519              this.project(latLngBounds.getNorthEast(), zoom)._subtract(topLeft)
4520          ]);
4521      },
4522  
4523      // layer point of the current center
4524      _getCenterLayerPoint: function () {
4525          return this.containerPointToLayerPoint(this.getSize()._divideBy(2));
4526      },
4527  
4528      // offset of the specified place to the current center in pixels
4529      _getCenterOffset: function (latlng) {
4530          return this.latLngToLayerPoint(latlng).subtract(this._getCenterLayerPoint());
4531      },
4532  
4533      // adjust center for view to get inside bounds
4534      _limitCenter: function (center, zoom, bounds) {
4535  
4536          if (!bounds) { return center; }
4537  
4538          var centerPoint = this.project(center, zoom),
4539              viewHalf = this.getSize().divideBy(2),
4540              viewBounds = new Bounds(centerPoint.subtract(viewHalf), centerPoint.add(viewHalf)),
4541              offset = this._getBoundsOffset(viewBounds, bounds, zoom);
4542  
4543          // If offset is less than a pixel, ignore.
4544          // This prevents unstable projections from getting into
4545          // an infinite loop of tiny offsets.
4546          if (offset.round().equals([0, 0])) {
4547              return center;
4548          }
4549  
4550          return this.unproject(centerPoint.add(offset), zoom);
4551      },
4552  
4553      // adjust offset for view to get inside bounds
4554      _limitOffset: function (offset, bounds) {
4555          if (!bounds) { return offset; }
4556  
4557          var viewBounds = this.getPixelBounds(),
4558              newBounds = new Bounds(viewBounds.min.add(offset), viewBounds.max.add(offset));
4559  
4560          return offset.add(this._getBoundsOffset(newBounds, bounds));
4561      },
4562  
4563      // returns offset needed for pxBounds to get inside maxBounds at a specified zoom
4564      _getBoundsOffset: function (pxBounds, maxBounds, zoom) {
4565          var projectedMaxBounds = toBounds(
4566                  this.project(maxBounds.getNorthEast(), zoom),
4567                  this.project(maxBounds.getSouthWest(), zoom)
4568              ),
4569              minOffset = projectedMaxBounds.min.subtract(pxBounds.min),
4570              maxOffset = projectedMaxBounds.max.subtract(pxBounds.max),
4571  
4572              dx = this._rebound(minOffset.x, -maxOffset.x),
4573              dy = this._rebound(minOffset.y, -maxOffset.y);
4574  
4575          return new Point(dx, dy);
4576      },
4577  
4578      _rebound: function (left, right) {
4579          return left + right > 0 ?
4580              Math.round(left - right) / 2 :
4581              Math.max(0, Math.ceil(left)) - Math.max(0, Math.floor(right));
4582      },
4583  
4584      _limitZoom: function (zoom) {
4585          var min = this.getMinZoom(),
4586              max = this.getMaxZoom(),
4587              snap = any3d ? this.options.zoomSnap : 1;
4588          if (snap) {
4589              zoom = Math.round(zoom / snap) * snap;
4590          }
4591          return Math.max(min, Math.min(max, zoom));
4592      },
4593  
4594      _onPanTransitionStep: function () {
4595          this.fire('move');
4596      },
4597  
4598      _onPanTransitionEnd: function () {
4599          removeClass(this._mapPane, 'leaflet-pan-anim');
4600          this.fire('moveend');
4601      },
4602  
4603      _tryAnimatedPan: function (center, options) {
4604          // difference between the new and current centers in pixels
4605          var offset = this._getCenterOffset(center)._trunc();
4606  
4607          // don't animate too far unless animate: true specified in options
4608          if ((options && options.animate) !== true && !this.getSize().contains(offset)) { return false; }
4609  
4610          this.panBy(offset, options);
4611  
4612          return true;
4613      },
4614  
4615      _createAnimProxy: function () {
4616  
4617          var proxy = this._proxy = create$1('div', 'leaflet-proxy leaflet-zoom-animated');
4618          this._panes.mapPane.appendChild(proxy);
4619  
4620          this.on('zoomanim', function (e) {
4621              var prop = TRANSFORM,
4622                  transform = this._proxy.style[prop];
4623  
4624              setTransform(this._proxy, this.project(e.center, e.zoom), this.getZoomScale(e.zoom, 1));
4625  
4626              // workaround for case when transform is the same and so transitionend event is not fired
4627              if (transform === this._proxy.style[prop] && this._animatingZoom) {
4628                  this._onZoomTransitionEnd();
4629              }
4630          }, this);
4631  
4632          this.on('load moveend', this._animMoveEnd, this);
4633  
4634          this._on('unload', this._destroyAnimProxy, this);
4635      },
4636  
4637      _destroyAnimProxy: function () {
4638          remove(this._proxy);
4639          this.off('load moveend', this._animMoveEnd, this);
4640          delete this._proxy;
4641      },
4642  
4643      _animMoveEnd: function () {
4644          var c = this.getCenter(),
4645              z = this.getZoom();
4646          setTransform(this._proxy, this.project(c, z), this.getZoomScale(z, 1));
4647      },
4648  
4649      _catchTransitionEnd: function (e) {
4650          if (this._animatingZoom && e.propertyName.indexOf('transform') >= 0) {
4651              this._onZoomTransitionEnd();
4652          }
4653      },
4654  
4655      _nothingToAnimate: function () {
4656          return !this._container.getElementsByClassName('leaflet-zoom-animated').length;
4657      },
4658  
4659      _tryAnimatedZoom: function (center, zoom, options) {
4660  
4661          if (this._animatingZoom) { return true; }
4662  
4663          options = options || {};
4664  
4665          // don't animate if disabled, not supported or zoom difference is too large
4666          if (!this._zoomAnimated || options.animate === false || this._nothingToAnimate() ||
4667                  Math.abs(zoom - this._zoom) > this.options.zoomAnimationThreshold) { return false; }
4668  
4669          // offset is the pixel coords of the zoom origin relative to the current center
4670          var scale = this.getZoomScale(zoom),
4671              offset = this._getCenterOffset(center)._divideBy(1 - 1 / scale);
4672  
4673          // don't animate if the zoom origin isn't within one screen from the current center, unless forced
4674          if (options.animate !== true && !this.getSize().contains(offset)) { return false; }
4675  
4676          requestAnimFrame(function () {
4677              this
4678                  ._moveStart(true, false)
4679                  ._animateZoom(center, zoom, true);
4680          }, this);
4681  
4682          return true;
4683      },
4684  
4685      _animateZoom: function (center, zoom, startAnim, noUpdate) {
4686          if (!this._mapPane) { return; }
4687  
4688          if (startAnim) {
4689              this._animatingZoom = true;
4690  
4691              // remember what center/zoom to set after animation
4692              this._animateToCenter = center;
4693              this._animateToZoom = zoom;
4694  
4695              addClass(this._mapPane, 'leaflet-zoom-anim');
4696          }
4697  
4698          // @section Other Events
4699          // @event zoomanim: ZoomAnimEvent
4700          // Fired at least once per zoom animation. For continuous zoom, like pinch zooming, fired once per frame during zoom.
4701          this.fire('zoomanim', {
4702              center: center,
4703              zoom: zoom,
4704              noUpdate: noUpdate
4705          });
4706  
4707          // Work around webkit not firing 'transitionend', see https://github.com/Leaflet/Leaflet/issues/3689, 2693
4708          setTimeout(bind(this._onZoomTransitionEnd, this), 250);
4709      },
4710  
4711      _onZoomTransitionEnd: function () {
4712          if (!this._animatingZoom) { return; }
4713  
4714          if (this._mapPane) {
4715              removeClass(this._mapPane, 'leaflet-zoom-anim');
4716          }
4717  
4718          this._animatingZoom = false;
4719  
4720          this._move(this._animateToCenter, this._animateToZoom);
4721  
4722          // This anim frame should prevent an obscure iOS webkit tile loading race condition.
4723          requestAnimFrame(function () {
4724              this._moveEnd(true);
4725          }, this);
4726      }
4727  });
4728  
4729  // @section
4730  
4731  // @factory L.map(id: String, options?: Map options)
4732  // Instantiates a map object given the DOM ID of a `<div>` element
4733  // and optionally an object literal with `Map options`.
4734  //
4735  // @alternative
4736  // @factory L.map(el: HTMLElement, options?: Map options)
4737  // Instantiates a map object given an instance of a `<div>` HTML element
4738  // and optionally an object literal with `Map options`.
4739  function createMap(id, options) {
4740      return new Map(id, options);
4741  }
4742  
4743  /*
4744   * @class Control
4745   * @aka L.Control
4746   * @inherits Class
4747   *
4748   * L.Control is a base class for implementing map controls. Handles positioning.
4749   * All other controls extend from this class.
4750   */
4751  
4752  var Control = Class.extend({
4753      // @section
4754      // @aka Control options
4755      options: {
4756          // @option position: String = 'topright'
4757          // The position of the control (one of the map corners). Possible values are `'topleft'`,
4758          // `'topright'`, `'bottomleft'` or `'bottomright'`
4759          position: 'topright'
4760      },
4761  
4762      initialize: function (options) {
4763          setOptions(this, options);
4764      },
4765  
4766      /* @section
4767       * Classes extending L.Control will inherit the following methods:
4768       *
4769       * @method getPosition: string
4770       * Returns the position of the control.
4771       */
4772      getPosition: function () {
4773          return this.options.position;
4774      },
4775  
4776      // @method setPosition(position: string): this
4777      // Sets the position of the control.
4778      setPosition: function (position) {
4779          var map = this._map;
4780  
4781          if (map) {
4782              map.removeControl(this);
4783          }
4784  
4785          this.options.position = position;
4786  
4787          if (map) {
4788              map.addControl(this);
4789          }
4790  
4791          return this;
4792      },
4793  
4794      // @method getContainer: HTMLElement
4795      // Returns the HTMLElement that contains the control.
4796      getContainer: function () {
4797          return this._container;
4798      },
4799  
4800      // @method addTo(map: Map): this
4801      // Adds the control to the given map.
4802      addTo: function (map) {
4803          this.remove();
4804          this._map = map;
4805  
4806          var container = this._container = this.onAdd(map),
4807              pos = this.getPosition(),
4808              corner = map._controlCorners[pos];
4809  
4810          addClass(container, 'leaflet-control');
4811  
4812          if (pos.indexOf('bottom') !== -1) {
4813              corner.insertBefore(container, corner.firstChild);
4814          } else {
4815              corner.appendChild(container);
4816          }
4817  
4818          this._map.on('unload', this.remove, this);
4819  
4820          return this;
4821      },
4822  
4823      // @method remove: this
4824      // Removes the control from the map it is currently active on.
4825      remove: function () {
4826          if (!this._map) {
4827              return this;
4828          }
4829  
4830          remove(this._container);
4831  
4832          if (this.onRemove) {
4833              this.onRemove(this._map);
4834          }
4835  
4836          this._map.off('unload', this.remove, this);
4837          this._map = null;
4838  
4839          return this;
4840      },
4841  
4842      _refocusOnMap: function (e) {
4843          // if map exists and event is not a keyboard event
4844          if (this._map && e && e.screenX > 0 && e.screenY > 0) {
4845              this._map.getContainer().focus();
4846          }
4847      }
4848  });
4849  
4850  var control = function (options) {
4851      return new Control(options);
4852  };
4853  
4854  /* @section Extension methods
4855   * @uninheritable
4856   *
4857   * Every control should extend from `L.Control` and (re-)implement the following methods.
4858   *
4859   * @method onAdd(map: Map): HTMLElement
4860   * Should return the container DOM element for the control and add listeners on relevant map events. Called on [`control.addTo(map)`](#control-addTo).
4861   *
4862   * @method onRemove(map: Map)
4863   * Optional method. Should contain all clean up code that removes the listeners previously added in [`onAdd`](#control-onadd). Called on [`control.remove()`](#control-remove).
4864   */
4865  
4866  /* @namespace Map
4867   * @section Methods for Layers and Controls
4868   */
4869  Map.include({
4870      // @method addControl(control: Control): this
4871      // Adds the given control to the map
4872      addControl: function (control) {
4873          control.addTo(this);
4874          return this;
4875      },
4876  
4877      // @method removeControl(control: Control): this
4878      // Removes the given control from the map
4879      removeControl: function (control) {
4880          control.remove();
4881          return this;
4882      },
4883  
4884      _initControlPos: function () {
4885          var corners = this._controlCorners = {},
4886              l = 'leaflet-',
4887              container = this._controlContainer =
4888                      create$1('div', l + 'control-container', this._container);
4889  
4890  		function createCorner(vSide, hSide) {
4891              var className = l + vSide + ' ' + l + hSide;
4892  
4893              corners[vSide + hSide] = create$1('div', className, container);
4894          }
4895  
4896          createCorner('top', 'left');
4897          createCorner('top', 'right');
4898          createCorner('bottom', 'left');
4899          createCorner('bottom', 'right');
4900      },
4901  
4902      _clearControlPos: function () {
4903          for (var i in this._controlCorners) {
4904              remove(this._controlCorners[i]);
4905          }
4906          remove(this._controlContainer);
4907          delete this._controlCorners;
4908          delete this._controlContainer;
4909      }
4910  });
4911  
4912  /*
4913   * @class Control.Layers
4914   * @aka L.Control.Layers
4915   * @inherits Control
4916   *
4917   * The layers control gives users the ability to switch between different base layers and switch overlays on/off (check out the [detailed example](http://leafletjs.com/examples/layers-control/)). Extends `Control`.
4918   *
4919   * @example
4920   *
4921   * ```js
4922   * var baseLayers = {
4923   *     "Mapbox": mapbox,
4924   *     "OpenStreetMap": osm
4925   * };
4926   *
4927   * var overlays = {
4928   *     "Marker": marker,
4929   *     "Roads": roadsLayer
4930   * };
4931   *
4932   * L.control.layers(baseLayers, overlays).addTo(map);
4933   * ```
4934   *
4935   * The `baseLayers` and `overlays` parameters are object literals with layer names as keys and `Layer` objects as values:
4936   *
4937   * ```js
4938   * {
4939   *     "<someName1>": layer1,
4940   *     "<someName2>": layer2
4941   * }
4942   * ```
4943   *
4944   * The layer names can contain HTML, which allows you to add additional styling to the items:
4945   *
4946   * ```js
4947   * {"<img src='my-layer-icon' /> <span class='my-layer-item'>My Layer</span>": myLayer}
4948   * ```
4949   */
4950  
4951  var Layers = Control.extend({
4952      // @section
4953      // @aka Control.Layers options
4954      options: {
4955          // @option collapsed: Boolean = true
4956          // If `true`, the control will be collapsed into an icon and expanded on mouse hover or touch.
4957          collapsed: true,
4958          position: 'topright',
4959  
4960          // @option autoZIndex: Boolean = true
4961          // 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.
4962          autoZIndex: true,
4963  
4964          // @option hideSingleBase: Boolean = false
4965          // If `true`, the base layers in the control will be hidden when there is only one.
4966          hideSingleBase: false,
4967  
4968          // @option sortLayers: Boolean = false
4969          // Whether to sort the layers. When `false`, layers will keep the order
4970          // in which they were added to the control.
4971          sortLayers: false,
4972  
4973          // @option sortFunction: Function = *
4974          // A [compare function](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Array/sort)
4975          // that will be used for sorting the layers, when `sortLayers` is `true`.
4976          // The function receives both the `L.Layer` instances and their names, as in
4977          // `sortFunction(layerA, layerB, nameA, nameB)`.
4978          // By default, it sorts layers alphabetically by their name.
4979          sortFunction: function (layerA, layerB, nameA, nameB) {
4980              return nameA < nameB ? -1 : (nameB < nameA ? 1 : 0);
4981          }
4982      },
4983  
4984      initialize: function (baseLayers, overlays, options) {
4985          setOptions(this, options);
4986  
4987          this._layerControlInputs = [];
4988          this._layers = [];
4989          this._lastZIndex = 0;
4990          this._handlingClick = false;
4991  
4992          for (var i in baseLayers) {
4993              this._addLayer(baseLayers[i], i);
4994          }
4995  
4996          for (i in overlays) {
4997              this._addLayer(overlays[i], i, true);
4998          }
4999      },
5000  
5001      onAdd: function (map) {
5002          this._initLayout();
5003          this._update();
5004  
5005          this._map = map;
5006          map.on('zoomend', this._checkDisabledLayers, this);
5007  
5008          for (var i = 0; i < this._layers.length; i++) {
5009              this._layers[i].layer.on('add remove', this._onLayerChange, this);
5010          }
5011  
5012          return this._container;
5013      },
5014  
5015      addTo: function (map) {
5016          Control.prototype.addTo.call(this, map);
5017          // Trigger expand after Layers Control has been inserted into DOM so that is now has an actual height.
5018          return this._expandIfNotCollapsed();
5019      },
5020  
5021      onRemove: function () {
5022          this._map.off('zoomend', this._checkDisabledLayers, this);
5023  
5024          for (var i = 0; i < this._layers.length; i++) {
5025              this._layers[i].layer.off('add remove', this._onLayerChange, this);
5026          }
5027      },
5028  
5029      // @method addBaseLayer(layer: Layer, name: String): this
5030      // Adds a base layer (radio button entry) with the given name to the control.
5031      addBaseLayer: function (layer, name) {
5032          this._addLayer(layer, name);
5033          return (this._map) ? this._update() : this;
5034      },
5035  
5036      // @method addOverlay(layer: Layer, name: String): this
5037      // Adds an overlay (checkbox entry) with the given name to the control.
5038      addOverlay: function (layer, name) {
5039          this._addLayer(layer, name, true);
5040          return (this._map) ? this._update() : this;
5041      },
5042  
5043      // @method removeLayer(layer: Layer): this
5044      // Remove the given layer from the control.
5045      removeLayer: function (layer) {
5046          layer.off('add remove', this._onLayerChange, this);
5047  
5048          var obj = this._getLayer(stamp(layer));
5049          if (obj) {
5050              this._layers.splice(this._layers.indexOf(obj), 1);
5051          }
5052          return (this._map) ? this._update() : this;
5053      },
5054  
5055      // @method expand(): this
5056      // Expand the control container if collapsed.
5057      expand: function () {
5058          addClass(this._container, 'leaflet-control-layers-expanded');
5059          this._section.style.height = null;
5060          var acceptableHeight = this._map.getSize().y - (this._container.offsetTop + 50);
5061          if (acceptableHeight < this._section.clientHeight) {
5062              addClass(this._section, 'leaflet-control-layers-scrollbar');
5063              this._section.style.height = acceptableHeight + 'px';
5064          } else {
5065              removeClass(this._section, 'leaflet-control-layers-scrollbar');
5066          }
5067          this._checkDisabledLayers();
5068          return this;
5069      },
5070  
5071      // @method collapse(): this
5072      // Collapse the control container if expanded.
5073      collapse: function () {
5074          removeClass(this._container, 'leaflet-control-layers-expanded');
5075          return this;
5076      },
5077  
5078      _initLayout: function () {
5079          var className = 'leaflet-control-layers',
5080              container = this._container = create$1('div', className),
5081              collapsed = this.options.collapsed;
5082  
5083          // makes this work on IE touch devices by stopping it from firing a mouseout event when the touch is released
5084          container.setAttribute('aria-haspopup', true);
5085  
5086          disableClickPropagation(container);
5087          disableScrollPropagation(container);
5088  
5089          var section = this._section = create$1('section', className + '-list');
5090  
5091          if (collapsed) {
5092              this._map.on('click', this.collapse, this);
5093  
5094              if (!android) {
5095                  on(container, {
5096                      mouseenter: this.expand,
5097                      mouseleave: this.collapse
5098                  }, this);
5099              }
5100          }
5101  
5102          var link = this._layersLink = create$1('a', className + '-toggle', container);
5103          link.href = '#';
5104          link.title = 'Layers';
5105  
5106          if (touch) {
5107              on(link, 'click', stop);
5108              on(link, 'click', this.expand, this);
5109          } else {
5110              on(link, 'focus', this.expand, this);
5111          }
5112  
5113          if (!collapsed) {
5114              this.expand();
5115          }
5116  
5117          this._baseLayersList = create$1('div', className + '-base', section);
5118          this._separator = create$1('div', className + '-separator', section);
5119          this._overlaysList = create$1('div', className + '-overlays', section);
5120  
5121          container.appendChild(section);
5122      },
5123  
5124      _getLayer: function (id) {
5125          for (var i = 0; i < this._layers.length; i++) {
5126  
5127              if (this._layers[i] && stamp(this._layers[i].layer) === id) {
5128                  return this._layers[i];
5129              }
5130          }
5131      },
5132  
5133      _addLayer: function (layer, name, overlay) {
5134          if (this._map) {
5135              layer.on('add remove', this._onLayerChange, this);
5136          }
5137  
5138          this._layers.push({
5139              layer: layer,
5140              name: name,
5141              overlay: overlay
5142          });
5143  
5144          if (this.options.sortLayers) {
5145              this._layers.sort(bind(function (a, b) {
5146                  return this.options.sortFunction(a.layer, b.layer, a.name, b.name);
5147              }, this));
5148          }
5149  
5150          if (this.options.autoZIndex && layer.setZIndex) {
5151              this._lastZIndex++;
5152              layer.setZIndex(this._lastZIndex);
5153          }
5154  
5155          this._expandIfNotCollapsed();
5156      },
5157  
5158      _update: function () {
5159          if (!this._container) { return this; }
5160  
5161          empty(this._baseLayersList);
5162          empty(this._overlaysList);
5163  
5164          this._layerControlInputs = [];
5165          var baseLayersPresent, overlaysPresent, i, obj, baseLayersCount = 0;
5166  
5167          for (i = 0; i < this._layers.length; i++) {
5168              obj = this._layers[i];
5169              this._addItem(obj);
5170              overlaysPresent = overlaysPresent || obj.overlay;
5171              baseLayersPresent = baseLayersPresent || !obj.overlay;
5172              baseLayersCount += !obj.overlay ? 1 : 0;
5173          }
5174  
5175          // Hide base layers section if there's only one layer.
5176          if (this.options.hideSingleBase) {
5177              baseLayersPresent = baseLayersPresent && baseLayersCount > 1;
5178              this._baseLayersList.style.display = baseLayersPresent ? '' : 'none';
5179          }
5180  
5181          this._separator.style.display = overlaysPresent && baseLayersPresent ? '' : 'none';
5182  
5183          return this;
5184      },
5185  
5186      _onLayerChange: function (e) {
5187          if (!this._handlingClick) {
5188              this._update();
5189          }
5190  
5191          var obj = this._getLayer(stamp(e.target));
5192  
5193          // @namespace Map
5194          // @section Layer events
5195          // @event baselayerchange: LayersControlEvent
5196          // Fired when the base layer is changed through the [layer control](#control-layers).
5197          // @event overlayadd: LayersControlEvent
5198          // Fired when an overlay is selected through the [layer control](#control-layers).
5199          // @event overlayremove: LayersControlEvent
5200          // Fired when an overlay is deselected through the [layer control](#control-layers).
5201          // @namespace Control.Layers
5202          var type = obj.overlay ?
5203              (e.type === 'add' ? 'overlayadd' : 'overlayremove') :
5204              (e.type === 'add' ? 'baselayerchange' : null);
5205  
5206          if (type) {
5207              this._map.fire(type, obj);
5208          }
5209      },
5210  
5211      // IE7 bugs out if you create a radio dynamically, so you have to do it this hacky way (see http://bit.ly/PqYLBe)
5212      _createRadioElement: function (name, checked) {
5213  
5214          var radioHtml = '<input type="radio" class="leaflet-control-layers-selector" name="' +
5215                  name + '"' + (checked ? ' checked="checked"' : '') + '/>';
5216  
5217          var radioFragment = document.createElement('div');
5218          radioFragment.innerHTML = radioHtml;
5219  
5220          return radioFragment.firstChild;
5221      },
5222  
5223      _addItem: function (obj) {
5224          var label = document.createElement('label'),
5225              checked = this._map.hasLayer(obj.layer),
5226              input;
5227  
5228          if (obj.overlay) {
5229              input = document.createElement('input');
5230              input.type = 'checkbox';
5231              input.className = 'leaflet-control-layers-selector';
5232              input.defaultChecked = checked;
5233          } else {
5234              input = this._createRadioElement('leaflet-base-layers_' + stamp(this), checked);
5235          }
5236  
5237          this._layerControlInputs.push(input);
5238          input.layerId = stamp(obj.layer);
5239  
5240          on(input, 'click', this._onInputClick, this);
5241  
5242          var name = document.createElement('span');
5243          name.innerHTML = ' ' + obj.name;
5244  
5245          // Helps from preventing layer control flicker when checkboxes are disabled
5246          // https://github.com/Leaflet/Leaflet/issues/2771
5247          var holder = document.createElement('div');
5248  
5249          label.appendChild(holder);
5250          holder.appendChild(input);
5251          holder.appendChild(name);
5252  
5253          var container = obj.overlay ? this._overlaysList : this._baseLayersList;
5254          container.appendChild(label);
5255  
5256          this._checkDisabledLayers();
5257          return label;
5258      },
5259  
5260      _onInputClick: function () {
5261          var inputs = this._layerControlInputs,
5262              input, layer;
5263          var addedLayers = [],
5264              removedLayers = [];
5265  
5266          this._handlingClick = true;
5267  
5268          for (var i = inputs.length - 1; i >= 0; i--) {
5269              input = inputs[i];
5270              layer = this._getLayer(input.layerId).layer;
5271  
5272              if (input.checked) {
5273                  addedLayers.push(layer);
5274              } else if (!input.checked) {
5275                  removedLayers.push(layer);
5276              }
5277          }
5278  
5279          // Bugfix issue 2318: Should remove all old layers before readding new ones
5280          for (i = 0; i < removedLayers.length; i++) {
5281              if (this._map.hasLayer(removedLayers[i])) {
5282                  this._map.removeLayer(removedLayers[i]);
5283              }
5284          }
5285          for (i = 0; i < addedLayers.length; i++) {
5286              if (!this._map.hasLayer(addedLayers[i])) {
5287                  this._map.addLayer(addedLayers[i]);
5288              }
5289          }
5290  
5291          this._handlingClick = false;
5292  
5293          this._refocusOnMap();
5294      },
5295  
5296      _checkDisabledLayers: function () {
5297          var inputs = this._layerControlInputs,
5298              input,
5299              layer,
5300              zoom = this._map.getZoom();
5301  
5302          for (var i = inputs.length - 1; i >= 0; i--) {
5303              input = inputs[i];
5304              layer = this._getLayer(input.layerId).layer;
5305              input.disabled = (layer.options.minZoom !== undefined && zoom < layer.options.minZoom) ||
5306                               (layer.options.maxZoom !== undefined && zoom > layer.options.maxZoom);
5307  
5308          }
5309      },
5310  
5311      _expandIfNotCollapsed: function () {
5312          if (this._map && !this.options.collapsed) {
5313              this.expand();
5314          }
5315          return this;
5316      },
5317  
5318      _expand: function () {
5319          // Backward compatibility, remove me in 1.1.
5320          return this.expand();
5321      },
5322  
5323      _collapse: function () {
5324          // Backward compatibility, remove me in 1.1.
5325          return this.collapse();
5326      }
5327  
5328  });
5329  
5330  
5331  // @factory L.control.layers(baselayers?: Object, overlays?: Object, options?: Control.Layers options)
5332  // 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.
5333  var layers = function (baseLayers, overlays, options) {
5334      return new Layers(baseLayers, overlays, options);
5335  };
5336  
5337  /*
5338   * @class Control.Zoom
5339   * @aka L.Control.Zoom
5340   * @inherits Control
5341   *
5342   * 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`.
5343   */
5344  
5345  var Zoom = Control.extend({
5346      // @section
5347      // @aka Control.Zoom options
5348      options: {
5349          position: 'topleft',
5350  
5351          // @option zoomInText: String = '+'
5352          // The text set on the 'zoom in' button.
5353          zoomInText: '+',
5354  
5355          // @option zoomInTitle: String = 'Zoom in'
5356          // The title set on the 'zoom in' button.
5357          zoomInTitle: 'Zoom in',
5358  
5359          // @option zoomOutText: String = '&#x2212;'
5360          // The text set on the 'zoom out' button.
5361          zoomOutText: '&#x2212;',
5362  
5363          // @option zoomOutTitle: String = 'Zoom out'
5364          // The title set on the 'zoom out' button.
5365          zoomOutTitle: 'Zoom out'
5366      },
5367  
5368      onAdd: function (map) {
5369          var zoomName = 'leaflet-control-zoom',
5370              container = create$1('div', zoomName + ' leaflet-bar'),
5371              options = this.options;
5372  
5373          this._zoomInButton  = this._createButton(options.zoomInText, options.zoomInTitle,
5374                  zoomName + '-in',  container, this._zoomIn);
5375          this._zoomOutButton = this._createButton(options.zoomOutText, options.zoomOutTitle,
5376                  zoomName + '-out', container, this._zoomOut);
5377  
5378          this._updateDisabled();
5379          map.on('zoomend zoomlevelschange', this._updateDisabled, this);
5380  
5381          return container;
5382      },
5383  
5384      onRemove: function (map) {
5385          map.off('zoomend zoomlevelschange', this._updateDisabled, this);
5386      },
5387  
5388      disable: function () {
5389          this._disabled = true;
5390          this._updateDisabled();
5391          return this;
5392      },
5393  
5394      enable: function () {
5395          this._disabled = false;
5396          this._updateDisabled();
5397          return this;
5398      },
5399  
5400      _zoomIn: function (e) {
5401          if (!this._disabled && this._map._zoom < this._map.getMaxZoom()) {
5402              this._map.zoomIn(this._map.options.zoomDelta * (e.shiftKey ? 3 : 1));
5403          }
5404      },
5405  
5406      _zoomOut: function (e) {
5407          if (!this._disabled && this._map._zoom > this._map.getMinZoom()) {
5408              this._map.zoomOut(this._map.options.zoomDelta * (e.shiftKey ? 3 : 1));
5409          }
5410      },
5411  
5412      _createButton: function (html, title, className, container, fn) {
5413          var link = create$1('a', className, container);
5414          link.innerHTML = html;
5415          link.href = '#';
5416          link.title = title;
5417  
5418          /*
5419           * Will force screen readers like VoiceOver to read this as "Zoom in - button"
5420           */
5421          link.setAttribute('role', 'button');
5422          link.setAttribute('aria-label', title);
5423  
5424          disableClickPropagation(link);
5425          on(link, 'click', stop);
5426          on(link, 'click', fn, this);
5427          on(link, 'click', this._refocusOnMap, this);
5428  
5429          return link;
5430      },
5431  
5432      _updateDisabled: function () {
5433          var map = this._map,
5434              className = 'leaflet-disabled';
5435  
5436          removeClass(this._zoomInButton, className);
5437          removeClass(this._zoomOutButton, className);
5438  
5439          if (this._disabled || map._zoom === map.getMinZoom()) {
5440              addClass(this._zoomOutButton, className);
5441          }
5442          if (this._disabled || map._zoom === map.getMaxZoom()) {
5443              addClass(this._zoomInButton, className);
5444          }
5445      }
5446  });
5447  
5448  // @namespace Map
5449  // @section Control options
5450  // @option zoomControl: Boolean = true
5451  // Whether a [zoom control](#control-zoom) is added to the map by default.
5452  Map.mergeOptions({
5453      zoomControl: true
5454  });
5455  
5456  Map.addInitHook(function () {
5457      if (this.options.zoomControl) {
5458          // @section Controls
5459          // @property zoomControl: Control.Zoom
5460          // The default zoom control (only available if the
5461          // [`zoomControl` option](#map-zoomcontrol) was `true` when creating the map).
5462          this.zoomControl = new Zoom();
5463          this.addControl(this.zoomControl);
5464      }
5465  });
5466  
5467  // @namespace Control.Zoom
5468  // @factory L.control.zoom(options: Control.Zoom options)
5469  // Creates a zoom control
5470  var zoom = function (options) {
5471      return new Zoom(options);
5472  };
5473  
5474  /*
5475   * @class Control.Scale
5476   * @aka L.Control.Scale
5477   * @inherits Control
5478   *
5479   * 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`.
5480   *
5481   * @example
5482   *
5483   * ```js
5484   * L.control.scale().addTo(map);
5485   * ```
5486   */
5487  
5488  var Scale = Control.extend({
5489      // @section
5490      // @aka Control.Scale options
5491      options: {
5492          position: 'bottomleft',
5493  
5494          // @option maxWidth: Number = 100
5495          // Maximum width of the control in pixels. The width is set dynamically to show round values (e.g. 100, 200, 500).
5496          maxWidth: 100,
5497  
5498          // @option metric: Boolean = True
5499          // Whether to show the metric scale line (m/km).
5500          metric: true,
5501  
5502          // @option imperial: Boolean = True
5503          // Whether to show the imperial scale line (mi/ft).
5504          imperial: true
5505  
5506          // @option updateWhenIdle: Boolean = false
5507          // If `true`, the control is updated on [`moveend`](#map-moveend), otherwise it's always up-to-date (updated on [`move`](#map-move)).
5508      },
5509  
5510      onAdd: function (map) {
5511          var className = 'leaflet-control-scale',
5512              container = create$1('div', className),
5513              options = this.options;
5514  
5515          this._addScales(options, className + '-line', container);
5516  
5517          map.on(options.updateWhenIdle ? 'moveend' : 'move', this._update, this);
5518          map.whenReady(this._update, this);
5519  
5520          return container;
5521      },
5522  
5523      onRemove: function (map) {
5524          map.off(this.options.updateWhenIdle ? 'moveend' : 'move', this._update, this);
5525      },
5526  
5527      _addScales: function (options, className, container) {
5528          if (options.metric) {
5529              this._mScale = create$1('div', className, container);
5530          }
5531          if (options.imperial) {
5532              this._iScale = create$1('div', className, container);
5533          }
5534      },
5535  
5536      _update: function () {
5537          var map = this._map,
5538              y = map.getSize().y / 2;
5539  
5540          var maxMeters = map.distance(
5541              map.containerPointToLatLng([0, y]),
5542              map.containerPointToLatLng([this.options.maxWidth, y]));
5543  
5544          this._updateScales(maxMeters);
5545      },
5546  
5547      _updateScales: function (maxMeters) {
5548          if (this.options.metric && maxMeters) {
5549              this._updateMetric(maxMeters);
5550          }
5551          if (this.options.imperial && maxMeters) {
5552              this._updateImperial(maxMeters);
5553          }
5554      },
5555  
5556      _updateMetric: function (maxMeters) {
5557          var meters = this._getRoundNum(maxMeters),
5558              label = meters < 1000 ? meters + ' m' : (meters / 1000) + ' km';
5559  
5560          this._updateScale(this._mScale, label, meters / maxMeters);
5561      },
5562  
5563      _updateImperial: function (maxMeters) {
5564          var maxFeet = maxMeters * 3.2808399,
5565              maxMiles, miles, feet;
5566  
5567          if (maxFeet > 5280) {
5568              maxMiles = maxFeet / 5280;
5569              miles = this._getRoundNum(maxMiles);
5570              this._updateScale(this._iScale, miles + ' mi', miles / maxMiles);
5571  
5572          } else {
5573              feet = this._getRoundNum(maxFeet);
5574              this._updateScale(this._iScale, feet + ' ft', feet / maxFeet);
5575          }
5576      },
5577  
5578      _updateScale: function (scale, text, ratio) {
5579          scale.style.width = Math.round(this.options.maxWidth * ratio) + 'px';
5580          scale.innerHTML = text;
5581      },
5582  
5583      _getRoundNum: function (num) {
5584          var pow10 = Math.pow(10, (Math.floor(num) + '').length - 1),
5585              d = num / pow10;
5586  
5587          d = d >= 10 ? 10 :
5588              d >= 5 ? 5 :
5589              d >= 3 ? 3 :
5590              d >= 2 ? 2 : 1;
5591  
5592          return pow10 * d;
5593      }
5594  });
5595  
5596  
5597  // @factory L.control.scale(options?: Control.Scale options)
5598  // Creates an scale control with the given options.
5599  var scale = function (options) {
5600      return new Scale(options);
5601  };
5602  
5603  /*
5604   * @class Control.Attribution
5605   * @aka L.Control.Attribution
5606   * @inherits Control
5607   *
5608   * 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.
5609   */
5610  
5611  var Attribution = Control.extend({
5612      // @section
5613      // @aka Control.Attribution options
5614      options: {
5615          position: 'bottomright',
5616  
5617          // @option prefix: String = 'Leaflet'
5618          // The HTML text shown before the attributions. Pass `false` to disable.
5619          prefix: '<a href="https://leafletjs.com" title="A JS library for interactive maps">Leaflet</a>'
5620      },
5621  
5622      initialize: function (options) {
5623          setOptions(this, options);
5624  
5625          this._attributions = {};
5626      },
5627  
5628      onAdd: function (map) {
5629          map.attributionControl = this;
5630          this._container = create$1('div', 'leaflet-control-attribution');
5631          disableClickPropagation(this._container);
5632  
5633          // TODO ugly, refactor
5634          for (var i in map._layers) {
5635              if (map._layers[i].getAttribution) {
5636                  this.addAttribution(map._layers[i].getAttribution());
5637              }
5638          }
5639  
5640          this._update();
5641  
5642          return this._container;
5643      },
5644  
5645      // @method setPrefix(prefix: String): this
5646      // Sets the text before the attributions.
5647      setPrefix: function (prefix) {
5648          this.options.prefix = prefix;
5649          this._update();
5650          return this;
5651      },
5652  
5653      // @method addAttribution(text: String): this
5654      // Adds an attribution text (e.g. `'Vector data &copy; Mapbox'`).
5655      addAttribution: function (text) {
5656          if (!text) { return this; }
5657  
5658          if (!this._attributions[text]) {
5659              this._attributions[text] = 0;
5660          }
5661          this._attributions[text]++;
5662  
5663          this._update();
5664  
5665          return this;
5666      },
5667  
5668      // @method removeAttribution(text: String): this
5669      // Removes an attribution text.
5670      removeAttribution: function (text) {
5671          if (!text) { return this; }
5672  
5673          if (this._attributions[text]) {
5674              this._attributions[text]--;
5675              this._update();
5676          }
5677  
5678          return this;
5679      },
5680  
5681      _update: function () {
5682          if (!this._map) { return; }
5683  
5684          var attribs = [];
5685  
5686          for (var i in this._attributions) {
5687              if (this._attributions[i]) {
5688                  attribs.push(i);
5689              }
5690          }
5691  
5692          var prefixAndAttribs = [];
5693  
5694          if (this.options.prefix) {
5695              prefixAndAttribs.push(this.options.prefix);
5696          }
5697          if (attribs.length) {
5698              prefixAndAttribs.push(attribs.join(', '));
5699          }
5700  
5701          this._container.innerHTML = prefixAndAttribs.join(' | ');
5702      }
5703  });
5704  
5705  // @namespace Map
5706  // @section Control options
5707  // @option attributionControl: Boolean = true
5708  // Whether a [attribution control](#control-attribution) is added to the map by default.
5709  Map.mergeOptions({
5710      attributionControl: true
5711  });
5712  
5713  Map.addInitHook(function () {
5714      if (this.options.attributionControl) {
5715          new Attribution().addTo(this);
5716      }
5717  });
5718  
5719  // @namespace Control.Attribution
5720  // @factory L.control.attribution(options: Control.Attribution options)
5721  // Creates an attribution control.
5722  var attribution = function (options) {
5723      return new Attribution(options);
5724  };
5725  
5726  Control.Layers = Layers;
5727  Control.Zoom = Zoom;
5728  Control.Scale = Scale;
5729  Control.Attribution = Attribution;
5730  
5731  control.layers = layers;
5732  control.zoom = zoom;
5733  control.scale = scale;
5734  control.attribution = attribution;
5735  
5736  /*
5737      L.Handler is a base class for handler classes that are used internally to inject
5738      interaction features like dragging to classes like Map and Marker.
5739  */
5740  
5741  // @class Handler
5742  // @aka L.Handler
5743  // Abstract class for map interaction handlers
5744  
5745  var Handler = Class.extend({
5746      initialize: function (map) {
5747          this._map = map;
5748      },
5749  
5750      // @method enable(): this
5751      // Enables the handler
5752      enable: function () {
5753          if (this._enabled) { return this; }
5754  
5755          this._enabled = true;
5756          this.addHooks();
5757          return this;
5758      },
5759  
5760      // @method disable(): this
5761      // Disables the handler
5762      disable: function () {
5763          if (!this._enabled) { return this; }
5764  
5765          this._enabled = false;
5766          this.removeHooks();
5767          return this;
5768      },
5769  
5770      // @method enabled(): Boolean
5771      // Returns `true` if the handler is enabled
5772      enabled: function () {
5773          return !!this._enabled;
5774      }
5775  
5776      // @section Extension methods
5777      // Classes inheriting from `Handler` must implement the two following methods:
5778      // @method addHooks()
5779      // Called when the handler is enabled, should add event hooks.
5780      // @method removeHooks()
5781      // Called when the handler is disabled, should remove the event hooks added previously.
5782  });
5783  
5784  // @section There is static function which can be called without instantiating L.Handler:
5785  // @function addTo(map: Map, name: String): this
5786  // Adds a new Handler to the given map with the given name.
5787  Handler.addTo = function (map, name) {
5788      map.addHandler(name, this);
5789      return this;
5790  };
5791  
5792  var Mixin = {Events: Events};
5793  
5794  /*
5795   * @class Draggable
5796   * @aka L.Draggable
5797   * @inherits Evented
5798   *
5799   * A class for making DOM elements draggable (including touch support).
5800   * Used internally for map and marker dragging. Only works for elements
5801   * that were positioned with [`L.DomUtil.setPosition`](#domutil-setposition).
5802   *
5803   * @example
5804   * ```js
5805   * var draggable = new L.Draggable(elementToDrag);
5806   * draggable.enable();
5807   * ```
5808   */
5809  
5810  var START = touch ? 'touchstart mousedown' : 'mousedown';
5811  var END = {
5812      mousedown: 'mouseup',
5813      touchstart: 'touchend',
5814      pointerdown: 'touchend',
5815      MSPointerDown: 'touchend'
5816  };
5817  var MOVE = {
5818      mousedown: 'mousemove',
5819      touchstart: 'touchmove',
5820      pointerdown: 'touchmove',
5821      MSPointerDown: 'touchmove'
5822  };
5823  
5824  
5825  var Draggable = Evented.extend({
5826  
5827      options: {
5828          // @section
5829          // @aka Draggable options
5830          // @option clickTolerance: Number = 3
5831          // The max number of pixels a user can shift the mouse pointer during a click
5832          // for it to be considered a valid click (as opposed to a mouse drag).
5833          clickTolerance: 3
5834      },
5835  
5836      // @constructor L.Draggable(el: HTMLElement, dragHandle?: HTMLElement, preventOutline?: Boolean, options?: Draggable options)
5837      // Creates a `Draggable` object for moving `el` when you start dragging the `dragHandle` element (equals `el` itself by default).
5838      initialize: function (element, dragStartTarget, preventOutline$$1, options) {
5839          setOptions(this, options);
5840  
5841          this._element = element;
5842          this._dragStartTarget = dragStartTarget || element;
5843          this._preventOutline = preventOutline$$1;
5844      },
5845  
5846      // @method enable()
5847      // Enables the dragging ability
5848      enable: function () {
5849          if (this._enabled) { return; }
5850  
5851          on(this._dragStartTarget, START, this._onDown, this);
5852  
5853          this._enabled = true;
5854      },
5855  
5856      // @method disable()
5857      // Disables the dragging ability
5858      disable: function () {
5859          if (!this._enabled) { return; }
5860  
5861          // If we're currently dragging this draggable,
5862          // disabling it counts as first ending the drag.
5863          if (Draggable._dragging === this) {
5864              this.finishDrag();
5865          }
5866  
5867          off(this._dragStartTarget, START, this._onDown, this);
5868  
5869          this._enabled = false;
5870          this._moved = false;
5871      },
5872  
5873      _onDown: function (e) {
5874          // Ignore simulated events, since we handle both touch and
5875          // mouse explicitly; otherwise we risk getting duplicates of
5876          // touch events, see #4315.
5877          // Also ignore the event if disabled; this happens in IE11
5878          // under some circumstances, see #3666.
5879          if (e._simulated || !this._enabled) { return; }
5880  
5881          this._moved = false;
5882  
5883          if (hasClass(this._element, 'leaflet-zoom-anim')) { return; }
5884  
5885          if (Draggable._dragging || e.shiftKey || ((e.which !== 1) && (e.button !== 1) && !e.touches)) { return; }
5886          Draggable._dragging = this;  // Prevent dragging multiple objects at once.
5887  
5888          if (this._preventOutline) {
5889              preventOutline(this._element);
5890          }
5891  
5892          disableImageDrag();
5893          disableTextSelection();
5894  
5895          if (this._moving) { return; }
5896  
5897          // @event down: Event
5898          // Fired when a drag is about to start.
5899          this.fire('down');
5900  
5901          var first = e.touches ? e.touches[0] : e,
5902              sizedParent = getSizedParentNode(this._element);
5903  
5904          this._startPoint = new Point(first.clientX, first.clientY);
5905  
5906          // Cache the scale, so that we can continuously compensate for it during drag (_onMove).
5907          this._parentScale = getScale(sizedParent);
5908  
5909          on(document, MOVE[e.type], this._onMove, this);
5910          on(document, END[e.type], this._onUp, this);
5911      },
5912  
5913      _onMove: function (e) {
5914          // Ignore simulated events, since we handle both touch and
5915          // mouse explicitly; otherwise we risk getting duplicates of
5916          // touch events, see #4315.
5917          // Also ignore the event if disabled; this happens in IE11
5918          // under some circumstances, see #3666.
5919          if (e._simulated || !this._enabled) { return; }
5920  
5921          if (e.touches && e.touches.length > 1) {
5922              this._moved = true;
5923              return;
5924          }
5925  
5926          var first = (e.touches && e.touches.length === 1 ? e.touches[0] : e),
5927              offset = new Point(first.clientX, first.clientY)._subtract(this._startPoint);
5928  
5929          if (!offset.x && !offset.y) { return; }
5930          if (Math.abs(offset.x) + Math.abs(offset.y) < this.options.clickTolerance) { return; }
5931  
5932          // We assume that the parent container's position, border and scale do not change for the duration of the drag.
5933          // Therefore there is no need to account for the position and border (they are eliminated by the subtraction)
5934          // and we can use the cached value for the scale.
5935          offset.x /= this._parentScale.x;
5936          offset.y /= this._parentScale.y;
5937  
5938          preventDefault(e);
5939  
5940          if (!this._moved) {
5941              // @event dragstart: Event
5942              // Fired when a drag starts
5943              this.fire('dragstart');
5944  
5945              this._moved = true;
5946              this._startPos = getPosition(this._element).subtract(offset);
5947  
5948              addClass(document.body, 'leaflet-dragging');
5949  
5950              this._lastTarget = e.target || e.srcElement;
5951              // IE and Edge do not give the <use> element, so fetch it
5952              // if necessary
5953              if ((window.SVGElementInstance) && (this._lastTarget instanceof SVGElementInstance)) {
5954                  this._lastTarget = this._lastTarget.correspondingUseElement;
5955              }
5956              addClass(this._lastTarget, 'leaflet-drag-target');
5957          }
5958  
5959          this._newPos = this._startPos.add(offset);
5960          this._moving = true;
5961  
5962          cancelAnimFrame(this._animRequest);
5963          this._lastEvent = e;
5964          this._animRequest = requestAnimFrame(this._updatePosition, this, true);
5965      },
5966  
5967      _updatePosition: function () {
5968          var e = {originalEvent: this._lastEvent};
5969  
5970          // @event predrag: Event
5971          // Fired continuously during dragging *before* each corresponding
5972          // update of the element's position.
5973          this.fire('predrag', e);
5974          setPosition(this._element, this._newPos);
5975  
5976          // @event drag: Event
5977          // Fired continuously during dragging.
5978          this.fire('drag', e);
5979      },
5980  
5981      _onUp: function (e) {
5982          // Ignore simulated events, since we handle both touch and
5983          // mouse explicitly; otherwise we risk getting duplicates of
5984          // touch events, see #4315.
5985          // Also ignore the event if disabled; this happens in IE11
5986          // under some circumstances, see #3666.
5987          if (e._simulated || !this._enabled) { return; }
5988          this.finishDrag();
5989      },
5990  
5991      finishDrag: function () {
5992          removeClass(document.body, 'leaflet-dragging');
5993  
5994          if (this._lastTarget) {
5995              removeClass(this._lastTarget, 'leaflet-drag-target');
5996              this._lastTarget = null;
5997          }
5998  
5999          for (var i in MOVE) {
6000              off(document, MOVE[i], this._onMove, this);
6001              off(document, END[i], this._onUp, this);
6002          }
6003  
6004          enableImageDrag();
6005          enableTextSelection();
6006  
6007          if (this._moved && this._moving) {
6008              // ensure drag is not fired after dragend
6009              cancelAnimFrame(this._animRequest);
6010  
6011              // @event dragend: DragEndEvent
6012              // Fired when the drag ends.
6013              this.fire('dragend', {
6014                  distance: this._newPos.distanceTo(this._startPos)
6015              });
6016          }
6017  
6018          this._moving = false;
6019          Draggable._dragging = false;
6020      }
6021  
6022  });
6023  
6024  /*
6025   * @namespace LineUtil
6026   *
6027   * Various utility functions for polyline points processing, used by Leaflet internally to make polylines lightning-fast.
6028   */
6029  
6030  // Simplify polyline with vertex reduction and Douglas-Peucker simplification.
6031  // Improves rendering performance dramatically by lessening the number of points to draw.
6032  
6033  // @function simplify(points: Point[], tolerance: Number): Point[]
6034  // Dramatically reduces the number of points in a polyline while retaining
6035  // its shape and returns a new array of simplified points, using the
6036  // [Douglas-Peucker algorithm](http://en.wikipedia.org/wiki/Douglas-Peucker_algorithm).
6037  // Used for a huge performance boost when processing/displaying Leaflet polylines for
6038  // each zoom level and also reducing visual noise. tolerance affects the amount of
6039  // simplification (lesser value means higher quality but slower and with more points).
6040  // Also released as a separated micro-library [Simplify.js](http://mourner.github.com/simplify-js/).
6041  function simplify(points, tolerance) {
6042      if (!tolerance || !points.length) {
6043          return points.slice();
6044      }
6045  
6046      var sqTolerance = tolerance * tolerance;
6047  
6048          // stage 1: vertex reduction
6049          points = _reducePoints(points, sqTolerance);
6050  
6051          // stage 2: Douglas-Peucker simplification
6052          points = _simplifyDP(points, sqTolerance);
6053  
6054      return points;
6055  }
6056  
6057  // @function pointToSegmentDistance(p: Point, p1: Point, p2: Point): Number
6058  // Returns the distance between point `p` and segment `p1` to `p2`.
6059  function pointToSegmentDistance(p, p1, p2) {
6060      return Math.sqrt(_sqClosestPointOnSegment(p, p1, p2, true));
6061  }
6062  
6063  // @function closestPointOnSegment(p: Point, p1: Point, p2: Point): Number
6064  // Returns the closest point from a point `p` on a segment `p1` to `p2`.
6065  function closestPointOnSegment(p, p1, p2) {
6066      return _sqClosestPointOnSegment(p, p1, p2);
6067  }
6068  
6069  // Douglas-Peucker simplification, see http://en.wikipedia.org/wiki/Douglas-Peucker_algorithm
6070  function _simplifyDP(points, sqTolerance) {
6071  
6072      var len = points.length,
6073          ArrayConstructor = typeof Uint8Array !== undefined + '' ? Uint8Array : Array,
6074          markers = new ArrayConstructor(len);
6075  
6076          markers[0] = markers[len - 1] = 1;
6077  
6078      _simplifyDPStep(points, markers, sqTolerance, 0, len - 1);
6079  
6080      var i,
6081          newPoints = [];
6082  
6083      for (i = 0; i < len; i++) {
6084          if (markers[i]) {
6085              newPoints.push(points[i]);
6086          }
6087      }
6088  
6089      return newPoints;
6090  }
6091  
6092  function _simplifyDPStep(points, markers, sqTolerance, first, last) {
6093  
6094      var maxSqDist = 0,
6095      index, i, sqDist;
6096  
6097      for (i = first + 1; i <= last - 1; i++) {
6098          sqDist = _sqClosestPointOnSegment(points[i], points[first], points[last], true);
6099  
6100          if (sqDist > maxSqDist) {
6101              index = i;
6102              maxSqDist = sqDist;
6103          }
6104      }
6105  
6106      if (maxSqDist > sqTolerance) {
6107          markers[index] = 1;
6108  
6109          _simplifyDPStep(points, markers, sqTolerance, first, index);
6110          _simplifyDPStep(points, markers, sqTolerance, index, last);
6111      }
6112  }
6113  
6114  // reduce points that are too close to each other to a single point
6115  function _reducePoints(points, sqTolerance) {
6116      var reducedPoints = [points[0]];
6117  
6118      for (var i = 1, prev = 0, len = points.length; i < len; i++) {
6119          if (_sqDist(points[i], points[prev]) > sqTolerance) {
6120              reducedPoints.push(points[i]);
6121              prev = i;
6122          }
6123      }
6124      if (prev < len - 1) {
6125          reducedPoints.push(points[len - 1]);
6126      }
6127      return reducedPoints;
6128  }
6129  
6130  var _lastCode;
6131  
6132  // @function clipSegment(a: Point, b: Point, bounds: Bounds, useLastCode?: Boolean, round?: Boolean): Point[]|Boolean
6133  // Clips the segment a to b by rectangular bounds with the
6134  // [Cohen-Sutherland algorithm](https://en.wikipedia.org/wiki/Cohen%E2%80%93Sutherland_algorithm)
6135  // (modifying the segment points directly!). Used by Leaflet to only show polyline
6136  // points that are on the screen or near, increasing performance.
6137  function clipSegment(a, b, bounds, useLastCode, round) {
6138      var codeA = useLastCode ? _lastCode : _getBitCode(a, bounds),
6139          codeB = _getBitCode(b, bounds),
6140  
6141          codeOut, p, newCode;
6142  
6143          // save 2nd code to avoid calculating it on the next segment
6144          _lastCode = codeB;
6145  
6146      while (true) {
6147          // if a,b is inside the clip window (trivial accept)
6148          if (!(codeA | codeB)) {
6149              return [a, b];
6150          }
6151  
6152          // if a,b is outside the clip window (trivial reject)
6153          if (codeA & codeB) {
6154              return false;
6155          }
6156  
6157          // other cases
6158          codeOut = codeA || codeB;
6159          p = _getEdgeIntersection(a, b, codeOut, bounds, round);
6160          newCode = _getBitCode(p, bounds);
6161  
6162          if (codeOut === codeA) {
6163              a = p;
6164              codeA = newCode;
6165          } else {
6166              b = p;
6167              codeB = newCode;
6168          }
6169      }
6170  }
6171  
6172  function _getEdgeIntersection(a, b, code, bounds, round) {
6173      var dx = b.x - a.x,
6174          dy = b.y - a.y,
6175          min = bounds.min,
6176          max = bounds.max,
6177          x, y;
6178  
6179      if (code & 8) { // top
6180          x = a.x + dx * (max.y - a.y) / dy;
6181          y = max.y;
6182  
6183      } else if (code & 4) { // bottom
6184          x = a.x + dx * (min.y - a.y) / dy;
6185          y = min.y;
6186  
6187      } else if (code & 2) { // right
6188          x = max.x;
6189          y = a.y + dy * (max.x - a.x) / dx;
6190  
6191      } else if (code & 1) { // left
6192          x = min.x;
6193          y = a.y + dy * (min.x - a.x) / dx;
6194      }
6195  
6196      return new Point(x, y, round);
6197  }
6198  
6199  function _getBitCode(p, bounds) {
6200      var code = 0;
6201  
6202      if (p.x < bounds.min.x) { // left
6203          code |= 1;
6204      } else if (p.x > bounds.max.x) { // right
6205          code |= 2;
6206      }
6207  
6208      if (p.y < bounds.min.y) { // bottom
6209          code |= 4;
6210      } else if (p.y > bounds.max.y) { // top
6211          code |= 8;
6212      }
6213  
6214      return code;
6215  }
6216  
6217  // square distance (to avoid unnecessary Math.sqrt calls)
6218  function _sqDist(p1, p2) {
6219      var dx = p2.x - p1.x,
6220          dy = p2.y - p1.y;
6221      return dx * dx + dy * dy;
6222  }
6223  
6224  // return closest point on segment or distance to that point
6225  function _sqClosestPointOnSegment(p, p1, p2, sqDist) {
6226      var x = p1.x,
6227          y = p1.y,
6228          dx = p2.x - x,
6229          dy = p2.y - y,
6230          dot = dx * dx + dy * dy,
6231          t;
6232  
6233      if (dot > 0) {
6234          t = ((p.x - x) * dx + (p.y - y) * dy) / dot;
6235  
6236          if (t > 1) {
6237              x = p2.x;
6238              y = p2.y;
6239          } else if (t > 0) {
6240              x += dx * t;
6241              y += dy * t;
6242          }
6243      }
6244  
6245      dx = p.x - x;
6246      dy = p.y - y;
6247  
6248      return sqDist ? dx * dx + dy * dy : new Point(x, y);
6249  }
6250  
6251  
6252  // @function isFlat(latlngs: LatLng[]): Boolean
6253  // Returns true if `latlngs` is a flat array, false is nested.
6254  function isFlat(latlngs) {
6255      return !isArray(latlngs[0]) || (typeof latlngs[0][0] !== 'object' && typeof latlngs[0][0] !== 'undefined');
6256  }
6257  
6258  function _flat(latlngs) {
6259      console.warn('Deprecated use of _flat, please use L.LineUtil.isFlat instead.');
6260      return isFlat(latlngs);
6261  }
6262  
6263  
6264  var LineUtil = (Object.freeze || Object)({
6265      simplify: simplify,
6266      pointToSegmentDistance: pointToSegmentDistance,
6267      closestPointOnSegment: closestPointOnSegment,
6268      clipSegment: clipSegment,
6269      _getEdgeIntersection: _getEdgeIntersection,
6270      _getBitCode: _getBitCode,
6271      _sqClosestPointOnSegment: _sqClosestPointOnSegment,
6272      isFlat: isFlat,
6273      _flat: _flat
6274  });
6275  
6276  /*
6277   * @namespace PolyUtil
6278   * Various utility functions for polygon geometries.
6279   */
6280  
6281  /* @function clipPolygon(points: Point[], bounds: Bounds, round?: Boolean): Point[]
6282   * 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)).
6283   * Used by Leaflet to only show polygon points that are on the screen or near, increasing
6284   * performance. Note that polygon points needs different algorithm for clipping
6285   * than polyline, so there's a separate method for it.
6286   */
6287  function clipPolygon(points, bounds, round) {
6288      var clippedPoints,
6289          edges = [1, 4, 2, 8],
6290          i, j, k,
6291          a, b,
6292          len, edge, p;
6293  
6294      for (i = 0, len = points.length; i < len; i++) {
6295          points[i]._code = _getBitCode(points[i], bounds);
6296      }
6297  
6298      // for each edge (left, bottom, right, top)
6299      for (k = 0; k < 4; k++) {
6300          edge = edges[k];
6301          clippedPoints = [];
6302  
6303          for (i = 0, len = points.length, j = len - 1; i < len; j = i++) {
6304              a = points[i];
6305              b = points[j];
6306  
6307              // if a is inside the clip window
6308              if (!(a._code & edge)) {
6309                  // if b is outside the clip window (a->b goes out of screen)
6310                  if (b._code & edge) {
6311                      p = _getEdgeIntersection(b, a, edge, bounds, round);
6312                      p._code = _getBitCode(p, bounds);
6313                      clippedPoints.push(p);
6314                  }
6315                  clippedPoints.push(a);
6316  
6317              // else if b is inside the clip window (a->b enters the screen)
6318              } else if (!(b._code & edge)) {
6319                  p = _getEdgeIntersection(b, a, edge, bounds, round);
6320                  p._code = _getBitCode(p, bounds);
6321                  clippedPoints.push(p);
6322              }
6323          }
6324          points = clippedPoints;
6325      }
6326  
6327      return points;
6328  }
6329  
6330  
6331  var PolyUtil = (Object.freeze || Object)({
6332      clipPolygon: clipPolygon
6333  });
6334  
6335  /*
6336   * @namespace Projection
6337   * @section
6338   * Leaflet comes with a set of already defined Projections out of the box:
6339   *
6340   * @projection L.Projection.LonLat
6341   *
6342   * Equirectangular, or Plate Carree projection — the most simple projection,
6343   * mostly used by GIS enthusiasts. Directly maps `x` as longitude, and `y` as
6344   * latitude. Also suitable for flat worlds, e.g. game maps. Used by the
6345   * `EPSG:4326` and `Simple` CRS.
6346   */
6347  
6348  var LonLat = {
6349      project: function (latlng) {
6350          return new Point(latlng.lng, latlng.lat);
6351      },
6352  
6353      unproject: function (point) {
6354          return new LatLng(point.y, point.x);
6355      },
6356  
6357      bounds: new Bounds([-180, -90], [180, 90])
6358  };
6359  
6360  /*
6361   * @namespace Projection
6362   * @projection L.Projection.Mercator
6363   *
6364   * Elliptical Mercator projection — more complex than Spherical Mercator. Assumes that Earth is an ellipsoid. Used by the EPSG:3395 CRS.
6365   */
6366  
6367  var Mercator = {
6368      R: 6378137,
6369      R_MINOR: 6356752.314245179,
6370  
6371      bounds: new Bounds([-20037508.34279, -15496570.73972], [20037508.34279, 18764656.23138]),
6372  
6373      project: function (latlng) {
6374          var d = Math.PI / 180,
6375              r = this.R,
6376              y = latlng.lat * d,
6377              tmp = this.R_MINOR / r,
6378              e = Math.sqrt(1 - tmp * tmp),
6379              con = e * Math.sin(y);
6380  
6381          var ts = Math.tan(Math.PI / 4 - y / 2) / Math.pow((1 - con) / (1 + con), e / 2);
6382          y = -r * Math.log(Math.max(ts, 1E-10));
6383  
6384          return new Point(latlng.lng * d * r, y);
6385      },
6386  
6387      unproject: function (point) {
6388          var d = 180 / Math.PI,
6389              r = this.R,
6390              tmp = this.R_MINOR / r,
6391              e = Math.sqrt(1 - tmp * tmp),
6392              ts = Math.exp(-point.y / r),
6393              phi = Math.PI / 2 - 2 * Math.atan(ts);
6394  
6395          for (var i = 0, dphi = 0.1, con; i < 15 && Math.abs(dphi) > 1e-7; i++) {
6396              con = e * Math.sin(phi);
6397              con = Math.pow((1 - con) / (1 + con), e / 2);
6398              dphi = Math.PI / 2 - 2 * Math.atan(ts * con) - phi;
6399              phi += dphi;
6400          }
6401  
6402          return new LatLng(phi * d, point.x * d / r);
6403      }
6404  };
6405  
6406  /*
6407   * @class Projection
6408  
6409   * An object with methods for projecting geographical coordinates of the world onto
6410   * a flat surface (and back). See [Map projection](http://en.wikipedia.org/wiki/Map_projection).
6411  
6412   * @property bounds: Bounds
6413   * The bounds (specified in CRS units) where the projection is valid
6414  
6415   * @method project(latlng: LatLng): Point
6416   * Projects geographical coordinates into a 2D point.
6417   * Only accepts actual `L.LatLng` instances, not arrays.
6418  
6419   * @method unproject(point: Point): LatLng
6420   * The inverse of `project`. Projects a 2D point into a geographical location.
6421   * Only accepts actual `L.Point` instances, not arrays.
6422  
6423   * Note that the projection instances do not inherit from Leafet's `Class` object,
6424   * and can't be instantiated. Also, new classes can't inherit from them,
6425   * and methods can't be added to them with the `include` function.
6426  
6427   */
6428  
6429  
6430  
6431  
6432  var index = (Object.freeze || Object)({
6433      LonLat: LonLat,
6434      Mercator: Mercator,
6435      SphericalMercator: SphericalMercator
6436  });
6437  
6438  /*
6439   * @namespace CRS
6440   * @crs L.CRS.EPSG3395
6441   *
6442   * Rarely used by some commercial tile providers. Uses Elliptical Mercator projection.
6443   */
6444  var EPSG3395 = extend({}, Earth, {
6445      code: 'EPSG:3395',
6446      projection: Mercator,
6447  
6448      transformation: (function () {
6449          var scale = 0.5 / (Math.PI * Mercator.R);
6450          return toTransformation(scale, 0.5, -scale, 0.5);
6451      }())
6452  });
6453  
6454  /*
6455   * @namespace CRS
6456   * @crs L.CRS.EPSG4326
6457   *
6458   * A common CRS among GIS enthusiasts. Uses simple Equirectangular projection.
6459   *
6460   * Leaflet 1.0.x complies with the [TMS coordinate scheme for EPSG:4326](https://wiki.osgeo.org/wiki/Tile_Map_Service_Specification#global-geodetic),
6461   * which is a breaking change from 0.7.x behaviour.  If you are using a `TileLayer`
6462   * with this CRS, ensure that there are two 256x256 pixel tiles covering the
6463   * whole earth at zoom level zero, and that the tile coordinate origin is (-180,+90),
6464   * or (-180,-90) for `TileLayer`s with [the `tms` option](#tilelayer-tms) set.
6465   */
6466  
6467  var EPSG4326 = extend({}, Earth, {
6468      code: 'EPSG:4326',
6469      projection: LonLat,
6470      transformation: toTransformation(1 / 180, 1, -1 / 180, 0.5)
6471  });
6472  
6473  /*
6474   * @namespace CRS
6475   * @crs L.CRS.Simple
6476   *
6477   * A simple CRS that maps longitude and latitude into `x` and `y` directly.
6478   * May be used for maps of flat surfaces (e.g. game maps). Note that the `y`
6479   * axis should still be inverted (going from bottom to top). `distance()` returns
6480   * simple euclidean distance.
6481   */
6482  
6483  var Simple = extend({}, CRS, {
6484      projection: LonLat,
6485      transformation: toTransformation(1, 0, -1, 0),
6486  
6487      scale: function (zoom) {
6488          return Math.pow(2, zoom);
6489      },
6490  
6491      zoom: function (scale) {
6492          return Math.log(scale) / Math.LN2;
6493      },
6494  
6495      distance: function (latlng1, latlng2) {
6496          var dx = latlng2.lng - latlng1.lng,
6497              dy = latlng2.lat - latlng1.lat;
6498  
6499          return Math.sqrt(dx * dx + dy * dy);
6500      },
6501  
6502      infinite: true
6503  });
6504  
6505  CRS.Earth = Earth;
6506  CRS.EPSG3395 = EPSG3395;
6507  CRS.EPSG3857 = EPSG3857;
6508  CRS.EPSG900913 = EPSG900913;
6509  CRS.EPSG4326 = EPSG4326;
6510  CRS.Simple = Simple;
6511  
6512  /*
6513   * @class Layer
6514   * @inherits Evented
6515   * @aka L.Layer
6516   * @aka ILayer
6517   *
6518   * A set of methods from the Layer base class that all Leaflet layers use.
6519   * Inherits all methods, options and events from `L.Evented`.
6520   *
6521   * @example
6522   *
6523   * ```js
6524   * var layer = L.marker(latlng).addTo(map);
6525   * layer.addTo(map);
6526   * layer.remove();
6527   * ```
6528   *
6529   * @event add: Event
6530   * Fired after the layer is added to a map
6531   *
6532   * @event remove: Event
6533   * Fired after the layer is removed from a map
6534   */
6535  
6536  
6537  var Layer = Evented.extend({
6538  
6539      // Classes extending `L.Layer` will inherit the following options:
6540      options: {
6541          // @option pane: String = 'overlayPane'
6542          // 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.
6543          pane: 'overlayPane',
6544  
6545          // @option attribution: String = null
6546          // 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.
6547          attribution: null,
6548  
6549          bubblingMouseEvents: true
6550      },
6551  
6552      /* @section
6553       * Classes extending `L.Layer` will inherit the following methods:
6554       *
6555       * @method addTo(map: Map|LayerGroup): this
6556       * Adds the layer to the given map or layer group.
6557       */
6558      addTo: function (map) {
6559          map.addLayer(this);
6560          return this;
6561      },
6562  
6563      // @method remove: this
6564      // Removes the layer from the map it is currently active on.
6565      remove: function () {
6566          return this.removeFrom(this._map || this._mapToAdd);
6567      },
6568  
6569      // @method removeFrom(map: Map): this
6570      // Removes the layer from the given map
6571      removeFrom: function (obj) {
6572          if (obj) {
6573              obj.removeLayer(this);
6574          }
6575          return this;
6576      },
6577  
6578      // @method getPane(name? : String): HTMLElement
6579      // Returns the `HTMLElement` representing the named pane on the map. If `name` is omitted, returns the pane for this layer.
6580      getPane: function (name) {
6581          return this._map.getPane(name ? (this.options[name] || name) : this.options.pane);
6582      },
6583  
6584      addInteractiveTarget: function (targetEl) {
6585          this._map._targets[stamp(targetEl)] = this;
6586          return this;
6587      },
6588  
6589      removeInteractiveTarget: function (targetEl) {
6590          delete this._map._targets[stamp(targetEl)];
6591          return this;
6592      },
6593  
6594      // @method getAttribution: String
6595      // Used by the `attribution control`, returns the [attribution option](#gridlayer-attribution).
6596      getAttribution: function () {
6597          return this.options.attribution;
6598      },
6599  
6600      _layerAdd: function (e) {
6601          var map = e.target;
6602  
6603          // check in case layer gets added and then removed before the map is ready
6604          if (!map.hasLayer(this)) { return; }
6605  
6606          this._map = map;
6607          this._zoomAnimated = map._zoomAnimated;
6608  
6609          if (this.getEvents) {
6610              var events = this.getEvents();
6611              map.on(events, this);
6612              this.once('remove', function () {
6613                  map.off(events, this);
6614              }, this);
6615          }
6616  
6617          this.onAdd(map);
6618  
6619          if (this.getAttribution && map.attributionControl) {
6620              map.attributionControl.addAttribution(this.getAttribution());
6621          }
6622  
6623          this.fire('add');
6624          map.fire('layeradd', {layer: this});
6625      }
6626  });
6627  
6628  /* @section Extension methods
6629   * @uninheritable
6630   *
6631   * Every layer should extend from `L.Layer` and (re-)implement the following methods.
6632   *
6633   * @method onAdd(map: Map): this
6634   * 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).
6635   *
6636   * @method onRemove(map: Map): this
6637   * 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).
6638   *
6639   * @method getEvents(): Object
6640   * 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.
6641   *
6642   * @method getAttribution(): String
6643   * This optional method should return a string containing HTML to be shown on the `Attribution control` whenever the layer is visible.
6644   *
6645   * @method beforeAdd(map: Map): this
6646   * 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.
6647   */
6648  
6649  
6650  /* @namespace Map
6651   * @section Layer events
6652   *
6653   * @event layeradd: LayerEvent
6654   * Fired when a new layer is added to the map.
6655   *
6656   * @event layerremove: LayerEvent
6657   * Fired when some layer is removed from the map
6658   *
6659   * @section Methods for Layers and Controls
6660   */
6661  Map.include({
6662      // @method addLayer(layer: Layer): this
6663      // Adds the given layer to the map
6664      addLayer: function (layer) {
6665          if (!layer._layerAdd) {
6666              throw new Error('The provided object is not a Layer.');
6667          }
6668  
6669          var id = stamp(layer);
6670          if (this._layers[id]) { return this; }
6671          this._layers[id] = layer;
6672  
6673          layer._mapToAdd = this;
6674  
6675          if (layer.beforeAdd) {
6676              layer.beforeAdd(this);
6677          }
6678  
6679          this.whenReady(layer._layerAdd, layer);
6680  
6681          return this;
6682      },
6683  
6684      // @method removeLayer(layer: Layer): this
6685      // Removes the given layer from the map.
6686      removeLayer: function (layer) {
6687          var id = stamp(layer);
6688  
6689          if (!this._layers[id]) { return this; }
6690  
6691          if (this._loaded) {
6692              layer.onRemove(this);
6693          }
6694  
6695          if (layer.getAttribution && this.attributionControl) {
6696              this.attributionControl.removeAttribution(layer.getAttribution());
6697          }
6698  
6699          delete this._layers[id];
6700  
6701          if (this._loaded) {
6702              this.fire('layerremove', {layer: layer});
6703              layer.fire('remove');
6704          }
6705  
6706          layer._map = layer._mapToAdd = null;
6707  
6708          return this;
6709      },
6710  
6711      // @method hasLayer(layer: Layer): Boolean
6712      // Returns `true` if the given layer is currently added to the map
6713      hasLayer: function (layer) {
6714          return !!layer && (stamp(layer) in this._layers);
6715      },
6716  
6717      /* @method eachLayer(fn: Function, context?: Object): this
6718       * Iterates over the layers of the map, optionally specifying context of the iterator function.
6719       * ```
6720       * map.eachLayer(function(layer){
6721       *     layer.bindPopup('Hello');
6722       * });
6723       * ```
6724       */
6725      eachLayer: function (method, context) {
6726          for (var i in this._layers) {
6727              method.call(context, this._layers[i]);
6728          }
6729          return this;
6730      },
6731  
6732      _addLayers: function (layers) {
6733          layers = layers ? (isArray(layers) ? layers : [layers]) : [];
6734  
6735          for (var i = 0, len = layers.length; i < len; i++) {
6736              this.addLayer(layers[i]);
6737          }
6738      },
6739  
6740      _addZoomLimit: function (layer) {
6741          if (isNaN(layer.options.maxZoom) || !isNaN(layer.options.minZoom)) {
6742              this._zoomBoundLayers[stamp(layer)] = layer;
6743              this._updateZoomLevels();
6744          }
6745      },
6746  
6747      _removeZoomLimit: function (layer) {
6748          var id = stamp(layer);
6749  
6750          if (this._zoomBoundLayers[id]) {
6751              delete this._zoomBoundLayers[id];
6752              this._updateZoomLevels();
6753          }
6754      },
6755  
6756      _updateZoomLevels: function () {
6757          var minZoom = Infinity,
6758              maxZoom = -Infinity,
6759              oldZoomSpan = this._getZoomSpan();
6760  
6761          for (var i in this._zoomBoundLayers) {
6762              var options = this._zoomBoundLayers[i].options;
6763  
6764              minZoom = options.minZoom === undefined ? minZoom : Math.min(minZoom, options.minZoom);
6765              maxZoom = options.maxZoom === undefined ? maxZoom : Math.max(maxZoom, options.maxZoom);
6766          }
6767  
6768          this._layersMaxZoom = maxZoom === -Infinity ? undefined : maxZoom;
6769          this._layersMinZoom = minZoom === Infinity ? undefined : minZoom;
6770  
6771          // @section Map state change events
6772          // @event zoomlevelschange: Event
6773          // Fired when the number of zoomlevels on the map is changed due
6774          // to adding or removing a layer.
6775          if (oldZoomSpan !== this._getZoomSpan()) {
6776              this.fire('zoomlevelschange');
6777          }
6778  
6779          if (this.options.maxZoom === undefined && this._layersMaxZoom && this.getZoom() > this._layersMaxZoom) {
6780              this.setZoom(this._layersMaxZoom);
6781          }
6782          if (this.options.minZoom === undefined && this._layersMinZoom && this.getZoom() < this._layersMinZoom) {
6783              this.setZoom(this._layersMinZoom);
6784          }
6785      }
6786  });
6787  
6788  /*
6789   * @class LayerGroup
6790   * @aka L.LayerGroup
6791   * @inherits Layer
6792   *
6793   * Used to group several layers and handle them as one. If you add it to the map,
6794   * any layers added or removed from the group will be added/removed on the map as
6795   * well. Extends `Layer`.
6796   *
6797   * @example
6798   *
6799   * ```js
6800   * L.layerGroup([marker1, marker2])
6801   *     .addLayer(polyline)
6802   *     .addTo(map);
6803   * ```
6804   */
6805  
6806  var LayerGroup = Layer.extend({
6807  
6808      initialize: function (layers, options) {
6809          setOptions(this, options);
6810  
6811          this._layers = {};
6812  
6813          var i, len;
6814  
6815          if (layers) {
6816              for (i = 0, len = layers.length; i < len; i++) {
6817                  this.addLayer(layers[i]);
6818              }
6819          }
6820      },
6821  
6822      // @method addLayer(layer: Layer): this
6823      // Adds the given layer to the group.
6824      addLayer: function (layer) {
6825          var id = this.getLayerId(layer);
6826  
6827          this._layers[id] = layer;
6828  
6829          if (this._map) {
6830              this._map.addLayer(layer);
6831          }
6832  
6833          return this;
6834      },
6835  
6836      // @method removeLayer(layer: Layer): this
6837      // Removes the given layer from the group.
6838      // @alternative
6839      // @method removeLayer(id: Number): this
6840      // Removes the layer with the given internal ID from the group.
6841      removeLayer: function (layer) {
6842          var id = layer in this._layers ? layer : this.getLayerId(layer);
6843  
6844          if (this._map && this._layers[id]) {
6845              this._map.removeLayer(this._layers[id]);
6846          }
6847  
6848          delete this._layers[id];
6849  
6850          return this;
6851      },
6852  
6853      // @method hasLayer(layer: Layer): Boolean
6854      // Returns `true` if the given layer is currently added to the group.
6855      // @alternative
6856      // @method hasLayer(id: Number): Boolean
6857      // Returns `true` if the given internal ID is currently added to the group.
6858      hasLayer: function (layer) {
6859          return !!layer && (layer in this._layers || this.getLayerId(layer) in this._layers);
6860      },
6861  
6862      // @method clearLayers(): this
6863      // Removes all the layers from the group.
6864      clearLayers: function () {
6865          return this.eachLayer(this.removeLayer, this);
6866      },
6867  
6868      // @method invoke(methodName: String, …): this
6869      // Calls `methodName` on every layer contained in this group, passing any
6870      // additional parameters. Has no effect if the layers contained do not
6871      // implement `methodName`.
6872      invoke: function (methodName) {
6873          var args = Array.prototype.slice.call(arguments, 1),
6874              i, layer;
6875  
6876          for (i in this._layers) {
6877              layer = this._layers[i];
6878  
6879              if (layer[methodName]) {
6880                  layer[methodName].apply(layer, args);
6881              }
6882          }
6883  
6884          return this;
6885      },
6886  
6887      onAdd: function (map) {
6888          this.eachLayer(map.addLayer, map);
6889      },
6890  
6891      onRemove: function (map) {
6892          this.eachLayer(map.removeLayer, map);
6893      },
6894  
6895      // @method eachLayer(fn: Function, context?: Object): this
6896      // Iterates over the layers of the group, optionally specifying context of the iterator function.
6897      // ```js
6898      // group.eachLayer(function (layer) {
6899      //     layer.bindPopup('Hello');
6900      // });
6901      // ```
6902      eachLayer: function (method, context) {
6903          for (var i in this._layers) {
6904              method.call(context, this._layers[i]);
6905          }
6906          return this;
6907      },
6908  
6909      // @method getLayer(id: Number): Layer
6910      // Returns the layer with the given internal ID.
6911      getLayer: function (id) {
6912          return this._layers[id];
6913      },
6914  
6915      // @method getLayers(): Layer[]
6916      // Returns an array of all the layers added to the group.
6917      getLayers: function () {
6918          var layers = [];
6919          this.eachLayer(layers.push, layers);
6920          return layers;
6921      },
6922  
6923      // @method setZIndex(zIndex: Number): this
6924      // Calls `setZIndex` on every layer contained in this group, passing the z-index.
6925      setZIndex: function (zIndex) {
6926          return this.invoke('setZIndex', zIndex);
6927      },
6928  
6929      // @method getLayerId(layer: Layer): Number
6930      // Returns the internal ID for a layer
6931      getLayerId: function (layer) {
6932          return stamp(layer);
6933      }
6934  });
6935  
6936  
6937  // @factory L.layerGroup(layers?: Layer[], options?: Object)
6938  // Create a layer group, optionally given an initial set of layers and an `options` object.
6939  var layerGroup = function (layers, options) {
6940      return new LayerGroup(layers, options);
6941  };
6942  
6943  /*
6944   * @class FeatureGroup
6945   * @aka L.FeatureGroup
6946   * @inherits LayerGroup
6947   *
6948   * Extended `LayerGroup` that makes it easier to do the same thing to all its member layers:
6949   *  * [`bindPopup`](#layer-bindpopup) binds a popup to all of the layers at once (likewise with [`bindTooltip`](#layer-bindtooltip))
6950   *  * Events are propagated to the `FeatureGroup`, so if the group has an event
6951   * handler, it will handle events from any of the layers. This includes mouse events
6952   * and custom events.
6953   *  * Has `layeradd` and `layerremove` events
6954   *
6955   * @example
6956   *
6957   * ```js
6958   * L.featureGroup([marker1, marker2, polyline])
6959   *     .bindPopup('Hello world!')
6960   *     .on('click', function() { alert('Clicked on a member of the group!'); })
6961   *     .addTo(map);
6962   * ```
6963   */
6964  
6965  var FeatureGroup = LayerGroup.extend({
6966  
6967      addLayer: function (layer) {
6968          if (this.hasLayer(layer)) {
6969              return this;
6970          }
6971  
6972          layer.addEventParent(this);
6973  
6974          LayerGroup.prototype.addLayer.call(this, layer);
6975  
6976          // @event layeradd: LayerEvent
6977          // Fired when a layer is added to this `FeatureGroup`
6978          return this.fire('layeradd', {layer: layer});
6979      },
6980  
6981      removeLayer: function (layer) {
6982          if (!this.hasLayer(layer)) {
6983              return this;
6984          }
6985          if (layer in this._layers) {
6986              layer = this._layers[layer];
6987          }
6988  
6989          layer.removeEventParent(this);
6990  
6991          LayerGroup.prototype.removeLayer.call(this, layer);
6992  
6993          // @event layerremove: LayerEvent
6994          // Fired when a layer is removed from this `FeatureGroup`
6995          return this.fire('layerremove', {layer: layer});
6996      },
6997  
6998      // @method setStyle(style: Path options): this
6999      // Sets the given path options to each layer of the group that has a `setStyle` method.
7000      setStyle: function (style) {
7001          return this.invoke('setStyle', style);
7002      },
7003  
7004      // @method bringToFront(): this
7005      // Brings the layer group to the top of all other layers
7006      bringToFront: function () {
7007          return this.invoke('bringToFront');
7008      },
7009  
7010      // @method bringToBack(): this
7011      // Brings the layer group to the back of all other layers
7012      bringToBack: function () {
7013          return this.invoke('bringToBack');
7014      },
7015  
7016      // @method getBounds(): LatLngBounds
7017      // Returns the LatLngBounds of the Feature Group (created from bounds and coordinates of its children).
7018      getBounds: function () {
7019          var bounds = new LatLngBounds();
7020  
7021          for (var id in this._layers) {
7022              var layer = this._layers[id];
7023              bounds.extend(layer.getBounds ? layer.getBounds() : layer.getLatLng());
7024          }
7025          return bounds;
7026      }
7027  });
7028  
7029  // @factory L.featureGroup(layers: Layer[])
7030  // Create a feature group, optionally given an initial set of layers.
7031  var featureGroup = function (layers) {
7032      return new FeatureGroup(layers);
7033  };
7034  
7035  /*
7036   * @class Icon
7037   * @aka L.Icon
7038   *
7039   * Represents an icon to provide when creating a marker.
7040   *
7041   * @example
7042   *
7043   * ```js
7044   * var myIcon = L.icon({
7045   *     iconUrl: 'my-icon.png',
7046   *     iconRetinaUrl: 'my-icon@2x.png',
7047   *     iconSize: [38, 95],
7048   *     iconAnchor: [22, 94],
7049   *     popupAnchor: [-3, -76],
7050   *     shadowUrl: 'my-icon-shadow.png',
7051   *     shadowRetinaUrl: 'my-icon-shadow@2x.png',
7052   *     shadowSize: [68, 95],
7053   *     shadowAnchor: [22, 94]
7054   * });
7055   *
7056   * L.marker([50.505, 30.57], {icon: myIcon}).addTo(map);
7057   * ```
7058   *
7059   * `L.Icon.Default` extends `L.Icon` and is the blue icon Leaflet uses for markers by default.
7060   *
7061   */
7062  
7063  var Icon = Class.extend({
7064  
7065      /* @section
7066       * @aka Icon options
7067       *
7068       * @option iconUrl: String = null
7069       * **(required)** The URL to the icon image (absolute or relative to your script path).
7070       *
7071       * @option iconRetinaUrl: String = null
7072       * The URL to a retina sized version of the icon image (absolute or relative to your
7073       * script path). Used for Retina screen devices.
7074       *
7075       * @option iconSize: Point = null
7076       * Size of the icon image in pixels.
7077       *
7078       * @option iconAnchor: Point = null
7079       * The coordinates of the "tip" of the icon (relative to its top left corner). The icon
7080       * will be aligned so that this point is at the marker's geographical location. Centered
7081       * by default if size is specified, also can be set in CSS with negative margins.
7082       *
7083       * @option popupAnchor: Point = [0, 0]
7084       * The coordinates of the point from which popups will "open", relative to the icon anchor.
7085       *
7086       * @option tooltipAnchor: Point = [0, 0]
7087       * The coordinates of the point from which tooltips will "open", relative to the icon anchor.
7088       *
7089       * @option shadowUrl: String = null
7090       * The URL to the icon shadow image. If not specified, no shadow image will be created.
7091       *
7092       * @option shadowRetinaUrl: String = null
7093       *
7094       * @option shadowSize: Point = null
7095       * Size of the shadow image in pixels.
7096       *
7097       * @option shadowAnchor: Point = null
7098       * The coordinates of the "tip" of the shadow (relative to its top left corner) (the same
7099       * as iconAnchor if not specified).
7100       *
7101       * @option className: String = ''
7102       * A custom class name to assign to both icon and shadow images. Empty by default.
7103       */
7104  
7105      options: {
7106          popupAnchor: [0, 0],
7107          tooltipAnchor: [0, 0]
7108      },
7109  
7110      initialize: function (options) {
7111          setOptions(this, options);
7112      },
7113  
7114      // @method createIcon(oldIcon?: HTMLElement): HTMLElement
7115      // Called internally when the icon has to be shown, returns a `<img>` HTML element
7116      // styled according to the options.
7117      createIcon: function (oldIcon) {
7118          return this._createIcon('icon', oldIcon);
7119      },
7120  
7121      // @method createShadow(oldIcon?: HTMLElement): HTMLElement
7122      // As `createIcon`, but for the shadow beneath it.
7123      createShadow: function (oldIcon) {
7124          return this._createIcon('shadow', oldIcon);
7125      },
7126  
7127      _createIcon: function (name, oldIcon) {
7128          var src = this._getIconUrl(name);
7129  
7130          if (!src) {
7131              if (name === 'icon') {
7132                  throw new Error('iconUrl not set in Icon options (see the docs).');
7133              }
7134              return null;
7135          }
7136  
7137          var img = this._createImg(src, oldIcon && oldIcon.tagName === 'IMG' ? oldIcon : null);
7138          this._setIconStyles(img, name);
7139  
7140          return img;
7141      },
7142  
7143      _setIconStyles: function (img, name) {
7144          var options = this.options;
7145          var sizeOption = options[name + 'Size'];
7146  
7147          if (typeof sizeOption === 'number') {
7148              sizeOption = [sizeOption, sizeOption];
7149          }
7150  
7151          var size = toPoint(sizeOption),
7152              anchor = toPoint(name === 'shadow' && options.shadowAnchor || options.iconAnchor ||
7153                      size && size.divideBy(2, true));
7154  
7155          img.className = 'leaflet-marker-' + name + ' ' + (options.className || '');
7156  
7157          if (anchor) {
7158              img.style.marginLeft = (-anchor.x) + 'px';
7159              img.style.marginTop  = (-anchor.y) + 'px';
7160          }
7161  
7162          if (size) {
7163              img.style.width  = size.x + 'px';
7164              img.style.height = size.y + 'px';
7165          }
7166      },
7167  
7168      _createImg: function (src, el) {
7169          el = el || document.createElement('img');
7170          el.src = src;
7171          return el;
7172      },
7173  
7174      _getIconUrl: function (name) {
7175          return retina && this.options[name + 'RetinaUrl'] || this.options[name + 'Url'];
7176      }
7177  });
7178  
7179  
7180  // @factory L.icon(options: Icon options)
7181  // Creates an icon instance with the given options.
7182  function icon(options) {
7183      return new Icon(options);
7184  }
7185  
7186  /*
7187   * @miniclass Icon.Default (Icon)
7188   * @aka L.Icon.Default
7189   * @section
7190   *
7191   * A trivial subclass of `Icon`, represents the icon to use in `Marker`s when
7192   * no icon is specified. Points to the blue marker image distributed with Leaflet
7193   * releases.
7194   *
7195   * In order to customize the default icon, just change the properties of `L.Icon.Default.prototype.options`
7196   * (which is a set of `Icon options`).
7197   *
7198   * If you want to _completely_ replace the default icon, override the
7199   * `L.Marker.prototype.options.icon` with your own icon instead.
7200   */
7201  
7202  var IconDefault = Icon.extend({
7203  
7204      options: {
7205          iconUrl:       'marker-icon.png',
7206          iconRetinaUrl: 'marker-icon-2x.png',
7207          shadowUrl:     'marker-shadow.png',
7208          iconSize:    [25, 41],
7209          iconAnchor:  [12, 41],
7210          popupAnchor: [1, -34],
7211          tooltipAnchor: [16, -28],
7212          shadowSize:  [41, 41]
7213      },
7214  
7215      _getIconUrl: function (name) {
7216          if (!IconDefault.imagePath) {    // Deprecated, backwards-compatibility only
7217              IconDefault.imagePath = this._detectIconPath();
7218          }
7219  
7220          // @option imagePath: String
7221          // `Icon.Default` will try to auto-detect the location of the
7222          // blue icon images. If you are placing these images in a non-standard
7223          // way, set this option to point to the right path.
7224          return (this.options.imagePath || IconDefault.imagePath) + Icon.prototype._getIconUrl.call(this, name);
7225      },
7226  
7227      _detectIconPath: function () {
7228          var el = create$1('div',  'leaflet-default-icon-path', document.body);
7229          var path = getStyle(el, 'background-image') ||
7230                     getStyle(el, 'backgroundImage');    // IE8
7231  
7232          document.body.removeChild(el);
7233  
7234          if (path === null || path.indexOf('url') !== 0) {
7235              path = '';
7236          } else {
7237              path = path.replace(/^url\(["']?/, '').replace(/marker-icon\.png["']?\)$/, '');
7238          }
7239  
7240          return path;
7241      }
7242  });
7243  
7244  /*
7245   * L.Handler.MarkerDrag is used internally by L.Marker to make the markers draggable.
7246   */
7247  
7248  
7249  /* @namespace Marker
7250   * @section Interaction handlers
7251   *
7252   * 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:
7253   *
7254   * ```js
7255   * marker.dragging.disable();
7256   * ```
7257   *
7258   * @property dragging: Handler
7259   * Marker dragging handler (by both mouse and touch). Only valid when the marker is on the map (Otherwise set [`marker.options.draggable`](#marker-draggable)).
7260   */
7261  
7262  var MarkerDrag = Handler.extend({
7263      initialize: function (marker) {
7264          this._marker = marker;
7265      },
7266  
7267      addHooks: function () {
7268          var icon = this._marker._icon;
7269  
7270          if (!this._draggable) {
7271              this._draggable = new Draggable(icon, icon, true);
7272          }
7273  
7274          this._draggable.on({
7275              dragstart: this._onDragStart,
7276              predrag: this._onPreDrag,
7277              drag: this._onDrag,
7278              dragend: this._onDragEnd
7279          }, this).enable();
7280  
7281          addClass(icon, 'leaflet-marker-draggable');
7282      },
7283  
7284      removeHooks: function () {
7285          this._draggable.off({
7286              dragstart: this._onDragStart,
7287              predrag: this._onPreDrag,
7288              drag: this._onDrag,
7289              dragend: this._onDragEnd
7290          }, this).disable();
7291  
7292          if (this._marker._icon) {
7293              removeClass(this._marker._icon, 'leaflet-marker-draggable');
7294          }
7295      },
7296  
7297      moved: function () {
7298          return this._draggable && this._draggable._moved;
7299      },
7300  
7301      _adjustPan: function (e) {
7302          var marker = this._marker,
7303              map = marker._map,
7304              speed = this._marker.options.autoPanSpeed,
7305              padding = this._marker.options.autoPanPadding,
7306              iconPos = getPosition(marker._icon),
7307              bounds = map.getPixelBounds(),
7308              origin = map.getPixelOrigin();
7309  
7310          var panBounds = toBounds(
7311              bounds.min._subtract(origin).add(padding),
7312              bounds.max._subtract(origin).subtract(padding)
7313          );
7314  
7315          if (!panBounds.contains(iconPos)) {
7316              // Compute incremental movement
7317              var movement = toPoint(
7318                  (Math.max(panBounds.max.x, iconPos.x) - panBounds.max.x) / (bounds.max.x - panBounds.max.x) -
7319                  (Math.min(panBounds.min.x, iconPos.x) - panBounds.min.x) / (bounds.min.x - panBounds.min.x),
7320  
7321                  (Math.max(panBounds.max.y, iconPos.y) - panBounds.max.y) / (bounds.max.y - panBounds.max.y) -
7322                  (Math.min(panBounds.min.y, iconPos.y) - panBounds.min.y) / (bounds.min.y - panBounds.min.y)
7323              ).multiplyBy(speed);
7324  
7325              map.panBy(movement, {animate: false});
7326  
7327              this._draggable._newPos._add(movement);
7328              this._draggable._startPos._add(movement);
7329  
7330              setPosition(marker._icon, this._draggable._newPos);
7331              this._onDrag(e);
7332  
7333              this._panRequest = requestAnimFrame(this._adjustPan.bind(this, e));
7334          }
7335      },
7336  
7337      _onDragStart: function () {
7338          // @section Dragging events
7339          // @event dragstart: Event
7340          // Fired when the user starts dragging the marker.
7341  
7342          // @event movestart: Event
7343          // Fired when the marker starts moving (because of dragging).
7344  
7345          this._oldLatLng = this._marker.getLatLng();
7346          this._marker
7347              .closePopup()
7348              .fire('movestart')
7349              .fire('dragstart');
7350      },
7351  
7352      _onPreDrag: function (e) {
7353          if (this._marker.options.autoPan) {
7354              cancelAnimFrame(this._panRequest);
7355              this._panRequest = requestAnimFrame(this._adjustPan.bind(this, e));
7356          }
7357      },
7358  
7359      _onDrag: function (e) {
7360          var marker = this._marker,
7361              shadow = marker._shadow,
7362              iconPos = getPosition(marker._icon),
7363              latlng = marker._map.layerPointToLatLng(iconPos);
7364  
7365          // update shadow position
7366          if (shadow) {
7367              setPosition(shadow, iconPos);
7368          }
7369  
7370          marker._latlng = latlng;
7371          e.latlng = latlng;
7372          e.oldLatLng = this._oldLatLng;
7373  
7374          // @event drag: Event
7375          // Fired repeatedly while the user drags the marker.
7376          marker
7377              .fire('move', e)
7378              .fire('drag', e);
7379      },
7380  
7381      _onDragEnd: function (e) {
7382          // @event dragend: DragEndEvent
7383          // Fired when the user stops dragging the marker.
7384  
7385           cancelAnimFrame(this._panRequest);
7386  
7387          // @event moveend: Event
7388          // Fired when the marker stops moving (because of dragging).
7389          delete this._oldLatLng;
7390          this._marker
7391              .fire('moveend')
7392              .fire('dragend', e);
7393      }
7394  });
7395  
7396  /*
7397   * @class Marker
7398   * @inherits Interactive layer
7399   * @aka L.Marker
7400   * L.Marker is used to display clickable/draggable icons on the map. Extends `Layer`.
7401   *
7402   * @example
7403   *
7404   * ```js
7405   * L.marker([50.5, 30.5]).addTo(map);
7406   * ```
7407   */
7408  
7409  var Marker = Layer.extend({
7410  
7411      // @section
7412      // @aka Marker options
7413      options: {
7414          // @option icon: Icon = *
7415          // Icon instance to use for rendering the marker.
7416          // See [Icon documentation](#L.Icon) for details on how to customize the marker icon.
7417          // If not specified, a common instance of `L.Icon.Default` is used.
7418          icon: new IconDefault(),
7419  
7420          // Option inherited from "Interactive layer" abstract class
7421          interactive: true,
7422  
7423          // @option keyboard: Boolean = true
7424          // Whether the marker can be tabbed to with a keyboard and clicked by pressing enter.
7425          keyboard: true,
7426  
7427          // @option title: String = ''
7428          // Text for the browser tooltip that appear on marker hover (no tooltip by default).
7429          title: '',
7430  
7431          // @option alt: String = ''
7432          // Text for the `alt` attribute of the icon image (useful for accessibility).
7433          alt: '',
7434  
7435          // @option zIndexOffset: Number = 0
7436          // 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).
7437          zIndexOffset: 0,
7438  
7439          // @option opacity: Number = 1.0
7440          // The opacity of the marker.
7441          opacity: 1,
7442  
7443          // @option riseOnHover: Boolean = false
7444          // If `true`, the marker will get on top of others when you hover the mouse over it.
7445          riseOnHover: false,
7446  
7447          // @option riseOffset: Number = 250
7448          // The z-index offset used for the `riseOnHover` feature.
7449          riseOffset: 250,
7450  
7451          // @option pane: String = 'markerPane'
7452          // `Map pane` where the markers icon will be added.
7453          pane: 'markerPane',
7454  
7455          // @option pane: String = 'shadowPane'
7456          // `Map pane` where the markers shadow will be added.
7457          shadowPane: 'shadowPane',
7458  
7459          // @option bubblingMouseEvents: Boolean = false
7460          // When `true`, a mouse event on this marker will trigger the same event on the map
7461          // (unless [`L.DomEvent.stopPropagation`](#domevent-stoppropagation) is used).
7462          bubblingMouseEvents: false,
7463  
7464          // @section Draggable marker options
7465          // @option draggable: Boolean = false
7466          // Whether the marker is draggable with mouse/touch or not.
7467          draggable: false,
7468  
7469          // @option autoPan: Boolean = false
7470          // Whether to pan the map when dragging this marker near its edge or not.
7471          autoPan: false,
7472  
7473          // @option autoPanPadding: Point = Point(50, 50)
7474          // Distance (in pixels to the left/right and to the top/bottom) of the
7475          // map edge to start panning the map.
7476          autoPanPadding: [50, 50],
7477  
7478          // @option autoPanSpeed: Number = 10
7479          // Number of pixels the map should pan by.
7480          autoPanSpeed: 10
7481      },
7482  
7483      /* @section
7484       *
7485       * In addition to [shared layer methods](#Layer) like `addTo()` and `remove()` and [popup methods](#Popup) like bindPopup() you can also use the following methods:
7486       */
7487  
7488      initialize: function (latlng, options) {
7489          setOptions(this, options);
7490          this._latlng = toLatLng(latlng);
7491      },
7492  
7493      onAdd: function (map) {
7494          this._zoomAnimated = this._zoomAnimated && map.options.markerZoomAnimation;
7495  
7496          if (this._zoomAnimated) {
7497              map.on('zoomanim', this._animateZoom, this);
7498          }
7499  
7500          this._initIcon();
7501          this.update();
7502      },
7503  
7504      onRemove: function (map) {
7505          if (this.dragging && this.dragging.enabled()) {
7506              this.options.draggable = true;
7507              this.dragging.removeHooks();
7508          }
7509          delete this.dragging;
7510  
7511          if (this._zoomAnimated) {
7512              map.off('zoomanim', this._animateZoom, this);
7513          }
7514  
7515          this._removeIcon();
7516          this._removeShadow();
7517      },
7518  
7519      getEvents: function () {
7520          return {
7521              zoom: this.update,
7522              viewreset: this.update
7523          };
7524      },
7525  
7526      // @method getLatLng: LatLng
7527      // Returns the current geographical position of the marker.
7528      getLatLng: function () {
7529          return this._latlng;
7530      },
7531  
7532      // @method setLatLng(latlng: LatLng): this
7533      // Changes the marker position to the given point.
7534      setLatLng: function (latlng) {
7535          var oldLatLng = this._latlng;
7536          this._latlng = toLatLng(latlng);
7537          this.update();
7538  
7539          // @event move: Event
7540          // 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`.
7541          return this.fire('move', {oldLatLng: oldLatLng, latlng: this._latlng});
7542      },
7543  
7544      // @method setZIndexOffset(offset: Number): this
7545      // Changes the [zIndex offset](#marker-zindexoffset) of the marker.
7546      setZIndexOffset: function (offset) {
7547          this.options.zIndexOffset = offset;
7548          return this.update();
7549      },
7550  
7551      // @method getIcon: Icon
7552      // Returns the current icon used by the marker
7553      getIcon: function () {
7554          return this.options.icon;
7555      },
7556  
7557      // @method setIcon(icon: Icon): this
7558      // Changes the marker icon.
7559      setIcon: function (icon) {
7560  
7561          this.options.icon = icon;
7562  
7563          if (this._map) {
7564              this._initIcon();
7565              this.update();
7566          }
7567  
7568          if (this._popup) {
7569              this.bindPopup(this._popup, this._popup.options);
7570          }
7571  
7572          return this;
7573      },
7574  
7575      getElement: function () {
7576          return this._icon;
7577      },
7578  
7579      update: function () {
7580  
7581          if (this._icon && this._map) {
7582              var pos = this._map.latLngToLayerPoint(this._latlng).round();
7583              this._setPos(pos);
7584          }
7585  
7586          return this;
7587      },
7588  
7589      _initIcon: function () {
7590          var options = this.options,
7591              classToAdd = 'leaflet-zoom-' + (this._zoomAnimated ? 'animated' : 'hide');
7592  
7593          var icon = options.icon.createIcon(this._icon),
7594              addIcon = false;
7595  
7596          // if we're not reusing the icon, remove the old one and init new one
7597          if (icon !== this._icon) {
7598              if (this._icon) {
7599                  this._removeIcon();
7600              }
7601              addIcon = true;
7602  
7603              if (options.title) {
7604                  icon.title = options.title;
7605              }
7606  
7607              if (icon.tagName === 'IMG') {
7608                  icon.alt = options.alt || '';
7609              }
7610          }
7611  
7612          addClass(icon, classToAdd);
7613  
7614          if (options.keyboard) {
7615              icon.tabIndex = '0';
7616          }
7617  
7618          this._icon = icon;
7619  
7620          if (options.riseOnHover) {
7621              this.on({
7622                  mouseover: this._bringToFront,
7623                  mouseout: this._resetZIndex
7624              });
7625          }
7626  
7627          var newShadow = options.icon.createShadow(this._shadow),
7628              addShadow = false;
7629  
7630          if (newShadow !== this._shadow) {
7631              this._removeShadow();
7632              addShadow = true;
7633          }
7634  
7635          if (newShadow) {
7636              addClass(newShadow, classToAdd);
7637              newShadow.alt = '';
7638          }
7639          this._shadow = newShadow;
7640  
7641  
7642          if (options.opacity < 1) {
7643              this._updateOpacity();
7644          }
7645  
7646  
7647          if (addIcon) {
7648              this.getPane().appendChild(this._icon);
7649          }
7650          this._initInteraction();
7651          if (newShadow && addShadow) {
7652              this.getPane(options.shadowPane).appendChild(this._shadow);
7653          }
7654      },
7655  
7656      _removeIcon: function () {
7657          if (this.options.riseOnHover) {
7658              this.off({
7659                  mouseover: this._bringToFront,
7660                  mouseout: this._resetZIndex
7661              });
7662          }
7663  
7664          remove(this._icon);
7665          this.removeInteractiveTarget(this._icon);
7666  
7667          this._icon = null;
7668      },
7669  
7670      _removeShadow: function () {
7671          if (this._shadow) {
7672              remove(this._shadow);
7673          }
7674          this._shadow = null;
7675      },
7676  
7677      _setPos: function (pos) {
7678  
7679          if (this._icon) {
7680              setPosition(this._icon, pos);
7681          }
7682  
7683          if (this._shadow) {
7684              setPosition(this._shadow, pos);
7685          }
7686  
7687          this._zIndex = pos.y + this.options.zIndexOffset;
7688  
7689          this._resetZIndex();
7690      },
7691  
7692      _updateZIndex: function (offset) {
7693          if (this._icon) {
7694              this._icon.style.zIndex = this._zIndex + offset;
7695          }
7696      },
7697  
7698      _animateZoom: function (opt) {
7699          var pos = this._map._latLngToNewLayerPoint(this._latlng, opt.zoom, opt.center).round();
7700  
7701          this._setPos(pos);
7702      },
7703  
7704      _initInteraction: function () {
7705  
7706          if (!this.options.interactive) { return; }
7707  
7708          addClass(this._icon, 'leaflet-interactive');
7709  
7710          this.addInteractiveTarget(this._icon);
7711  
7712          if (MarkerDrag) {
7713              var draggable = this.options.draggable;
7714              if (this.dragging) {
7715                  draggable = this.dragging.enabled();
7716                  this.dragging.disable();
7717              }
7718  
7719              this.dragging = new MarkerDrag(this);
7720  
7721              if (draggable) {
7722                  this.dragging.enable();
7723              }
7724          }
7725      },
7726  
7727      // @method setOpacity(opacity: Number): this
7728      // Changes the opacity of the marker.
7729      setOpacity: function (opacity) {
7730          this.options.opacity = opacity;
7731          if (this._map) {
7732              this._updateOpacity();
7733          }
7734  
7735          return this;
7736      },
7737  
7738      _updateOpacity: function () {
7739          var opacity = this.options.opacity;
7740  
7741          if (this._icon) {
7742              setOpacity(this._icon, opacity);
7743          }
7744  
7745          if (this._shadow) {
7746              setOpacity(this._shadow, opacity);
7747          }
7748      },
7749  
7750      _bringToFront: function () {
7751          this._updateZIndex(this.options.riseOffset);
7752      },
7753  
7754      _resetZIndex: function () {
7755          this._updateZIndex(0);
7756      },
7757  
7758      _getPopupAnchor: function () {
7759          return this.options.icon.options.popupAnchor;
7760      },
7761  
7762      _getTooltipAnchor: function () {
7763          return this.options.icon.options.tooltipAnchor;
7764      }
7765  });
7766  
7767  
7768  // factory L.marker(latlng: LatLng, options? : Marker options)
7769  
7770  // @factory L.marker(latlng: LatLng, options? : Marker options)
7771  // Instantiates a Marker object given a geographical point and optionally an options object.
7772  function marker(latlng, options) {
7773      return new Marker(latlng, options);
7774  }
7775  
7776  /*
7777   * @class Path
7778   * @aka L.Path
7779   * @inherits Interactive layer
7780   *
7781   * An abstract class that contains options and constants shared between vector
7782   * overlays (Polygon, Polyline, Circle). Do not use it directly. Extends `Layer`.
7783   */
7784  
7785  var Path = Layer.extend({
7786  
7787      // @section
7788      // @aka Path options
7789      options: {
7790          // @option stroke: Boolean = true
7791          // Whether to draw stroke along the path. Set it to `false` to disable borders on polygons or circles.
7792          stroke: true,
7793  
7794          // @option color: String = '#3388ff'
7795          // Stroke color
7796          color: '#3388ff',
7797  
7798          // @option weight: Number = 3
7799          // Stroke width in pixels
7800          weight: 3,
7801  
7802          // @option opacity: Number = 1.0
7803          // Stroke opacity
7804          opacity: 1,
7805  
7806          // @option lineCap: String= 'round'
7807          // A string that defines [shape to be used at the end](https://developer.mozilla.org/docs/Web/SVG/Attribute/stroke-linecap) of the stroke.
7808          lineCap: 'round',
7809  
7810          // @option lineJoin: String = 'round'
7811          // A string that defines [shape to be used at the corners](https://developer.mozilla.org/docs/Web/SVG/Attribute/stroke-linejoin) of the stroke.
7812          lineJoin: 'round',
7813  
7814          // @option dashArray: String = null
7815          // 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).
7816          dashArray: null,
7817  
7818          // @option dashOffset: String = null
7819          // 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).
7820          dashOffset: null,
7821  
7822          // @option fill: Boolean = depends
7823          // Whether to fill the path with color. Set it to `false` to disable filling on polygons or circles.
7824          fill: false,
7825  
7826          // @option fillColor: String = *
7827          // Fill color. Defaults to the value of the [`color`](#path-color) option
7828          fillColor: null,
7829  
7830          // @option fillOpacity: Number = 0.2
7831          // Fill opacity.
7832          fillOpacity: 0.2,
7833  
7834          // @option fillRule: String = 'evenodd'
7835          // A string that defines [how the inside of a shape](https://developer.mozilla.org/docs/Web/SVG/Attribute/fill-rule) is determined.
7836          fillRule: 'evenodd',
7837  
7838          // className: '',
7839  
7840          // Option inherited from "Interactive layer" abstract class
7841          interactive: true,
7842  
7843          // @option bubblingMouseEvents: Boolean = true
7844          // When `true`, a mouse event on this path will trigger the same event on the map
7845          // (unless [`L.DomEvent.stopPropagation`](#domevent-stoppropagation) is used).
7846          bubblingMouseEvents: true
7847      },
7848  
7849      beforeAdd: function (map) {
7850          // Renderer is set here because we need to call renderer.getEvents
7851          // before this.getEvents.
7852          this._renderer = map.getRenderer(this);
7853      },
7854  
7855      onAdd: function () {
7856          this._renderer._initPath(this);
7857          this._reset();
7858          this._renderer._addPath(this);
7859      },
7860  
7861      onRemove: function () {
7862          this._renderer._removePath(this);
7863      },
7864  
7865      // @method redraw(): this
7866      // Redraws the layer. Sometimes useful after you changed the coordinates that the path uses.
7867      redraw: function () {
7868          if (this._map) {
7869              this._renderer._updatePath(this);
7870          }
7871          return this;
7872      },
7873  
7874      // @method setStyle(style: Path options): this
7875      // Changes the appearance of a Path based on the options in the `Path options` object.
7876      setStyle: function (style) {
7877          setOptions(this, style);
7878          if (this._renderer) {
7879              this._renderer._updateStyle(this);
7880              if (this.options.stroke && style && style.hasOwnProperty('weight')) {
7881                  this._updateBounds();
7882              }
7883          }
7884          return this;
7885      },
7886  
7887      // @method bringToFront(): this
7888      // Brings the layer to the top of all path layers.
7889      bringToFront: function () {
7890          if (this._renderer) {
7891              this._renderer._bringToFront(this);
7892          }
7893          return this;
7894      },
7895  
7896      // @method bringToBack(): this
7897      // Brings the layer to the bottom of all path layers.
7898      bringToBack: function () {
7899          if (this._renderer) {
7900              this._renderer._bringToBack(this);
7901          }
7902          return this;
7903      },
7904  
7905      getElement: function () {
7906          return this._path;
7907      },
7908  
7909      _reset: function () {
7910          // defined in child classes
7911          this._project();
7912          this._update();
7913      },
7914  
7915      _clickTolerance: function () {
7916          // used when doing hit detection for Canvas layers
7917          return (this.options.stroke ? this.options.weight / 2 : 0) + this._renderer.options.tolerance;
7918      }
7919  });
7920  
7921  /*
7922   * @class CircleMarker
7923   * @aka L.CircleMarker
7924   * @inherits Path
7925   *
7926   * A circle of a fixed size with radius specified in pixels. Extends `Path`.
7927   */
7928  
7929  var CircleMarker = Path.extend({
7930  
7931      // @section
7932      // @aka CircleMarker options
7933      options: {
7934          fill: true,
7935  
7936          // @option radius: Number = 10
7937          // Radius of the circle marker, in pixels
7938          radius: 10
7939      },
7940  
7941      initialize: function (latlng, options) {
7942          setOptions(this, options);
7943          this._latlng = toLatLng(latlng);
7944          this._radius = this.options.radius;
7945      },
7946  
7947      // @method setLatLng(latLng: LatLng): this
7948      // Sets the position of a circle marker to a new location.
7949      setLatLng: function (latlng) {
7950          var oldLatLng = this._latlng;
7951          this._latlng = toLatLng(latlng);
7952          this.redraw();
7953  
7954          // @event move: Event
7955          // Fired when the marker is moved via [`setLatLng`](#circlemarker-setlatlng). Old and new coordinates are included in event arguments as `oldLatLng`, `latlng`.
7956          return this.fire('move', {oldLatLng: oldLatLng, latlng: this._latlng});
7957      },
7958  
7959      // @method getLatLng(): LatLng
7960      // Returns the current geographical position of the circle marker
7961      getLatLng: function () {
7962          return this._latlng;
7963      },
7964  
7965      // @method setRadius(radius: Number): this
7966      // Sets the radius of a circle marker. Units are in pixels.
7967      setRadius: function (radius) {
7968          this.options.radius = this._radius = radius;
7969          return this.redraw();
7970      },
7971  
7972      // @method getRadius(): Number
7973      // Returns the current radius of the circle
7974      getRadius: function () {
7975          return this._radius;
7976      },
7977  
7978      setStyle : function (options) {
7979          var radius = options && options.radius || this._radius;
7980          Path.prototype.setStyle.call(this, options);
7981          this.setRadius(radius);
7982          return this;
7983      },
7984  
7985      _project: function () {
7986          this._point = this._map.latLngToLayerPoint(this._latlng);
7987          this._updateBounds();
7988      },
7989  
7990      _updateBounds: function () {
7991          var r = this._radius,
7992              r2 = this._radiusY || r,
7993              w = this._clickTolerance(),
7994              p = [r + w, r2 + w];
7995          this._pxBounds = new Bounds(this._point.subtract(p), this._point.add(p));
7996      },
7997  
7998      _update: function () {
7999          if (this._map) {
8000              this._updatePath();
8001          }
8002      },
8003  
8004      _updatePath: function () {
8005          this._renderer._updateCircle(this);
8006      },
8007  
8008      _empty: function () {
8009          return this._radius && !this._renderer._bounds.intersects(this._pxBounds);
8010      },
8011  
8012      // Needed by the `Canvas` renderer for interactivity
8013      _containsPoint: function (p) {
8014          return p.distanceTo(this._point) <= this._radius + this._clickTolerance();
8015      }
8016  });
8017  
8018  
8019  // @factory L.circleMarker(latlng: LatLng, options?: CircleMarker options)
8020  // Instantiates a circle marker object given a geographical point, and an optional options object.
8021  function circleMarker(latlng, options) {
8022      return new CircleMarker(latlng, options);
8023  }
8024  
8025  /*
8026   * @class Circle
8027   * @aka L.Circle
8028   * @inherits CircleMarker
8029   *
8030   * A class for drawing circle overlays on a map. Extends `CircleMarker`.
8031   *
8032   * It's an approximation and starts to diverge from a real circle closer to poles (due to projection distortion).
8033   *
8034   * @example
8035   *
8036   * ```js
8037   * L.circle([50.5, 30.5], {radius: 200}).addTo(map);
8038   * ```
8039   */
8040  
8041  var Circle = CircleMarker.extend({
8042  
8043      initialize: function (latlng, options, legacyOptions) {
8044          if (typeof options === 'number') {
8045              // Backwards compatibility with 0.7.x factory (latlng, radius, options?)
8046              options = extend({}, legacyOptions, {radius: options});
8047          }
8048          setOptions(this, options);
8049          this._latlng = toLatLng(latlng);
8050  
8051          if (isNaN(this.options.radius)) { throw new Error('Circle radius cannot be NaN'); }
8052  
8053          // @section
8054          // @aka Circle options
8055          // @option radius: Number; Radius of the circle, in meters.
8056          this._mRadius = this.options.radius;
8057      },
8058  
8059      // @method setRadius(radius: Number): this
8060      // Sets the radius of a circle. Units are in meters.
8061      setRadius: function (radius) {
8062          this._mRadius = radius;
8063          return this.redraw();
8064      },
8065  
8066      // @method getRadius(): Number
8067      // Returns the current radius of a circle. Units are in meters.
8068      getRadius: function () {
8069          return this._mRadius;
8070      },
8071  
8072      // @method getBounds(): LatLngBounds
8073      // Returns the `LatLngBounds` of the path.
8074      getBounds: function () {
8075          var half = [this._radius, this._radiusY || this._radius];
8076  
8077          return new LatLngBounds(
8078              this._map.layerPointToLatLng(this._point.subtract(half)),
8079              this._map.layerPointToLatLng(this._point.add(half)));
8080      },
8081  
8082      setStyle: Path.prototype.setStyle,
8083  
8084      _project: function () {
8085  
8086          var lng = this._latlng.lng,
8087              lat = this._latlng.lat,
8088              map = this._map,
8089              crs = map.options.crs;
8090  
8091          if (crs.distance === Earth.distance) {
8092              var d = Math.PI / 180,
8093                  latR = (this._mRadius / Earth.R) / d,
8094                  top = map.project([lat + latR, lng]),
8095                  bottom = map.project([lat - latR, lng]),
8096                  p = top.add(bottom).divideBy(2),
8097                  lat2 = map.unproject(p).lat,
8098                  lngR = Math.acos((Math.cos(latR * d) - Math.sin(lat * d) * Math.sin(lat2 * d)) /
8099                          (Math.cos(lat * d) * Math.cos(lat2 * d))) / d;
8100  
8101              if (isNaN(lngR) || lngR === 0) {
8102                  lngR = latR / Math.cos(Math.PI / 180 * lat); // Fallback for edge case, #2425
8103              }
8104  
8105              this._point = p.subtract(map.getPixelOrigin());
8106              this._radius = isNaN(lngR) ? 0 : p.x - map.project([lat2, lng - lngR]).x;
8107              this._radiusY = p.y - top.y;
8108  
8109          } else {
8110              var latlng2 = crs.unproject(crs.project(this._latlng).subtract([this._mRadius, 0]));
8111  
8112              this._point = map.latLngToLayerPoint(this._latlng);
8113              this._radius = this._point.x - map.latLngToLayerPoint(latlng2).x;
8114          }
8115  
8116          this._updateBounds();
8117      }
8118  });
8119  
8120  // @factory L.circle(latlng: LatLng, options?: Circle options)
8121  // Instantiates a circle object given a geographical point, and an options object
8122  // which contains the circle radius.
8123  // @alternative
8124  // @factory L.circle(latlng: LatLng, radius: Number, options?: Circle options)
8125  // Obsolete way of instantiating a circle, for compatibility with 0.7.x code.
8126  // Do not use in new applications or plugins.
8127  function circle(latlng, options, legacyOptions) {
8128      return new Circle(latlng, options, legacyOptions);
8129  }
8130  
8131  /*
8132   * @class Polyline
8133   * @aka L.Polyline
8134   * @inherits Path
8135   *
8136   * A class for drawing polyline overlays on a map. Extends `Path`.
8137   *
8138   * @example
8139   *
8140   * ```js
8141   * // create a red polyline from an array of LatLng points
8142   * var latlngs = [
8143   *     [45.51, -122.68],
8144   *     [37.77, -122.43],
8145   *     [34.04, -118.2]
8146   * ];
8147   *
8148   * var polyline = L.polyline(latlngs, {color: 'red'}).addTo(map);
8149   *
8150   * // zoom the map to the polyline
8151   * map.fitBounds(polyline.getBounds());
8152   * ```
8153   *
8154   * You can also pass a multi-dimensional array to represent a `MultiPolyline` shape:
8155   *
8156   * ```js
8157   * // create a red polyline from an array of arrays of LatLng points
8158   * var latlngs = [
8159   *     [[45.51, -122.68],
8160   *      [37.77, -122.43],
8161   *      [34.04, -118.2]],
8162   *     [[40.78, -73.91],
8163   *      [41.83, -87.62],
8164   *      [32.76, -96.72]]
8165   * ];
8166   * ```
8167   */
8168  
8169  
8170  var Polyline = Path.extend({
8171  
8172      // @section
8173      // @aka Polyline options
8174      options: {
8175          // @option smoothFactor: Number = 1.0
8176          // How much to simplify the polyline on each zoom level. More means
8177          // better performance and smoother look, and less means more accurate representation.
8178          smoothFactor: 1.0,
8179  
8180          // @option noClip: Boolean = false
8181          // Disable polyline clipping.
8182          noClip: false
8183      },
8184  
8185      initialize: function (latlngs, options) {
8186          setOptions(this, options);
8187          this._setLatLngs(latlngs);
8188      },
8189  
8190      // @method getLatLngs(): LatLng[]
8191      // Returns an array of the points in the path, or nested arrays of points in case of multi-polyline.
8192      getLatLngs: function () {
8193          return this._latlngs;
8194      },
8195  
8196      // @method setLatLngs(latlngs: LatLng[]): this
8197      // Replaces all the points in the polyline with the given array of geographical points.
8198      setLatLngs: function (latlngs) {
8199          this._setLatLngs(latlngs);
8200          return this.redraw();
8201      },
8202  
8203      // @method isEmpty(): Boolean
8204      // Returns `true` if the Polyline has no LatLngs.
8205      isEmpty: function () {
8206          return !this._latlngs.length;
8207      },
8208  
8209      // @method closestLayerPoint(p: Point): Point
8210      // Returns the point closest to `p` on the Polyline.
8211      closestLayerPoint: function (p) {
8212          var minDistance = Infinity,
8213              minPoint = null,
8214              closest = _sqClosestPointOnSegment,
8215              p1, p2;
8216  
8217          for (var j = 0, jLen = this._parts.length; j < jLen; j++) {
8218              var points = this._parts[j];
8219  
8220              for (var i = 1, len = points.length; i < len; i++) {
8221                  p1 = points[i - 1];
8222                  p2 = points[i];
8223  
8224                  var sqDist = closest(p, p1, p2, true);
8225  
8226                  if (sqDist < minDistance) {
8227                      minDistance = sqDist;
8228                      minPoint = closest(p, p1, p2);
8229                  }
8230              }
8231          }
8232          if (minPoint) {
8233              minPoint.distance = Math.sqrt(minDistance);
8234          }
8235          return minPoint;
8236      },
8237  
8238      // @method getCenter(): LatLng
8239      // Returns the center ([centroid](http://en.wikipedia.org/wiki/Centroid)) of the polyline.
8240      getCenter: function () {
8241          // throws error when not yet added to map as this center calculation requires projected coordinates
8242          if (!this._map) {
8243              throw new Error('Must add layer to map before using getCenter()');
8244          }
8245  
8246          var i, halfDist, segDist, dist, p1, p2, ratio,
8247              points = this._rings[0],
8248              len = points.length;
8249  
8250          if (!len) { return null; }
8251  
8252          // polyline centroid algorithm; only uses the first ring if there are multiple
8253  
8254          for (i = 0, halfDist = 0; i < len - 1; i++) {
8255              halfDist += points[i].distanceTo(points[i + 1]) / 2;
8256          }
8257  
8258          // The line is so small in the current view that all points are on the same pixel.
8259          if (halfDist === 0) {
8260              return this._map.layerPointToLatLng(points[0]);
8261          }
8262  
8263          for (i = 0, dist = 0; i < len - 1; i++) {
8264              p1 = points[i];
8265              p2 = points[i + 1];
8266              segDist = p1.distanceTo(p2);
8267              dist += segDist;
8268  
8269              if (dist > halfDist) {
8270                  ratio = (dist - halfDist) / segDist;
8271                  return this._map.layerPointToLatLng([
8272                      p2.x - ratio * (p2.x - p1.x),
8273                      p2.y - ratio * (p2.y - p1.y)
8274                  ]);
8275              }
8276          }
8277      },
8278  
8279      // @method getBounds(): LatLngBounds
8280      // Returns the `LatLngBounds` of the path.
8281      getBounds: function () {
8282          return this._bounds;
8283      },
8284  
8285      // @method addLatLng(latlng: LatLng, latlngs? LatLng[]): this
8286      // Adds a given point to the polyline. By default, adds to the first ring of
8287      // the polyline in case of a multi-polyline, but can be overridden by passing
8288      // a specific ring as a LatLng array (that you can earlier access with [`getLatLngs`](#polyline-getlatlngs)).
8289      addLatLng: function (latlng, latlngs) {
8290          latlngs = latlngs || this._defaultShape();
8291          latlng = toLatLng(latlng);
8292          latlngs.push(latlng);
8293          this._bounds.extend(latlng);
8294          return this.redraw();
8295      },
8296  
8297      _setLatLngs: function (latlngs) {
8298          this._bounds = new LatLngBounds();
8299          this._latlngs = this._convertLatLngs(latlngs);
8300      },
8301  
8302      _defaultShape: function () {
8303          return isFlat(this._latlngs) ? this._latlngs : this._latlngs[0];
8304      },
8305  
8306      // recursively convert latlngs input into actual LatLng instances; calculate bounds along the way
8307      _convertLatLngs: function (latlngs) {
8308          var result = [],
8309              flat = isFlat(latlngs);
8310  
8311          for (var i = 0, len = latlngs.length; i < len; i++) {
8312              if (flat) {
8313                  result[i] = toLatLng(latlngs[i]);
8314                  this._bounds.extend(result[i]);
8315              } else {
8316                  result[i] = this._convertLatLngs(latlngs[i]);
8317              }
8318          }
8319  
8320          return result;
8321      },
8322  
8323      _project: function () {
8324          var pxBounds = new Bounds();
8325          this._rings = [];
8326          this._projectLatlngs(this._latlngs, this._rings, pxBounds);
8327  
8328          if (this._bounds.isValid() && pxBounds.isValid()) {
8329              this._rawPxBounds = pxBounds;
8330              this._updateBounds();
8331          }
8332      },
8333  
8334      _updateBounds: function () {
8335          var w = this._clickTolerance(),
8336              p = new Point(w, w);
8337          this._pxBounds = new Bounds([
8338              this._rawPxBounds.min.subtract(p),
8339              this._rawPxBounds.max.add(p)
8340          ]);
8341      },
8342  
8343      // recursively turns latlngs into a set of rings with projected coordinates
8344      _projectLatlngs: function (latlngs, result, projectedBounds) {
8345          var flat = latlngs[0] instanceof LatLng,
8346              len = latlngs.length,
8347              i, ring;
8348  
8349          if (flat) {
8350              ring = [];
8351              for (i = 0; i < len; i++) {
8352                  ring[i] = this._map.latLngToLayerPoint(latlngs[i]);
8353                  projectedBounds.extend(ring[i]);
8354              }
8355              result.push(ring);
8356          } else {
8357              for (i = 0; i < len; i++) {
8358                  this._projectLatlngs(latlngs[i], result, projectedBounds);
8359              }
8360          }
8361      },
8362  
8363      // clip polyline by renderer bounds so that we have less to render for performance
8364      _clipPoints: function () {
8365          var bounds = this._renderer._bounds;
8366  
8367          this._parts = [];
8368          if (!this._pxBounds || !this._pxBounds.intersects(bounds)) {
8369              return;
8370          }
8371  
8372          if (this.options.noClip) {
8373              this._parts = this._rings;
8374              return;
8375          }
8376  
8377          var parts = this._parts,
8378              i, j, k, len, len2, segment, points;
8379  
8380          for (i = 0, k = 0, len = this._rings.length; i < len; i++) {
8381              points = this._rings[i];
8382  
8383              for (j = 0, len2 = points.length; j < len2 - 1; j++) {
8384                  segment = clipSegment(points[j], points[j + 1], bounds, j, true);
8385  
8386                  if (!segment) { continue; }
8387  
8388                  parts[k] = parts[k] || [];
8389                  parts[k].push(segment[0]);
8390  
8391                  // if segment goes out of screen, or it's the last one, it's the end of the line part
8392                  if ((segment[1] !== points[j + 1]) || (j === len2 - 2)) {
8393                      parts[k].push(segment[1]);
8394                      k++;
8395                  }
8396              }
8397          }
8398      },
8399  
8400      // simplify each clipped part of the polyline for performance
8401      _simplifyPoints: function () {
8402          var parts = this._parts,
8403              tolerance = this.options.smoothFactor;
8404  
8405          for (var i = 0, len = parts.length; i < len; i++) {
8406              parts[i] = simplify(parts[i], tolerance);
8407          }
8408      },
8409  
8410      _update: function () {
8411          if (!this._map) { return; }
8412  
8413          this._clipPoints();
8414          this._simplifyPoints();
8415          this._updatePath();
8416      },
8417  
8418      _updatePath: function () {
8419          this._renderer._updatePoly(this);
8420      },
8421  
8422      // Needed by the `Canvas` renderer for interactivity
8423      _containsPoint: function (p, closed) {
8424          var i, j, k, len, len2, part,
8425              w = this._clickTolerance();
8426  
8427          if (!this._pxBounds || !this._pxBounds.contains(p)) { return false; }
8428  
8429          // hit detection for polylines
8430          for (i = 0, len = this._parts.length; i < len; i++) {
8431              part = this._parts[i];
8432  
8433              for (j = 0, len2 = part.length, k = len2 - 1; j < len2; k = j++) {
8434                  if (!closed && (j === 0)) { continue; }
8435  
8436                  if (pointToSegmentDistance(p, part[k], part[j]) <= w) {
8437                      return true;
8438                  }
8439              }
8440          }
8441          return false;
8442      }
8443  });
8444  
8445  // @factory L.polyline(latlngs: LatLng[], options?: Polyline options)
8446  // Instantiates a polyline object given an array of geographical points and
8447  // optionally an options object. You can create a `Polyline` object with
8448  // multiple separate lines (`MultiPolyline`) by passing an array of arrays
8449  // of geographic points.
8450  function polyline(latlngs, options) {
8451      return new Polyline(latlngs, options);
8452  }
8453  
8454  // Retrocompat. Allow plugins to support Leaflet versions before and after 1.1.
8455  Polyline._flat = _flat;
8456  
8457  /*
8458   * @class Polygon
8459   * @aka L.Polygon
8460   * @inherits Polyline
8461   *
8462   * A class for drawing polygon overlays on a map. Extends `Polyline`.
8463   *
8464   * 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.
8465   *
8466   *
8467   * @example
8468   *
8469   * ```js
8470   * // create a red polygon from an array of LatLng points
8471   * var latlngs = [[37, -109.05],[41, -109.03],[41, -102.05],[37, -102.04]];
8472   *
8473   * var polygon = L.polygon(latlngs, {color: 'red'}).addTo(map);
8474   *
8475   * // zoom the map to the polygon
8476   * map.fitBounds(polygon.getBounds());
8477   * ```
8478   *
8479   * 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:
8480   *
8481   * ```js
8482   * var latlngs = [
8483   *   [[37, -109.05],[41, -109.03],[41, -102.05],[37, -102.04]], // outer ring
8484   *   [[37.29, -108.58],[40.71, -108.58],[40.71, -102.50],[37.29, -102.50]] // hole
8485   * ];
8486   * ```
8487   *
8488   * Additionally, you can pass a multi-dimensional array to represent a MultiPolygon shape.
8489   *
8490   * ```js
8491   * var latlngs = [
8492   *   [ // first polygon
8493   *     [[37, -109.05],[41, -109.03],[41, -102.05],[37, -102.04]], // outer ring
8494   *     [[37.29, -108.58],[40.71, -108.58],[40.71, -102.50],[37.29, -102.50]] // hole
8495   *   ],
8496   *   [ // second polygon
8497   *     [[41, -111.03],[45, -111.04],[45, -104.05],[41, -104.05]]
8498   *   ]
8499   * ];
8500   * ```
8501   */
8502  
8503  var Polygon = Polyline.extend({
8504  
8505      options: {
8506          fill: true
8507      },
8508  
8509      isEmpty: function () {
8510          return !this._latlngs.length || !this._latlngs[0].length;
8511      },
8512  
8513      getCenter: function () {
8514          // throws error when not yet added to map as this center calculation requires projected coordinates
8515          if (!this._map) {
8516              throw new Error('Must add layer to map before using getCenter()');
8517          }
8518  
8519          var i, j, p1, p2, f, area, x, y, center,
8520              points = this._rings[0],
8521              len = points.length;
8522  
8523          if (!len) { return null; }
8524  
8525          // polygon centroid algorithm; only uses the first ring if there are multiple
8526  
8527          area = x = y = 0;
8528  
8529          for (i = 0, j = len - 1; i < len; j = i++) {
8530              p1 = points[i];
8531              p2 = points[j];
8532  
8533              f = p1.y * p2.x - p2.y * p1.x;
8534              x += (p1.x + p2.x) * f;
8535              y += (p1.y + p2.y) * f;
8536              area += f * 3;
8537          }
8538  
8539          if (area === 0) {
8540              // Polygon is so small that all points are on same pixel.
8541              center = points[0];
8542          } else {
8543              center = [x / area, y / area];
8544          }
8545          return this._map.layerPointToLatLng(center);
8546      },
8547  
8548      _convertLatLngs: function (latlngs) {
8549          var result = Polyline.prototype._convertLatLngs.call(this, latlngs),
8550              len = result.length;
8551  
8552          // remove last point if it equals first one
8553          if (len >= 2 && result[0] instanceof LatLng && result[0].equals(result[len - 1])) {
8554              result.pop();
8555          }
8556          return result;
8557      },
8558  
8559      _setLatLngs: function (latlngs) {
8560          Polyline.prototype._setLatLngs.call(this, latlngs);
8561          if (isFlat(this._latlngs)) {
8562              this._latlngs = [this._latlngs];
8563          }
8564      },
8565  
8566      _defaultShape: function () {
8567          return isFlat(this._latlngs[0]) ? this._latlngs[0] : this._latlngs[0][0];
8568      },
8569  
8570      _clipPoints: function () {
8571          // polygons need a different clipping algorithm so we redefine that
8572  
8573          var bounds = this._renderer._bounds,
8574              w = this.options.weight,
8575              p = new Point(w, w);
8576  
8577          // increase clip padding by stroke width to avoid stroke on clip edges
8578          bounds = new Bounds(bounds.min.subtract(p), bounds.max.add(p));
8579  
8580          this._parts = [];
8581          if (!this._pxBounds || !this._pxBounds.intersects(bounds)) {
8582              return;
8583          }
8584  
8585          if (this.options.noClip) {
8586              this._parts = this._rings;
8587              return;
8588          }
8589  
8590          for (var i = 0, len = this._rings.length, clipped; i < len; i++) {
8591              clipped = clipPolygon(this._rings[i], bounds, true);
8592              if (clipped.length) {
8593                  this._parts.push(clipped);
8594              }
8595          }
8596      },
8597  
8598      _updatePath: function () {
8599          this._renderer._updatePoly(this, true);
8600      },
8601  
8602      // Needed by the `Canvas` renderer for interactivity
8603      _containsPoint: function (p) {
8604          var inside = false,
8605              part, p1, p2, i, j, k, len, len2;
8606  
8607          if (!this._pxBounds || !this._pxBounds.contains(p)) { return false; }
8608  
8609          // ray casting algorithm for detecting if point is in polygon
8610          for (i = 0, len = this._parts.length; i < len; i++) {
8611              part = this._parts[i];
8612  
8613              for (j = 0, len2 = part.length, k = len2 - 1; j < len2; k = j++) {
8614                  p1 = part[j];
8615                  p2 = part[k];
8616  
8617                  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)) {
8618                      inside = !inside;
8619                  }
8620              }
8621          }
8622  
8623          // also check if it's on polygon stroke
8624          return inside || Polyline.prototype._containsPoint.call(this, p, true);
8625      }
8626  
8627  });
8628  
8629  
8630  // @factory L.polygon(latlngs: LatLng[], options?: Polyline options)
8631  function polygon(latlngs, options) {
8632      return new Polygon(latlngs, options);
8633  }
8634  
8635  /*
8636   * @class GeoJSON
8637   * @aka L.GeoJSON
8638   * @inherits FeatureGroup
8639   *
8640   * Represents a GeoJSON object or an array of GeoJSON objects. Allows you to parse
8641   * GeoJSON data and display it on the map. Extends `FeatureGroup`.
8642   *
8643   * @example
8644   *
8645   * ```js
8646   * L.geoJSON(data, {
8647   *     style: function (feature) {
8648   *         return {color: feature.properties.color};
8649   *     }
8650   * }).bindPopup(function (layer) {
8651   *     return layer.feature.properties.description;
8652   * }).addTo(map);
8653   * ```
8654   */
8655  
8656  var GeoJSON = FeatureGroup.extend({
8657  
8658      /* @section
8659       * @aka GeoJSON options
8660       *
8661       * @option pointToLayer: Function = *
8662       * A `Function` defining how GeoJSON points spawn Leaflet layers. It is internally
8663       * called when data is added, passing the GeoJSON point feature and its `LatLng`.
8664       * The default is to spawn a default `Marker`:
8665       * ```js
8666       * function(geoJsonPoint, latlng) {
8667       *     return L.marker(latlng);
8668       * }
8669       * ```
8670       *
8671       * @option style: Function = *
8672       * A `Function` defining the `Path options` for styling GeoJSON lines and polygons,
8673       * called internally when data is added.
8674       * The default value is to not override any defaults:
8675       * ```js
8676       * function (geoJsonFeature) {
8677       *     return {}
8678       * }
8679       * ```
8680       *
8681       * @option onEachFeature: Function = *
8682       * A `Function` that will be called once for each created `Feature`, after it has
8683       * been created and styled. Useful for attaching events and popups to features.
8684       * The default is to do nothing with the newly created layers:
8685       * ```js
8686       * function (feature, layer) {}
8687       * ```
8688       *
8689       * @option filter: Function = *
8690       * A `Function` that will be used to decide whether to include a feature or not.
8691       * The default is to include all features:
8692       * ```js
8693       * function (geoJsonFeature) {
8694       *     return true;
8695       * }
8696       * ```
8697       * Note: dynamically changing the `filter` option will have effect only on newly
8698       * added data. It will _not_ re-evaluate already included features.
8699       *
8700       * @option coordsToLatLng: Function = *
8701       * A `Function` that will be used for converting GeoJSON coordinates to `LatLng`s.
8702       * The default is the `coordsToLatLng` static method.
8703       *
8704       * @option markersInheritOptions: Boolean = false
8705       * Whether default Markers for "Point" type Features inherit from group options.
8706       */
8707  
8708      initialize: function (geojson, options) {
8709          setOptions(this, options);
8710  
8711          this._layers = {};
8712  
8713          if (geojson) {
8714              this.addData(geojson);
8715          }
8716      },
8717  
8718      // @method addData( <GeoJSON> data ): this
8719      // Adds a GeoJSON object to the layer.
8720      addData: function (geojson) {
8721          var features = isArray(geojson) ? geojson : geojson.features,
8722              i, len, feature;
8723  
8724          if (features) {
8725              for (i = 0, len = features.length; i < len; i++) {
8726                  // only add this if geometry or geometries are set and not null
8727                  feature = features[i];
8728                  if (feature.geometries || feature.geometry || feature.features || feature.coordinates) {
8729                      this.addData(feature);
8730                  }
8731              }
8732              return this;
8733          }
8734  
8735          var options = this.options;
8736  
8737          if (options.filter && !options.filter(geojson)) { return this; }
8738  
8739          var layer = geometryToLayer(geojson, options);
8740          if (!layer) {
8741              return this;
8742          }
8743          layer.feature = asFeature(geojson);
8744  
8745          layer.defaultOptions = layer.options;
8746          this.resetStyle(layer);
8747  
8748          if (options.onEachFeature) {
8749              options.onEachFeature(geojson, layer);
8750          }
8751  
8752          return this.addLayer(layer);
8753      },
8754  
8755      // @method resetStyle( <Path> layer? ): this
8756      // Resets the given vector layer's style to the original GeoJSON style, useful for resetting style after hover events.
8757      // If `layer` is omitted, the style of all features in the current layer is reset.
8758      resetStyle: function (layer) {
8759          if (layer === undefined) {
8760              return this.eachLayer(this.resetStyle, this);
8761          }
8762          // reset any custom styles
8763          layer.options = extend({}, layer.defaultOptions);
8764          this._setLayerStyle(layer, this.options.style);
8765          return this;
8766      },
8767  
8768      // @method setStyle( <Function> style ): this
8769      // Changes styles of GeoJSON vector layers with the given style function.
8770      setStyle: function (style) {
8771          return this.eachLayer(function (layer) {
8772              this._setLayerStyle(layer, style);
8773          }, this);
8774      },
8775  
8776      _setLayerStyle: function (layer, style) {
8777          if (layer.setStyle) {
8778              if (typeof style === 'function') {
8779                  style = style(layer.feature);
8780              }
8781              layer.setStyle(style);
8782          }
8783      }
8784  });
8785  
8786  // @section
8787  // There are several static functions which can be called without instantiating L.GeoJSON:
8788  
8789  // @function geometryToLayer(featureData: Object, options?: GeoJSON options): Layer
8790  // Creates a `Layer` from a given GeoJSON feature. Can use a custom
8791  // [`pointToLayer`](#geojson-pointtolayer) and/or [`coordsToLatLng`](#geojson-coordstolatlng)
8792  // functions if provided as options.
8793  function geometryToLayer(geojson, options) {
8794  
8795      var geometry = geojson.type === 'Feature' ? geojson.geometry : geojson,
8796          coords = geometry ? geometry.coordinates : null,
8797          layers = [],
8798          pointToLayer = options && options.pointToLayer,
8799          _coordsToLatLng = options && options.coordsToLatLng || coordsToLatLng,
8800          latlng, latlngs, i, len;
8801  
8802      if (!coords && !geometry) {
8803          return null;
8804      }
8805  
8806      switch (geometry.type) {
8807      case 'Point':
8808          latlng = _coordsToLatLng(coords);
8809          return _pointToLayer(pointToLayer, geojson, latlng, options);
8810  
8811      case 'MultiPoint':
8812          for (i = 0, len = coords.length; i < len; i++) {
8813              latlng = _coordsToLatLng(coords[i]);
8814              layers.push(_pointToLayer(pointToLayer, geojson, latlng, options));
8815          }
8816          return new FeatureGroup(layers);
8817  
8818      case 'LineString':
8819      case 'MultiLineString':
8820          latlngs = coordsToLatLngs(coords, geometry.type === 'LineString' ? 0 : 1, _coordsToLatLng);
8821          return new Polyline(latlngs, options);
8822  
8823      case 'Polygon':
8824      case 'MultiPolygon':
8825          latlngs = coordsToLatLngs(coords, geometry.type === 'Polygon' ? 1 : 2, _coordsToLatLng);
8826          return new Polygon(latlngs, options);
8827  
8828      case 'GeometryCollection':
8829          for (i = 0, len = geometry.geometries.length; i < len; i++) {
8830              var layer = geometryToLayer({
8831                  geometry: geometry.geometries[i],
8832                  type: 'Feature',
8833                  properties: geojson.properties
8834              }, options);
8835  
8836              if (layer) {
8837                  layers.push(layer);
8838              }
8839          }
8840          return new FeatureGroup(layers);
8841  
8842      default:
8843          throw new Error('Invalid GeoJSON object.');
8844      }
8845  }
8846  
8847  function _pointToLayer(pointToLayerFn, geojson, latlng, options) {
8848      return pointToLayerFn ?
8849          pointToLayerFn(geojson, latlng) :
8850          new Marker(latlng, options && options.markersInheritOptions && options);
8851  }
8852  
8853  // @function coordsToLatLng(coords: Array): LatLng
8854  // Creates a `LatLng` object from an array of 2 numbers (longitude, latitude)
8855  // or 3 numbers (longitude, latitude, altitude) used in GeoJSON for points.
8856  function coordsToLatLng(coords) {
8857      return new LatLng(coords[1], coords[0], coords[2]);
8858  }
8859  
8860  // @function coordsToLatLngs(coords: Array, levelsDeep?: Number, coordsToLatLng?: Function): Array
8861  // Creates a multidimensional array of `LatLng`s from a GeoJSON coordinates array.
8862  // `levelsDeep` specifies the nesting level (0 is for an array of points, 1 for an array of arrays of points, etc., 0 by default).
8863  // Can use a custom [`coordsToLatLng`](#geojson-coordstolatlng) function.
8864  function coordsToLatLngs(coords, levelsDeep, _coordsToLatLng) {
8865      var latlngs = [];
8866  
8867      for (var i = 0, len = coords.length, latlng; i < len; i++) {
8868          latlng = levelsDeep ?
8869              coordsToLatLngs(coords[i], levelsDeep - 1, _coordsToLatLng) :
8870              (_coordsToLatLng || coordsToLatLng)(coords[i]);
8871  
8872          latlngs.push(latlng);
8873      }
8874  
8875      return latlngs;
8876  }
8877  
8878  // @function latLngToCoords(latlng: LatLng, precision?: Number): Array
8879  // Reverse of [`coordsToLatLng`](#geojson-coordstolatlng)
8880  function latLngToCoords(latlng, precision) {
8881      precision = typeof precision === 'number' ? precision : 6;
8882      return latlng.alt !== undefined ?
8883          [formatNum(latlng.lng, precision), formatNum(latlng.lat, precision), formatNum(latlng.alt, precision)] :
8884          [formatNum(latlng.lng, precision), formatNum(latlng.lat, precision)];
8885  }
8886  
8887  // @function latLngsToCoords(latlngs: Array, levelsDeep?: Number, closed?: Boolean): Array
8888  // Reverse of [`coordsToLatLngs`](#geojson-coordstolatlngs)
8889  // `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.
8890  function latLngsToCoords(latlngs, levelsDeep, closed, precision) {
8891      var coords = [];
8892  
8893      for (var i = 0, len = latlngs.length; i < len; i++) {
8894          coords.push(levelsDeep ?
8895              latLngsToCoords(latlngs[i], levelsDeep - 1, closed, precision) :
8896              latLngToCoords(latlngs[i], precision));
8897      }
8898  
8899      if (!levelsDeep && closed) {
8900          coords.push(coords[0]);
8901      }
8902  
8903      return coords;
8904  }
8905  
8906  function getFeature(layer, newGeometry) {
8907      return layer.feature ?
8908          extend({}, layer.feature, {geometry: newGeometry}) :
8909          asFeature(newGeometry);
8910  }
8911  
8912  // @function asFeature(geojson: Object): Object
8913  // Normalize GeoJSON geometries/features into GeoJSON features.
8914  function asFeature(geojson) {
8915      if (geojson.type === 'Feature' || geojson.type === 'FeatureCollection') {
8916          return geojson;
8917      }
8918  
8919      return {
8920          type: 'Feature',
8921          properties: {},
8922          geometry: geojson
8923      };
8924  }
8925  
8926  var PointToGeoJSON = {
8927      toGeoJSON: function (precision) {
8928          return getFeature(this, {
8929              type: 'Point',
8930              coordinates: latLngToCoords(this.getLatLng(), precision)
8931          });
8932      }
8933  };
8934  
8935  // @namespace Marker
8936  // @section Other methods
8937  // @method toGeoJSON(precision?: Number): Object
8938  // `precision` is the number of decimal places for coordinates.
8939  // The default value is 6 places.
8940  // Returns a [`GeoJSON`](http://en.wikipedia.org/wiki/GeoJSON) representation of the marker (as a GeoJSON `Point` Feature).
8941  Marker.include(PointToGeoJSON);
8942  
8943  // @namespace CircleMarker
8944  // @method toGeoJSON(precision?: Number): Object
8945  // `precision` is the number of decimal places for coordinates.
8946  // The default value is 6 places.
8947  // Returns a [`GeoJSON`](http://en.wikipedia.org/wiki/GeoJSON) representation of the circle marker (as a GeoJSON `Point` Feature).
8948  Circle.include(PointToGeoJSON);
8949  CircleMarker.include(PointToGeoJSON);
8950  
8951  
8952  // @namespace Polyline
8953  // @method toGeoJSON(precision?: Number): Object
8954  // `precision` is the number of decimal places for coordinates.
8955  // The default value is 6 places.
8956  // Returns a [`GeoJSON`](http://en.wikipedia.org/wiki/GeoJSON) representation of the polyline (as a GeoJSON `LineString` or `MultiLineString` Feature).
8957  Polyline.include({
8958      toGeoJSON: function (precision) {
8959          var multi = !isFlat(this._latlngs);
8960  
8961          var coords = latLngsToCoords(this._latlngs, multi ? 1 : 0, false, precision);
8962  
8963          return getFeature(this, {
8964              type: (multi ? 'Multi' : '') + 'LineString',
8965              coordinates: coords
8966          });
8967      }
8968  });
8969  
8970  // @namespace Polygon
8971  // @method toGeoJSON(precision?: Number): Object
8972  // `precision` is the number of decimal places for coordinates.
8973  // The default value is 6 places.
8974  // Returns a [`GeoJSON`](http://en.wikipedia.org/wiki/GeoJSON) representation of the polygon (as a GeoJSON `Polygon` or `MultiPolygon` Feature).
8975  Polygon.include({
8976      toGeoJSON: function (precision) {
8977          var holes = !isFlat(this._latlngs),
8978              multi = holes && !isFlat(this._latlngs[0]);
8979  
8980          var coords = latLngsToCoords(this._latlngs, multi ? 2 : holes ? 1 : 0, true, precision);
8981  
8982          if (!holes) {
8983              coords = [coords];
8984          }
8985  
8986          return getFeature(this, {
8987              type: (multi ? 'Multi' : '') + 'Polygon',
8988              coordinates: coords
8989          });
8990      }
8991  });
8992  
8993  
8994  // @namespace LayerGroup
8995  LayerGroup.include({
8996      toMultiPoint: function (precision) {
8997          var coords = [];
8998  
8999          this.eachLayer(function (layer) {
9000              coords.push(layer.toGeoJSON(precision).geometry.coordinates);
9001          });
9002  
9003          return getFeature(this, {
9004              type: 'MultiPoint',
9005              coordinates: coords
9006          });
9007      },
9008  
9009      // @method toGeoJSON(precision?: Number): Object
9010      // `precision` is the number of decimal places for coordinates.
9011      // The default value is 6 places.
9012      // Returns a [`GeoJSON`](http://en.wikipedia.org/wiki/GeoJSON) representation of the layer group (as a GeoJSON `FeatureCollection`, `GeometryCollection`, or `MultiPoint`).
9013      toGeoJSON: function (precision) {
9014  
9015          var type = this.feature && this.feature.geometry && this.feature.geometry.type;
9016  
9017          if (type === 'MultiPoint') {
9018              return this.toMultiPoint(precision);
9019          }
9020  
9021          var isGeometryCollection = type === 'GeometryCollection',
9022              jsons = [];
9023  
9024          this.eachLayer(function (layer) {
9025              if (layer.toGeoJSON) {
9026                  var json = layer.toGeoJSON(precision);
9027                  if (isGeometryCollection) {
9028                      jsons.push(json.geometry);
9029                  } else {
9030                      var feature = asFeature(json);
9031                      // Squash nested feature collections
9032                      if (feature.type === 'FeatureCollection') {
9033                          jsons.push.apply(jsons, feature.features);
9034                      } else {
9035                          jsons.push(feature);
9036                      }
9037                  }
9038              }
9039          });
9040  
9041          if (isGeometryCollection) {
9042              return getFeature(this, {
9043                  geometries: jsons,
9044                  type: 'GeometryCollection'
9045              });
9046          }
9047  
9048          return {
9049              type: 'FeatureCollection',
9050              features: jsons
9051          };
9052      }
9053  });
9054  
9055  // @namespace GeoJSON
9056  // @factory L.geoJSON(geojson?: Object, options?: GeoJSON options)
9057  // Creates a GeoJSON layer. Optionally accepts an object in
9058  // [GeoJSON format](https://tools.ietf.org/html/rfc7946) to display on the map
9059  // (you can alternatively add it later with `addData` method) and an `options` object.
9060  function geoJSON(geojson, options) {
9061      return new GeoJSON(geojson, options);
9062  }
9063  
9064  // Backward compatibility.
9065  var geoJson = geoJSON;
9066  
9067  /*
9068   * @class ImageOverlay
9069   * @aka L.ImageOverlay
9070   * @inherits Interactive layer
9071   *
9072   * Used to load and display a single image over specific bounds of the map. Extends `Layer`.
9073   *
9074   * @example
9075   *
9076   * ```js
9077   * var imageUrl = 'http://www.lib.utexas.edu/maps/historical/newark_nj_1922.jpg',
9078   *     imageBounds = [[40.712216, -74.22655], [40.773941, -74.12544]];
9079   * L.imageOverlay(imageUrl, imageBounds).addTo(map);
9080   * ```
9081   */
9082  
9083  var ImageOverlay = Layer.extend({
9084  
9085      // @section
9086      // @aka ImageOverlay options
9087      options: {
9088          // @option opacity: Number = 1.0
9089          // The opacity of the image overlay.
9090          opacity: 1,
9091  
9092          // @option alt: String = ''
9093          // Text for the `alt` attribute of the image (useful for accessibility).
9094          alt: '',
9095  
9096          // @option interactive: Boolean = false
9097          // If `true`, the image overlay will emit [mouse events](#interactive-layer) when clicked or hovered.
9098          interactive: false,
9099  
9100          // @option crossOrigin: Boolean|String = false
9101          // Whether the crossOrigin attribute will be added to the image.
9102          // 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.
9103          // Refer to [CORS Settings](https://developer.mozilla.org/en-US/docs/Web/HTML/CORS_settings_attributes) for valid String values.
9104          crossOrigin: false,
9105  
9106          // @option errorOverlayUrl: String = ''
9107          // URL to the overlay image to show in place of the overlay that failed to load.
9108          errorOverlayUrl: '',
9109  
9110          // @option zIndex: Number = 1
9111          // The explicit [zIndex](https://developer.mozilla.org/docs/Web/CSS/CSS_Positioning/Understanding_z_index) of the overlay layer.
9112          zIndex: 1,
9113  
9114          // @option className: String = ''
9115          // A custom class name to assign to the image. Empty by default.
9116          className: ''
9117      },
9118  
9119      initialize: function (url, bounds, options) { // (String, LatLngBounds, Object)
9120          this._url = url;
9121          this._bounds = toLatLngBounds(bounds);
9122  
9123          setOptions(this, options);
9124      },
9125  
9126      onAdd: function () {
9127          if (!this._image) {
9128              this._initImage();
9129  
9130              if (this.options.opacity < 1) {
9131                  this._updateOpacity();
9132              }
9133          }
9134  
9135          if (this.options.interactive) {
9136              addClass(this._image, 'leaflet-interactive');
9137              this.addInteractiveTarget(this._image);
9138          }
9139  
9140          this.getPane().appendChild(this._image);
9141          this._reset();
9142      },
9143  
9144      onRemove: function () {
9145          remove(this._image);
9146          if (this.options.interactive) {
9147              this.removeInteractiveTarget(this._image);
9148          }
9149      },
9150  
9151      // @method setOpacity(opacity: Number): this
9152      // Sets the opacity of the overlay.
9153      setOpacity: function (opacity) {
9154          this.options.opacity = opacity;
9155  
9156          if (this._image) {
9157              this._updateOpacity();
9158          }
9159          return this;
9160      },
9161  
9162      setStyle: function (styleOpts) {
9163          if (styleOpts.opacity) {
9164              this.setOpacity(styleOpts.opacity);
9165          }
9166          return this;
9167      },
9168  
9169      // @method bringToFront(): this
9170      // Brings the layer to the top of all overlays.
9171      bringToFront: function () {
9172          if (this._map) {
9173              toFront(this._image);
9174          }
9175          return this;
9176      },
9177  
9178      // @method bringToBack(): this
9179      // Brings the layer to the bottom of all overlays.
9180      bringToBack: function () {
9181          if (this._map) {
9182              toBack(this._image);
9183          }
9184          return this;
9185      },
9186  
9187      // @method setUrl(url: String): this
9188      // Changes the URL of the image.
9189      setUrl: function (url) {
9190          this._url = url;
9191  
9192          if (this._image) {
9193              this._image.src = url;
9194          }
9195          return this;
9196      },
9197  
9198      // @method setBounds(bounds: LatLngBounds): this
9199      // Update the bounds that this ImageOverlay covers
9200      setBounds: function (bounds) {
9201          this._bounds = toLatLngBounds(bounds);
9202  
9203          if (this._map) {
9204              this._reset();
9205          }
9206          return this;
9207      },
9208  
9209      getEvents: function () {
9210          var events = {
9211              zoom: this._reset,
9212              viewreset: this._reset
9213          };
9214  
9215          if (this._zoomAnimated) {
9216              events.zoomanim = this._animateZoom;
9217          }
9218  
9219          return events;
9220      },
9221  
9222      // @method setZIndex(value: Number): this
9223      // Changes the [zIndex](#imageoverlay-zindex) of the image overlay.
9224      setZIndex: function (value) {
9225          this.options.zIndex = value;
9226          this._updateZIndex();
9227          return this;
9228      },
9229  
9230      // @method getBounds(): LatLngBounds
9231      // Get the bounds that this ImageOverlay covers
9232      getBounds: function () {
9233          return this._bounds;
9234      },
9235  
9236      // @method getElement(): HTMLElement
9237      // Returns the instance of [`HTMLImageElement`](https://developer.mozilla.org/docs/Web/API/HTMLImageElement)
9238      // used by this overlay.
9239      getElement: function () {
9240          return this._image;
9241      },
9242  
9243      _initImage: function () {
9244          var wasElementSupplied = this._url.tagName === 'IMG';
9245          var img = this._image = wasElementSupplied ? this._url : create$1('img');
9246  
9247          addClass(img, 'leaflet-image-layer');
9248          if (this._zoomAnimated) { addClass(img, 'leaflet-zoom-animated'); }
9249          if (this.options.className) { addClass(img, this.options.className); }
9250  
9251          img.onselectstart = falseFn;
9252          img.onmousemove = falseFn;
9253  
9254          // @event load: Event
9255          // Fired when the ImageOverlay layer has loaded its image
9256          img.onload = bind(this.fire, this, 'load');
9257          img.onerror = bind(this._overlayOnError, this, 'error');
9258  
9259          if (this.options.crossOrigin || this.options.crossOrigin === '') {
9260              img.crossOrigin = this.options.crossOrigin === true ? '' : this.options.crossOrigin;
9261          }
9262  
9263          if (this.options.zIndex) {
9264              this._updateZIndex();
9265          }
9266  
9267          if (wasElementSupplied) {
9268              this._url = img.src;
9269              return;
9270          }
9271  
9272          img.src = this._url;
9273          img.alt = this.options.alt;
9274      },
9275  
9276      _animateZoom: function (e) {
9277          var scale = this._map.getZoomScale(e.zoom),
9278              offset = this._map._latLngBoundsToNewLayerBounds(this._bounds, e.zoom, e.center).min;
9279  
9280          setTransform(this._image, offset, scale);
9281      },
9282  
9283      _reset: function () {
9284          var image = this._image,
9285              bounds = new Bounds(
9286                  this._map.latLngToLayerPoint(this._bounds.getNorthWest()),
9287                  this._map.latLngToLayerPoint(this._bounds.getSouthEast())),
9288              size = bounds.getSize();
9289  
9290          setPosition(image, bounds.min);
9291  
9292          image.style.width  = size.x + 'px';
9293          image.style.height = size.y + 'px';
9294      },
9295  
9296      _updateOpacity: function () {
9297          setOpacity(this._image, this.options.opacity);
9298      },
9299  
9300      _updateZIndex: function () {
9301          if (this._image && this.options.zIndex !== undefined && this.options.zIndex !== null) {
9302              this._image.style.zIndex = this.options.zIndex;
9303          }
9304      },
9305  
9306      _overlayOnError: function () {
9307          // @event error: Event
9308          // Fired when the ImageOverlay layer fails to load its image
9309          this.fire('error');
9310  
9311          var errorUrl = this.options.errorOverlayUrl;
9312          if (errorUrl && this._url !== errorUrl) {
9313              this._url = errorUrl;
9314              this._image.src = errorUrl;
9315          }
9316      }
9317  });
9318  
9319  // @factory L.imageOverlay(imageUrl: String, bounds: LatLngBounds, options?: ImageOverlay options)
9320  // Instantiates an image overlay object given the URL of the image and the
9321  // geographical bounds it is tied to.
9322  var imageOverlay = function (url, bounds, options) {
9323      return new ImageOverlay(url, bounds, options);
9324  };
9325  
9326  /*
9327   * @class VideoOverlay
9328   * @aka L.VideoOverlay
9329   * @inherits ImageOverlay
9330   *
9331   * Used to load and display a video player over specific bounds of the map. Extends `ImageOverlay`.
9332   *
9333   * A video overlay uses the [`<video>`](https://developer.mozilla.org/docs/Web/HTML/Element/video)
9334   * HTML5 element.
9335   *
9336   * @example
9337   *
9338   * ```js
9339   * var videoUrl = 'https://www.mapbox.com/bites/00188/patricia_nasa.webm',
9340   *     videoBounds = [[ 32, -130], [ 13, -100]];
9341   * L.videoOverlay(videoUrl, videoBounds ).addTo(map);
9342   * ```
9343   */
9344  
9345  var VideoOverlay = ImageOverlay.extend({
9346  
9347      // @section
9348      // @aka VideoOverlay options
9349      options: {
9350          // @option autoplay: Boolean = true
9351          // Whether the video starts playing automatically when loaded.
9352          autoplay: true,
9353  
9354          // @option loop: Boolean = true
9355          // Whether the video will loop back to the beginning when played.
9356          loop: true,
9357  
9358          // @option keepAspectRatio: Boolean = true
9359          // Whether the video will save aspect ratio after the projection.
9360          // Relevant for supported browsers. Browser compatibility- https://developer.mozilla.org/en-US/docs/Web/CSS/object-fit
9361          keepAspectRatio: true
9362      },
9363  
9364      _initImage: function () {
9365          var wasElementSupplied = this._url.tagName === 'VIDEO';
9366          var vid = this._image = wasElementSupplied ? this._url : create$1('video');
9367  
9368          addClass(vid, 'leaflet-image-layer');
9369          if (this._zoomAnimated) { addClass(vid, 'leaflet-zoom-animated'); }
9370          if (this.options.className) { addClass(vid, this.options.className); }
9371  
9372          vid.onselectstart = falseFn;
9373          vid.onmousemove = falseFn;
9374  
9375          // @event load: Event
9376          // Fired when the video has finished loading the first frame
9377          vid.onloadeddata = bind(this.fire, this, 'load');
9378  
9379          if (wasElementSupplied) {
9380              var sourceElements = vid.getElementsByTagName('source');
9381              var sources = [];
9382              for (var j = 0; j < sourceElements.length; j++) {
9383                  sources.push(sourceElements[j].src);
9384              }
9385  
9386              this._url = (sourceElements.length > 0) ? sources : [vid.src];
9387              return;
9388          }
9389  
9390          if (!isArray(this._url)) { this._url = [this._url]; }
9391  
9392          if (!this.options.keepAspectRatio && vid.style.hasOwnProperty('objectFit')) { vid.style['objectFit'] = 'fill'; }
9393          vid.autoplay = !!this.options.autoplay;
9394          vid.loop = !!this.options.loop;
9395          for (var i = 0; i < this._url.length; i++) {
9396              var source = create$1('source');
9397              source.src = this._url[i];
9398              vid.appendChild(source);
9399          }
9400      }
9401  
9402      // @method getElement(): HTMLVideoElement
9403      // Returns the instance of [`HTMLVideoElement`](https://developer.mozilla.org/docs/Web/API/HTMLVideoElement)
9404      // used by this overlay.
9405  });
9406  
9407  
9408  // @factory L.videoOverlay(video: String|Array|HTMLVideoElement, bounds: LatLngBounds, options?: VideoOverlay options)
9409  // Instantiates an image overlay object given the URL of the video (or array of URLs, or even a video element) and the
9410  // geographical bounds it is tied to.
9411  
9412  function videoOverlay(video, bounds, options) {
9413      return new VideoOverlay(video, bounds, options);
9414  }
9415  
9416  /*
9417   * @class SVGOverlay
9418   * @aka L.SVGOverlay
9419   * @inherits ImageOverlay