[ Index ]

MailPress 7.2

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

title

Body

[close]

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