2  * Leaflet 1.5.1+build.2e3e0ff, a JS library for interactive maps. http://leafletjs.com
 
   3  * (c) 2010-2018 Vladimir Agafonkin, (c) 2010-2011 CloudMade
 
   6 (function (global, factory) {
 
   7         typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) :
 
   8         typeof define === 'function' && define.amd ? define(['exports'], factory) :
 
   9         (factory((global.L = {})));
 
  10 }(this, (function (exports) { 'use strict';
 
  12 var version = "1.5.1+build.2e3e0ffb";
 
  17  * Various utility functions, used by Leaflet internally.
 
  20 var freeze = Object.freeze;
 
  21 Object.freeze = function (obj) { return obj; };
 
  23 // @function extend(dest: Object, src?: Object): Object
 
  24 // Merges the properties of the `src` object (or multiple objects) into `dest` object and returns the latter. Has an `L.extend` shortcut.
 
  25 function extend(dest) {
 
  28         for (j = 1, len = arguments.length; j < len; j++) {
 
  37 // @function create(proto: Object, properties?: Object): Object
 
  38 // Compatibility polyfill for [Object.create](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Object/create)
 
  39 var create = Object.create || (function () {
 
  41         return function (proto) {
 
  47 // @function bind(fn: Function, …): Function
 
  48 // Returns a new function bound to the arguments passed, like [Function.prototype.bind](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Function/bind).
 
  49 // Has a `L.bind()` shortcut.
 
  50 function bind(fn, obj) {
 
  51         var slice = Array.prototype.slice;
 
  54                 return fn.bind.apply(fn, slice.call(arguments, 1));
 
  57         var args = slice.call(arguments, 2);
 
  60                 return fn.apply(obj, args.length ? args.concat(slice.call(arguments)) : arguments);
 
  64 // @property lastId: Number
 
  65 // Last unique ID used by [`stamp()`](#util-stamp)
 
  68 // @function stamp(obj: Object): Number
 
  69 // Returns the unique ID of an object, assigning it one if it doesn't have it.
 
  72         obj._leaflet_id = obj._leaflet_id || ++lastId;
 
  73         return obj._leaflet_id;
 
  77 // @function throttle(fn: Function, time: Number, context: Object): Function
 
  78 // Returns a function which executes function `fn` with the given scope `context`
 
  79 // (so that the `this` keyword refers to `context` inside `fn`'s code). The function
 
  80 // `fn` will be called no more than one time per given amount of `time`. The arguments
 
  81 // received by the bound function will be any arguments passed when binding the
 
  82 // function, followed by any arguments passed when invoking the bound function.
 
  83 // Has an `L.throttle` shortcut.
 
  84 function throttle(fn, time, context) {
 
  85         var lock, args, wrapperFn, later;
 
  88                 // reset lock and call if queued
 
  91                         wrapperFn.apply(context, args);
 
  96         wrapperFn = function () {
 
  98                         // called too soon, queue to call later
 
 102                         // call and lock until later
 
 103                         fn.apply(context, arguments);
 
 104                         setTimeout(later, time);
 
 112 // @function wrapNum(num: Number, range: Number[], includeMax?: Boolean): Number
 
 113 // Returns the number `num` modulo `range` in such a way so it lies within
 
 114 // `range[0]` and `range[1]`. The returned value will be always smaller than
 
 115 // `range[1]` unless `includeMax` is set to `true`.
 
 116 function wrapNum(x, range, includeMax) {
 
 120         return x === max && includeMax ? x : ((x - min) % d + d) % d + min;
 
 123 // @function falseFn(): Function
 
 124 // Returns a function which always returns `false`.
 
 125 function falseFn() { return false; }
 
 127 // @function formatNum(num: Number, digits?: Number): Number
 
 128 // Returns the number `num` rounded to `digits` decimals, or to 6 decimals by default.
 
 129 function formatNum(num, digits) {
 
 130         digits = (digits === undefined ? 6 : digits);
 
 131         return +(Math.round(num + ('e+' + digits)) + ('e-' + digits));
 
 134 // @function trim(str: String): String
 
 135 // Compatibility polyfill for [String.prototype.trim](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String/Trim)
 
 137         return str.trim ? str.trim() : str.replace(/^\s+|\s+$/g, '');
 
 140 // @function splitWords(str: String): String[]
 
 141 // Trims and splits the string on whitespace and returns the array of parts.
 
 142 function splitWords(str) {
 
 143         return trim(str).split(/\s+/);
 
 146 // @function setOptions(obj: Object, options: Object): Object
 
 147 // Merges the given properties to the `options` of the `obj` object, returning the resulting options. See `Class options`. Has an `L.setOptions` shortcut.
 
 148 function setOptions(obj, options) {
 
 149         if (!obj.hasOwnProperty('options')) {
 
 150                 obj.options = obj.options ? create(obj.options) : {};
 
 152         for (var i in options) {
 
 153                 obj.options[i] = options[i];
 
 158 // @function getParamString(obj: Object, existingUrl?: String, uppercase?: Boolean): String
 
 159 // Converts an object into a parameter URL string, e.g. `{a: "foo", b: "bar"}`
 
 160 // translates to `'?a=foo&b=bar'`. If `existingUrl` is set, the parameters will
 
 161 // be appended at the end. If `uppercase` is `true`, the parameter names will
 
 162 // be uppercased (e.g. `'?A=foo&B=bar'`)
 
 163 function getParamString(obj, existingUrl, uppercase) {
 
 166                 params.push(encodeURIComponent(uppercase ? i.toUpperCase() : i) + '=' + encodeURIComponent(obj[i]));
 
 168         return ((!existingUrl || existingUrl.indexOf('?') === -1) ? '?' : '&') + params.join('&');
 
 171 var templateRe = /\{ *([\w_-]+) *\}/g;
 
 173 // @function template(str: String, data: Object): String
 
 174 // Simple templating facility, accepts a template string of the form `'Hello {a}, {b}'`
 
 175 // and a data object like `{a: 'foo', b: 'bar'}`, returns evaluated string
 
 176 // `('Hello foo, bar')`. You can also specify functions instead of strings for
 
 177 // data values — they will be evaluated passing `data` as an argument.
 
 178 function template(str, data) {
 
 179         return str.replace(templateRe, function (str, key) {
 
 180                 var value = data[key];
 
 182                 if (value === undefined) {
 
 183                         throw new Error('No value provided for variable ' + str);
 
 185                 } else if (typeof value === 'function') {
 
 192 // @function isArray(obj): Boolean
 
 193 // Compatibility polyfill for [Array.isArray](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Array/isArray)
 
 194 var isArray = Array.isArray || function (obj) {
 
 195         return (Object.prototype.toString.call(obj) === '[object Array]');
 
 198 // @function indexOf(array: Array, el: Object): Number
 
 199 // Compatibility polyfill for [Array.prototype.indexOf](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Array/indexOf)
 
 200 function indexOf(array, el) {
 
 201         for (var i = 0; i < array.length; i++) {
 
 202                 if (array[i] === el) { return i; }
 
 207 // @property emptyImageUrl: String
 
 208 // Data URI string containing a base64-encoded empty GIF image.
 
 209 // Used as a hack to free memory from unused images on WebKit-powered
 
 210 // mobile devices (by setting image `src` to this string).
 
 211 var emptyImageUrl = 'data:image/gif;base64,R0lGODlhAQABAAD/ACwAAAAAAQABAAACADs=';
 
 213 // inspired by http://paulirish.com/2011/requestanimationframe-for-smart-animating/
 
 215 function getPrefixed(name) {
 
 216         return window['webkit' + name] || window['moz' + name] || window['ms' + name];
 
 221 // fallback for IE 7-8
 
 222 function timeoutDefer(fn) {
 
 223         var time = +new Date(),
 
 224             timeToCall = Math.max(0, 16 - (time - lastTime));
 
 226         lastTime = time + timeToCall;
 
 227         return window.setTimeout(fn, timeToCall);
 
 230 var requestFn = window.requestAnimationFrame || getPrefixed('RequestAnimationFrame') || timeoutDefer;
 
 231 var cancelFn = window.cancelAnimationFrame || getPrefixed('CancelAnimationFrame') ||
 
 232                 getPrefixed('CancelRequestAnimationFrame') || function (id) { window.clearTimeout(id); };
 
 234 // @function requestAnimFrame(fn: Function, context?: Object, immediate?: Boolean): Number
 
 235 // Schedules `fn` to be executed when the browser repaints. `fn` is bound to
 
 236 // `context` if given. When `immediate` is set, `fn` is called immediately if
 
 237 // the browser doesn't have native support for
 
 238 // [`window.requestAnimationFrame`](https://developer.mozilla.org/docs/Web/API/window/requestAnimationFrame),
 
 239 // otherwise it's delayed. Returns a request ID that can be used to cancel the request.
 
 240 function requestAnimFrame(fn, context, immediate) {
 
 241         if (immediate && requestFn === timeoutDefer) {
 
 244                 return requestFn.call(window, bind(fn, context));
 
 248 // @function cancelAnimFrame(id: Number): undefined
 
 249 // Cancels a previous `requestAnimFrame`. See also [window.cancelAnimationFrame](https://developer.mozilla.org/docs/Web/API/window/cancelAnimationFrame).
 
 250 function cancelAnimFrame(id) {
 
 252                 cancelFn.call(window, id);
 
 257 var Util = (Object.freeze || Object)({
 
 267         formatNum: formatNum,
 
 269         splitWords: splitWords,
 
 270         setOptions: setOptions,
 
 271         getParamString: getParamString,
 
 275         emptyImageUrl: emptyImageUrl,
 
 276         requestFn: requestFn,
 
 278         requestAnimFrame: requestAnimFrame,
 
 279         cancelAnimFrame: cancelAnimFrame
 
 288 // Thanks to John Resig and Dean Edwards for inspiration!
 
 292 Class.extend = function (props) {
 
 294         // @function extend(props: Object): Function
 
 295         // [Extends the current class](#class-inheritance) given the properties to be included.
 
 296         // Returns a Javascript function that is a class constructor (to be called with `new`).
 
 297         var NewClass = function () {
 
 299                 // call the constructor
 
 300                 if (this.initialize) {
 
 301                         this.initialize.apply(this, arguments);
 
 304                 // call all constructor hooks
 
 305                 this.callInitHooks();
 
 308         var parentProto = NewClass.__super__ = this.prototype;
 
 310         var proto = create(parentProto);
 
 311         proto.constructor = NewClass;
 
 313         NewClass.prototype = proto;
 
 315         // inherit parent's statics
 
 316         for (var i in this) {
 
 317                 if (this.hasOwnProperty(i) && i !== 'prototype' && i !== '__super__') {
 
 318                         NewClass[i] = this[i];
 
 322         // mix static properties into the class
 
 324                 extend(NewClass, props.statics);
 
 325                 delete props.statics;
 
 328         // mix includes into the prototype
 
 329         if (props.includes) {
 
 330                 checkDeprecatedMixinEvents(props.includes);
 
 331                 extend.apply(null, [proto].concat(props.includes));
 
 332                 delete props.includes;
 
 337                 props.options = extend(create(proto.options), props.options);
 
 340         // mix given properties into the prototype
 
 341         extend(proto, props);
 
 343         proto._initHooks = [];
 
 345         // add method for calling all hooks
 
 346         proto.callInitHooks = function () {
 
 348                 if (this._initHooksCalled) { return; }
 
 350                 if (parentProto.callInitHooks) {
 
 351                         parentProto.callInitHooks.call(this);
 
 354                 this._initHooksCalled = true;
 
 356                 for (var i = 0, len = proto._initHooks.length; i < len; i++) {
 
 357                         proto._initHooks[i].call(this);
 
 365 // @function include(properties: Object): this
 
 366 // [Includes a mixin](#class-includes) into the current class.
 
 367 Class.include = function (props) {
 
 368         extend(this.prototype, props);
 
 372 // @function mergeOptions(options: Object): this
 
 373 // [Merges `options`](#class-options) into the defaults of the class.
 
 374 Class.mergeOptions = function (options) {
 
 375         extend(this.prototype.options, options);
 
 379 // @function addInitHook(fn: Function): this
 
 380 // Adds a [constructor hook](#class-constructor-hooks) to the class.
 
 381 Class.addInitHook = function (fn) { // (Function) || (String, args...)
 
 382         var args = Array.prototype.slice.call(arguments, 1);
 
 384         var init = typeof fn === 'function' ? fn : function () {
 
 385                 this[fn].apply(this, args);
 
 388         this.prototype._initHooks = this.prototype._initHooks || [];
 
 389         this.prototype._initHooks.push(init);
 
 393 function checkDeprecatedMixinEvents(includes) {
 
 394         if (typeof L === 'undefined' || !L || !L.Mixin) { return; }
 
 396         includes = isArray(includes) ? includes : [includes];
 
 398         for (var i = 0; i < includes.length; i++) {
 
 399                 if (includes[i] === L.Mixin.Events) {
 
 400                         console.warn('Deprecated include of L.Mixin.Events: ' +
 
 401                                 'this property will be removed in future releases, ' +
 
 402                                 'please inherit from L.Evented instead.', new Error().stack);
 
 412  * A set of methods shared between event-powered classes (like `Map` and `Marker`). Generally, events allow you to execute some function when something happens with an object (e.g. the user clicks on the map, causing the map to fire `'click'` event).
 
 417  * map.on('click', function(e) {
 
 422  * Leaflet deals with event listeners by reference, so if you want to add a listener and then remove it, define it as a function:
 
 425  * function onClick(e) { ... }
 
 427  * map.on('click', onClick);
 
 428  * map.off('click', onClick);
 
 433         /* @method on(type: String, fn: Function, context?: Object): this
 
 434          * Adds a listener function (`fn`) to a particular event type of the object. You can optionally specify the context of the listener (object the this keyword will point to). You can also pass several space-separated types (e.g. `'click dblclick'`).
 
 437          * @method on(eventMap: Object): this
 
 438          * Adds a set of type/listener pairs, e.g. `{click: onClick, mousemove: onMouseMove}`
 
 440         on: function (types, fn, context) {
 
 442                 // types can be a map of types/handlers
 
 443                 if (typeof types === 'object') {
 
 444                         for (var type in types) {
 
 445                                 // we don't process space-separated events here for performance;
 
 446                                 // it's a hot path since Layer uses the on(obj) syntax
 
 447                                 this._on(type, types[type], fn);
 
 451                         // types can be a string of space-separated words
 
 452                         types = splitWords(types);
 
 454                         for (var i = 0, len = types.length; i < len; i++) {
 
 455                                 this._on(types[i], fn, context);
 
 462         /* @method off(type: String, fn?: Function, context?: Object): this
 
 463          * Removes a previously added listener function. If no function is specified, it will remove all the listeners of that particular event from the object. Note that if you passed a custom context to `on`, you must pass the same context to `off` in order to remove the listener.
 
 466          * @method off(eventMap: Object): this
 
 467          * Removes a set of type/listener pairs.
 
 471          * Removes all listeners to all events on the object. This includes implicitly attached events.
 
 473         off: function (types, fn, context) {
 
 476                         // clear all listeners if called without arguments
 
 479                 } else if (typeof types === 'object') {
 
 480                         for (var type in types) {
 
 481                                 this._off(type, types[type], fn);
 
 485                         types = splitWords(types);
 
 487                         for (var i = 0, len = types.length; i < len; i++) {
 
 488                                 this._off(types[i], fn, context);
 
 495         // attach listener (without syntactic sugar now)
 
 496         _on: function (type, fn, context) {
 
 497                 this._events = this._events || {};
 
 499                 /* get/init listeners for type */
 
 500                 var typeListeners = this._events[type];
 
 501                 if (!typeListeners) {
 
 503                         this._events[type] = typeListeners;
 
 506                 if (context === this) {
 
 507                         // Less memory footprint.
 
 510                 var newListener = {fn: fn, ctx: context},
 
 511                     listeners = typeListeners;
 
 513                 // check if fn already there
 
 514                 for (var i = 0, len = listeners.length; i < len; i++) {
 
 515                         if (listeners[i].fn === fn && listeners[i].ctx === context) {
 
 520                 listeners.push(newListener);
 
 523         _off: function (type, fn, context) {
 
 528                 if (!this._events) { return; }
 
 530                 listeners = this._events[type];
 
 537                         // Set all removed listeners to noop so they are not called if remove happens in fire
 
 538                         for (i = 0, len = listeners.length; i < len; i++) {
 
 539                                 listeners[i].fn = falseFn;
 
 541                         // clear all listeners for a type if function isn't specified
 
 542                         delete this._events[type];
 
 546                 if (context === this) {
 
 552                         // find fn and remove it
 
 553                         for (i = 0, len = listeners.length; i < len; i++) {
 
 554                                 var l = listeners[i];
 
 555                                 if (l.ctx !== context) { continue; }
 
 558                                         // set the removed listener to noop so that's not called if remove happens in fire
 
 561                                         if (this._firingCount) {
 
 562                                                 /* copy array in case events are being fired */
 
 563                                                 this._events[type] = listeners = listeners.slice();
 
 565                                         listeners.splice(i, 1);
 
 573         // @method fire(type: String, data?: Object, propagate?: Boolean): this
 
 574         // Fires an event of the specified type. You can optionally provide an data
 
 575         // object — the first argument of the listener function will contain its
 
 576         // properties. The event can optionally be propagated to event parents.
 
 577         fire: function (type, data, propagate) {
 
 578                 if (!this.listens(type, propagate)) { return this; }
 
 580                 var event = extend({}, data, {
 
 583                         sourceTarget: data && data.sourceTarget || this
 
 587                         var listeners = this._events[type];
 
 590                                 this._firingCount = (this._firingCount + 1) || 1;
 
 591                                 for (var i = 0, len = listeners.length; i < len; i++) {
 
 592                                         var l = listeners[i];
 
 593                                         l.fn.call(l.ctx || this, event);
 
 601                         // propagate the event to parents (set with addEventParent)
 
 602                         this._propagateEvent(event);
 
 608         // @method listens(type: String): Boolean
 
 609         // Returns `true` if a particular event type has any listeners attached to it.
 
 610         listens: function (type, propagate) {
 
 611                 var listeners = this._events && this._events[type];
 
 612                 if (listeners && listeners.length) { return true; }
 
 615                         // also check parents for listeners if event propagates
 
 616                         for (var id in this._eventParents) {
 
 617                                 if (this._eventParents[id].listens(type, propagate)) { return true; }
 
 623         // @method once(…): this
 
 624         // Behaves as [`on(…)`](#evented-on), except the listener will only get fired once and then removed.
 
 625         once: function (types, fn, context) {
 
 627                 if (typeof types === 'object') {
 
 628                         for (var type in types) {
 
 629                                 this.once(type, types[type], fn);
 
 634                 var handler = bind(function () {
 
 636                             .off(types, fn, context)
 
 637                             .off(types, handler, context);
 
 640                 // add a listener that's executed once and removed after that
 
 642                     .on(types, fn, context)
 
 643                     .on(types, handler, context);
 
 646         // @method addEventParent(obj: Evented): this
 
 647         // Adds an event parent - an `Evented` that will receive propagated events
 
 648         addEventParent: function (obj) {
 
 649                 this._eventParents = this._eventParents || {};
 
 650                 this._eventParents[stamp(obj)] = obj;
 
 654         // @method removeEventParent(obj: Evented): this
 
 655         // Removes an event parent, so it will stop receiving propagated events
 
 656         removeEventParent: function (obj) {
 
 657                 if (this._eventParents) {
 
 658                         delete this._eventParents[stamp(obj)];
 
 663         _propagateEvent: function (e) {
 
 664                 for (var id in this._eventParents) {
 
 665                         this._eventParents[id].fire(e.type, extend({
 
 667                                 propagatedFrom: e.target
 
 673 // aliases; we should ditch those eventually
 
 675 // @method addEventListener(…): this
 
 676 // Alias to [`on(…)`](#evented-on)
 
 677 Events.addEventListener = Events.on;
 
 679 // @method removeEventListener(…): this
 
 680 // Alias to [`off(…)`](#evented-off)
 
 682 // @method clearAllEventListeners(…): this
 
 683 // Alias to [`off()`](#evented-off)
 
 684 Events.removeEventListener = Events.clearAllEventListeners = Events.off;
 
 686 // @method addOneTimeEventListener(…): this
 
 687 // Alias to [`once(…)`](#evented-once)
 
 688 Events.addOneTimeEventListener = Events.once;
 
 690 // @method fireEvent(…): this
 
 691 // Alias to [`fire(…)`](#evented-fire)
 
 692 Events.fireEvent = Events.fire;
 
 694 // @method hasEventListeners(…): Boolean
 
 695 // Alias to [`listens(…)`](#evented-listens)
 
 696 Events.hasEventListeners = Events.listens;
 
 698 var Evented = Class.extend(Events);
 
 704  * Represents a point with `x` and `y` coordinates in pixels.
 
 709  * var point = L.point(200, 300);
 
 712  * All Leaflet methods and options that accept `Point` objects also accept them in a simple Array form (unless noted otherwise), so these lines are equivalent:
 
 715  * map.panBy([200, 300]);
 
 716  * map.panBy(L.point(200, 300));
 
 719  * Note that `Point` does not inherit from Leafet's `Class` object,
 
 720  * which means new classes can't inherit from it, and new methods
 
 721  * can't be added to it with the `include` function.
 
 724 function Point(x, y, round) {
 
 725         // @property x: Number; The `x` coordinate of the point
 
 726         this.x = (round ? Math.round(x) : x);
 
 727         // @property y: Number; The `y` coordinate of the point
 
 728         this.y = (round ? Math.round(y) : y);
 
 731 var trunc = Math.trunc || function (v) {
 
 732         return v > 0 ? Math.floor(v) : Math.ceil(v);
 
 737         // @method clone(): Point
 
 738         // Returns a copy of the current point.
 
 740                 return new Point(this.x, this.y);
 
 743         // @method add(otherPoint: Point): Point
 
 744         // Returns the result of addition of the current and the given points.
 
 745         add: function (point) {
 
 746                 // non-destructive, returns a new point
 
 747                 return this.clone()._add(toPoint(point));
 
 750         _add: function (point) {
 
 751                 // destructive, used directly for performance in situations where it's safe to modify existing point
 
 757         // @method subtract(otherPoint: Point): Point
 
 758         // Returns the result of subtraction of the given point from the current.
 
 759         subtract: function (point) {
 
 760                 return this.clone()._subtract(toPoint(point));
 
 763         _subtract: function (point) {
 
 769         // @method divideBy(num: Number): Point
 
 770         // Returns the result of division of the current point by the given number.
 
 771         divideBy: function (num) {
 
 772                 return this.clone()._divideBy(num);
 
 775         _divideBy: function (num) {
 
 781         // @method multiplyBy(num: Number): Point
 
 782         // Returns the result of multiplication of the current point by the given number.
 
 783         multiplyBy: function (num) {
 
 784                 return this.clone()._multiplyBy(num);
 
 787         _multiplyBy: function (num) {
 
 793         // @method scaleBy(scale: Point): Point
 
 794         // Multiply each coordinate of the current point by each coordinate of
 
 795         // `scale`. In linear algebra terms, multiply the point by the
 
 796         // [scaling matrix](https://en.wikipedia.org/wiki/Scaling_%28geometry%29#Matrix_representation)
 
 797         // defined by `scale`.
 
 798         scaleBy: function (point) {
 
 799                 return new Point(this.x * point.x, this.y * point.y);
 
 802         // @method unscaleBy(scale: Point): Point
 
 803         // Inverse of `scaleBy`. Divide each coordinate of the current point by
 
 804         // each coordinate of `scale`.
 
 805         unscaleBy: function (point) {
 
 806                 return new Point(this.x / point.x, this.y / point.y);
 
 809         // @method round(): Point
 
 810         // Returns a copy of the current point with rounded coordinates.
 
 812                 return this.clone()._round();
 
 815         _round: function () {
 
 816                 this.x = Math.round(this.x);
 
 817                 this.y = Math.round(this.y);
 
 821         // @method floor(): Point
 
 822         // Returns a copy of the current point with floored coordinates (rounded down).
 
 824                 return this.clone()._floor();
 
 827         _floor: function () {
 
 828                 this.x = Math.floor(this.x);
 
 829                 this.y = Math.floor(this.y);
 
 833         // @method ceil(): Point
 
 834         // Returns a copy of the current point with ceiled coordinates (rounded up).
 
 836                 return this.clone()._ceil();
 
 840                 this.x = Math.ceil(this.x);
 
 841                 this.y = Math.ceil(this.y);
 
 845         // @method trunc(): Point
 
 846         // Returns a copy of the current point with truncated coordinates (rounded towards zero).
 
 848                 return this.clone()._trunc();
 
 851         _trunc: function () {
 
 852                 this.x = trunc(this.x);
 
 853                 this.y = trunc(this.y);
 
 857         // @method distanceTo(otherPoint: Point): Number
 
 858         // Returns the cartesian distance between the current and the given points.
 
 859         distanceTo: function (point) {
 
 860                 point = toPoint(point);
 
 862                 var x = point.x - this.x,
 
 863                     y = point.y - this.y;
 
 865                 return Math.sqrt(x * x + y * y);
 
 868         // @method equals(otherPoint: Point): Boolean
 
 869         // Returns `true` if the given point has the same coordinates.
 
 870         equals: function (point) {
 
 871                 point = toPoint(point);
 
 873                 return point.x === this.x &&
 
 877         // @method contains(otherPoint: Point): Boolean
 
 878         // Returns `true` if both coordinates of the given point are less than the corresponding current point coordinates (in absolute values).
 
 879         contains: function (point) {
 
 880                 point = toPoint(point);
 
 882                 return Math.abs(point.x) <= Math.abs(this.x) &&
 
 883                        Math.abs(point.y) <= Math.abs(this.y);
 
 886         // @method toString(): String
 
 887         // Returns a string representation of the point for debugging purposes.
 
 888         toString: function () {
 
 890                         formatNum(this.x) + ', ' +
 
 891                         formatNum(this.y) + ')';
 
 895 // @factory L.point(x: Number, y: Number, round?: Boolean)
 
 896 // Creates a Point object with the given `x` and `y` coordinates. If optional `round` is set to true, rounds the `x` and `y` values.
 
 899 // @factory L.point(coords: Number[])
 
 900 // Expects an array of the form `[x, y]` instead.
 
 903 // @factory L.point(coords: Object)
 
 904 // Expects a plain object of the form `{x: Number, y: Number}` instead.
 
 905 function toPoint(x, y, round) {
 
 906         if (x instanceof Point) {
 
 910                 return new Point(x[0], x[1]);
 
 912         if (x === undefined || x === null) {
 
 915         if (typeof x === 'object' && 'x' in x && 'y' in x) {
 
 916                 return new Point(x.x, x.y);
 
 918         return new Point(x, y, round);
 
 925  * Represents a rectangular area in pixel coordinates.
 
 930  * var p1 = L.point(10, 10),
 
 931  * p2 = L.point(40, 60),
 
 932  * bounds = L.bounds(p1, p2);
 
 935  * All Leaflet methods that accept `Bounds` objects also accept them in a simple Array form (unless noted otherwise), so the bounds example above can be passed like this:
 
 938  * otherBounds.intersects([[10, 10], [40, 60]]);
 
 941  * Note that `Bounds` does not inherit from Leafet's `Class` object,
 
 942  * which means new classes can't inherit from it, and new methods
 
 943  * can't be added to it with the `include` function.
 
 946 function Bounds(a, b) {
 
 949         var points = b ? [a, b] : a;
 
 951         for (var i = 0, len = points.length; i < len; i++) {
 
 952                 this.extend(points[i]);
 
 957         // @method extend(point: Point): this
 
 958         // Extends the bounds to contain the given point.
 
 959         extend: function (point) { // (Point)
 
 960                 point = toPoint(point);
 
 962                 // @property min: Point
 
 963                 // The top left corner of the rectangle.
 
 964                 // @property max: Point
 
 965                 // The bottom right corner of the rectangle.
 
 966                 if (!this.min && !this.max) {
 
 967                         this.min = point.clone();
 
 968                         this.max = point.clone();
 
 970                         this.min.x = Math.min(point.x, this.min.x);
 
 971                         this.max.x = Math.max(point.x, this.max.x);
 
 972                         this.min.y = Math.min(point.y, this.min.y);
 
 973                         this.max.y = Math.max(point.y, this.max.y);
 
 978         // @method getCenter(round?: Boolean): Point
 
 979         // Returns the center point of the bounds.
 
 980         getCenter: function (round) {
 
 982                         (this.min.x + this.max.x) / 2,
 
 983                         (this.min.y + this.max.y) / 2, round);
 
 986         // @method getBottomLeft(): Point
 
 987         // Returns the bottom-left point of the bounds.
 
 988         getBottomLeft: function () {
 
 989                 return new Point(this.min.x, this.max.y);
 
 992         // @method getTopRight(): Point
 
 993         // Returns the top-right point of the bounds.
 
 994         getTopRight: function () { // -> Point
 
 995                 return new Point(this.max.x, this.min.y);
 
 998         // @method getTopLeft(): Point
 
 999         // Returns the top-left point of the bounds (i.e. [`this.min`](#bounds-min)).
 
1000         getTopLeft: function () {
 
1001                 return this.min; // left, top
 
1004         // @method getBottomRight(): Point
 
1005         // Returns the bottom-right point of the bounds (i.e. [`this.max`](#bounds-max)).
 
1006         getBottomRight: function () {
 
1007                 return this.max; // right, bottom
 
1010         // @method getSize(): Point
 
1011         // Returns the size of the given bounds
 
1012         getSize: function () {
 
1013                 return this.max.subtract(this.min);
 
1016         // @method contains(otherBounds: Bounds): Boolean
 
1017         // Returns `true` if the rectangle contains the given one.
 
1019         // @method contains(point: Point): Boolean
 
1020         // Returns `true` if the rectangle contains the given point.
 
1021         contains: function (obj) {
 
1024                 if (typeof obj[0] === 'number' || obj instanceof Point) {
 
1027                         obj = toBounds(obj);
 
1030                 if (obj instanceof Bounds) {
 
1037                 return (min.x >= this.min.x) &&
 
1038                        (max.x <= this.max.x) &&
 
1039                        (min.y >= this.min.y) &&
 
1040                        (max.y <= this.max.y);
 
1043         // @method intersects(otherBounds: Bounds): Boolean
 
1044         // Returns `true` if the rectangle intersects the given bounds. Two bounds
 
1045         // intersect if they have at least one point in common.
 
1046         intersects: function (bounds) { // (Bounds) -> Boolean
 
1047                 bounds = toBounds(bounds);
 
1053                     xIntersects = (max2.x >= min.x) && (min2.x <= max.x),
 
1054                     yIntersects = (max2.y >= min.y) && (min2.y <= max.y);
 
1056                 return xIntersects && yIntersects;
 
1059         // @method overlaps(otherBounds: Bounds): Boolean
 
1060         // Returns `true` if the rectangle overlaps the given bounds. Two bounds
 
1061         // overlap if their intersection is an area.
 
1062         overlaps: function (bounds) { // (Bounds) -> Boolean
 
1063                 bounds = toBounds(bounds);
 
1069                     xOverlaps = (max2.x > min.x) && (min2.x < max.x),
 
1070                     yOverlaps = (max2.y > min.y) && (min2.y < max.y);
 
1072                 return xOverlaps && yOverlaps;
 
1075         isValid: function () {
 
1076                 return !!(this.min && this.max);
 
1081 // @factory L.bounds(corner1: Point, corner2: Point)
 
1082 // Creates a Bounds object from two corners coordinate pairs.
 
1084 // @factory L.bounds(points: Point[])
 
1085 // Creates a Bounds object from the given array of points.
 
1086 function toBounds(a, b) {
 
1087         if (!a || a instanceof Bounds) {
 
1090         return new Bounds(a, b);
 
1094  * @class LatLngBounds
 
1095  * @aka L.LatLngBounds
 
1097  * Represents a rectangular geographical area on a map.
 
1102  * var corner1 = L.latLng(40.712, -74.227),
 
1103  * corner2 = L.latLng(40.774, -74.125),
 
1104  * bounds = L.latLngBounds(corner1, corner2);
 
1107  * All Leaflet methods that accept LatLngBounds objects also accept them in a simple Array form (unless noted otherwise), so the bounds example above can be passed like this:
 
1111  *      [40.712, -74.227],
 
1116  * Caution: if the area crosses the antimeridian (often confused with the International Date Line), you must specify corners _outside_ the [-180, 180] degrees longitude range.
 
1118  * Note that `LatLngBounds` does not inherit from Leafet's `Class` object,
 
1119  * which means new classes can't inherit from it, and new methods
 
1120  * can't be added to it with the `include` function.
 
1123 function LatLngBounds(corner1, corner2) { // (LatLng, LatLng) or (LatLng[])
 
1124         if (!corner1) { return; }
 
1126         var latlngs = corner2 ? [corner1, corner2] : corner1;
 
1128         for (var i = 0, len = latlngs.length; i < len; i++) {
 
1129                 this.extend(latlngs[i]);
 
1133 LatLngBounds.prototype = {
 
1135         // @method extend(latlng: LatLng): this
 
1136         // Extend the bounds to contain the given point
 
1139         // @method extend(otherBounds: LatLngBounds): this
 
1140         // Extend the bounds to contain the given bounds
 
1141         extend: function (obj) {
 
1142                 var sw = this._southWest,
 
1143                     ne = this._northEast,
 
1146                 if (obj instanceof LatLng) {
 
1150                 } else if (obj instanceof LatLngBounds) {
 
1151                         sw2 = obj._southWest;
 
1152                         ne2 = obj._northEast;
 
1154                         if (!sw2 || !ne2) { return this; }
 
1157                         return obj ? this.extend(toLatLng(obj) || toLatLngBounds(obj)) : this;
 
1161                         this._southWest = new LatLng(sw2.lat, sw2.lng);
 
1162                         this._northEast = new LatLng(ne2.lat, ne2.lng);
 
1164                         sw.lat = Math.min(sw2.lat, sw.lat);
 
1165                         sw.lng = Math.min(sw2.lng, sw.lng);
 
1166                         ne.lat = Math.max(ne2.lat, ne.lat);
 
1167                         ne.lng = Math.max(ne2.lng, ne.lng);
 
1173         // @method pad(bufferRatio: Number): LatLngBounds
 
1174         // Returns bounds created by extending or retracting the current bounds by a given ratio in each direction.
 
1175         // For example, a ratio of 0.5 extends the bounds by 50% in each direction.
 
1176         // Negative values will retract the bounds.
 
1177         pad: function (bufferRatio) {
 
1178                 var sw = this._southWest,
 
1179                     ne = this._northEast,
 
1180                     heightBuffer = Math.abs(sw.lat - ne.lat) * bufferRatio,
 
1181                     widthBuffer = Math.abs(sw.lng - ne.lng) * bufferRatio;
 
1183                 return new LatLngBounds(
 
1184                         new LatLng(sw.lat - heightBuffer, sw.lng - widthBuffer),
 
1185                         new LatLng(ne.lat + heightBuffer, ne.lng + widthBuffer));
 
1188         // @method getCenter(): LatLng
 
1189         // Returns the center point of the bounds.
 
1190         getCenter: function () {
 
1192                         (this._southWest.lat + this._northEast.lat) / 2,
 
1193                         (this._southWest.lng + this._northEast.lng) / 2);
 
1196         // @method getSouthWest(): LatLng
 
1197         // Returns the south-west point of the bounds.
 
1198         getSouthWest: function () {
 
1199                 return this._southWest;
 
1202         // @method getNorthEast(): LatLng
 
1203         // Returns the north-east point of the bounds.
 
1204         getNorthEast: function () {
 
1205                 return this._northEast;
 
1208         // @method getNorthWest(): LatLng
 
1209         // Returns the north-west point of the bounds.
 
1210         getNorthWest: function () {
 
1211                 return new LatLng(this.getNorth(), this.getWest());
 
1214         // @method getSouthEast(): LatLng
 
1215         // Returns the south-east point of the bounds.
 
1216         getSouthEast: function () {
 
1217                 return new LatLng(this.getSouth(), this.getEast());
 
1220         // @method getWest(): Number
 
1221         // Returns the west longitude of the bounds
 
1222         getWest: function () {
 
1223                 return this._southWest.lng;
 
1226         // @method getSouth(): Number
 
1227         // Returns the south latitude of the bounds
 
1228         getSouth: function () {
 
1229                 return this._southWest.lat;
 
1232         // @method getEast(): Number
 
1233         // Returns the east longitude of the bounds
 
1234         getEast: function () {
 
1235                 return this._northEast.lng;
 
1238         // @method getNorth(): Number
 
1239         // Returns the north latitude of the bounds
 
1240         getNorth: function () {
 
1241                 return this._northEast.lat;
 
1244         // @method contains(otherBounds: LatLngBounds): Boolean
 
1245         // Returns `true` if the rectangle contains the given one.
 
1248         // @method contains (latlng: LatLng): Boolean
 
1249         // Returns `true` if the rectangle contains the given point.
 
1250         contains: function (obj) { // (LatLngBounds) or (LatLng) -> Boolean
 
1251                 if (typeof obj[0] === 'number' || obj instanceof LatLng || 'lat' in obj) {
 
1252                         obj = toLatLng(obj);
 
1254                         obj = toLatLngBounds(obj);
 
1257                 var sw = this._southWest,
 
1258                     ne = this._northEast,
 
1261                 if (obj instanceof LatLngBounds) {
 
1262                         sw2 = obj.getSouthWest();
 
1263                         ne2 = obj.getNorthEast();
 
1268                 return (sw2.lat >= sw.lat) && (ne2.lat <= ne.lat) &&
 
1269                        (sw2.lng >= sw.lng) && (ne2.lng <= ne.lng);
 
1272         // @method intersects(otherBounds: LatLngBounds): Boolean
 
1273         // Returns `true` if the rectangle intersects the given bounds. Two bounds intersect if they have at least one point in common.
 
1274         intersects: function (bounds) {
 
1275                 bounds = toLatLngBounds(bounds);
 
1277                 var sw = this._southWest,
 
1278                     ne = this._northEast,
 
1279                     sw2 = bounds.getSouthWest(),
 
1280                     ne2 = bounds.getNorthEast(),
 
1282                     latIntersects = (ne2.lat >= sw.lat) && (sw2.lat <= ne.lat),
 
1283                     lngIntersects = (ne2.lng >= sw.lng) && (sw2.lng <= ne.lng);
 
1285                 return latIntersects && lngIntersects;
 
1288         // @method overlaps(otherBounds: Bounds): Boolean
 
1289         // Returns `true` if the rectangle overlaps the given bounds. Two bounds overlap if their intersection is an area.
 
1290         overlaps: function (bounds) {
 
1291                 bounds = toLatLngBounds(bounds);
 
1293                 var sw = this._southWest,
 
1294                     ne = this._northEast,
 
1295                     sw2 = bounds.getSouthWest(),
 
1296                     ne2 = bounds.getNorthEast(),
 
1298                     latOverlaps = (ne2.lat > sw.lat) && (sw2.lat < ne.lat),
 
1299                     lngOverlaps = (ne2.lng > sw.lng) && (sw2.lng < ne.lng);
 
1301                 return latOverlaps && lngOverlaps;
 
1304         // @method toBBoxString(): String
 
1305         // Returns a string with bounding box coordinates in a 'southwest_lng,southwest_lat,northeast_lng,northeast_lat' format. Useful for sending requests to web services that return geo data.
 
1306         toBBoxString: function () {
 
1307                 return [this.getWest(), this.getSouth(), this.getEast(), this.getNorth()].join(',');
 
1310         // @method equals(otherBounds: LatLngBounds, maxMargin?: Number): Boolean
 
1311         // Returns `true` if the rectangle is equivalent (within a small margin of error) to the given bounds. The margin of error can be overridden by setting `maxMargin` to a small number.
 
1312         equals: function (bounds, maxMargin) {
 
1313                 if (!bounds) { return false; }
 
1315                 bounds = toLatLngBounds(bounds);
 
1317                 return this._southWest.equals(bounds.getSouthWest(), maxMargin) &&
 
1318                        this._northEast.equals(bounds.getNorthEast(), maxMargin);
 
1321         // @method isValid(): Boolean
 
1322         // Returns `true` if the bounds are properly initialized.
 
1323         isValid: function () {
 
1324                 return !!(this._southWest && this._northEast);
 
1328 // TODO International date line?
 
1330 // @factory L.latLngBounds(corner1: LatLng, corner2: LatLng)
 
1331 // Creates a `LatLngBounds` object by defining two diagonally opposite corners of the rectangle.
 
1334 // @factory L.latLngBounds(latlngs: LatLng[])
 
1335 // Creates a `LatLngBounds` object defined by the geographical points it contains. Very useful for zooming the map to fit a particular set of locations with [`fitBounds`](#map-fitbounds).
 
1336 function toLatLngBounds(a, b) {
 
1337         if (a instanceof LatLngBounds) {
 
1340         return new LatLngBounds(a, b);
 
1346  * Represents a geographical point with a certain latitude and longitude.
 
1351  * var latlng = L.latLng(50.5, 30.5);
 
1354  * All Leaflet methods that accept LatLng objects also accept them in a simple Array form and simple object form (unless noted otherwise), so these lines are equivalent:
 
1357  * map.panTo([50, 30]);
 
1358  * map.panTo({lon: 30, lat: 50});
 
1359  * map.panTo({lat: 50, lng: 30});
 
1360  * map.panTo(L.latLng(50, 30));
 
1363  * Note that `LatLng` does not inherit from Leaflet's `Class` object,
 
1364  * which means new classes can't inherit from it, and new methods
 
1365  * can't be added to it with the `include` function.
 
1368 function LatLng(lat, lng, alt) {
 
1369         if (isNaN(lat) || isNaN(lng)) {
 
1370                 throw new Error('Invalid LatLng object: (' + lat + ', ' + lng + ')');
 
1373         // @property lat: Number
 
1374         // Latitude in degrees
 
1377         // @property lng: Number
 
1378         // Longitude in degrees
 
1381         // @property alt: Number
 
1382         // Altitude in meters (optional)
 
1383         if (alt !== undefined) {
 
1388 LatLng.prototype = {
 
1389         // @method equals(otherLatLng: LatLng, maxMargin?: Number): Boolean
 
1390         // Returns `true` if the given `LatLng` point is at the same position (within a small margin of error). The margin of error can be overridden by setting `maxMargin` to a small number.
 
1391         equals: function (obj, maxMargin) {
 
1392                 if (!obj) { return false; }
 
1394                 obj = toLatLng(obj);
 
1396                 var margin = Math.max(
 
1397                         Math.abs(this.lat - obj.lat),
 
1398                         Math.abs(this.lng - obj.lng));
 
1400                 return margin <= (maxMargin === undefined ? 1.0E-9 : maxMargin);
 
1403         // @method toString(): String
 
1404         // Returns a string representation of the point (for debugging purposes).
 
1405         toString: function (precision) {
 
1407                         formatNum(this.lat, precision) + ', ' +
 
1408                         formatNum(this.lng, precision) + ')';
 
1411         // @method distanceTo(otherLatLng: LatLng): Number
 
1412         // Returns the distance (in meters) to the given `LatLng` calculated using the [Spherical Law of Cosines](https://en.wikipedia.org/wiki/Spherical_law_of_cosines).
 
1413         distanceTo: function (other) {
 
1414                 return Earth.distance(this, toLatLng(other));
 
1417         // @method wrap(): LatLng
 
1418         // Returns a new `LatLng` object with the longitude wrapped so it's always between -180 and +180 degrees.
 
1420                 return Earth.wrapLatLng(this);
 
1423         // @method toBounds(sizeInMeters: Number): LatLngBounds
 
1424         // Returns a new `LatLngBounds` object in which each boundary is `sizeInMeters/2` meters apart from the `LatLng`.
 
1425         toBounds: function (sizeInMeters) {
 
1426                 var latAccuracy = 180 * sizeInMeters / 40075017,
 
1427                     lngAccuracy = latAccuracy / Math.cos((Math.PI / 180) * this.lat);
 
1429                 return toLatLngBounds(
 
1430                         [this.lat - latAccuracy, this.lng - lngAccuracy],
 
1431                         [this.lat + latAccuracy, this.lng + lngAccuracy]);
 
1434         clone: function () {
 
1435                 return new LatLng(this.lat, this.lng, this.alt);
 
1441 // @factory L.latLng(latitude: Number, longitude: Number, altitude?: Number): LatLng
 
1442 // Creates an object representing a geographical point with the given latitude and longitude (and optionally altitude).
 
1445 // @factory L.latLng(coords: Array): LatLng
 
1446 // Expects an array of the form `[Number, Number]` or `[Number, Number, Number]` instead.
 
1449 // @factory L.latLng(coords: Object): LatLng
 
1450 // Expects an plain object of the form `{lat: Number, lng: Number}` or `{lat: Number, lng: Number, alt: Number}` instead.
 
1452 function toLatLng(a, b, c) {
 
1453         if (a instanceof LatLng) {
 
1456         if (isArray(a) && typeof a[0] !== 'object') {
 
1457                 if (a.length === 3) {
 
1458                         return new LatLng(a[0], a[1], a[2]);
 
1460                 if (a.length === 2) {
 
1461                         return new LatLng(a[0], a[1]);
 
1465         if (a === undefined || a === null) {
 
1468         if (typeof a === 'object' && 'lat' in a) {
 
1469                 return new LatLng(a.lat, 'lng' in a ? a.lng : a.lon, a.alt);
 
1471         if (b === undefined) {
 
1474         return new LatLng(a, b, c);
 
1480  * Object that defines coordinate reference systems for projecting
 
1481  * geographical points into pixel (screen) coordinates and back (and to
 
1482  * coordinates in other units for [WMS](https://en.wikipedia.org/wiki/Web_Map_Service) services). See
 
1483  * [spatial reference system](http://en.wikipedia.org/wiki/Coordinate_reference_system).
 
1485  * Leaflet defines the most usual CRSs by default. If you want to use a
 
1486  * CRS not defined by default, take a look at the
 
1487  * [Proj4Leaflet](https://github.com/kartena/Proj4Leaflet) plugin.
 
1489  * Note that the CRS instances do not inherit from Leafet's `Class` object,
 
1490  * and can't be instantiated. Also, new classes can't inherit from them,
 
1491  * and methods can't be added to them with the `include` function.
 
1495         // @method latLngToPoint(latlng: LatLng, zoom: Number): Point
 
1496         // Projects geographical coordinates into pixel coordinates for a given zoom.
 
1497         latLngToPoint: function (latlng, zoom) {
 
1498                 var projectedPoint = this.projection.project(latlng),
 
1499                     scale = this.scale(zoom);
 
1501                 return this.transformation._transform(projectedPoint, scale);
 
1504         // @method pointToLatLng(point: Point, zoom: Number): LatLng
 
1505         // The inverse of `latLngToPoint`. Projects pixel coordinates on a given
 
1506         // zoom into geographical coordinates.
 
1507         pointToLatLng: function (point, zoom) {
 
1508                 var scale = this.scale(zoom),
 
1509                     untransformedPoint = this.transformation.untransform(point, scale);
 
1511                 return this.projection.unproject(untransformedPoint);
 
1514         // @method project(latlng: LatLng): Point
 
1515         // Projects geographical coordinates into coordinates in units accepted for
 
1516         // this CRS (e.g. meters for EPSG:3857, for passing it to WMS services).
 
1517         project: function (latlng) {
 
1518                 return this.projection.project(latlng);
 
1521         // @method unproject(point: Point): LatLng
 
1522         // Given a projected coordinate returns the corresponding LatLng.
 
1523         // The inverse of `project`.
 
1524         unproject: function (point) {
 
1525                 return this.projection.unproject(point);
 
1528         // @method scale(zoom: Number): Number
 
1529         // Returns the scale used when transforming projected coordinates into
 
1530         // pixel coordinates for a particular zoom. For example, it returns
 
1531         // `256 * 2^zoom` for Mercator-based CRS.
 
1532         scale: function (zoom) {
 
1533                 return 256 * Math.pow(2, zoom);
 
1536         // @method zoom(scale: Number): Number
 
1537         // Inverse of `scale()`, returns the zoom level corresponding to a scale
 
1538         // factor of `scale`.
 
1539         zoom: function (scale) {
 
1540                 return Math.log(scale / 256) / Math.LN2;
 
1543         // @method getProjectedBounds(zoom: Number): Bounds
 
1544         // Returns the projection's bounds scaled and transformed for the provided `zoom`.
 
1545         getProjectedBounds: function (zoom) {
 
1546                 if (this.infinite) { return null; }
 
1548                 var b = this.projection.bounds,
 
1549                     s = this.scale(zoom),
 
1550                     min = this.transformation.transform(b.min, s),
 
1551                     max = this.transformation.transform(b.max, s);
 
1553                 return new Bounds(min, max);
 
1556         // @method distance(latlng1: LatLng, latlng2: LatLng): Number
 
1557         // Returns the distance between two geographical coordinates.
 
1559         // @property code: String
 
1560         // Standard code name of the CRS passed into WMS services (e.g. `'EPSG:3857'`)
 
1562         // @property wrapLng: Number[]
 
1563         // An array of two numbers defining whether the longitude (horizontal) coordinate
 
1564         // axis wraps around a given range and how. Defaults to `[-180, 180]` in most
 
1565         // geographical CRSs. If `undefined`, the longitude axis does not wrap around.
 
1567         // @property wrapLat: Number[]
 
1568         // Like `wrapLng`, but for the latitude (vertical) axis.
 
1570         // wrapLng: [min, max],
 
1571         // wrapLat: [min, max],
 
1573         // @property infinite: Boolean
 
1574         // If true, the coordinate space will be unbounded (infinite in both axes)
 
1577         // @method wrapLatLng(latlng: LatLng): LatLng
 
1578         // Returns a `LatLng` where lat and lng has been wrapped according to the
 
1579         // CRS's `wrapLat` and `wrapLng` properties, if they are outside the CRS's bounds.
 
1580         wrapLatLng: function (latlng) {
 
1581                 var lng = this.wrapLng ? wrapNum(latlng.lng, this.wrapLng, true) : latlng.lng,
 
1582                     lat = this.wrapLat ? wrapNum(latlng.lat, this.wrapLat, true) : latlng.lat,
 
1585                 return new LatLng(lat, lng, alt);
 
1588         // @method wrapLatLngBounds(bounds: LatLngBounds): LatLngBounds
 
1589         // Returns a `LatLngBounds` with the same size as the given one, ensuring
 
1590         // that its center is within the CRS's bounds.
 
1591         // Only accepts actual `L.LatLngBounds` instances, not arrays.
 
1592         wrapLatLngBounds: function (bounds) {
 
1593                 var center = bounds.getCenter(),
 
1594                     newCenter = this.wrapLatLng(center),
 
1595                     latShift = center.lat - newCenter.lat,
 
1596                     lngShift = center.lng - newCenter.lng;
 
1598                 if (latShift === 0 && lngShift === 0) {
 
1602                 var sw = bounds.getSouthWest(),
 
1603                     ne = bounds.getNorthEast(),
 
1604                     newSw = new LatLng(sw.lat - latShift, sw.lng - lngShift),
 
1605                     newNe = new LatLng(ne.lat - latShift, ne.lng - lngShift);
 
1607                 return new LatLngBounds(newSw, newNe);
 
1615  * Serves as the base for CRS that are global such that they cover the earth.
 
1616  * Can only be used as the base for other CRS and cannot be used directly,
 
1617  * since it does not have a `code`, `projection` or `transformation`. `distance()` returns
 
1621 var Earth = extend({}, CRS, {
 
1622         wrapLng: [-180, 180],
 
1624         // Mean Earth Radius, as recommended for use by
 
1625         // the International Union of Geodesy and Geophysics,
 
1626         // see http://rosettacode.org/wiki/Haversine_formula
 
1629         // distance between two geographical points using spherical law of cosines approximation
 
1630         distance: function (latlng1, latlng2) {
 
1631                 var rad = Math.PI / 180,
 
1632                     lat1 = latlng1.lat * rad,
 
1633                     lat2 = latlng2.lat * rad,
 
1634                     sinDLat = Math.sin((latlng2.lat - latlng1.lat) * rad / 2),
 
1635                     sinDLon = Math.sin((latlng2.lng - latlng1.lng) * rad / 2),
 
1636                     a = sinDLat * sinDLat + Math.cos(lat1) * Math.cos(lat2) * sinDLon * sinDLon,
 
1637                     c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
 
1643  * @namespace Projection
 
1644  * @projection L.Projection.SphericalMercator
 
1646  * Spherical Mercator projection — the most common projection for online maps,
 
1647  * used by almost all free and commercial tile providers. Assumes that Earth is
 
1648  * a sphere. Used by the `EPSG:3857` CRS.
 
1651 var earthRadius = 6378137;
 
1653 var SphericalMercator = {
 
1656         MAX_LATITUDE: 85.0511287798,
 
1658         project: function (latlng) {
 
1659                 var d = Math.PI / 180,
 
1660                     max = this.MAX_LATITUDE,
 
1661                     lat = Math.max(Math.min(max, latlng.lat), -max),
 
1662                     sin = Math.sin(lat * d);
 
1665                         this.R * latlng.lng * d,
 
1666                         this.R * Math.log((1 + sin) / (1 - sin)) / 2);
 
1669         unproject: function (point) {
 
1670                 var d = 180 / Math.PI;
 
1673                         (2 * Math.atan(Math.exp(point.y / this.R)) - (Math.PI / 2)) * d,
 
1674                         point.x * d / this.R);
 
1677         bounds: (function () {
 
1678                 var d = earthRadius * Math.PI;
 
1679                 return new Bounds([-d, -d], [d, d]);
 
1684  * @class Transformation
 
1685  * @aka L.Transformation
 
1687  * Represents an affine transformation: a set of coefficients `a`, `b`, `c`, `d`
 
1688  * for transforming a point of a form `(x, y)` into `(a*x + b, c*y + d)` and doing
 
1689  * the reverse. Used by Leaflet in its projections code.
 
1694  * var transformation = L.transformation(2, 5, -1, 10),
 
1695  *      p = L.point(1, 2),
 
1696  *      p2 = transformation.transform(p), //  L.point(7, 8)
 
1697  *      p3 = transformation.untransform(p2); //  L.point(1, 2)
 
1702 // factory new L.Transformation(a: Number, b: Number, c: Number, d: Number)
 
1703 // Creates a `Transformation` object with the given coefficients.
 
1704 function Transformation(a, b, c, d) {
 
1706                 // use array properties
 
1719 Transformation.prototype = {
 
1720         // @method transform(point: Point, scale?: Number): Point
 
1721         // Returns a transformed point, optionally multiplied by the given scale.
 
1722         // Only accepts actual `L.Point` instances, not arrays.
 
1723         transform: function (point, scale) { // (Point, Number) -> Point
 
1724                 return this._transform(point.clone(), scale);
 
1727         // destructive transform (faster)
 
1728         _transform: function (point, scale) {
 
1730                 point.x = scale * (this._a * point.x + this._b);
 
1731                 point.y = scale * (this._c * point.y + this._d);
 
1735         // @method untransform(point: Point, scale?: Number): Point
 
1736         // Returns the reverse transformation of the given point, optionally divided
 
1737         // by the given scale. Only accepts actual `L.Point` instances, not arrays.
 
1738         untransform: function (point, scale) {
 
1741                         (point.x / scale - this._b) / this._a,
 
1742                         (point.y / scale - this._d) / this._c);
 
1746 // factory L.transformation(a: Number, b: Number, c: Number, d: Number)
 
1748 // @factory L.transformation(a: Number, b: Number, c: Number, d: Number)
 
1749 // Instantiates a Transformation object with the given coefficients.
 
1752 // @factory L.transformation(coefficients: Array): Transformation
 
1753 // Expects an coefficients array of the form
 
1754 // `[a: Number, b: Number, c: Number, d: Number]`.
 
1756 function toTransformation(a, b, c, d) {
 
1757         return new Transformation(a, b, c, d);
 
1762  * @crs L.CRS.EPSG3857
 
1764  * The most common CRS for online maps, used by almost all free and commercial
 
1765  * tile providers. Uses Spherical Mercator projection. Set in by default in
 
1766  * Map's `crs` option.
 
1769 var EPSG3857 = extend({}, Earth, {
 
1771         projection: SphericalMercator,
 
1773         transformation: (function () {
 
1774                 var scale = 0.5 / (Math.PI * SphericalMercator.R);
 
1775                 return toTransformation(scale, 0.5, -scale, 0.5);
 
1779 var EPSG900913 = extend({}, EPSG3857, {
 
1783 // @namespace SVG; @section
 
1784 // There are several static functions which can be called without instantiating L.SVG:
 
1786 // @function create(name: String): SVGElement
 
1787 // Returns a instance of [SVGElement](https://developer.mozilla.org/docs/Web/API/SVGElement),
 
1788 // corresponding to the class name passed. For example, using 'line' will return
 
1789 // an instance of [SVGLineElement](https://developer.mozilla.org/docs/Web/API/SVGLineElement).
 
1790 function svgCreate(name) {
 
1791         return document.createElementNS('http://www.w3.org/2000/svg', name);
 
1794 // @function pointsToPath(rings: Point[], closed: Boolean): String
 
1795 // Generates a SVG path string for multiple rings, with each ring turning
 
1796 // into "M..L..L.." instructions
 
1797 function pointsToPath(rings, closed) {
 
1799         i, j, len, len2, points, p;
 
1801         for (i = 0, len = rings.length; i < len; i++) {
 
1804                 for (j = 0, len2 = points.length; j < len2; j++) {
 
1806                         str += (j ? 'L' : 'M') + p.x + ' ' + p.y;
 
1809                 // closes the ring for polygons; "x" is VML syntax
 
1810                 str += closed ? (svg ? 'z' : 'x') : '';
 
1813         // SVG complains about empty path strings
 
1814         return str || 'M0 0';
 
1818  * @namespace Browser
 
1821  * A namespace with static properties for browser/feature detection used by Leaflet internally.
 
1826  * if (L.Browser.ielt9) {
 
1827  *   alert('Upgrade your browser, dude!');
 
1832 var style$1 = document.documentElement.style;
 
1834 // @property ie: Boolean; `true` for all Internet Explorer versions (not Edge).
 
1835 var ie = 'ActiveXObject' in window;
 
1837 // @property ielt9: Boolean; `true` for Internet Explorer versions less than 9.
 
1838 var ielt9 = ie && !document.addEventListener;
 
1840 // @property edge: Boolean; `true` for the Edge web browser.
 
1841 var edge = 'msLaunchUri' in navigator && !('documentMode' in document);
 
1843 // @property webkit: Boolean;
 
1844 // `true` for webkit-based browsers like Chrome and Safari (including mobile versions).
 
1845 var webkit = userAgentContains('webkit');
 
1847 // @property android: Boolean
 
1848 // `true` for any browser running on an Android platform.
 
1849 var android = userAgentContains('android');
 
1851 // @property android23: Boolean; `true` for browsers running on Android 2 or Android 3.
 
1852 var android23 = userAgentContains('android 2') || userAgentContains('android 3');
 
1854 /* See https://stackoverflow.com/a/17961266 for details on detecting stock Android */
 
1855 var webkitVer = parseInt(/WebKit\/([0-9]+)|$/.exec(navigator.userAgent)[1], 10); // also matches AppleWebKit
 
1856 // @property androidStock: Boolean; `true` for the Android stock browser (i.e. not Chrome)
 
1857 var androidStock = android && userAgentContains('Google') && webkitVer < 537 && !('AudioNode' in window);
 
1859 // @property opera: Boolean; `true` for the Opera browser
 
1860 var opera = !!window.opera;
 
1862 // @property chrome: Boolean; `true` for the Chrome browser.
 
1863 var chrome = userAgentContains('chrome');
 
1865 // @property gecko: Boolean; `true` for gecko-based browsers like Firefox.
 
1866 var gecko = userAgentContains('gecko') && !webkit && !opera && !ie;
 
1868 // @property safari: Boolean; `true` for the Safari browser.
 
1869 var safari = !chrome && userAgentContains('safari');
 
1871 var phantom = userAgentContains('phantom');
 
1873 // @property opera12: Boolean
 
1874 // `true` for the Opera browser supporting CSS transforms (version 12 or later).
 
1875 var opera12 = 'OTransition' in style$1;
 
1877 // @property win: Boolean; `true` when the browser is running in a Windows platform
 
1878 var win = navigator.platform.indexOf('Win') === 0;
 
1880 // @property ie3d: Boolean; `true` for all Internet Explorer versions supporting CSS transforms.
 
1881 var ie3d = ie && ('transition' in style$1);
 
1883 // @property webkit3d: Boolean; `true` for webkit-based browsers supporting CSS transforms.
 
1884 var webkit3d = ('WebKitCSSMatrix' in window) && ('m11' in new window.WebKitCSSMatrix()) && !android23;
 
1886 // @property gecko3d: Boolean; `true` for gecko-based browsers supporting CSS transforms.
 
1887 var gecko3d = 'MozPerspective' in style$1;
 
1889 // @property any3d: Boolean
 
1890 // `true` for all browsers supporting CSS transforms.
 
1891 var any3d = !window.L_DISABLE_3D && (ie3d || webkit3d || gecko3d) && !opera12 && !phantom;
 
1893 // @property mobile: Boolean; `true` for all browsers running in a mobile device.
 
1894 var mobile = typeof orientation !== 'undefined' || userAgentContains('mobile');
 
1896 // @property mobileWebkit: Boolean; `true` for all webkit-based browsers in a mobile device.
 
1897 var mobileWebkit = mobile && webkit;
 
1899 // @property mobileWebkit3d: Boolean
 
1900 // `true` for all webkit-based browsers in a mobile device supporting CSS transforms.
 
1901 var mobileWebkit3d = mobile && webkit3d;
 
1903 // @property msPointer: Boolean
 
1904 // `true` for browsers implementing the Microsoft touch events model (notably IE10).
 
1905 var msPointer = !window.PointerEvent && window.MSPointerEvent;
 
1907 // @property pointer: Boolean
 
1908 // `true` for all browsers supporting [pointer events](https://msdn.microsoft.com/en-us/library/dn433244%28v=vs.85%29.aspx).
 
1909 var pointer = !!(window.PointerEvent || msPointer);
 
1911 // @property touch: Boolean
 
1912 // `true` for all browsers supporting [touch events](https://developer.mozilla.org/docs/Web/API/Touch_events).
 
1913 // This does not necessarily mean that the browser is running in a computer with
 
1914 // a touchscreen, it only means that the browser is capable of understanding
 
1916 var touch = !window.L_NO_TOUCH && (pointer || 'ontouchstart' in window ||
 
1917                 (window.DocumentTouch && document instanceof window.DocumentTouch));
 
1919 // @property mobileOpera: Boolean; `true` for the Opera browser in a mobile device.
 
1920 var mobileOpera = mobile && opera;
 
1922 // @property mobileGecko: Boolean
 
1923 // `true` for gecko-based browsers running in a mobile device.
 
1924 var mobileGecko = mobile && gecko;
 
1926 // @property retina: Boolean
 
1927 // `true` for browsers on a high-resolution "retina" screen or on any screen when browser's display zoom is more than 100%.
 
1928 var retina = (window.devicePixelRatio || (window.screen.deviceXDPI / window.screen.logicalXDPI)) > 1;
 
1931 // @property canvas: Boolean
 
1932 // `true` when the browser supports [`<canvas>`](https://developer.mozilla.org/docs/Web/API/Canvas_API).
 
1933 var canvas = (function () {
 
1934         return !!document.createElement('canvas').getContext;
 
1937 // @property svg: Boolean
 
1938 // `true` when the browser supports [SVG](https://developer.mozilla.org/docs/Web/SVG).
 
1939 var svg = !!(document.createElementNS && svgCreate('svg').createSVGRect);
 
1941 // @property vml: Boolean
 
1942 // `true` if the browser supports [VML](https://en.wikipedia.org/wiki/Vector_Markup_Language).
 
1943 var vml = !svg && (function () {
 
1945                 var div = document.createElement('div');
 
1946                 div.innerHTML = '<v:shape adj="1"/>';
 
1948                 var shape = div.firstChild;
 
1949                 shape.style.behavior = 'url(#default#VML)';
 
1951                 return shape && (typeof shape.adj === 'object');
 
1959 function userAgentContains(str) {
 
1960         return navigator.userAgent.toLowerCase().indexOf(str) >= 0;
 
1964 var Browser = (Object.freeze || Object)({
 
1970         android23: android23,
 
1971         androidStock: androidStock,
 
1984         mobileWebkit: mobileWebkit,
 
1985         mobileWebkit3d: mobileWebkit3d,
 
1986         msPointer: msPointer,
 
1989         mobileOpera: mobileOpera,
 
1990         mobileGecko: mobileGecko,
 
1998  * Extends L.DomEvent to provide touch support for Internet Explorer and Windows-based devices.
 
2002 var POINTER_DOWN =   msPointer ? 'MSPointerDown'   : 'pointerdown';
 
2003 var POINTER_MOVE =   msPointer ? 'MSPointerMove'   : 'pointermove';
 
2004 var POINTER_UP =     msPointer ? 'MSPointerUp'     : 'pointerup';
 
2005 var POINTER_CANCEL = msPointer ? 'MSPointerCancel' : 'pointercancel';
 
2006 var TAG_WHITE_LIST = ['INPUT', 'SELECT', 'OPTION'];
 
2009 var _pointerDocListener = false;
 
2011 // DomEvent.DoubleTap needs to know about this
 
2012 var _pointersCount = 0;
 
2014 // Provides a touch events wrapper for (ms)pointer events.
 
2015 // ref http://www.w3.org/TR/pointerevents/ https://www.w3.org/Bugs/Public/show_bug.cgi?id=22890
 
2017 function addPointerListener(obj, type, handler, id) {
 
2018         if (type === 'touchstart') {
 
2019                 _addPointerStart(obj, handler, id);
 
2021         } else if (type === 'touchmove') {
 
2022                 _addPointerMove(obj, handler, id);
 
2024         } else if (type === 'touchend') {
 
2025                 _addPointerEnd(obj, handler, id);
 
2031 function removePointerListener(obj, type, id) {
 
2032         var handler = obj['_leaflet_' + type + id];
 
2034         if (type === 'touchstart') {
 
2035                 obj.removeEventListener(POINTER_DOWN, handler, false);
 
2037         } else if (type === 'touchmove') {
 
2038                 obj.removeEventListener(POINTER_MOVE, handler, false);
 
2040         } else if (type === 'touchend') {
 
2041                 obj.removeEventListener(POINTER_UP, handler, false);
 
2042                 obj.removeEventListener(POINTER_CANCEL, handler, false);
 
2048 function _addPointerStart(obj, handler, id) {
 
2049         var onDown = bind(function (e) {
 
2050                 if (e.pointerType !== 'mouse' && e.MSPOINTER_TYPE_MOUSE && e.pointerType !== e.MSPOINTER_TYPE_MOUSE) {
 
2051                         // In IE11, some touch events needs to fire for form controls, or
 
2052                         // the controls will stop working. We keep a whitelist of tag names that
 
2053                         // need these events. For other target tags, we prevent default on the event.
 
2054                         if (TAG_WHITE_LIST.indexOf(e.target.tagName) < 0) {
 
2061                 _handlePointer(e, handler);
 
2064         obj['_leaflet_touchstart' + id] = onDown;
 
2065         obj.addEventListener(POINTER_DOWN, onDown, false);
 
2067         // need to keep track of what pointers and how many are active to provide e.touches emulation
 
2068         if (!_pointerDocListener) {
 
2069                 // we listen documentElement as any drags that end by moving the touch off the screen get fired there
 
2070                 document.documentElement.addEventListener(POINTER_DOWN, _globalPointerDown, true);
 
2071                 document.documentElement.addEventListener(POINTER_MOVE, _globalPointerMove, true);
 
2072                 document.documentElement.addEventListener(POINTER_UP, _globalPointerUp, true);
 
2073                 document.documentElement.addEventListener(POINTER_CANCEL, _globalPointerUp, true);
 
2075                 _pointerDocListener = true;
 
2079 function _globalPointerDown(e) {
 
2080         _pointers[e.pointerId] = e;
 
2084 function _globalPointerMove(e) {
 
2085         if (_pointers[e.pointerId]) {
 
2086                 _pointers[e.pointerId] = e;
 
2090 function _globalPointerUp(e) {
 
2091         delete _pointers[e.pointerId];
 
2095 function _handlePointer(e, handler) {
 
2097         for (var i in _pointers) {
 
2098                 e.touches.push(_pointers[i]);
 
2100         e.changedTouches = [e];
 
2105 function _addPointerMove(obj, handler, id) {
 
2106         var onMove = function (e) {
 
2107                 // don't fire touch moves when mouse isn't down
 
2108                 if ((e.pointerType === e.MSPOINTER_TYPE_MOUSE || e.pointerType === 'mouse') && e.buttons === 0) { return; }
 
2110                 _handlePointer(e, handler);
 
2113         obj['_leaflet_touchmove' + id] = onMove;
 
2114         obj.addEventListener(POINTER_MOVE, onMove, false);
 
2117 function _addPointerEnd(obj, handler, id) {
 
2118         var onUp = function (e) {
 
2119                 _handlePointer(e, handler);
 
2122         obj['_leaflet_touchend' + id] = onUp;
 
2123         obj.addEventListener(POINTER_UP, onUp, false);
 
2124         obj.addEventListener(POINTER_CANCEL, onUp, false);
 
2128  * Extends the event handling code with double tap support for mobile browsers.
 
2131 var _touchstart = msPointer ? 'MSPointerDown' : pointer ? 'pointerdown' : 'touchstart';
 
2132 var _touchend = msPointer ? 'MSPointerUp' : pointer ? 'pointerup' : 'touchend';
 
2133 var _pre = '_leaflet_';
 
2135 // inspired by Zepto touch code by Thomas Fuchs
 
2136 function addDoubleTapListener(obj, handler, id) {
 
2141         function onTouchStart(e) {
 
2145                         if ((!edge) || e.pointerType === 'mouse') { return; }
 
2146                         count = _pointersCount;
 
2148                         count = e.touches.length;
 
2151                 if (count > 1) { return; }
 
2153                 var now = Date.now(),
 
2154                     delta = now - (last || now);
 
2156                 touch$$1 = e.touches ? e.touches[0] : e;
 
2157                 doubleTap = (delta > 0 && delta <= delay);
 
2161         function onTouchEnd(e) {
 
2162                 if (doubleTap && !touch$$1.cancelBubble) {
 
2164                                 if ((!edge) || e.pointerType === 'mouse') { return; }
 
2165                                 // work around .type being readonly with MSPointer* events
 
2169                                 for (i in touch$$1) {
 
2171                                         newTouch[i] = prop && prop.bind ? prop.bind(touch$$1) : prop;
 
2173                                 touch$$1 = newTouch;
 
2175                         touch$$1.type = 'dblclick';
 
2176                         touch$$1.button = 0;
 
2182         obj[_pre + _touchstart + id] = onTouchStart;
 
2183         obj[_pre + _touchend + id] = onTouchEnd;
 
2184         obj[_pre + 'dblclick' + id] = handler;
 
2186         obj.addEventListener(_touchstart, onTouchStart, false);
 
2187         obj.addEventListener(_touchend, onTouchEnd, false);
 
2189         // On some platforms (notably, chrome<55 on win10 + touchscreen + mouse),
 
2190         // the browser doesn't fire touchend/pointerup events but does fire
 
2191         // native dblclicks. See #4127.
 
2192         // Edge 14 also fires native dblclicks, but only for pointerType mouse, see #5180.
 
2193         obj.addEventListener('dblclick', handler, false);
 
2198 function removeDoubleTapListener(obj, id) {
 
2199         var touchstart = obj[_pre + _touchstart + id],
 
2200             touchend = obj[_pre + _touchend + id],
 
2201             dblclick = obj[_pre + 'dblclick' + id];
 
2203         obj.removeEventListener(_touchstart, touchstart, false);
 
2204         obj.removeEventListener(_touchend, touchend, false);
 
2206                 obj.removeEventListener('dblclick', dblclick, false);
 
2213  * @namespace DomUtil
 
2215  * Utility functions to work with the [DOM](https://developer.mozilla.org/docs/Web/API/Document_Object_Model)
 
2216  * tree, used by Leaflet internally.
 
2218  * Most functions expecting or returning a `HTMLElement` also work for
 
2219  * SVG elements. The only difference is that classes refer to CSS classes
 
2220  * in HTML and SVG classes in SVG.
 
2224 // @property TRANSFORM: String
 
2225 // Vendor-prefixed transform style name (e.g. `'webkitTransform'` for WebKit).
 
2226 var TRANSFORM = testProp(
 
2227         ['transform', 'webkitTransform', 'OTransform', 'MozTransform', 'msTransform']);
 
2229 // webkitTransition comes first because some browser versions that drop vendor prefix don't do
 
2230 // the same for the transitionend event, in particular the Android 4.1 stock browser
 
2232 // @property TRANSITION: String
 
2233 // Vendor-prefixed transition style name.
 
2234 var TRANSITION = testProp(
 
2235         ['webkitTransition', 'transition', 'OTransition', 'MozTransition', 'msTransition']);
 
2237 // @property TRANSITION_END: String
 
2238 // Vendor-prefixed transitionend event name.
 
2239 var TRANSITION_END =
 
2240         TRANSITION === 'webkitTransition' || TRANSITION === 'OTransition' ? TRANSITION + 'End' : 'transitionend';
 
2243 // @function get(id: String|HTMLElement): HTMLElement
 
2244 // Returns an element given its DOM id, or returns the element itself
 
2245 // if it was passed directly.
 
2247         return typeof id === 'string' ? document.getElementById(id) : id;
 
2250 // @function getStyle(el: HTMLElement, styleAttrib: String): String
 
2251 // Returns the value for a certain style attribute on an element,
 
2252 // including computed values or values set through CSS.
 
2253 function getStyle(el, style) {
 
2254         var value = el.style[style] || (el.currentStyle && el.currentStyle[style]);
 
2256         if ((!value || value === 'auto') && document.defaultView) {
 
2257                 var css = document.defaultView.getComputedStyle(el, null);
 
2258                 value = css ? css[style] : null;
 
2260         return value === 'auto' ? null : value;
 
2263 // @function create(tagName: String, className?: String, container?: HTMLElement): HTMLElement
 
2264 // Creates an HTML element with `tagName`, sets its class to `className`, and optionally appends it to `container` element.
 
2265 function create$1(tagName, className, container) {
 
2266         var el = document.createElement(tagName);
 
2267         el.className = className || '';
 
2270                 container.appendChild(el);
 
2275 // @function remove(el: HTMLElement)
 
2276 // Removes `el` from its parent element
 
2277 function remove(el) {
 
2278         var parent = el.parentNode;
 
2280                 parent.removeChild(el);
 
2284 // @function empty(el: HTMLElement)
 
2285 // Removes all of `el`'s children elements from `el`
 
2286 function empty(el) {
 
2287         while (el.firstChild) {
 
2288                 el.removeChild(el.firstChild);
 
2292 // @function toFront(el: HTMLElement)
 
2293 // Makes `el` the last child of its parent, so it renders in front of the other children.
 
2294 function toFront(el) {
 
2295         var parent = el.parentNode;
 
2296         if (parent && parent.lastChild !== el) {
 
2297                 parent.appendChild(el);
 
2301 // @function toBack(el: HTMLElement)
 
2302 // Makes `el` the first child of its parent, so it renders behind the other children.
 
2303 function toBack(el) {
 
2304         var parent = el.parentNode;
 
2305         if (parent && parent.firstChild !== el) {
 
2306                 parent.insertBefore(el, parent.firstChild);
 
2310 // @function hasClass(el: HTMLElement, name: String): Boolean
 
2311 // Returns `true` if the element's class attribute contains `name`.
 
2312 function hasClass(el, name) {
 
2313         if (el.classList !== undefined) {
 
2314                 return el.classList.contains(name);
 
2316         var className = getClass(el);
 
2317         return className.length > 0 && new RegExp('(^|\\s)' + name + '(\\s|$)').test(className);
 
2320 // @function addClass(el: HTMLElement, name: String)
 
2321 // Adds `name` to the element's class attribute.
 
2322 function addClass(el, name) {
 
2323         if (el.classList !== undefined) {
 
2324                 var classes = splitWords(name);
 
2325                 for (var i = 0, len = classes.length; i < len; i++) {
 
2326                         el.classList.add(classes[i]);
 
2328         } else if (!hasClass(el, name)) {
 
2329                 var className = getClass(el);
 
2330                 setClass(el, (className ? className + ' ' : '') + name);
 
2334 // @function removeClass(el: HTMLElement, name: String)
 
2335 // Removes `name` from the element's class attribute.
 
2336 function removeClass(el, name) {
 
2337         if (el.classList !== undefined) {
 
2338                 el.classList.remove(name);
 
2340                 setClass(el, trim((' ' + getClass(el) + ' ').replace(' ' + name + ' ', ' ')));
 
2344 // @function setClass(el: HTMLElement, name: String)
 
2345 // Sets the element's class.
 
2346 function setClass(el, name) {
 
2347         if (el.className.baseVal === undefined) {
 
2348                 el.className = name;
 
2350                 // in case of SVG element
 
2351                 el.className.baseVal = name;
 
2355 // @function getClass(el: HTMLElement): String
 
2356 // Returns the element's class.
 
2357 function getClass(el) {
 
2358         // Check if the element is an SVGElementInstance and use the correspondingElement instead
 
2359         // (Required for linked SVG elements in IE11.)
 
2360         if (el.correspondingElement) {
 
2361                 el = el.correspondingElement;
 
2363         return el.className.baseVal === undefined ? el.className : el.className.baseVal;
 
2366 // @function setOpacity(el: HTMLElement, opacity: Number)
 
2367 // Set the opacity of an element (including old IE support).
 
2368 // `opacity` must be a number from `0` to `1`.
 
2369 function setOpacity(el, value) {
 
2370         if ('opacity' in el.style) {
 
2371                 el.style.opacity = value;
 
2372         } else if ('filter' in el.style) {
 
2373                 _setOpacityIE(el, value);
 
2377 function _setOpacityIE(el, value) {
 
2379             filterName = 'DXImageTransform.Microsoft.Alpha';
 
2381         // filters collection throws an error if we try to retrieve a filter that doesn't exist
 
2383                 filter = el.filters.item(filterName);
 
2385                 // don't set opacity to 1 if we haven't already set an opacity,
 
2386                 // it isn't needed and breaks transparent pngs.
 
2387                 if (value === 1) { return; }
 
2390         value = Math.round(value * 100);
 
2393                 filter.Enabled = (value !== 100);
 
2394                 filter.Opacity = value;
 
2396                 el.style.filter += ' progid:' + filterName + '(opacity=' + value + ')';
 
2400 // @function testProp(props: String[]): String|false
 
2401 // Goes through the array of style names and returns the first name
 
2402 // that is a valid style name for an element. If no such name is found,
 
2403 // it returns false. Useful for vendor-prefixed styles like `transform`.
 
2404 function testProp(props) {
 
2405         var style = document.documentElement.style;
 
2407         for (var i = 0; i < props.length; i++) {
 
2408                 if (props[i] in style) {
 
2415 // @function setTransform(el: HTMLElement, offset: Point, scale?: Number)
 
2416 // Resets the 3D CSS transform of `el` so it is translated by `offset` pixels
 
2417 // and optionally scaled by `scale`. Does not have an effect if the
 
2418 // browser doesn't support 3D CSS transforms.
 
2419 function setTransform(el, offset, scale) {
 
2420         var pos = offset || new Point(0, 0);
 
2422         el.style[TRANSFORM] =
 
2424                         'translate(' + pos.x + 'px,' + pos.y + 'px)' :
 
2425                         'translate3d(' + pos.x + 'px,' + pos.y + 'px,0)') +
 
2426                 (scale ? ' scale(' + scale + ')' : '');
 
2429 // @function setPosition(el: HTMLElement, position: Point)
 
2430 // Sets the position of `el` to coordinates specified by `position`,
 
2431 // using CSS translate or top/left positioning depending on the browser
 
2432 // (used by Leaflet internally to position its layers).
 
2433 function setPosition(el, point) {
 
2436         el._leaflet_pos = point;
 
2440                 setTransform(el, point);
 
2442                 el.style.left = point.x + 'px';
 
2443                 el.style.top = point.y + 'px';
 
2447 // @function getPosition(el: HTMLElement): Point
 
2448 // Returns the coordinates of an element previously positioned with setPosition.
 
2449 function getPosition(el) {
 
2450         // this method is only used for elements previously positioned using setPosition,
 
2451         // so it's safe to cache the position for performance
 
2453         return el._leaflet_pos || new Point(0, 0);
 
2456 // @function disableTextSelection()
 
2457 // Prevents the user from generating `selectstart` DOM events, usually generated
 
2458 // when the user drags the mouse through a page with text. Used internally
 
2459 // by Leaflet to override the behaviour of any click-and-drag interaction on
 
2460 // the map. Affects drag interactions on the whole document.
 
2462 // @function enableTextSelection()
 
2463 // Cancels the effects of a previous [`L.DomUtil.disableTextSelection`](#domutil-disabletextselection).
 
2464 var disableTextSelection;
 
2465 var enableTextSelection;
 
2467 if ('onselectstart' in document) {
 
2468         disableTextSelection = function () {
 
2469                 on(window, 'selectstart', preventDefault);
 
2471         enableTextSelection = function () {
 
2472                 off(window, 'selectstart', preventDefault);
 
2475         var userSelectProperty = testProp(
 
2476                 ['userSelect', 'WebkitUserSelect', 'OUserSelect', 'MozUserSelect', 'msUserSelect']);
 
2478         disableTextSelection = function () {
 
2479                 if (userSelectProperty) {
 
2480                         var style = document.documentElement.style;
 
2481                         _userSelect = style[userSelectProperty];
 
2482                         style[userSelectProperty] = 'none';
 
2485         enableTextSelection = function () {
 
2486                 if (userSelectProperty) {
 
2487                         document.documentElement.style[userSelectProperty] = _userSelect;
 
2488                         _userSelect = undefined;
 
2493 // @function disableImageDrag()
 
2494 // As [`L.DomUtil.disableTextSelection`](#domutil-disabletextselection), but
 
2495 // for `dragstart` DOM events, usually generated when the user drags an image.
 
2496 function disableImageDrag() {
 
2497         on(window, 'dragstart', preventDefault);
 
2500 // @function enableImageDrag()
 
2501 // Cancels the effects of a previous [`L.DomUtil.disableImageDrag`](#domutil-disabletextselection).
 
2502 function enableImageDrag() {
 
2503         off(window, 'dragstart', preventDefault);
 
2506 var _outlineElement;
 
2508 // @function preventOutline(el: HTMLElement)
 
2509 // Makes the [outline](https://developer.mozilla.org/docs/Web/CSS/outline)
 
2510 // of the element `el` invisible. Used internally by Leaflet to prevent
 
2511 // focusable elements from displaying an outline when the user performs a
 
2512 // drag interaction on them.
 
2513 function preventOutline(element) {
 
2514         while (element.tabIndex === -1) {
 
2515                 element = element.parentNode;
 
2517         if (!element.style) { return; }
 
2519         _outlineElement = element;
 
2520         _outlineStyle = element.style.outline;
 
2521         element.style.outline = 'none';
 
2522         on(window, 'keydown', restoreOutline);
 
2525 // @function restoreOutline()
 
2526 // Cancels the effects of a previous [`L.DomUtil.preventOutline`]().
 
2527 function restoreOutline() {
 
2528         if (!_outlineElement) { return; }
 
2529         _outlineElement.style.outline = _outlineStyle;
 
2530         _outlineElement = undefined;
 
2531         _outlineStyle = undefined;
 
2532         off(window, 'keydown', restoreOutline);
 
2535 // @function getSizedParentNode(el: HTMLElement): HTMLElement
 
2536 // Finds the closest parent node which size (width and height) is not null.
 
2537 function getSizedParentNode(element) {
 
2539                 element = element.parentNode;
 
2540         } while ((!element.offsetWidth || !element.offsetHeight) && element !== document.body);
 
2544 // @function getScale(el: HTMLElement): Object
 
2545 // Computes the CSS scale currently applied on the element.
 
2546 // Returns an object with `x` and `y` members as horizontal and vertical scales respectively,
 
2547 // and `boundingClientRect` as the result of [`getBoundingClientRect()`](https://developer.mozilla.org/en-US/docs/Web/API/Element/getBoundingClientRect).
 
2548 function getScale(element) {
 
2549         var rect = element.getBoundingClientRect(); // Read-only in old browsers.
 
2552                 x: rect.width / element.offsetWidth || 1,
 
2553                 y: rect.height / element.offsetHeight || 1,
 
2554                 boundingClientRect: rect
 
2559 var DomUtil = (Object.freeze || Object)({
 
2560         TRANSFORM: TRANSFORM,
 
2561         TRANSITION: TRANSITION,
 
2562         TRANSITION_END: TRANSITION_END,
 
2572         removeClass: removeClass,
 
2575         setOpacity: setOpacity,
 
2577         setTransform: setTransform,
 
2578         setPosition: setPosition,
 
2579         getPosition: getPosition,
 
2580         disableTextSelection: disableTextSelection,
 
2581         enableTextSelection: enableTextSelection,
 
2582         disableImageDrag: disableImageDrag,
 
2583         enableImageDrag: enableImageDrag,
 
2584         preventOutline: preventOutline,
 
2585         restoreOutline: restoreOutline,
 
2586         getSizedParentNode: getSizedParentNode,
 
2591  * @namespace DomEvent
 
2592  * Utility functions to work with the [DOM events](https://developer.mozilla.org/docs/Web/API/Event), used by Leaflet internally.
 
2595 // Inspired by John Resig, Dean Edwards and YUI addEvent implementations.
 
2597 // @function on(el: HTMLElement, types: String, fn: Function, context?: Object): this
 
2598 // Adds a listener function (`fn`) to a particular DOM event type of the
 
2599 // element `el`. You can optionally specify the context of the listener
 
2600 // (object the `this` keyword will point to). You can also pass several
 
2601 // space-separated types (e.g. `'click dblclick'`).
 
2604 // @function on(el: HTMLElement, eventMap: Object, context?: Object): this
 
2605 // Adds a set of type/listener pairs, e.g. `{click: onClick, mousemove: onMouseMove}`
 
2606 function on(obj, types, fn, context) {
 
2608         if (typeof types === 'object') {
 
2609                 for (var type in types) {
 
2610                         addOne(obj, type, types[type], fn);
 
2613                 types = splitWords(types);
 
2615                 for (var i = 0, len = types.length; i < len; i++) {
 
2616                         addOne(obj, types[i], fn, context);
 
2623 var eventsKey = '_leaflet_events';
 
2625 // @function off(el: HTMLElement, types: String, fn: Function, context?: Object): this
 
2626 // Removes a previously added listener function.
 
2627 // Note that if you passed a custom context to on, you must pass the same
 
2628 // context to `off` in order to remove the listener.
 
2631 // @function off(el: HTMLElement, eventMap: Object, context?: Object): this
 
2632 // Removes a set of type/listener pairs, e.g. `{click: onClick, mousemove: onMouseMove}`
 
2633 function off(obj, types, fn, context) {
 
2635         if (typeof types === 'object') {
 
2636                 for (var type in types) {
 
2637                         removeOne(obj, type, types[type], fn);
 
2640                 types = splitWords(types);
 
2642                 for (var i = 0, len = types.length; i < len; i++) {
 
2643                         removeOne(obj, types[i], fn, context);
 
2646                 for (var j in obj[eventsKey]) {
 
2647                         removeOne(obj, j, obj[eventsKey][j]);
 
2649                 delete obj[eventsKey];
 
2655 function addOne(obj, type, fn, context) {
 
2656         var id = type + stamp(fn) + (context ? '_' + stamp(context) : '');
 
2658         if (obj[eventsKey] && obj[eventsKey][id]) { return this; }
 
2660         var handler = function (e) {
 
2661                 return fn.call(context || obj, e || window.event);
 
2664         var originalHandler = handler;
 
2666         if (pointer && type.indexOf('touch') === 0) {
 
2667                 // Needs DomEvent.Pointer.js
 
2668                 addPointerListener(obj, type, handler, id);
 
2670         } else if (touch && (type === 'dblclick') && addDoubleTapListener &&
 
2671                    !(pointer && chrome)) {
 
2672                 // Chrome >55 does not need the synthetic dblclicks from addDoubleTapListener
 
2674                 addDoubleTapListener(obj, handler, id);
 
2676         } else if ('addEventListener' in obj) {
 
2678                 if (type === 'mousewheel') {
 
2679                         obj.addEventListener('onwheel' in obj ? 'wheel' : 'mousewheel', handler, false);
 
2681                 } else if ((type === 'mouseenter') || (type === 'mouseleave')) {
 
2682                         handler = function (e) {
 
2683                                 e = e || window.event;
 
2684                                 if (isExternalTarget(obj, e)) {
 
2688                         obj.addEventListener(type === 'mouseenter' ? 'mouseover' : 'mouseout', handler, false);
 
2691                         if (type === 'click' && android) {
 
2692                                 handler = function (e) {
 
2693                                         filterClick(e, originalHandler);
 
2696                         obj.addEventListener(type, handler, false);
 
2699         } else if ('attachEvent' in obj) {
 
2700                 obj.attachEvent('on' + type, handler);
 
2703         obj[eventsKey] = obj[eventsKey] || {};
 
2704         obj[eventsKey][id] = handler;
 
2707 function removeOne(obj, type, fn, context) {
 
2709         var id = type + stamp(fn) + (context ? '_' + stamp(context) : ''),
 
2710             handler = obj[eventsKey] && obj[eventsKey][id];
 
2712         if (!handler) { return this; }
 
2714         if (pointer && type.indexOf('touch') === 0) {
 
2715                 removePointerListener(obj, type, id);
 
2717         } else if (touch && (type === 'dblclick') && removeDoubleTapListener &&
 
2718                    !(pointer && chrome)) {
 
2719                 removeDoubleTapListener(obj, id);
 
2721         } else if ('removeEventListener' in obj) {
 
2723                 if (type === 'mousewheel') {
 
2724                         obj.removeEventListener('onwheel' in obj ? 'wheel' : 'mousewheel', handler, false);
 
2727                         obj.removeEventListener(
 
2728                                 type === 'mouseenter' ? 'mouseover' :
 
2729                                 type === 'mouseleave' ? 'mouseout' : type, handler, false);
 
2732         } else if ('detachEvent' in obj) {
 
2733                 obj.detachEvent('on' + type, handler);
 
2736         obj[eventsKey][id] = null;
 
2739 // @function stopPropagation(ev: DOMEvent): this
 
2740 // Stop the given event from propagation to parent elements. Used inside the listener functions:
 
2742 // L.DomEvent.on(div, 'click', function (ev) {
 
2743 //      L.DomEvent.stopPropagation(ev);
 
2746 function stopPropagation(e) {
 
2748         if (e.stopPropagation) {
 
2749                 e.stopPropagation();
 
2750         } else if (e.originalEvent) {  // In case of Leaflet event.
 
2751                 e.originalEvent._stopped = true;
 
2753                 e.cancelBubble = true;
 
2760 // @function disableScrollPropagation(el: HTMLElement): this
 
2761 // Adds `stopPropagation` to the element's `'mousewheel'` events (plus browser variants).
 
2762 function disableScrollPropagation(el) {
 
2763         addOne(el, 'mousewheel', stopPropagation);
 
2767 // @function disableClickPropagation(el: HTMLElement): this
 
2768 // Adds `stopPropagation` to the element's `'click'`, `'doubleclick'`,
 
2769 // `'mousedown'` and `'touchstart'` events (plus browser variants).
 
2770 function disableClickPropagation(el) {
 
2771         on(el, 'mousedown touchstart dblclick', stopPropagation);
 
2772         addOne(el, 'click', fakeStop);
 
2776 // @function preventDefault(ev: DOMEvent): this
 
2777 // Prevents the default action of the DOM Event `ev` from happening (such as
 
2778 // following a link in the href of the a element, or doing a POST request
 
2779 // with page reload when a `<form>` is submitted).
 
2780 // Use it inside listener functions.
 
2781 function preventDefault(e) {
 
2782         if (e.preventDefault) {
 
2785                 e.returnValue = false;
 
2790 // @function stop(ev: DOMEvent): this
 
2791 // Does `stopPropagation` and `preventDefault` at the same time.
 
2798 // @function getMousePosition(ev: DOMEvent, container?: HTMLElement): Point
 
2799 // Gets normalized mouse position from a DOM event relative to the
 
2800 // `container` (border excluded) or to the whole page if not specified.
 
2801 function getMousePosition(e, container) {
 
2803                 return new Point(e.clientX, e.clientY);
 
2806         var scale = getScale(container),
 
2807             offset = scale.boundingClientRect; // left and top  values are in page scale (like the event clientX/Y)
 
2810                 // offset.left/top values are in page scale (like clientX/Y),
 
2811                 // whereas clientLeft/Top (border width) values are the original values (before CSS scale applies).
 
2812                 (e.clientX - offset.left) / scale.x - container.clientLeft,
 
2813                 (e.clientY - offset.top) / scale.y - container.clientTop
 
2817 // Chrome on Win scrolls double the pixels as in other platforms (see #4538),
 
2818 // and Firefox scrolls device pixels, not CSS pixels
 
2820         (win && chrome) ? 2 * window.devicePixelRatio :
 
2821         gecko ? window.devicePixelRatio : 1;
 
2823 // @function getWheelDelta(ev: DOMEvent): Number
 
2824 // Gets normalized wheel delta from a mousewheel DOM event, in vertical
 
2825 // pixels scrolled (negative if scrolling down).
 
2826 // Events from pointing devices without precise scrolling are mapped to
 
2827 // a best guess of 60 pixels.
 
2828 function getWheelDelta(e) {
 
2829         return (edge) ? e.wheelDeltaY / 2 : // Don't trust window-geometry-based delta
 
2830                (e.deltaY && e.deltaMode === 0) ? -e.deltaY / wheelPxFactor : // Pixels
 
2831                (e.deltaY && e.deltaMode === 1) ? -e.deltaY * 20 : // Lines
 
2832                (e.deltaY && e.deltaMode === 2) ? -e.deltaY * 60 : // Pages
 
2833                (e.deltaX || e.deltaZ) ? 0 :     // Skip horizontal/depth wheel events
 
2834                e.wheelDelta ? (e.wheelDeltaY || e.wheelDelta) / 2 : // Legacy IE pixels
 
2835                (e.detail && Math.abs(e.detail) < 32765) ? -e.detail * 20 : // Legacy Moz lines
 
2836                e.detail ? e.detail / -32765 * 60 : // Legacy Moz pages
 
2840 var skipEvents = {};
 
2842 function fakeStop(e) {
 
2843         // fakes stopPropagation by setting a special event flag, checked/reset with skipped(e)
 
2844         skipEvents[e.type] = true;
 
2847 function skipped(e) {
 
2848         var events = skipEvents[e.type];
 
2849         // reset when checking, as it's only used in map container and propagates outside of the map
 
2850         skipEvents[e.type] = false;
 
2854 // check if element really left/entered the event target (for mouseenter/mouseleave)
 
2855 function isExternalTarget(el, e) {
 
2857         var related = e.relatedTarget;
 
2859         if (!related) { return true; }
 
2862                 while (related && (related !== el)) {
 
2863                         related = related.parentNode;
 
2868         return (related !== el);
 
2873 // this is a horrible workaround for a bug in Android where a single touch triggers two click events
 
2874 function filterClick(e, handler) {
 
2875         var timeStamp = (e.timeStamp || (e.originalEvent && e.originalEvent.timeStamp)),
 
2876             elapsed = lastClick && (timeStamp - lastClick);
 
2878         // are they closer together than 500ms yet more than 100ms?
 
2879         // Android typically triggers them ~300ms apart while multiple listeners
 
2880         // on the same event should be triggered far faster;
 
2881         // or check if click is simulated on the element, and if it is, reject any non-simulated events
 
2883         if ((elapsed && elapsed > 100 && elapsed < 500) || (e.target._simulatedClick && !e._simulated)) {
 
2887         lastClick = timeStamp;
 
2895 var DomEvent = (Object.freeze || Object)({
 
2898         stopPropagation: stopPropagation,
 
2899         disableScrollPropagation: disableScrollPropagation,
 
2900         disableClickPropagation: disableClickPropagation,
 
2901         preventDefault: preventDefault,
 
2903         getMousePosition: getMousePosition,
 
2904         getWheelDelta: getWheelDelta,
 
2907         isExternalTarget: isExternalTarget,
 
2913  * @class PosAnimation
 
2914  * @aka L.PosAnimation
 
2916  * Used internally for panning animations, utilizing CSS3 Transitions for modern browsers and a timer fallback for IE6-9.
 
2920  * var fx = new L.PosAnimation();
 
2921  * fx.run(el, [300, 500], 0.5);
 
2924  * @constructor L.PosAnimation()
 
2925  * Creates a `PosAnimation` object.
 
2929 var PosAnimation = Evented.extend({
 
2931         // @method run(el: HTMLElement, newPos: Point, duration?: Number, easeLinearity?: Number)
 
2932         // Run an animation of a given element to a new position, optionally setting
 
2933         // duration in seconds (`0.25` by default) and easing linearity factor (3rd
 
2934         // argument of the [cubic bezier curve](http://cubic-bezier.com/#0,0,.5,1),
 
2935         // `0.5` by default).
 
2936         run: function (el, newPos, duration, easeLinearity) {
 
2940                 this._inProgress = true;
 
2941                 this._duration = duration || 0.25;
 
2942                 this._easeOutPower = 1 / Math.max(easeLinearity || 0.5, 0.2);
 
2944                 this._startPos = getPosition(el);
 
2945                 this._offset = newPos.subtract(this._startPos);
 
2946                 this._startTime = +new Date();
 
2948                 // @event start: Event
 
2949                 // Fired when the animation starts
 
2956         // Stops the animation (if currently running).
 
2958                 if (!this._inProgress) { return; }
 
2964         _animate: function () {
 
2966                 this._animId = requestAnimFrame(this._animate, this);
 
2970         _step: function (round) {
 
2971                 var elapsed = (+new Date()) - this._startTime,
 
2972                     duration = this._duration * 1000;
 
2974                 if (elapsed < duration) {
 
2975                         this._runFrame(this._easeOut(elapsed / duration), round);
 
2982         _runFrame: function (progress, round) {
 
2983                 var pos = this._startPos.add(this._offset.multiplyBy(progress));
 
2987                 setPosition(this._el, pos);
 
2989                 // @event step: Event
 
2990                 // Fired continuously during the animation.
 
2994         _complete: function () {
 
2995                 cancelAnimFrame(this._animId);
 
2997                 this._inProgress = false;
 
2998                 // @event end: Event
 
2999                 // Fired when the animation ends.
 
3003         _easeOut: function (t) {
 
3004                 return 1 - Math.pow(1 - t, this._easeOutPower);
 
3013  * The central class of the API — it is used to create a map on a page and manipulate it.
 
3018  * // initialize the map on the "map" div with a given center and zoom
 
3019  * var map = L.map('map', {
 
3020  *      center: [51.505, -0.09],
 
3027 var Map = Evented.extend({
 
3030                 // @section Map State Options
 
3031                 // @option crs: CRS = L.CRS.EPSG3857
 
3032                 // The [Coordinate Reference System](#crs) to use. Don't change this if you're not
 
3033                 // sure what it means.
 
3036                 // @option center: LatLng = undefined
 
3037                 // Initial geographic center of the map
 
3040                 // @option zoom: Number = undefined
 
3041                 // Initial map zoom level
 
3044                 // @option minZoom: Number = *
 
3045                 // Minimum zoom level of the map.
 
3046                 // If not specified and at least one `GridLayer` or `TileLayer` is in the map,
 
3047                 // the lowest of their `minZoom` options will be used instead.
 
3050                 // @option maxZoom: Number = *
 
3051                 // Maximum zoom level of the map.
 
3052                 // If not specified and at least one `GridLayer` or `TileLayer` is in the map,
 
3053                 // the highest of their `maxZoom` options will be used instead.
 
3056                 // @option layers: Layer[] = []
 
3057                 // Array of layers that will be added to the map initially
 
3060                 // @option maxBounds: LatLngBounds = null
 
3061                 // When this option is set, the map restricts the view to the given
 
3062                 // geographical bounds, bouncing the user back if the user tries to pan
 
3063                 // outside the view. To set the restriction dynamically, use
 
3064                 // [`setMaxBounds`](#map-setmaxbounds) method.
 
3065                 maxBounds: undefined,
 
3067                 // @option renderer: Renderer = *
 
3068                 // The default method for drawing vector layers on the map. `L.SVG`
 
3069                 // or `L.Canvas` by default depending on browser support.
 
3070                 renderer: undefined,
 
3073                 // @section Animation Options
 
3074                 // @option zoomAnimation: Boolean = true
 
3075                 // Whether the map zoom animation is enabled. By default it's enabled
 
3076                 // in all browsers that support CSS3 Transitions except Android.
 
3077                 zoomAnimation: true,
 
3079                 // @option zoomAnimationThreshold: Number = 4
 
3080                 // Won't animate zoom if the zoom difference exceeds this value.
 
3081                 zoomAnimationThreshold: 4,
 
3083                 // @option fadeAnimation: Boolean = true
 
3084                 // Whether the tile fade animation is enabled. By default it's enabled
 
3085                 // in all browsers that support CSS3 Transitions except Android.
 
3086                 fadeAnimation: true,
 
3088                 // @option markerZoomAnimation: Boolean = true
 
3089                 // Whether markers animate their zoom with the zoom animation, if disabled
 
3090                 // they will disappear for the length of the animation. By default it's
 
3091                 // enabled in all browsers that support CSS3 Transitions except Android.
 
3092                 markerZoomAnimation: true,
 
3094                 // @option transform3DLimit: Number = 2^23
 
3095                 // Defines the maximum size of a CSS translation transform. The default
 
3096                 // value should not be changed unless a web browser positions layers in
 
3097                 // the wrong place after doing a large `panBy`.
 
3098                 transform3DLimit: 8388608, // Precision limit of a 32-bit float
 
3100                 // @section Interaction Options
 
3101                 // @option zoomSnap: Number = 1
 
3102                 // Forces the map's zoom level to always be a multiple of this, particularly
 
3103                 // right after a [`fitBounds()`](#map-fitbounds) or a pinch-zoom.
 
3104                 // By default, the zoom level snaps to the nearest integer; lower values
 
3105                 // (e.g. `0.5` or `0.1`) allow for greater granularity. A value of `0`
 
3106                 // means the zoom level will not be snapped after `fitBounds` or a pinch-zoom.
 
3109                 // @option zoomDelta: Number = 1
 
3110                 // Controls how much the map's zoom level will change after a
 
3111                 // [`zoomIn()`](#map-zoomin), [`zoomOut()`](#map-zoomout), pressing `+`
 
3112                 // or `-` on the keyboard, or using the [zoom controls](#control-zoom).
 
3113                 // Values smaller than `1` (e.g. `0.5`) allow for greater granularity.
 
3116                 // @option trackResize: Boolean = true
 
3117                 // Whether the map automatically handles browser window resize to update itself.
 
3121         initialize: function (id, options) { // (HTMLElement or String, Object)
 
3122                 options = setOptions(this, options);
 
3124                 // Make sure to assign internal flags at the beginning,
 
3125                 // to avoid inconsistent state in some edge cases.
 
3126                 this._handlers = [];
 
3128                 this._zoomBoundLayers = {};
 
3129                 this._sizeChanged = true;
 
3131                 this._initContainer(id);
 
3134                 // hack for https://github.com/Leaflet/Leaflet/issues/1980
 
3135                 this._onResize = bind(this._onResize, this);
 
3139                 if (options.maxBounds) {
 
3140                         this.setMaxBounds(options.maxBounds);
 
3143                 if (options.zoom !== undefined) {
 
3144                         this._zoom = this._limitZoom(options.zoom);
 
3147                 if (options.center && options.zoom !== undefined) {
 
3148                         this.setView(toLatLng(options.center), options.zoom, {reset: true});
 
3151                 this.callInitHooks();
 
3153                 // don't animate on browsers without hardware-accelerated transitions or old Android/Opera
 
3154                 this._zoomAnimated = TRANSITION && any3d && !mobileOpera &&
 
3155                                 this.options.zoomAnimation;
 
3157                 // zoom transitions run with the same duration for all layers, so if one of transitionend events
 
3158                 // happens after starting zoom animation (propagating to the map pane), we know that it ended globally
 
3159                 if (this._zoomAnimated) {
 
3160                         this._createAnimProxy();
 
3161                         on(this._proxy, TRANSITION_END, this._catchTransitionEnd, this);
 
3164                 this._addLayers(this.options.layers);
 
3168         // @section Methods for modifying map state
 
3170         // @method setView(center: LatLng, zoom: Number, options?: Zoom/pan options): this
 
3171         // Sets the view of the map (geographical center and zoom) with the given
 
3172         // animation options.
 
3173         setView: function (center, zoom, options) {
 
3175                 zoom = zoom === undefined ? this._zoom : this._limitZoom(zoom);
 
3176                 center = this._limitCenter(toLatLng(center), zoom, this.options.maxBounds);
 
3177                 options = options || {};
 
3181                 if (this._loaded && !options.reset && options !== true) {
 
3183                         if (options.animate !== undefined) {
 
3184                                 options.zoom = extend({animate: options.animate}, options.zoom);
 
3185                                 options.pan = extend({animate: options.animate, duration: options.duration}, options.pan);
 
3188                         // try animating pan or zoom
 
3189                         var moved = (this._zoom !== zoom) ?
 
3190                                 this._tryAnimatedZoom && this._tryAnimatedZoom(center, zoom, options.zoom) :
 
3191                                 this._tryAnimatedPan(center, options.pan);
 
3194                                 // prevent resize handler call, the view will refresh after animation anyway
 
3195                                 clearTimeout(this._sizeTimer);
 
3200                 // animation didn't start, just reset the map view
 
3201                 this._resetView(center, zoom);
 
3206         // @method setZoom(zoom: Number, options?: Zoom/pan options): this
 
3207         // Sets the zoom of the map.
 
3208         setZoom: function (zoom, options) {
 
3209                 if (!this._loaded) {
 
3213                 return this.setView(this.getCenter(), zoom, {zoom: options});
 
3216         // @method zoomIn(delta?: Number, options?: Zoom options): this
 
3217         // Increases the zoom of the map by `delta` ([`zoomDelta`](#map-zoomdelta) by default).
 
3218         zoomIn: function (delta, options) {
 
3219                 delta = delta || (any3d ? this.options.zoomDelta : 1);
 
3220                 return this.setZoom(this._zoom + delta, options);
 
3223         // @method zoomOut(delta?: Number, options?: Zoom options): this
 
3224         // Decreases the zoom of the map by `delta` ([`zoomDelta`](#map-zoomdelta) by default).
 
3225         zoomOut: function (delta, options) {
 
3226                 delta = delta || (any3d ? this.options.zoomDelta : 1);
 
3227                 return this.setZoom(this._zoom - delta, options);
 
3230         // @method setZoomAround(latlng: LatLng, zoom: Number, options: Zoom options): this
 
3231         // Zooms the map while keeping a specified geographical point on the map
 
3232         // stationary (e.g. used internally for scroll zoom and double-click zoom).
 
3234         // @method setZoomAround(offset: Point, zoom: Number, options: Zoom options): this
 
3235         // Zooms the map while keeping a specified pixel on the map (relative to the top-left corner) stationary.
 
3236         setZoomAround: function (latlng, zoom, options) {
 
3237                 var scale = this.getZoomScale(zoom),
 
3238                     viewHalf = this.getSize().divideBy(2),
 
3239                     containerPoint = latlng instanceof Point ? latlng : this.latLngToContainerPoint(latlng),
 
3241                     centerOffset = containerPoint.subtract(viewHalf).multiplyBy(1 - 1 / scale),
 
3242                     newCenter = this.containerPointToLatLng(viewHalf.add(centerOffset));
 
3244                 return this.setView(newCenter, zoom, {zoom: options});
 
3247         _getBoundsCenterZoom: function (bounds, options) {
 
3249                 options = options || {};
 
3250                 bounds = bounds.getBounds ? bounds.getBounds() : toLatLngBounds(bounds);
 
3252                 var paddingTL = toPoint(options.paddingTopLeft || options.padding || [0, 0]),
 
3253                     paddingBR = toPoint(options.paddingBottomRight || options.padding || [0, 0]),
 
3255                     zoom = this.getBoundsZoom(bounds, false, paddingTL.add(paddingBR));
 
3257                 zoom = (typeof options.maxZoom === 'number') ? Math.min(options.maxZoom, zoom) : zoom;
 
3259                 if (zoom === Infinity) {
 
3261                                 center: bounds.getCenter(),
 
3266                 var paddingOffset = paddingBR.subtract(paddingTL).divideBy(2),
 
3268                     swPoint = this.project(bounds.getSouthWest(), zoom),
 
3269                     nePoint = this.project(bounds.getNorthEast(), zoom),
 
3270                     center = this.unproject(swPoint.add(nePoint).divideBy(2).add(paddingOffset), zoom);
 
3278         // @method fitBounds(bounds: LatLngBounds, options?: fitBounds options): this
 
3279         // Sets a map view that contains the given geographical bounds with the
 
3280         // maximum zoom level possible.
 
3281         fitBounds: function (bounds, options) {
 
3283                 bounds = toLatLngBounds(bounds);
 
3285                 if (!bounds.isValid()) {
 
3286                         throw new Error('Bounds are not valid.');
 
3289                 var target = this._getBoundsCenterZoom(bounds, options);
 
3290                 return this.setView(target.center, target.zoom, options);
 
3293         // @method fitWorld(options?: fitBounds options): this
 
3294         // Sets a map view that mostly contains the whole world with the maximum
 
3295         // zoom level possible.
 
3296         fitWorld: function (options) {
 
3297                 return this.fitBounds([[-90, -180], [90, 180]], options);
 
3300         // @method panTo(latlng: LatLng, options?: Pan options): this
 
3301         // Pans the map to a given center.
 
3302         panTo: function (center, options) { // (LatLng)
 
3303                 return this.setView(center, this._zoom, {pan: options});
 
3306         // @method panBy(offset: Point, options?: Pan options): this
 
3307         // Pans the map by a given number of pixels (animated).
 
3308         panBy: function (offset, options) {
 
3309                 offset = toPoint(offset).round();
 
3310                 options = options || {};
 
3312                 if (!offset.x && !offset.y) {
 
3313                         return this.fire('moveend');
 
3315                 // If we pan too far, Chrome gets issues with tiles
 
3316                 // and makes them disappear or appear in the wrong place (slightly offset) #2602
 
3317                 if (options.animate !== true && !this.getSize().contains(offset)) {
 
3318                         this._resetView(this.unproject(this.project(this.getCenter()).add(offset)), this.getZoom());
 
3322                 if (!this._panAnim) {
 
3323                         this._panAnim = new PosAnimation();
 
3326                                 'step': this._onPanTransitionStep,
 
3327                                 'end': this._onPanTransitionEnd
 
3331                 // don't fire movestart if animating inertia
 
3332                 if (!options.noMoveStart) {
 
3333                         this.fire('movestart');
 
3336                 // animate pan unless animate: false specified
 
3337                 if (options.animate !== false) {
 
3338                         addClass(this._mapPane, 'leaflet-pan-anim');
 
3340                         var newPos = this._getMapPanePos().subtract(offset).round();
 
3341                         this._panAnim.run(this._mapPane, newPos, options.duration || 0.25, options.easeLinearity);
 
3343                         this._rawPanBy(offset);
 
3344                         this.fire('move').fire('moveend');
 
3350         // @method flyTo(latlng: LatLng, zoom?: Number, options?: Zoom/pan options): this
 
3351         // Sets the view of the map (geographical center and zoom) performing a smooth
 
3352         // pan-zoom animation.
 
3353         flyTo: function (targetCenter, targetZoom, options) {
 
3355                 options = options || {};
 
3356                 if (options.animate === false || !any3d) {
 
3357                         return this.setView(targetCenter, targetZoom, options);
 
3362                 var from = this.project(this.getCenter()),
 
3363                     to = this.project(targetCenter),
 
3364                     size = this.getSize(),
 
3365                     startZoom = this._zoom;
 
3367                 targetCenter = toLatLng(targetCenter);
 
3368                 targetZoom = targetZoom === undefined ? startZoom : targetZoom;
 
3370                 var w0 = Math.max(size.x, size.y),
 
3371                     w1 = w0 * this.getZoomScale(startZoom, targetZoom),
 
3372                     u1 = (to.distanceTo(from)) || 1,
 
3377                         var s1 = i ? -1 : 1,
 
3379                             t1 = w1 * w1 - w0 * w0 + s1 * rho2 * rho2 * u1 * u1,
 
3380                             b1 = 2 * s2 * rho2 * u1,
 
3382                             sq = Math.sqrt(b * b + 1) - b;
 
3384                             // workaround for floating point precision bug when sq = 0, log = -Infinite,
 
3385                             // thus triggering an infinite loop in flyTo
 
3386                             var log = sq < 0.000000001 ? -18 : Math.log(sq);
 
3391                 function sinh(n) { return (Math.exp(n) - Math.exp(-n)) / 2; }
 
3392                 function cosh(n) { return (Math.exp(n) + Math.exp(-n)) / 2; }
 
3393                 function tanh(n) { return sinh(n) / cosh(n); }
 
3397                 function w(s) { return w0 * (cosh(r0) / cosh(r0 + rho * s)); }
 
3398                 function u(s) { return w0 * (cosh(r0) * tanh(r0 + rho * s) - sinh(r0)) / rho2; }
 
3400                 function easeOut(t) { return 1 - Math.pow(1 - t, 1.5); }
 
3402                 var start = Date.now(),
 
3403                     S = (r(1) - r0) / rho,
 
3404                     duration = options.duration ? 1000 * options.duration : 1000 * S * 0.8;
 
3407                         var t = (Date.now() - start) / duration,
 
3411                                 this._flyToFrame = requestAnimFrame(frame, this);
 
3414                                         this.unproject(from.add(to.subtract(from).multiplyBy(u(s) / u1)), startZoom),
 
3415                                         this.getScaleZoom(w0 / w(s), startZoom),
 
3420                                         ._move(targetCenter, targetZoom)
 
3425                 this._moveStart(true, options.noMoveStart);
 
3431         // @method flyToBounds(bounds: LatLngBounds, options?: fitBounds options): this
 
3432         // Sets the view of the map with a smooth animation like [`flyTo`](#map-flyto),
 
3433         // but takes a bounds parameter like [`fitBounds`](#map-fitbounds).
 
3434         flyToBounds: function (bounds, options) {
 
3435                 var target = this._getBoundsCenterZoom(bounds, options);
 
3436                 return this.flyTo(target.center, target.zoom, options);
 
3439         // @method setMaxBounds(bounds: Bounds): this
 
3440         // Restricts the map view to the given bounds (see the [maxBounds](#map-maxbounds) option).
 
3441         setMaxBounds: function (bounds) {
 
3442                 bounds = toLatLngBounds(bounds);
 
3444                 if (!bounds.isValid()) {
 
3445                         this.options.maxBounds = null;
 
3446                         return this.off('moveend', this._panInsideMaxBounds);
 
3447                 } else if (this.options.maxBounds) {
 
3448                         this.off('moveend', this._panInsideMaxBounds);
 
3451                 this.options.maxBounds = bounds;
 
3454                         this._panInsideMaxBounds();
 
3457                 return this.on('moveend', this._panInsideMaxBounds);
 
3460         // @method setMinZoom(zoom: Number): this
 
3461         // Sets the lower limit for the available zoom levels (see the [minZoom](#map-minzoom) option).
 
3462         setMinZoom: function (zoom) {
 
3463                 var oldZoom = this.options.minZoom;
 
3464                 this.options.minZoom = zoom;
 
3466                 if (this._loaded && oldZoom !== zoom) {
 
3467                         this.fire('zoomlevelschange');
 
3469                         if (this.getZoom() < this.options.minZoom) {
 
3470                                 return this.setZoom(zoom);
 
3477         // @method setMaxZoom(zoom: Number): this
 
3478         // Sets the upper limit for the available zoom levels (see the [maxZoom](#map-maxzoom) option).
 
3479         setMaxZoom: function (zoom) {
 
3480                 var oldZoom = this.options.maxZoom;
 
3481                 this.options.maxZoom = zoom;
 
3483                 if (this._loaded && oldZoom !== zoom) {
 
3484                         this.fire('zoomlevelschange');
 
3486                         if (this.getZoom() > this.options.maxZoom) {
 
3487                                 return this.setZoom(zoom);
 
3494         // @method panInsideBounds(bounds: LatLngBounds, options?: Pan options): this
 
3495         // 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.
 
3496         panInsideBounds: function (bounds, options) {
 
3497                 this._enforcingBounds = true;
 
3498                 var center = this.getCenter(),
 
3499                     newCenter = this._limitCenter(center, this._zoom, toLatLngBounds(bounds));
 
3501                 if (!center.equals(newCenter)) {
 
3502                         this.panTo(newCenter, options);
 
3505                 this._enforcingBounds = false;
 
3509         // @method panInside(latlng: LatLng, options?: options): this
 
3510         // Pans the map the minimum amount to make the `latlng` visible. Use
 
3511         // `padding`, `paddingTopLeft` and `paddingTopRight` options to fit
 
3512         // the display to more restricted bounds, like [`fitBounds`](#map-fitbounds).
 
3513         // If `latlng` is already within the (optionally padded) display bounds,
 
3514         // the map will not be panned.
 
3515         panInside: function (latlng, options) {
 
3516                 options = options || {};
 
3518                 var paddingTL = toPoint(options.paddingTopLeft || options.padding || [0, 0]),
 
3519                     paddingBR = toPoint(options.paddingBottomRight || options.padding || [0, 0]),
 
3520                     center = this.getCenter(),
 
3521                     pixelCenter = this.project(center),
 
3522                     pixelPoint = this.project(latlng),
 
3523                     pixelBounds = this.getPixelBounds(),
 
3524                     halfPixelBounds = pixelBounds.getSize().divideBy(2),
 
3525                     paddedBounds = toBounds([pixelBounds.min.add(paddingTL), pixelBounds.max.subtract(paddingBR)]);
 
3527                 if (!paddedBounds.contains(pixelPoint)) {
 
3528                         this._enforcingBounds = true;
 
3529                         var diff = pixelCenter.subtract(pixelPoint),
 
3530                             newCenter = toPoint(pixelPoint.x + diff.x, pixelPoint.y + diff.y);
 
3532                         if (pixelPoint.x < paddedBounds.min.x || pixelPoint.x > paddedBounds.max.x) {
 
3533                                 newCenter.x = pixelCenter.x - diff.x;
 
3535                                         newCenter.x += halfPixelBounds.x - paddingTL.x;
 
3537                                         newCenter.x -= halfPixelBounds.x - paddingBR.x;
 
3540                         if (pixelPoint.y < paddedBounds.min.y || pixelPoint.y > paddedBounds.max.y) {
 
3541                                 newCenter.y = pixelCenter.y - diff.y;
 
3543                                         newCenter.y += halfPixelBounds.y - paddingTL.y;
 
3545                                         newCenter.y -= halfPixelBounds.y - paddingBR.y;
 
3548                         this.panTo(this.unproject(newCenter), options);
 
3549                         this._enforcingBounds = false;
 
3554         // @method invalidateSize(options: Zoom/pan options): this
 
3555         // Checks if the map container size changed and updates the map if so —
 
3556         // call it after you've changed the map size dynamically, also animating
 
3557         // pan by default. If `options.pan` is `false`, panning will not occur.
 
3558         // If `options.debounceMoveend` is `true`, it will delay `moveend` event so
 
3559         // that it doesn't happen often even if the method is called many
 
3563         // @method invalidateSize(animate: Boolean): this
 
3564         // Checks if the map container size changed and updates the map if so —
 
3565         // call it after you've changed the map size dynamically, also animating
 
3567         invalidateSize: function (options) {
 
3568                 if (!this._loaded) { return this; }
 
3573                 }, options === true ? {animate: true} : options);
 
3575                 var oldSize = this.getSize();
 
3576                 this._sizeChanged = true;
 
3577                 this._lastCenter = null;
 
3579                 var newSize = this.getSize(),
 
3580                     oldCenter = oldSize.divideBy(2).round(),
 
3581                     newCenter = newSize.divideBy(2).round(),
 
3582                     offset = oldCenter.subtract(newCenter);
 
3584                 if (!offset.x && !offset.y) { return this; }
 
3586                 if (options.animate && options.pan) {
 
3591                                 this._rawPanBy(offset);
 
3596                         if (options.debounceMoveend) {
 
3597                                 clearTimeout(this._sizeTimer);
 
3598                                 this._sizeTimer = setTimeout(bind(this.fire, this, 'moveend'), 200);
 
3600                                 this.fire('moveend');
 
3604                 // @section Map state change events
 
3605                 // @event resize: ResizeEvent
 
3606                 // Fired when the map is resized.
 
3607                 return this.fire('resize', {
 
3613         // @section Methods for modifying map state
 
3614         // @method stop(): this
 
3615         // Stops the currently running `panTo` or `flyTo` animation, if any.
 
3617                 this.setZoom(this._limitZoom(this._zoom));
 
3618                 if (!this.options.zoomSnap) {
 
3619                         this.fire('viewreset');
 
3621                 return this._stop();
 
3624         // @section Geolocation methods
 
3625         // @method locate(options?: Locate options): this
 
3626         // Tries to locate the user using the Geolocation API, firing a [`locationfound`](#map-locationfound)
 
3627         // event with location data on success or a [`locationerror`](#map-locationerror) event on failure,
 
3628         // and optionally sets the map view to the user's location with respect to
 
3629         // detection accuracy (or to the world view if geolocation failed).
 
3630         // Note that, if your page doesn't use HTTPS, this method will fail in
 
3631         // modern browsers ([Chrome 50 and newer](https://sites.google.com/a/chromium.org/dev/Home/chromium-security/deprecating-powerful-features-on-insecure-origins))
 
3632         // See `Locate options` for more details.
 
3633         locate: function (options) {
 
3635                 options = this._locateOptions = extend({
 
3639                         // maxZoom: <Number>
 
3641                         // enableHighAccuracy: false
 
3644                 if (!('geolocation' in navigator)) {
 
3645                         this._handleGeolocationError({
 
3647                                 message: 'Geolocation not supported.'
 
3652                 var onResponse = bind(this._handleGeolocationResponse, this),
 
3653                     onError = bind(this._handleGeolocationError, this);
 
3655                 if (options.watch) {
 
3656                         this._locationWatchId =
 
3657                                 navigator.geolocation.watchPosition(onResponse, onError, options);
 
3659                         navigator.geolocation.getCurrentPosition(onResponse, onError, options);
 
3664         // @method stopLocate(): this
 
3665         // Stops watching location previously initiated by `map.locate({watch: true})`
 
3666         // and aborts resetting the map view if map.locate was called with
 
3667         // `{setView: true}`.
 
3668         stopLocate: function () {
 
3669                 if (navigator.geolocation && navigator.geolocation.clearWatch) {
 
3670                         navigator.geolocation.clearWatch(this._locationWatchId);
 
3672                 if (this._locateOptions) {
 
3673                         this._locateOptions.setView = false;
 
3678         _handleGeolocationError: function (error) {
 
3680                     message = error.message ||
 
3681                             (c === 1 ? 'permission denied' :
 
3682                             (c === 2 ? 'position unavailable' : 'timeout'));
 
3684                 if (this._locateOptions.setView && !this._loaded) {
 
3688                 // @section Location events
 
3689                 // @event locationerror: ErrorEvent
 
3690                 // Fired when geolocation (using the [`locate`](#map-locate) method) failed.
 
3691                 this.fire('locationerror', {
 
3693                         message: 'Geolocation error: ' + message + '.'
 
3697         _handleGeolocationResponse: function (pos) {
 
3698                 var lat = pos.coords.latitude,
 
3699                     lng = pos.coords.longitude,
 
3700                     latlng = new LatLng(lat, lng),
 
3701                     bounds = latlng.toBounds(pos.coords.accuracy * 2),
 
3702                     options = this._locateOptions;
 
3704                 if (options.setView) {
 
3705                         var zoom = this.getBoundsZoom(bounds);
 
3706                         this.setView(latlng, options.maxZoom ? Math.min(zoom, options.maxZoom) : zoom);
 
3712                         timestamp: pos.timestamp
 
3715                 for (var i in pos.coords) {
 
3716                         if (typeof pos.coords[i] === 'number') {
 
3717                                 data[i] = pos.coords[i];
 
3721                 // @event locationfound: LocationEvent
 
3722                 // Fired when geolocation (using the [`locate`](#map-locate) method)
 
3723                 // went successfully.
 
3724                 this.fire('locationfound', data);
 
3727         // TODO Appropriate docs section?
 
3728         // @section Other Methods
 
3729         // @method addHandler(name: String, HandlerClass: Function): this
 
3730         // Adds a new `Handler` to the map, given its name and constructor function.
 
3731         addHandler: function (name, HandlerClass) {
 
3732                 if (!HandlerClass) { return this; }
 
3734                 var handler = this[name] = new HandlerClass(this);
 
3736                 this._handlers.push(handler);
 
3738                 if (this.options[name]) {
 
3745         // @method remove(): this
 
3746         // Destroys the map and clears all related event listeners.
 
3747         remove: function () {
 
3749                 this._initEvents(true);
 
3751                 if (this._containerId !== this._container._leaflet_id) {
 
3752                         throw new Error('Map container is being reused by another instance');
 
3756                         // throws error in IE6-8
 
3757                         delete this._container._leaflet_id;
 
3758                         delete this._containerId;
 
3761                         this._container._leaflet_id = undefined;
 
3763                         this._containerId = undefined;
 
3766                 if (this._locationWatchId !== undefined) {
 
3772                 remove(this._mapPane);
 
3774                 if (this._clearControlPos) {
 
3775                         this._clearControlPos();
 
3777                 if (this._resizeRequest) {
 
3778                         cancelAnimFrame(this._resizeRequest);
 
3779                         this._resizeRequest = null;
 
3782                 this._clearHandlers();
 
3785                         // @section Map state change events
 
3786                         // @event unload: Event
 
3787                         // Fired when the map is destroyed with [remove](#map-remove) method.
 
3788                         this.fire('unload');
 
3792                 for (i in this._layers) {
 
3793                         this._layers[i].remove();
 
3795                 for (i in this._panes) {
 
3796                         remove(this._panes[i]);
 
3801                 delete this._mapPane;
 
3802                 delete this._renderer;
 
3807         // @section Other Methods
 
3808         // @method createPane(name: String, container?: HTMLElement): HTMLElement
 
3809         // Creates a new [map pane](#map-pane) with the given name if it doesn't exist already,
 
3810         // then returns it. The pane is created as a child of `container`, or
 
3811         // as a child of the main map pane if not set.
 
3812         createPane: function (name, container) {
 
3813                 var className = 'leaflet-pane' + (name ? ' leaflet-' + name.replace('Pane', '') + '-pane' : ''),
 
3814                     pane = create$1('div', className, container || this._mapPane);
 
3817                         this._panes[name] = pane;
 
3822         // @section Methods for Getting Map State
 
3824         // @method getCenter(): LatLng
 
3825         // Returns the geographical center of the map view
 
3826         getCenter: function () {
 
3827                 this._checkIfLoaded();
 
3829                 if (this._lastCenter && !this._moved()) {
 
3830                         return this._lastCenter;
 
3832                 return this.layerPointToLatLng(this._getCenterLayerPoint());
 
3835         // @method getZoom(): Number
 
3836         // Returns the current zoom level of the map view
 
3837         getZoom: function () {
 
3841         // @method getBounds(): LatLngBounds
 
3842         // Returns the geographical bounds visible in the current map view
 
3843         getBounds: function () {
 
3844                 var bounds = this.getPixelBounds(),
 
3845                     sw = this.unproject(bounds.getBottomLeft()),
 
3846                     ne = this.unproject(bounds.getTopRight());
 
3848                 return new LatLngBounds(sw, ne);
 
3851         // @method getMinZoom(): Number
 
3852         // 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.
 
3853         getMinZoom: function () {
 
3854                 return this.options.minZoom === undefined ? this._layersMinZoom || 0 : this.options.minZoom;
 
3857         // @method getMaxZoom(): Number
 
3858         // Returns the maximum zoom level of the map (if set in the `maxZoom` option of the map or of any layers).
 
3859         getMaxZoom: function () {
 
3860                 return this.options.maxZoom === undefined ?
 
3861                         (this._layersMaxZoom === undefined ? Infinity : this._layersMaxZoom) :
 
3862                         this.options.maxZoom;
 
3865         // @method getBoundsZoom(bounds: LatLngBounds, inside?: Boolean, padding?: Point): Number
 
3866         // Returns the maximum zoom level on which the given bounds fit to the map
 
3867         // view in its entirety. If `inside` (optional) is set to `true`, the method
 
3868         // instead returns the minimum zoom level on which the map view fits into
 
3869         // the given bounds in its entirety.
 
3870         getBoundsZoom: function (bounds, inside, padding) { // (LatLngBounds[, Boolean, Point]) -> Number
 
3871                 bounds = toLatLngBounds(bounds);
 
3872                 padding = toPoint(padding || [0, 0]);
 
3874                 var zoom = this.getZoom() || 0,
 
3875                     min = this.getMinZoom(),
 
3876                     max = this.getMaxZoom(),
 
3877                     nw = bounds.getNorthWest(),
 
3878                     se = bounds.getSouthEast(),
 
3879                     size = this.getSize().subtract(padding),
 
3880                     boundsSize = toBounds(this.project(se, zoom), this.project(nw, zoom)).getSize(),
 
3881                     snap = any3d ? this.options.zoomSnap : 1,
 
3882                     scalex = size.x / boundsSize.x,
 
3883                     scaley = size.y / boundsSize.y,
 
3884                     scale = inside ? Math.max(scalex, scaley) : Math.min(scalex, scaley);
 
3886                 zoom = this.getScaleZoom(scale, zoom);
 
3889                         zoom = Math.round(zoom / (snap / 100)) * (snap / 100); // don't jump if within 1% of a snap level
 
3890                         zoom = inside ? Math.ceil(zoom / snap) * snap : Math.floor(zoom / snap) * snap;
 
3893                 return Math.max(min, Math.min(max, zoom));
 
3896         // @method getSize(): Point
 
3897         // Returns the current size of the map container (in pixels).
 
3898         getSize: function () {
 
3899                 if (!this._size || this._sizeChanged) {
 
3900                         this._size = new Point(
 
3901                                 this._container.clientWidth || 0,
 
3902                                 this._container.clientHeight || 0);
 
3904                         this._sizeChanged = false;
 
3906                 return this._size.clone();
 
3909         // @method getPixelBounds(): Bounds
 
3910         // Returns the bounds of the current map view in projected pixel
 
3911         // coordinates (sometimes useful in layer and overlay implementations).
 
3912         getPixelBounds: function (center, zoom) {
 
3913                 var topLeftPoint = this._getTopLeftPoint(center, zoom);
 
3914                 return new Bounds(topLeftPoint, topLeftPoint.add(this.getSize()));
 
3917         // TODO: Check semantics - isn't the pixel origin the 0,0 coord relative to
 
3918         // the map pane? "left point of the map layer" can be confusing, specially
 
3919         // since there can be negative offsets.
 
3920         // @method getPixelOrigin(): Point
 
3921         // Returns the projected pixel coordinates of the top left point of
 
3922         // the map layer (useful in custom layer and overlay implementations).
 
3923         getPixelOrigin: function () {
 
3924                 this._checkIfLoaded();
 
3925                 return this._pixelOrigin;
 
3928         // @method getPixelWorldBounds(zoom?: Number): Bounds
 
3929         // Returns the world's bounds in pixel coordinates for zoom level `zoom`.
 
3930         // If `zoom` is omitted, the map's current zoom level is used.
 
3931         getPixelWorldBounds: function (zoom) {
 
3932                 return this.options.crs.getProjectedBounds(zoom === undefined ? this.getZoom() : zoom);
 
3935         // @section Other Methods
 
3937         // @method getPane(pane: String|HTMLElement): HTMLElement
 
3938         // Returns a [map pane](#map-pane), given its name or its HTML element (its identity).
 
3939         getPane: function (pane) {
 
3940                 return typeof pane === 'string' ? this._panes[pane] : pane;
 
3943         // @method getPanes(): Object
 
3944         // Returns a plain object containing the names of all [panes](#map-pane) as keys and
 
3945         // the panes as values.
 
3946         getPanes: function () {
 
3950         // @method getContainer: HTMLElement
 
3951         // Returns the HTML element that contains the map.
 
3952         getContainer: function () {
 
3953                 return this._container;
 
3957         // @section Conversion Methods
 
3959         // @method getZoomScale(toZoom: Number, fromZoom: Number): Number
 
3960         // Returns the scale factor to be applied to a map transition from zoom level
 
3961         // `fromZoom` to `toZoom`. Used internally to help with zoom animations.
 
3962         getZoomScale: function (toZoom, fromZoom) {
 
3963                 // TODO replace with universal implementation after refactoring projections
 
3964                 var crs = this.options.crs;
 
3965                 fromZoom = fromZoom === undefined ? this._zoom : fromZoom;
 
3966                 return crs.scale(toZoom) / crs.scale(fromZoom);
 
3969         // @method getScaleZoom(scale: Number, fromZoom: Number): Number
 
3970         // Returns the zoom level that the map would end up at, if it is at `fromZoom`
 
3971         // level and everything is scaled by a factor of `scale`. Inverse of
 
3972         // [`getZoomScale`](#map-getZoomScale).
 
3973         getScaleZoom: function (scale, fromZoom) {
 
3974                 var crs = this.options.crs;
 
3975                 fromZoom = fromZoom === undefined ? this._zoom : fromZoom;
 
3976                 var zoom = crs.zoom(scale * crs.scale(fromZoom));
 
3977                 return isNaN(zoom) ? Infinity : zoom;
 
3980         // @method project(latlng: LatLng, zoom: Number): Point
 
3981         // Projects a geographical coordinate `LatLng` according to the projection
 
3982         // of the map's CRS, then scales it according to `zoom` and the CRS's
 
3983         // `Transformation`. The result is pixel coordinate relative to
 
3985         project: function (latlng, zoom) {
 
3986                 zoom = zoom === undefined ? this._zoom : zoom;
 
3987                 return this.options.crs.latLngToPoint(toLatLng(latlng), zoom);
 
3990         // @method unproject(point: Point, zoom: Number): LatLng
 
3991         // Inverse of [`project`](#map-project).
 
3992         unproject: function (point, zoom) {
 
3993                 zoom = zoom === undefined ? this._zoom : zoom;
 
3994                 return this.options.crs.pointToLatLng(toPoint(point), zoom);
 
3997         // @method layerPointToLatLng(point: Point): LatLng
 
3998         // Given a pixel coordinate relative to the [origin pixel](#map-getpixelorigin),
 
3999         // returns the corresponding geographical coordinate (for the current zoom level).
 
4000         layerPointToLatLng: function (point) {
 
4001                 var projectedPoint = toPoint(point).add(this.getPixelOrigin());
 
4002                 return this.unproject(projectedPoint);
 
4005         // @method latLngToLayerPoint(latlng: LatLng): Point
 
4006         // Given a geographical coordinate, returns the corresponding pixel coordinate
 
4007         // relative to the [origin pixel](#map-getpixelorigin).
 
4008         latLngToLayerPoint: function (latlng) {
 
4009                 var projectedPoint = this.project(toLatLng(latlng))._round();
 
4010                 return projectedPoint._subtract(this.getPixelOrigin());
 
4013         // @method wrapLatLng(latlng: LatLng): LatLng
 
4014         // Returns a `LatLng` where `lat` and `lng` has been wrapped according to the
 
4015         // map's CRS's `wrapLat` and `wrapLng` properties, if they are outside the
 
4017         // By default this means longitude is wrapped around the dateline so its
 
4018         // value is between -180 and +180 degrees.
 
4019         wrapLatLng: function (latlng) {
 
4020                 return this.options.crs.wrapLatLng(toLatLng(latlng));
 
4023         // @method wrapLatLngBounds(bounds: LatLngBounds): LatLngBounds
 
4024         // Returns a `LatLngBounds` with the same size as the given one, ensuring that
 
4025         // its center is within the CRS's bounds.
 
4026         // By default this means the center longitude is wrapped around the dateline so its
 
4027         // value is between -180 and +180 degrees, and the majority of the bounds
 
4028         // overlaps the CRS's bounds.
 
4029         wrapLatLngBounds: function (latlng) {
 
4030                 return this.options.crs.wrapLatLngBounds(toLatLngBounds(latlng));
 
4033         // @method distance(latlng1: LatLng, latlng2: LatLng): Number
 
4034         // Returns the distance between two geographical coordinates according to
 
4035         // the map's CRS. By default this measures distance in meters.
 
4036         distance: function (latlng1, latlng2) {
 
4037                 return this.options.crs.distance(toLatLng(latlng1), toLatLng(latlng2));
 
4040         // @method containerPointToLayerPoint(point: Point): Point
 
4041         // Given a pixel coordinate relative to the map container, returns the corresponding
 
4042         // pixel coordinate relative to the [origin pixel](#map-getpixelorigin).
 
4043         containerPointToLayerPoint: function (point) { // (Point)
 
4044                 return toPoint(point).subtract(this._getMapPanePos());
 
4047         // @method layerPointToContainerPoint(point: Point): Point
 
4048         // Given a pixel coordinate relative to the [origin pixel](#map-getpixelorigin),
 
4049         // returns the corresponding pixel coordinate relative to the map container.
 
4050         layerPointToContainerPoint: function (point) { // (Point)
 
4051                 return toPoint(point).add(this._getMapPanePos());
 
4054         // @method containerPointToLatLng(point: Point): LatLng
 
4055         // Given a pixel coordinate relative to the map container, returns
 
4056         // the corresponding geographical coordinate (for the current zoom level).
 
4057         containerPointToLatLng: function (point) {
 
4058                 var layerPoint = this.containerPointToLayerPoint(toPoint(point));
 
4059                 return this.layerPointToLatLng(layerPoint);
 
4062         // @method latLngToContainerPoint(latlng: LatLng): Point
 
4063         // Given a geographical coordinate, returns the corresponding pixel coordinate
 
4064         // relative to the map container.
 
4065         latLngToContainerPoint: function (latlng) {
 
4066                 return this.layerPointToContainerPoint(this.latLngToLayerPoint(toLatLng(latlng)));
 
4069         // @method mouseEventToContainerPoint(ev: MouseEvent): Point
 
4070         // Given a MouseEvent object, returns the pixel coordinate relative to the
 
4071         // map container where the event took place.
 
4072         mouseEventToContainerPoint: function (e) {
 
4073                 return getMousePosition(e, this._container);
 
4076         // @method mouseEventToLayerPoint(ev: MouseEvent): Point
 
4077         // Given a MouseEvent object, returns the pixel coordinate relative to
 
4078         // the [origin pixel](#map-getpixelorigin) where the event took place.
 
4079         mouseEventToLayerPoint: function (e) {
 
4080                 return this.containerPointToLayerPoint(this.mouseEventToContainerPoint(e));
 
4083         // @method mouseEventToLatLng(ev: MouseEvent): LatLng
 
4084         // Given a MouseEvent object, returns geographical coordinate where the
 
4085         // event took place.
 
4086         mouseEventToLatLng: function (e) { // (MouseEvent)
 
4087                 return this.layerPointToLatLng(this.mouseEventToLayerPoint(e));
 
4091         // map initialization methods
 
4093         _initContainer: function (id) {
 
4094                 var container = this._container = get(id);
 
4097                         throw new Error('Map container not found.');
 
4098                 } else if (container._leaflet_id) {
 
4099                         throw new Error('Map container is already initialized.');
 
4102                 on(container, 'scroll', this._onScroll, this);
 
4103                 this._containerId = stamp(container);
 
4106         _initLayout: function () {
 
4107                 var container = this._container;
 
4109                 this._fadeAnimated = this.options.fadeAnimation && any3d;
 
4111                 addClass(container, 'leaflet-container' +
 
4112                         (touch ? ' leaflet-touch' : '') +
 
4113                         (retina ? ' leaflet-retina' : '') +
 
4114                         (ielt9 ? ' leaflet-oldie' : '') +
 
4115                         (safari ? ' leaflet-safari' : '') +
 
4116                         (this._fadeAnimated ? ' leaflet-fade-anim' : ''));
 
4118                 var position = getStyle(container, 'position');
 
4120                 if (position !== 'absolute' && position !== 'relative' && position !== 'fixed') {
 
4121                         container.style.position = 'relative';
 
4126                 if (this._initControlPos) {
 
4127                         this._initControlPos();
 
4131         _initPanes: function () {
 
4132                 var panes = this._panes = {};
 
4133                 this._paneRenderers = {};
 
4137                 // Panes are DOM elements used to control the ordering of layers on the map. You
 
4138                 // can access panes with [`map.getPane`](#map-getpane) or
 
4139                 // [`map.getPanes`](#map-getpanes) methods. New panes can be created with the
 
4140                 // [`map.createPane`](#map-createpane) method.
 
4142                 // Every map has the following default panes that differ only in zIndex.
 
4144                 // @pane mapPane: HTMLElement = 'auto'
 
4145                 // Pane that contains all other map panes
 
4147                 this._mapPane = this.createPane('mapPane', this._container);
 
4148                 setPosition(this._mapPane, new Point(0, 0));
 
4150                 // @pane tilePane: HTMLElement = 200
 
4151                 // Pane for `GridLayer`s and `TileLayer`s
 
4152                 this.createPane('tilePane');
 
4153                 // @pane overlayPane: HTMLElement = 400
 
4154                 // Pane for vectors (`Path`s, like `Polyline`s and `Polygon`s), `ImageOverlay`s and `VideoOverlay`s
 
4155                 this.createPane('shadowPane');
 
4156                 // @pane shadowPane: HTMLElement = 500
 
4157                 // Pane for overlay shadows (e.g. `Marker` shadows)
 
4158                 this.createPane('overlayPane');
 
4159                 // @pane markerPane: HTMLElement = 600
 
4160                 // Pane for `Icon`s of `Marker`s
 
4161                 this.createPane('markerPane');
 
4162                 // @pane tooltipPane: HTMLElement = 650
 
4163                 // Pane for `Tooltip`s.
 
4164                 this.createPane('tooltipPane');
 
4165                 // @pane popupPane: HTMLElement = 700
 
4166                 // Pane for `Popup`s.
 
4167                 this.createPane('popupPane');
 
4169                 if (!this.options.markerZoomAnimation) {
 
4170                         addClass(panes.markerPane, 'leaflet-zoom-hide');
 
4171                         addClass(panes.shadowPane, 'leaflet-zoom-hide');
 
4176         // private methods that modify map state
 
4178         // @section Map state change events
 
4179         _resetView: function (center, zoom) {
 
4180                 setPosition(this._mapPane, new Point(0, 0));
 
4182                 var loading = !this._loaded;
 
4183                 this._loaded = true;
 
4184                 zoom = this._limitZoom(zoom);
 
4186                 this.fire('viewprereset');
 
4188                 var zoomChanged = this._zoom !== zoom;
 
4190                         ._moveStart(zoomChanged, false)
 
4191                         ._move(center, zoom)
 
4192                         ._moveEnd(zoomChanged);
 
4194                 // @event viewreset: Event
 
4195                 // Fired when the map needs to redraw its content (this usually happens
 
4196                 // on map zoom or load). Very useful for creating custom overlays.
 
4197                 this.fire('viewreset');
 
4199                 // @event load: Event
 
4200                 // Fired when the map is initialized (when its center and zoom are set
 
4201                 // for the first time).
 
4207         _moveStart: function (zoomChanged, noMoveStart) {
 
4208                 // @event zoomstart: Event
 
4209                 // Fired when the map zoom is about to change (e.g. before zoom animation).
 
4210                 // @event movestart: Event
 
4211                 // Fired when the view of the map starts changing (e.g. user starts dragging the map).
 
4213                         this.fire('zoomstart');
 
4216                         this.fire('movestart');
 
4221         _move: function (center, zoom, data) {
 
4222                 if (zoom === undefined) {
 
4225                 var zoomChanged = this._zoom !== zoom;
 
4228                 this._lastCenter = center;
 
4229                 this._pixelOrigin = this._getNewPixelOrigin(center);
 
4231                 // @event zoom: Event
 
4232                 // Fired repeatedly during any change in zoom level, including zoom
 
4233                 // and fly animations.
 
4234                 if (zoomChanged || (data && data.pinch)) {      // Always fire 'zoom' if pinching because #3530
 
4235                         this.fire('zoom', data);
 
4238                 // @event move: Event
 
4239                 // Fired repeatedly during any movement of the map, including pan and
 
4241                 return this.fire('move', data);
 
4244         _moveEnd: function (zoomChanged) {
 
4245                 // @event zoomend: Event
 
4246                 // Fired when the map has changed, after any animations.
 
4248                         this.fire('zoomend');
 
4251                 // @event moveend: Event
 
4252                 // Fired when the center of the map stops changing (e.g. user stopped
 
4253                 // dragging the map).
 
4254                 return this.fire('moveend');
 
4257         _stop: function () {
 
4258                 cancelAnimFrame(this._flyToFrame);
 
4259                 if (this._panAnim) {
 
4260                         this._panAnim.stop();
 
4265         _rawPanBy: function (offset) {
 
4266                 setPosition(this._mapPane, this._getMapPanePos().subtract(offset));
 
4269         _getZoomSpan: function () {
 
4270                 return this.getMaxZoom() - this.getMinZoom();
 
4273         _panInsideMaxBounds: function () {
 
4274                 if (!this._enforcingBounds) {
 
4275                         this.panInsideBounds(this.options.maxBounds);
 
4279         _checkIfLoaded: function () {
 
4280                 if (!this._loaded) {
 
4281                         throw new Error('Set map center and zoom first.');
 
4285         // DOM event handling
 
4287         // @section Interaction events
 
4288         _initEvents: function (remove$$1) {
 
4290                 this._targets[stamp(this._container)] = this;
 
4292                 var onOff = remove$$1 ? off : on;
 
4294                 // @event click: MouseEvent
 
4295                 // Fired when the user clicks (or taps) the map.
 
4296                 // @event dblclick: MouseEvent
 
4297                 // Fired when the user double-clicks (or double-taps) the map.
 
4298                 // @event mousedown: MouseEvent
 
4299                 // Fired when the user pushes the mouse button on the map.
 
4300                 // @event mouseup: MouseEvent
 
4301                 // Fired when the user releases the mouse button on the map.
 
4302                 // @event mouseover: MouseEvent
 
4303                 // Fired when the mouse enters the map.
 
4304                 // @event mouseout: MouseEvent
 
4305                 // Fired when the mouse leaves the map.
 
4306                 // @event mousemove: MouseEvent
 
4307                 // Fired while the mouse moves over the map.
 
4308                 // @event contextmenu: MouseEvent
 
4309                 // Fired when the user pushes the right mouse button on the map, prevents
 
4310                 // default browser context menu from showing if there are listeners on
 
4311                 // this event. Also fired on mobile when the user holds a single touch
 
4312                 // for a second (also called long press).
 
4313                 // @event keypress: KeyboardEvent
 
4314                 // Fired when the user presses a key from the keyboard that produces a character value while the map is focused.
 
4315                 // @event keydown: KeyboardEvent
 
4316                 // Fired when the user presses a key from the keyboard while the map is focused. Unlike the `keypress` event,
 
4317                 // the `keydown` event is fired for keys that produce a character value and for keys
 
4318                 // that do not produce a character value.
 
4319                 // @event keyup: KeyboardEvent
 
4320                 // Fired when the user releases a key from the keyboard while the map is focused.
 
4321                 onOff(this._container, 'click dblclick mousedown mouseup ' +
 
4322                         'mouseover mouseout mousemove contextmenu keypress keydown keyup', this._handleDOMEvent, this);
 
4324                 if (this.options.trackResize) {
 
4325                         onOff(window, 'resize', this._onResize, this);
 
4328                 if (any3d && this.options.transform3DLimit) {
 
4329                         (remove$$1 ? this.off : this.on).call(this, 'moveend', this._onMoveEnd);
 
4333         _onResize: function () {
 
4334                 cancelAnimFrame(this._resizeRequest);
 
4335                 this._resizeRequest = requestAnimFrame(
 
4336                         function () { this.invalidateSize({debounceMoveend: true}); }, this);
 
4339         _onScroll: function () {
 
4340                 this._container.scrollTop  = 0;
 
4341                 this._container.scrollLeft = 0;
 
4344         _onMoveEnd: function () {
 
4345                 var pos = this._getMapPanePos();
 
4346                 if (Math.max(Math.abs(pos.x), Math.abs(pos.y)) >= this.options.transform3DLimit) {
 
4347                         // https://bugzilla.mozilla.org/show_bug.cgi?id=1203873 but Webkit also have
 
4348                         // a pixel offset on very high values, see: http://jsfiddle.net/dg6r5hhb/
 
4349                         this._resetView(this.getCenter(), this.getZoom());
 
4353         _findEventTargets: function (e, type) {
 
4356                     isHover = type === 'mouseout' || type === 'mouseover',
 
4357                     src = e.target || e.srcElement,
 
4361                         target = this._targets[stamp(src)];
 
4362                         if (target && (type === 'click' || type === 'preclick') && !e._simulated && this._draggableMoved(target)) {
 
4363                                 // Prevent firing click after you just dragged an object.
 
4367                         if (target && target.listens(type, true)) {
 
4368                                 if (isHover && !isExternalTarget(src, e)) { break; }
 
4369                                 targets.push(target);
 
4370                                 if (isHover) { break; }
 
4372                         if (src === this._container) { break; }
 
4373                         src = src.parentNode;
 
4375                 if (!targets.length && !dragging && !isHover && isExternalTarget(src, e)) {
 
4381         _handleDOMEvent: function (e) {
 
4382                 if (!this._loaded || skipped(e)) { return; }
 
4386                 if (type === 'mousedown' || type === 'keypress' || type === 'keyup' || type === 'keydown') {
 
4387                         // prevents outline when clicking on keyboard-focusable element
 
4388                         preventOutline(e.target || e.srcElement);
 
4391                 this._fireDOMEvent(e, type);
 
4394         _mouseEvents: ['click', 'dblclick', 'mouseover', 'mouseout', 'contextmenu'],
 
4396         _fireDOMEvent: function (e, type, targets) {
 
4398                 if (e.type === 'click') {
 
4399                         // Fire a synthetic 'preclick' event which propagates up (mainly for closing popups).
 
4400                         // @event preclick: MouseEvent
 
4401                         // Fired before mouse click on the map (sometimes useful when you
 
4402                         // want something to happen on click before any existing click
 
4403                         // handlers start running).
 
4404                         var synth = extend({}, e);
 
4405                         synth.type = 'preclick';
 
4406                         this._fireDOMEvent(synth, synth.type, targets);
 
4409                 if (e._stopped) { return; }
 
4411                 // Find the layer the event is propagating from and its parents.
 
4412                 targets = (targets || []).concat(this._findEventTargets(e, type));
 
4414                 if (!targets.length) { return; }
 
4416                 var target = targets[0];
 
4417                 if (type === 'contextmenu' && target.listens(type, true)) {
 
4425                 if (e.type !== 'keypress' && e.type !== 'keydown' && e.type !== 'keyup') {
 
4426                         var isMarker = target.getLatLng && (!target._radius || target._radius <= 10);
 
4427                         data.containerPoint = isMarker ?
 
4428                                 this.latLngToContainerPoint(target.getLatLng()) : this.mouseEventToContainerPoint(e);
 
4429                         data.layerPoint = this.containerPointToLayerPoint(data.containerPoint);
 
4430                         data.latlng = isMarker ? target.getLatLng() : this.layerPointToLatLng(data.layerPoint);
 
4433                 for (var i = 0; i < targets.length; i++) {
 
4434                         targets[i].fire(type, data, true);
 
4435                         if (data.originalEvent._stopped ||
 
4436                                 (targets[i].options.bubblingMouseEvents === false && indexOf(this._mouseEvents, type) !== -1)) { return; }
 
4440         _draggableMoved: function (obj) {
 
4441                 obj = obj.dragging && obj.dragging.enabled() ? obj : this;
 
4442                 return (obj.dragging && obj.dragging.moved()) || (this.boxZoom && this.boxZoom.moved());
 
4445         _clearHandlers: function () {
 
4446                 for (var i = 0, len = this._handlers.length; i < len; i++) {
 
4447                         this._handlers[i].disable();
 
4451         // @section Other Methods
 
4453         // @method whenReady(fn: Function, context?: Object): this
 
4454         // Runs the given function `fn` when the map gets initialized with
 
4455         // a view (center and zoom) and at least one layer, or immediately
 
4456         // if it's already initialized, optionally passing a function context.
 
4457         whenReady: function (callback, context) {
 
4459                         callback.call(context || this, {target: this});
 
4461                         this.on('load', callback, context);
 
4467         // private methods for getting map state
 
4469         _getMapPanePos: function () {
 
4470                 return getPosition(this._mapPane) || new Point(0, 0);
 
4473         _moved: function () {
 
4474                 var pos = this._getMapPanePos();
 
4475                 return pos && !pos.equals([0, 0]);
 
4478         _getTopLeftPoint: function (center, zoom) {
 
4479                 var pixelOrigin = center && zoom !== undefined ?
 
4480                         this._getNewPixelOrigin(center, zoom) :
 
4481                         this.getPixelOrigin();
 
4482                 return pixelOrigin.subtract(this._getMapPanePos());
 
4485         _getNewPixelOrigin: function (center, zoom) {
 
4486                 var viewHalf = this.getSize()._divideBy(2);
 
4487                 return this.project(center, zoom)._subtract(viewHalf)._add(this._getMapPanePos())._round();
 
4490         _latLngToNewLayerPoint: function (latlng, zoom, center) {
 
4491                 var topLeft = this._getNewPixelOrigin(center, zoom);
 
4492                 return this.project(latlng, zoom)._subtract(topLeft);
 
4495         _latLngBoundsToNewLayerBounds: function (latLngBounds, zoom, center) {
 
4496                 var topLeft = this._getNewPixelOrigin(center, zoom);
 
4498                         this.project(latLngBounds.getSouthWest(), zoom)._subtract(topLeft),
 
4499                         this.project(latLngBounds.getNorthWest(), zoom)._subtract(topLeft),
 
4500                         this.project(latLngBounds.getSouthEast(), zoom)._subtract(topLeft),
 
4501                         this.project(latLngBounds.getNorthEast(), zoom)._subtract(topLeft)
 
4505         // layer point of the current center
 
4506         _getCenterLayerPoint: function () {
 
4507                 return this.containerPointToLayerPoint(this.getSize()._divideBy(2));
 
4510         // offset of the specified place to the current center in pixels
 
4511         _getCenterOffset: function (latlng) {
 
4512                 return this.latLngToLayerPoint(latlng).subtract(this._getCenterLayerPoint());
 
4515         // adjust center for view to get inside bounds
 
4516         _limitCenter: function (center, zoom, bounds) {
 
4518                 if (!bounds) { return center; }
 
4520                 var centerPoint = this.project(center, zoom),
 
4521                     viewHalf = this.getSize().divideBy(2),
 
4522                     viewBounds = new Bounds(centerPoint.subtract(viewHalf), centerPoint.add(viewHalf)),
 
4523                     offset = this._getBoundsOffset(viewBounds, bounds, zoom);
 
4525                 // If offset is less than a pixel, ignore.
 
4526                 // This prevents unstable projections from getting into
 
4527                 // an infinite loop of tiny offsets.
 
4528                 if (offset.round().equals([0, 0])) {
 
4532                 return this.unproject(centerPoint.add(offset), zoom);
 
4535         // adjust offset for view to get inside bounds
 
4536         _limitOffset: function (offset, bounds) {
 
4537                 if (!bounds) { return offset; }
 
4539                 var viewBounds = this.getPixelBounds(),
 
4540                     newBounds = new Bounds(viewBounds.min.add(offset), viewBounds.max.add(offset));
 
4542                 return offset.add(this._getBoundsOffset(newBounds, bounds));
 
4545         // returns offset needed for pxBounds to get inside maxBounds at a specified zoom
 
4546         _getBoundsOffset: function (pxBounds, maxBounds, zoom) {
 
4547                 var projectedMaxBounds = toBounds(
 
4548                         this.project(maxBounds.getNorthEast(), zoom),
 
4549                         this.project(maxBounds.getSouthWest(), zoom)
 
4551                     minOffset = projectedMaxBounds.min.subtract(pxBounds.min),
 
4552                     maxOffset = projectedMaxBounds.max.subtract(pxBounds.max),
 
4554                     dx = this._rebound(minOffset.x, -maxOffset.x),
 
4555                     dy = this._rebound(minOffset.y, -maxOffset.y);
 
4557                 return new Point(dx, dy);
 
4560         _rebound: function (left, right) {
 
4561                 return left + right > 0 ?
 
4562                         Math.round(left - right) / 2 :
 
4563                         Math.max(0, Math.ceil(left)) - Math.max(0, Math.floor(right));
 
4566         _limitZoom: function (zoom) {
 
4567                 var min = this.getMinZoom(),
 
4568                     max = this.getMaxZoom(),
 
4569                     snap = any3d ? this.options.zoomSnap : 1;
 
4571                         zoom = Math.round(zoom / snap) * snap;
 
4573                 return Math.max(min, Math.min(max, zoom));
 
4576         _onPanTransitionStep: function () {
 
4580         _onPanTransitionEnd: function () {
 
4581                 removeClass(this._mapPane, 'leaflet-pan-anim');
 
4582                 this.fire('moveend');
 
4585         _tryAnimatedPan: function (center, options) {
 
4586                 // difference between the new and current centers in pixels
 
4587                 var offset = this._getCenterOffset(center)._trunc();
 
4589                 // don't animate too far unless animate: true specified in options
 
4590                 if ((options && options.animate) !== true && !this.getSize().contains(offset)) { return false; }
 
4592                 this.panBy(offset, options);
 
4597         _createAnimProxy: function () {
 
4599                 var proxy = this._proxy = create$1('div', 'leaflet-proxy leaflet-zoom-animated');
 
4600                 this._panes.mapPane.appendChild(proxy);
 
4602                 this.on('zoomanim', function (e) {
 
4603                         var prop = TRANSFORM,
 
4604                             transform = this._proxy.style[prop];
 
4606                         setTransform(this._proxy, this.project(e.center, e.zoom), this.getZoomScale(e.zoom, 1));
 
4608                         // workaround for case when transform is the same and so transitionend event is not fired
 
4609                         if (transform === this._proxy.style[prop] && this._animatingZoom) {
 
4610                                 this._onZoomTransitionEnd();
 
4614                 this.on('load moveend', function () {
 
4615                         var c = this.getCenter(),
 
4617                         setTransform(this._proxy, this.project(c, z), this.getZoomScale(z, 1));
 
4620                 this._on('unload', this._destroyAnimProxy, this);
 
4623         _destroyAnimProxy: function () {
 
4624                 remove(this._proxy);
 
4628         _catchTransitionEnd: function (e) {
 
4629                 if (this._animatingZoom && e.propertyName.indexOf('transform') >= 0) {
 
4630                         this._onZoomTransitionEnd();
 
4634         _nothingToAnimate: function () {
 
4635                 return !this._container.getElementsByClassName('leaflet-zoom-animated').length;
 
4638         _tryAnimatedZoom: function (center, zoom, options) {
 
4640                 if (this._animatingZoom) { return true; }
 
4642                 options = options || {};
 
4644                 // don't animate if disabled, not supported or zoom difference is too large
 
4645                 if (!this._zoomAnimated || options.animate === false || this._nothingToAnimate() ||
 
4646                         Math.abs(zoom - this._zoom) > this.options.zoomAnimationThreshold) { return false; }
 
4648                 // offset is the pixel coords of the zoom origin relative to the current center
 
4649                 var scale = this.getZoomScale(zoom),
 
4650                     offset = this._getCenterOffset(center)._divideBy(1 - 1 / scale);
 
4652                 // don't animate if the zoom origin isn't within one screen from the current center, unless forced
 
4653                 if (options.animate !== true && !this.getSize().contains(offset)) { return false; }
 
4655                 requestAnimFrame(function () {
 
4657                             ._moveStart(true, false)
 
4658                             ._animateZoom(center, zoom, true);
 
4664         _animateZoom: function (center, zoom, startAnim, noUpdate) {
 
4665                 if (!this._mapPane) { return; }
 
4668                         this._animatingZoom = true;
 
4670                         // remember what center/zoom to set after animation
 
4671                         this._animateToCenter = center;
 
4672                         this._animateToZoom = zoom;
 
4674                         addClass(this._mapPane, 'leaflet-zoom-anim');
 
4677                 // @event zoomanim: ZoomAnimEvent
 
4678                 // Fired at least once per zoom animation. For continuous zoom, like pinch zooming, fired once per frame during zoom.
 
4679                 this.fire('zoomanim', {
 
4685                 // Work around webkit not firing 'transitionend', see https://github.com/Leaflet/Leaflet/issues/3689, 2693
 
4686                 setTimeout(bind(this._onZoomTransitionEnd, this), 250);
 
4689         _onZoomTransitionEnd: function () {
 
4690                 if (!this._animatingZoom) { return; }
 
4692                 if (this._mapPane) {
 
4693                         removeClass(this._mapPane, 'leaflet-zoom-anim');
 
4696                 this._animatingZoom = false;
 
4698                 this._move(this._animateToCenter, this._animateToZoom);
 
4700                 // This anim frame should prevent an obscure iOS webkit tile loading race condition.
 
4701                 requestAnimFrame(function () {
 
4702                         this._moveEnd(true);
 
4709 // @factory L.map(id: String, options?: Map options)
 
4710 // Instantiates a map object given the DOM ID of a `<div>` element
 
4711 // and optionally an object literal with `Map options`.
 
4714 // @factory L.map(el: HTMLElement, options?: Map options)
 
4715 // Instantiates a map object given an instance of a `<div>` HTML element
 
4716 // and optionally an object literal with `Map options`.
 
4717 function createMap(id, options) {
 
4718         return new Map(id, options);
 
4726  * L.Control is a base class for implementing map controls. Handles positioning.
 
4727  * All other controls extend from this class.
 
4730 var Control = Class.extend({
 
4732         // @aka Control options
 
4734                 // @option position: String = 'topright'
 
4735                 // The position of the control (one of the map corners). Possible values are `'topleft'`,
 
4736                 // `'topright'`, `'bottomleft'` or `'bottomright'`
 
4737                 position: 'topright'
 
4740         initialize: function (options) {
 
4741                 setOptions(this, options);
 
4745          * Classes extending L.Control will inherit the following methods:
 
4747          * @method getPosition: string
 
4748          * Returns the position of the control.
 
4750         getPosition: function () {
 
4751                 return this.options.position;
 
4754         // @method setPosition(position: string): this
 
4755         // Sets the position of the control.
 
4756         setPosition: function (position) {
 
4757                 var map = this._map;
 
4760                         map.removeControl(this);
 
4763                 this.options.position = position;
 
4766                         map.addControl(this);
 
4772         // @method getContainer: HTMLElement
 
4773         // Returns the HTMLElement that contains the control.
 
4774         getContainer: function () {
 
4775                 return this._container;
 
4778         // @method addTo(map: Map): this
 
4779         // Adds the control to the given map.
 
4780         addTo: function (map) {
 
4784                 var container = this._container = this.onAdd(map),
 
4785                     pos = this.getPosition(),
 
4786                     corner = map._controlCorners[pos];
 
4788                 addClass(container, 'leaflet-control');
 
4790                 if (pos.indexOf('bottom') !== -1) {
 
4791                         corner.insertBefore(container, corner.firstChild);
 
4793                         corner.appendChild(container);
 
4796                 this._map.on('unload', this.remove, this);
 
4801         // @method remove: this
 
4802         // Removes the control from the map it is currently active on.
 
4803         remove: function () {
 
4808                 remove(this._container);
 
4810                 if (this.onRemove) {
 
4811                         this.onRemove(this._map);
 
4814                 this._map.off('unload', this.remove, this);
 
4820         _refocusOnMap: function (e) {
 
4821                 // if map exists and event is not a keyboard event
 
4822                 if (this._map && e && e.screenX > 0 && e.screenY > 0) {
 
4823                         this._map.getContainer().focus();
 
4828 var control = function (options) {
 
4829         return new Control(options);
 
4832 /* @section Extension methods
 
4835  * Every control should extend from `L.Control` and (re-)implement the following methods.
 
4837  * @method onAdd(map: Map): HTMLElement
 
4838  * Should return the container DOM element for the control and add listeners on relevant map events. Called on [`control.addTo(map)`](#control-addTo).
 
4840  * @method onRemove(map: Map)
 
4841  * Optional method. Should contain all clean up code that removes the listeners previously added in [`onAdd`](#control-onadd). Called on [`control.remove()`](#control-remove).
 
4845  * @section Methods for Layers and Controls
 
4848         // @method addControl(control: Control): this
 
4849         // Adds the given control to the map
 
4850         addControl: function (control) {
 
4851                 control.addTo(this);
 
4855         // @method removeControl(control: Control): this
 
4856         // Removes the given control from the map
 
4857         removeControl: function (control) {
 
4862         _initControlPos: function () {
 
4863                 var corners = this._controlCorners = {},
 
4865                     container = this._controlContainer =
 
4866                             create$1('div', l + 'control-container', this._container);
 
4868                 function createCorner(vSide, hSide) {
 
4869                         var className = l + vSide + ' ' + l + hSide;
 
4871                         corners[vSide + hSide] = create$1('div', className, container);
 
4874                 createCorner('top', 'left');
 
4875                 createCorner('top', 'right');
 
4876                 createCorner('bottom', 'left');
 
4877                 createCorner('bottom', 'right');
 
4880         _clearControlPos: function () {
 
4881                 for (var i in this._controlCorners) {
 
4882                         remove(this._controlCorners[i]);
 
4884                 remove(this._controlContainer);
 
4885                 delete this._controlCorners;
 
4886                 delete this._controlContainer;
 
4891  * @class Control.Layers
 
4892  * @aka L.Control.Layers
 
4895  * 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`.
 
4900  * var baseLayers = {
 
4902  *      "OpenStreetMap": osm
 
4907  *      "Roads": roadsLayer
 
4910  * L.control.layers(baseLayers, overlays).addTo(map);
 
4913  * The `baseLayers` and `overlays` parameters are object literals with layer names as keys and `Layer` objects as values:
 
4917  *     "<someName1>": layer1,
 
4918  *     "<someName2>": layer2
 
4922  * The layer names can contain HTML, which allows you to add additional styling to the items:
 
4925  * {"<img src='my-layer-icon' /> <span class='my-layer-item'>My Layer</span>": myLayer}
 
4929 var Layers = Control.extend({
 
4931         // @aka Control.Layers options
 
4933                 // @option collapsed: Boolean = true
 
4934                 // If `true`, the control will be collapsed into an icon and expanded on mouse hover or touch.
 
4936                 position: 'topright',
 
4938                 // @option autoZIndex: Boolean = true
 
4939                 // 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.
 
4942                 // @option hideSingleBase: Boolean = false
 
4943                 // If `true`, the base layers in the control will be hidden when there is only one.
 
4944                 hideSingleBase: false,
 
4946                 // @option sortLayers: Boolean = false
 
4947                 // Whether to sort the layers. When `false`, layers will keep the order
 
4948                 // in which they were added to the control.
 
4951                 // @option sortFunction: Function = *
 
4952                 // A [compare function](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Array/sort)
 
4953                 // that will be used for sorting the layers, when `sortLayers` is `true`.
 
4954                 // The function receives both the `L.Layer` instances and their names, as in
 
4955                 // `sortFunction(layerA, layerB, nameA, nameB)`.
 
4956                 // By default, it sorts layers alphabetically by their name.
 
4957                 sortFunction: function (layerA, layerB, nameA, nameB) {
 
4958                         return nameA < nameB ? -1 : (nameB < nameA ? 1 : 0);
 
4962         initialize: function (baseLayers, overlays, options) {
 
4963                 setOptions(this, options);
 
4965                 this._layerControlInputs = [];
 
4967                 this._lastZIndex = 0;
 
4968                 this._handlingClick = false;
 
4970                 for (var i in baseLayers) {
 
4971                         this._addLayer(baseLayers[i], i);
 
4974                 for (i in overlays) {
 
4975                         this._addLayer(overlays[i], i, true);
 
4979         onAdd: function (map) {
 
4984                 map.on('zoomend', this._checkDisabledLayers, this);
 
4986                 for (var i = 0; i < this._layers.length; i++) {
 
4987                         this._layers[i].layer.on('add remove', this._onLayerChange, this);
 
4990                 return this._container;
 
4993         addTo: function (map) {
 
4994                 Control.prototype.addTo.call(this, map);
 
4995                 // Trigger expand after Layers Control has been inserted into DOM so that is now has an actual height.
 
4996                 return this._expandIfNotCollapsed();
 
4999         onRemove: function () {
 
5000                 this._map.off('zoomend', this._checkDisabledLayers, this);
 
5002                 for (var i = 0; i < this._layers.length; i++) {
 
5003                         this._layers[i].layer.off('add remove', this._onLayerChange, this);
 
5007         // @method addBaseLayer(layer: Layer, name: String): this
 
5008         // Adds a base layer (radio button entry) with the given name to the control.
 
5009         addBaseLayer: function (layer, name) {
 
5010                 this._addLayer(layer, name);
 
5011                 return (this._map) ? this._update() : this;
 
5014         // @method addOverlay(layer: Layer, name: String): this
 
5015         // Adds an overlay (checkbox entry) with the given name to the control.
 
5016         addOverlay: function (layer, name) {
 
5017                 this._addLayer(layer, name, true);
 
5018                 return (this._map) ? this._update() : this;
 
5021         // @method removeLayer(layer: Layer): this
 
5022         // Remove the given layer from the control.
 
5023         removeLayer: function (layer) {
 
5024                 layer.off('add remove', this._onLayerChange, this);
 
5026                 var obj = this._getLayer(stamp(layer));
 
5028                         this._layers.splice(this._layers.indexOf(obj), 1);
 
5030                 return (this._map) ? this._update() : this;
 
5033         // @method expand(): this
 
5034         // Expand the control container if collapsed.
 
5035         expand: function () {
 
5036                 addClass(this._container, 'leaflet-control-layers-expanded');
 
5037                 this._section.style.height = null;
 
5038                 var acceptableHeight = this._map.getSize().y - (this._container.offsetTop + 50);
 
5039                 if (acceptableHeight < this._section.clientHeight) {
 
5040                         addClass(this._section, 'leaflet-control-layers-scrollbar');
 
5041                         this._section.style.height = acceptableHeight + 'px';
 
5043                         removeClass(this._section, 'leaflet-control-layers-scrollbar');
 
5045                 this._checkDisabledLayers();
 
5049         // @method collapse(): this
 
5050         // Collapse the control container if expanded.
 
5051         collapse: function () {
 
5052                 removeClass(this._container, 'leaflet-control-layers-expanded');
 
5056         _initLayout: function () {
 
5057                 var className = 'leaflet-control-layers',
 
5058                     container = this._container = create$1('div', className),
 
5059                     collapsed = this.options.collapsed;
 
5061                 // makes this work on IE touch devices by stopping it from firing a mouseout event when the touch is released
 
5062                 container.setAttribute('aria-haspopup', true);
 
5064                 disableClickPropagation(container);
 
5065                 disableScrollPropagation(container);
 
5067                 var section = this._section = create$1('section', className + '-list');
 
5070                         this._map.on('click', this.collapse, this);
 
5074                                         mouseenter: this.expand,
 
5075                                         mouseleave: this.collapse
 
5080                 var link = this._layersLink = create$1('a', className + '-toggle', container);
 
5082                 link.title = 'Layers';
 
5085                         on(link, 'click', stop);
 
5086                         on(link, 'click', this.expand, this);
 
5088                         on(link, 'focus', this.expand, this);
 
5095                 this._baseLayersList = create$1('div', className + '-base', section);
 
5096                 this._separator = create$1('div', className + '-separator', section);
 
5097                 this._overlaysList = create$1('div', className + '-overlays', section);
 
5099                 container.appendChild(section);
 
5102         _getLayer: function (id) {
 
5103                 for (var i = 0; i < this._layers.length; i++) {
 
5105                         if (this._layers[i] && stamp(this._layers[i].layer) === id) {
 
5106                                 return this._layers[i];
 
5111         _addLayer: function (layer, name, overlay) {
 
5113                         layer.on('add remove', this._onLayerChange, this);
 
5122                 if (this.options.sortLayers) {
 
5123                         this._layers.sort(bind(function (a, b) {
 
5124                                 return this.options.sortFunction(a.layer, b.layer, a.name, b.name);
 
5128                 if (this.options.autoZIndex && layer.setZIndex) {
 
5130                         layer.setZIndex(this._lastZIndex);
 
5133                 this._expandIfNotCollapsed();
 
5136         _update: function () {
 
5137                 if (!this._container) { return this; }
 
5139                 empty(this._baseLayersList);
 
5140                 empty(this._overlaysList);
 
5142                 this._layerControlInputs = [];
 
5143                 var baseLayersPresent, overlaysPresent, i, obj, baseLayersCount = 0;
 
5145                 for (i = 0; i < this._layers.length; i++) {
 
5146                         obj = this._layers[i];
 
5148                         overlaysPresent = overlaysPresent || obj.overlay;
 
5149                         baseLayersPresent = baseLayersPresent || !obj.overlay;
 
5150                         baseLayersCount += !obj.overlay ? 1 : 0;
 
5153                 // Hide base layers section if there's only one layer.
 
5154                 if (this.options.hideSingleBase) {
 
5155                         baseLayersPresent = baseLayersPresent && baseLayersCount > 1;
 
5156                         this._baseLayersList.style.display = baseLayersPresent ? '' : 'none';
 
5159                 this._separator.style.display = overlaysPresent && baseLayersPresent ? '' : 'none';
 
5164         _onLayerChange: function (e) {
 
5165                 if (!this._handlingClick) {
 
5169                 var obj = this._getLayer(stamp(e.target));
 
5172                 // @section Layer events
 
5173                 // @event baselayerchange: LayersControlEvent
 
5174                 // Fired when the base layer is changed through the [layer control](#control-layers).
 
5175                 // @event overlayadd: LayersControlEvent
 
5176                 // Fired when an overlay is selected through the [layer control](#control-layers).
 
5177                 // @event overlayremove: LayersControlEvent
 
5178                 // Fired when an overlay is deselected through the [layer control](#control-layers).
 
5179                 // @namespace Control.Layers
 
5180                 var type = obj.overlay ?
 
5181                         (e.type === 'add' ? 'overlayadd' : 'overlayremove') :
 
5182                         (e.type === 'add' ? 'baselayerchange' : null);
 
5185                         this._map.fire(type, obj);
 
5189         // IE7 bugs out if you create a radio dynamically, so you have to do it this hacky way (see http://bit.ly/PqYLBe)
 
5190         _createRadioElement: function (name, checked) {
 
5192                 var radioHtml = '<input type="radio" class="leaflet-control-layers-selector" name="' +
 
5193                                 name + '"' + (checked ? ' checked="checked"' : '') + '/>';
 
5195                 var radioFragment = document.createElement('div');
 
5196                 radioFragment.innerHTML = radioHtml;
 
5198                 return radioFragment.firstChild;
 
5201         _addItem: function (obj) {
 
5202                 var label = document.createElement('label'),
 
5203                     checked = this._map.hasLayer(obj.layer),
 
5207                         input = document.createElement('input');
 
5208                         input.type = 'checkbox';
 
5209                         input.className = 'leaflet-control-layers-selector';
 
5210                         input.defaultChecked = checked;
 
5212                         input = this._createRadioElement('leaflet-base-layers_' + stamp(this), checked);
 
5215                 this._layerControlInputs.push(input);
 
5216                 input.layerId = stamp(obj.layer);
 
5218                 on(input, 'click', this._onInputClick, this);
 
5220                 var name = document.createElement('span');
 
5221                 name.innerHTML = ' ' + obj.name;
 
5223                 // Helps from preventing layer control flicker when checkboxes are disabled
 
5224                 // https://github.com/Leaflet/Leaflet/issues/2771
 
5225                 var holder = document.createElement('div');
 
5227                 label.appendChild(holder);
 
5228                 holder.appendChild(input);
 
5229                 holder.appendChild(name);
 
5231                 var container = obj.overlay ? this._overlaysList : this._baseLayersList;
 
5232                 container.appendChild(label);
 
5234                 this._checkDisabledLayers();
 
5238         _onInputClick: function () {
 
5239                 var inputs = this._layerControlInputs,
 
5241                 var addedLayers = [],
 
5244                 this._handlingClick = true;
 
5246                 for (var i = inputs.length - 1; i >= 0; i--) {
 
5248                         layer = this._getLayer(input.layerId).layer;
 
5250                         if (input.checked) {
 
5251                                 addedLayers.push(layer);
 
5252                         } else if (!input.checked) {
 
5253                                 removedLayers.push(layer);
 
5257                 // Bugfix issue 2318: Should remove all old layers before readding new ones
 
5258                 for (i = 0; i < removedLayers.length; i++) {
 
5259                         if (this._map.hasLayer(removedLayers[i])) {
 
5260                                 this._map.removeLayer(removedLayers[i]);
 
5263                 for (i = 0; i < addedLayers.length; i++) {
 
5264                         if (!this._map.hasLayer(addedLayers[i])) {
 
5265                                 this._map.addLayer(addedLayers[i]);
 
5269                 this._handlingClick = false;
 
5271                 this._refocusOnMap();
 
5274         _checkDisabledLayers: function () {
 
5275                 var inputs = this._layerControlInputs,
 
5278                     zoom = this._map.getZoom();
 
5280                 for (var i = inputs.length - 1; i >= 0; i--) {
 
5282                         layer = this._getLayer(input.layerId).layer;
 
5283                         input.disabled = (layer.options.minZoom !== undefined && zoom < layer.options.minZoom) ||
 
5284                                          (layer.options.maxZoom !== undefined && zoom > layer.options.maxZoom);
 
5289         _expandIfNotCollapsed: function () {
 
5290                 if (this._map && !this.options.collapsed) {
 
5296         _expand: function () {
 
5297                 // Backward compatibility, remove me in 1.1.
 
5298                 return this.expand();
 
5301         _collapse: function () {
 
5302                 // Backward compatibility, remove me in 1.1.
 
5303                 return this.collapse();
 
5309 // @factory L.control.layers(baselayers?: Object, overlays?: Object, options?: Control.Layers options)
 
5310 // Creates an attribution 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.
 
5311 var layers = function (baseLayers, overlays, options) {
 
5312         return new Layers(baseLayers, overlays, options);
 
5316  * @class Control.Zoom
 
5317  * @aka L.Control.Zoom
 
5320  * 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`.
 
5323 var Zoom = Control.extend({
 
5325         // @aka Control.Zoom options
 
5327                 position: 'topleft',
 
5329                 // @option zoomInText: String = '+'
 
5330                 // The text set on the 'zoom in' button.
 
5333                 // @option zoomInTitle: String = 'Zoom in'
 
5334                 // The title set on the 'zoom in' button.
 
5335                 zoomInTitle: 'Zoom in',
 
5337                 // @option zoomOutText: String = '−'
 
5338                 // The text set on the 'zoom out' button.
 
5339                 zoomOutText: '−',
 
5341                 // @option zoomOutTitle: String = 'Zoom out'
 
5342                 // The title set on the 'zoom out' button.
 
5343                 zoomOutTitle: 'Zoom out'
 
5346         onAdd: function (map) {
 
5347                 var zoomName = 'leaflet-control-zoom',
 
5348                     container = create$1('div', zoomName + ' leaflet-bar'),
 
5349                     options = this.options;
 
5351                 this._zoomInButton  = this._createButton(options.zoomInText, options.zoomInTitle,
 
5352                         zoomName + '-in',  container, this._zoomIn);
 
5353                 this._zoomOutButton = this._createButton(options.zoomOutText, options.zoomOutTitle,
 
5354                         zoomName + '-out', container, this._zoomOut);
 
5356                 this._updateDisabled();
 
5357                 map.on('zoomend zoomlevelschange', this._updateDisabled, this);
 
5362         onRemove: function (map) {
 
5363                 map.off('zoomend zoomlevelschange', this._updateDisabled, this);
 
5366         disable: function () {
 
5367                 this._disabled = true;
 
5368                 this._updateDisabled();
 
5372         enable: function () {
 
5373                 this._disabled = false;
 
5374                 this._updateDisabled();
 
5378         _zoomIn: function (e) {
 
5379                 if (!this._disabled && this._map._zoom < this._map.getMaxZoom()) {
 
5380                         this._map.zoomIn(this._map.options.zoomDelta * (e.shiftKey ? 3 : 1));
 
5384         _zoomOut: function (e) {
 
5385                 if (!this._disabled && this._map._zoom > this._map.getMinZoom()) {
 
5386                         this._map.zoomOut(this._map.options.zoomDelta * (e.shiftKey ? 3 : 1));
 
5390         _createButton: function (html, title, className, container, fn) {
 
5391                 var link = create$1('a', className, container);
 
5392                 link.innerHTML = html;
 
5397                  * Will force screen readers like VoiceOver to read this as "Zoom in - button"
 
5399                 link.setAttribute('role', 'button');
 
5400                 link.setAttribute('aria-label', title);
 
5402                 disableClickPropagation(link);
 
5403                 on(link, 'click', stop);
 
5404                 on(link, 'click', fn, this);
 
5405                 on(link, 'click', this._refocusOnMap, this);
 
5410         _updateDisabled: function () {
 
5411                 var map = this._map,
 
5412                     className = 'leaflet-disabled';
 
5414                 removeClass(this._zoomInButton, className);
 
5415                 removeClass(this._zoomOutButton, className);
 
5417                 if (this._disabled || map._zoom === map.getMinZoom()) {
 
5418                         addClass(this._zoomOutButton, className);
 
5420                 if (this._disabled || map._zoom === map.getMaxZoom()) {
 
5421                         addClass(this._zoomInButton, className);
 
5427 // @section Control options
 
5428 // @option zoomControl: Boolean = true
 
5429 // Whether a [zoom control](#control-zoom) is added to the map by default.
 
5434 Map.addInitHook(function () {
 
5435         if (this.options.zoomControl) {
 
5436                 // @section Controls
 
5437                 // @property zoomControl: Control.Zoom
 
5438                 // The default zoom control (only available if the
 
5439                 // [`zoomControl` option](#map-zoomcontrol) was `true` when creating the map).
 
5440                 this.zoomControl = new Zoom();
 
5441                 this.addControl(this.zoomControl);
 
5445 // @namespace Control.Zoom
 
5446 // @factory L.control.zoom(options: Control.Zoom options)
 
5447 // Creates a zoom control
 
5448 var zoom = function (options) {
 
5449         return new Zoom(options);
 
5453  * @class Control.Scale
 
5454  * @aka L.Control.Scale
 
5457  * 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`.
 
5462  * L.control.scale().addTo(map);
 
5466 var Scale = Control.extend({
 
5468         // @aka Control.Scale options
 
5470                 position: 'bottomleft',
 
5472                 // @option maxWidth: Number = 100
 
5473                 // Maximum width of the control in pixels. The width is set dynamically to show round values (e.g. 100, 200, 500).
 
5476                 // @option metric: Boolean = True
 
5477                 // Whether to show the metric scale line (m/km).
 
5480                 // @option imperial: Boolean = True
 
5481                 // Whether to show the imperial scale line (mi/ft).
 
5484                 // @option updateWhenIdle: Boolean = false
 
5485                 // If `true`, the control is updated on [`moveend`](#map-moveend), otherwise it's always up-to-date (updated on [`move`](#map-move)).
 
5488         onAdd: function (map) {
 
5489                 var className = 'leaflet-control-scale',
 
5490                     container = create$1('div', className),
 
5491                     options = this.options;
 
5493                 this._addScales(options, className + '-line', container);
 
5495                 map.on(options.updateWhenIdle ? 'moveend' : 'move', this._update, this);
 
5496                 map.whenReady(this._update, this);
 
5501         onRemove: function (map) {
 
5502                 map.off(this.options.updateWhenIdle ? 'moveend' : 'move', this._update, this);
 
5505         _addScales: function (options, className, container) {
 
5506                 if (options.metric) {
 
5507                         this._mScale = create$1('div', className, container);
 
5509                 if (options.imperial) {
 
5510                         this._iScale = create$1('div', className, container);
 
5514         _update: function () {
 
5515                 var map = this._map,
 
5516                     y = map.getSize().y / 2;
 
5518                 var maxMeters = map.distance(
 
5519                         map.containerPointToLatLng([0, y]),
 
5520                         map.containerPointToLatLng([this.options.maxWidth, y]));
 
5522                 this._updateScales(maxMeters);
 
5525         _updateScales: function (maxMeters) {
 
5526                 if (this.options.metric && maxMeters) {
 
5527                         this._updateMetric(maxMeters);
 
5529                 if (this.options.imperial && maxMeters) {
 
5530                         this._updateImperial(maxMeters);
 
5534         _updateMetric: function (maxMeters) {
 
5535                 var meters = this._getRoundNum(maxMeters),
 
5536                     label = meters < 1000 ? meters + ' m' : (meters / 1000) + ' km';
 
5538                 this._updateScale(this._mScale, label, meters / maxMeters);
 
5541         _updateImperial: function (maxMeters) {
 
5542                 var maxFeet = maxMeters * 3.2808399,
 
5543                     maxMiles, miles, feet;
 
5545                 if (maxFeet > 5280) {
 
5546                         maxMiles = maxFeet / 5280;
 
5547                         miles = this._getRoundNum(maxMiles);
 
5548                         this._updateScale(this._iScale, miles + ' mi', miles / maxMiles);
 
5551                         feet = this._getRoundNum(maxFeet);
 
5552                         this._updateScale(this._iScale, feet + ' ft', feet / maxFeet);
 
5556         _updateScale: function (scale, text, ratio) {
 
5557                 scale.style.width = Math.round(this.options.maxWidth * ratio) + 'px';
 
5558                 scale.innerHTML = text;
 
5561         _getRoundNum: function (num) {
 
5562                 var pow10 = Math.pow(10, (Math.floor(num) + '').length - 1),
 
5575 // @factory L.control.scale(options?: Control.Scale options)
 
5576 // Creates an scale control with the given options.
 
5577 var scale = function (options) {
 
5578         return new Scale(options);
 
5582  * @class Control.Attribution
 
5583  * @aka L.Control.Attribution
 
5586  * 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.
 
5589 var Attribution = Control.extend({
 
5591         // @aka Control.Attribution options
 
5593                 position: 'bottomright',
 
5595                 // @option prefix: String = 'Leaflet'
 
5596                 // The HTML text shown before the attributions. Pass `false` to disable.
 
5597                 prefix: '<a href="https://leafletjs.com" title="A JS library for interactive maps">Leaflet</a>'
 
5600         initialize: function (options) {
 
5601                 setOptions(this, options);
 
5603                 this._attributions = {};
 
5606         onAdd: function (map) {
 
5607                 map.attributionControl = this;
 
5608                 this._container = create$1('div', 'leaflet-control-attribution');
 
5609                 disableClickPropagation(this._container);
 
5611                 // TODO ugly, refactor
 
5612                 for (var i in map._layers) {
 
5613                         if (map._layers[i].getAttribution) {
 
5614                                 this.addAttribution(map._layers[i].getAttribution());
 
5620                 return this._container;
 
5623         // @method setPrefix(prefix: String): this
 
5624         // Sets the text before the attributions.
 
5625         setPrefix: function (prefix) {
 
5626                 this.options.prefix = prefix;
 
5631         // @method addAttribution(text: String): this
 
5632         // Adds an attribution text (e.g. `'Vector data © Mapbox'`).
 
5633         addAttribution: function (text) {
 
5634                 if (!text) { return this; }
 
5636                 if (!this._attributions[text]) {
 
5637                         this._attributions[text] = 0;
 
5639                 this._attributions[text]++;
 
5646         // @method removeAttribution(text: String): this
 
5647         // Removes an attribution text.
 
5648         removeAttribution: function (text) {
 
5649                 if (!text) { return this; }
 
5651                 if (this._attributions[text]) {
 
5652                         this._attributions[text]--;
 
5659         _update: function () {
 
5660                 if (!this._map) { return; }
 
5664                 for (var i in this._attributions) {
 
5665                         if (this._attributions[i]) {
 
5670                 var prefixAndAttribs = [];
 
5672                 if (this.options.prefix) {
 
5673                         prefixAndAttribs.push(this.options.prefix);
 
5675                 if (attribs.length) {
 
5676                         prefixAndAttribs.push(attribs.join(', '));
 
5679                 this._container.innerHTML = prefixAndAttribs.join(' | ');
 
5684 // @section Control options
 
5685 // @option attributionControl: Boolean = true
 
5686 // Whether a [attribution control](#control-attribution) is added to the map by default.
 
5688         attributionControl: true
 
5691 Map.addInitHook(function () {
 
5692         if (this.options.attributionControl) {
 
5693                 new Attribution().addTo(this);
 
5697 // @namespace Control.Attribution
 
5698 // @factory L.control.attribution(options: Control.Attribution options)
 
5699 // Creates an attribution control.
 
5700 var attribution = function (options) {
 
5701         return new Attribution(options);
 
5704 Control.Layers = Layers;
 
5705 Control.Zoom = Zoom;
 
5706 Control.Scale = Scale;
 
5707 Control.Attribution = Attribution;
 
5709 control.layers = layers;
 
5710 control.zoom = zoom;
 
5711 control.scale = scale;
 
5712 control.attribution = attribution;
 
5715         L.Handler is a base class for handler classes that are used internally to inject
 
5716         interaction features like dragging to classes like Map and Marker.
 
5721 // Abstract class for map interaction handlers
 
5723 var Handler = Class.extend({
 
5724         initialize: function (map) {
 
5728         // @method enable(): this
 
5729         // Enables the handler
 
5730         enable: function () {
 
5731                 if (this._enabled) { return this; }
 
5733                 this._enabled = true;
 
5738         // @method disable(): this
 
5739         // Disables the handler
 
5740         disable: function () {
 
5741                 if (!this._enabled) { return this; }
 
5743                 this._enabled = false;
 
5748         // @method enabled(): Boolean
 
5749         // Returns `true` if the handler is enabled
 
5750         enabled: function () {
 
5751                 return !!this._enabled;
 
5754         // @section Extension methods
 
5755         // Classes inheriting from `Handler` must implement the two following methods:
 
5756         // @method addHooks()
 
5757         // Called when the handler is enabled, should add event hooks.
 
5758         // @method removeHooks()
 
5759         // Called when the handler is disabled, should remove the event hooks added previously.
 
5762 // @section There is static function which can be called without instantiating L.Handler:
 
5763 // @function addTo(map: Map, name: String): this
 
5764 // Adds a new Handler to the given map with the given name.
 
5765 Handler.addTo = function (map, name) {
 
5766         map.addHandler(name, this);
 
5770 var Mixin = {Events: Events};
 
5777  * A class for making DOM elements draggable (including touch support).
 
5778  * Used internally for map and marker dragging. Only works for elements
 
5779  * that were positioned with [`L.DomUtil.setPosition`](#domutil-setposition).
 
5783  * var draggable = new L.Draggable(elementToDrag);
 
5784  * draggable.enable();
 
5788 var START = touch ? 'touchstart mousedown' : 'mousedown';
 
5790         mousedown: 'mouseup',
 
5791         touchstart: 'touchend',
 
5792         pointerdown: 'touchend',
 
5793         MSPointerDown: 'touchend'
 
5796         mousedown: 'mousemove',
 
5797         touchstart: 'touchmove',
 
5798         pointerdown: 'touchmove',
 
5799         MSPointerDown: 'touchmove'
 
5803 var Draggable = Evented.extend({
 
5807                 // @aka Draggable options
 
5808                 // @option clickTolerance: Number = 3
 
5809                 // The max number of pixels a user can shift the mouse pointer during a click
 
5810                 // for it to be considered a valid click (as opposed to a mouse drag).
 
5814         // @constructor L.Draggable(el: HTMLElement, dragHandle?: HTMLElement, preventOutline?: Boolean, options?: Draggable options)
 
5815         // Creates a `Draggable` object for moving `el` when you start dragging the `dragHandle` element (equals `el` itself by default).
 
5816         initialize: function (element, dragStartTarget, preventOutline$$1, options) {
 
5817                 setOptions(this, options);
 
5819                 this._element = element;
 
5820                 this._dragStartTarget = dragStartTarget || element;
 
5821                 this._preventOutline = preventOutline$$1;
 
5825         // Enables the dragging ability
 
5826         enable: function () {
 
5827                 if (this._enabled) { return; }
 
5829                 on(this._dragStartTarget, START, this._onDown, this);
 
5831                 this._enabled = true;
 
5834         // @method disable()
 
5835         // Disables the dragging ability
 
5836         disable: function () {
 
5837                 if (!this._enabled) { return; }
 
5839                 // If we're currently dragging this draggable,
 
5840                 // disabling it counts as first ending the drag.
 
5841                 if (Draggable._dragging === this) {
 
5845                 off(this._dragStartTarget, START, this._onDown, this);
 
5847                 this._enabled = false;
 
5848                 this._moved = false;
 
5851         _onDown: function (e) {
 
5852                 // Ignore simulated events, since we handle both touch and
 
5853                 // mouse explicitly; otherwise we risk getting duplicates of
 
5854                 // touch events, see #4315.
 
5855                 // Also ignore the event if disabled; this happens in IE11
 
5856                 // under some circumstances, see #3666.
 
5857                 if (e._simulated || !this._enabled) { return; }
 
5859                 this._moved = false;
 
5861                 if (hasClass(this._element, 'leaflet-zoom-anim')) { return; }
 
5863                 if (Draggable._dragging || e.shiftKey || ((e.which !== 1) && (e.button !== 1) && !e.touches)) { return; }
 
5864                 Draggable._dragging = this;  // Prevent dragging multiple objects at once.
 
5866                 if (this._preventOutline) {
 
5867                         preventOutline(this._element);
 
5871                 disableTextSelection();
 
5873                 if (this._moving) { return; }
 
5875                 // @event down: Event
 
5876                 // Fired when a drag is about to start.
 
5879                 var first = e.touches ? e.touches[0] : e,
 
5880                     sizedParent = getSizedParentNode(this._element);
 
5882                 this._startPoint = new Point(first.clientX, first.clientY);
 
5884                 // Cache the scale, so that we can continuously compensate for it during drag (_onMove).
 
5885                 this._parentScale = getScale(sizedParent);
 
5887                 on(document, MOVE[e.type], this._onMove, this);
 
5888                 on(document, END[e.type], this._onUp, this);
 
5891         _onMove: function (e) {
 
5892                 // Ignore simulated events, since we handle both touch and
 
5893                 // mouse explicitly; otherwise we risk getting duplicates of
 
5894                 // touch events, see #4315.
 
5895                 // Also ignore the event if disabled; this happens in IE11
 
5896                 // under some circumstances, see #3666.
 
5897                 if (e._simulated || !this._enabled) { return; }
 
5899                 if (e.touches && e.touches.length > 1) {
 
5904                 var first = (e.touches && e.touches.length === 1 ? e.touches[0] : e),
 
5905                     offset = new Point(first.clientX, first.clientY)._subtract(this._startPoint);
 
5907                 if (!offset.x && !offset.y) { return; }
 
5908                 if (Math.abs(offset.x) + Math.abs(offset.y) < this.options.clickTolerance) { return; }
 
5910                 // We assume that the parent container's position, border and scale do not change for the duration of the drag.
 
5911                 // Therefore there is no need to account for the position and border (they are eliminated by the subtraction)
 
5912                 // and we can use the cached value for the scale.
 
5913                 offset.x /= this._parentScale.x;
 
5914                 offset.y /= this._parentScale.y;
 
5919                         // @event dragstart: Event
 
5920                         // Fired when a drag starts
 
5921                         this.fire('dragstart');
 
5924                         this._startPos = getPosition(this._element).subtract(offset);
 
5926                         addClass(document.body, 'leaflet-dragging');
 
5928                         this._lastTarget = e.target || e.srcElement;
 
5929                         // IE and Edge do not give the <use> element, so fetch it
 
5931                         if ((window.SVGElementInstance) && (this._lastTarget instanceof SVGElementInstance)) {
 
5932                                 this._lastTarget = this._lastTarget.correspondingUseElement;
 
5934                         addClass(this._lastTarget, 'leaflet-drag-target');
 
5937                 this._newPos = this._startPos.add(offset);
 
5938                 this._moving = true;
 
5940                 cancelAnimFrame(this._animRequest);
 
5941                 this._lastEvent = e;
 
5942                 this._animRequest = requestAnimFrame(this._updatePosition, this, true);
 
5945         _updatePosition: function () {
 
5946                 var e = {originalEvent: this._lastEvent};
 
5948                 // @event predrag: Event
 
5949                 // Fired continuously during dragging *before* each corresponding
 
5950                 // update of the element's position.
 
5951                 this.fire('predrag', e);
 
5952                 setPosition(this._element, this._newPos);
 
5954                 // @event drag: Event
 
5955                 // Fired continuously during dragging.
 
5956                 this.fire('drag', e);
 
5959         _onUp: function (e) {
 
5960                 // Ignore simulated events, since we handle both touch and
 
5961                 // mouse explicitly; otherwise we risk getting duplicates of
 
5962                 // touch events, see #4315.
 
5963                 // Also ignore the event if disabled; this happens in IE11
 
5964                 // under some circumstances, see #3666.
 
5965                 if (e._simulated || !this._enabled) { return; }
 
5969         finishDrag: function () {
 
5970                 removeClass(document.body, 'leaflet-dragging');
 
5972                 if (this._lastTarget) {
 
5973                         removeClass(this._lastTarget, 'leaflet-drag-target');
 
5974                         this._lastTarget = null;
 
5977                 for (var i in MOVE) {
 
5978                         off(document, MOVE[i], this._onMove, this);
 
5979                         off(document, END[i], this._onUp, this);
 
5983                 enableTextSelection();
 
5985                 if (this._moved && this._moving) {
 
5986                         // ensure drag is not fired after dragend
 
5987                         cancelAnimFrame(this._animRequest);
 
5989                         // @event dragend: DragEndEvent
 
5990                         // Fired when the drag ends.
 
5991                         this.fire('dragend', {
 
5992                                 distance: this._newPos.distanceTo(this._startPos)
 
5996                 this._moving = false;
 
5997                 Draggable._dragging = false;
 
6003  * @namespace LineUtil
 
6005  * Various utility functions for polyline points processing, used by Leaflet internally to make polylines lightning-fast.
 
6008 // Simplify polyline with vertex reduction and Douglas-Peucker simplification.
 
6009 // Improves rendering performance dramatically by lessening the number of points to draw.
 
6011 // @function simplify(points: Point[], tolerance: Number): Point[]
 
6012 // Dramatically reduces the number of points in a polyline while retaining
 
6013 // its shape and returns a new array of simplified points, using the
 
6014 // [Douglas-Peucker algorithm](http://en.wikipedia.org/wiki/Douglas-Peucker_algorithm).
 
6015 // Used for a huge performance boost when processing/displaying Leaflet polylines for
 
6016 // each zoom level and also reducing visual noise. tolerance affects the amount of
 
6017 // simplification (lesser value means higher quality but slower and with more points).
 
6018 // Also released as a separated micro-library [Simplify.js](http://mourner.github.com/simplify-js/).
 
6019 function simplify(points, tolerance) {
 
6020         if (!tolerance || !points.length) {
 
6021                 return points.slice();
 
6024         var sqTolerance = tolerance * tolerance;
 
6026             // stage 1: vertex reduction
 
6027             points = _reducePoints(points, sqTolerance);
 
6029             // stage 2: Douglas-Peucker simplification
 
6030             points = _simplifyDP(points, sqTolerance);
 
6035 // @function pointToSegmentDistance(p: Point, p1: Point, p2: Point): Number
 
6036 // Returns the distance between point `p` and segment `p1` to `p2`.
 
6037 function pointToSegmentDistance(p, p1, p2) {
 
6038         return Math.sqrt(_sqClosestPointOnSegment(p, p1, p2, true));
 
6041 // @function closestPointOnSegment(p: Point, p1: Point, p2: Point): Number
 
6042 // Returns the closest point from a point `p` on a segment `p1` to `p2`.
 
6043 function closestPointOnSegment(p, p1, p2) {
 
6044         return _sqClosestPointOnSegment(p, p1, p2);
 
6047 // Douglas-Peucker simplification, see http://en.wikipedia.org/wiki/Douglas-Peucker_algorithm
 
6048 function _simplifyDP(points, sqTolerance) {
 
6050         var len = points.length,
 
6051             ArrayConstructor = typeof Uint8Array !== undefined + '' ? Uint8Array : Array,
 
6052             markers = new ArrayConstructor(len);
 
6054             markers[0] = markers[len - 1] = 1;
 
6056         _simplifyDPStep(points, markers, sqTolerance, 0, len - 1);
 
6061         for (i = 0; i < len; i++) {
 
6063                         newPoints.push(points[i]);
 
6070 function _simplifyDPStep(points, markers, sqTolerance, first, last) {
 
6075         for (i = first + 1; i <= last - 1; i++) {
 
6076                 sqDist = _sqClosestPointOnSegment(points[i], points[first], points[last], true);
 
6078                 if (sqDist > maxSqDist) {
 
6084         if (maxSqDist > sqTolerance) {
 
6087                 _simplifyDPStep(points, markers, sqTolerance, first, index);
 
6088                 _simplifyDPStep(points, markers, sqTolerance, index, last);
 
6092 // reduce points that are too close to each other to a single point
 
6093 function _reducePoints(points, sqTolerance) {
 
6094         var reducedPoints = [points[0]];
 
6096         for (var i = 1, prev = 0, len = points.length; i < len; i++) {
 
6097                 if (_sqDist(points[i], points[prev]) > sqTolerance) {
 
6098                         reducedPoints.push(points[i]);
 
6102         if (prev < len - 1) {
 
6103                 reducedPoints.push(points[len - 1]);
 
6105         return reducedPoints;
 
6110 // @function clipSegment(a: Point, b: Point, bounds: Bounds, useLastCode?: Boolean, round?: Boolean): Point[]|Boolean
 
6111 // Clips the segment a to b by rectangular bounds with the
 
6112 // [Cohen-Sutherland algorithm](https://en.wikipedia.org/wiki/Cohen%E2%80%93Sutherland_algorithm)
 
6113 // (modifying the segment points directly!). Used by Leaflet to only show polyline
 
6114 // points that are on the screen or near, increasing performance.
 
6115 function clipSegment(a, b, bounds, useLastCode, round) {
 
6116         var codeA = useLastCode ? _lastCode : _getBitCode(a, bounds),
 
6117             codeB = _getBitCode(b, bounds),
 
6119             codeOut, p, newCode;
 
6121             // save 2nd code to avoid calculating it on the next segment
 
6125                 // if a,b is inside the clip window (trivial accept)
 
6126                 if (!(codeA | codeB)) {
 
6130                 // if a,b is outside the clip window (trivial reject)
 
6131                 if (codeA & codeB) {
 
6136                 codeOut = codeA || codeB;
 
6137                 p = _getEdgeIntersection(a, b, codeOut, bounds, round);
 
6138                 newCode = _getBitCode(p, bounds);
 
6140                 if (codeOut === codeA) {
 
6150 function _getEdgeIntersection(a, b, code, bounds, round) {
 
6157         if (code & 8) { // top
 
6158                 x = a.x + dx * (max.y - a.y) / dy;
 
6161         } else if (code & 4) { // bottom
 
6162                 x = a.x + dx * (min.y - a.y) / dy;
 
6165         } else if (code & 2) { // right
 
6167                 y = a.y + dy * (max.x - a.x) / dx;
 
6169         } else if (code & 1) { // left
 
6171                 y = a.y + dy * (min.x - a.x) / dx;
 
6174         return new Point(x, y, round);
 
6177 function _getBitCode(p, bounds) {
 
6180         if (p.x < bounds.min.x) { // left
 
6182         } else if (p.x > bounds.max.x) { // right
 
6186         if (p.y < bounds.min.y) { // bottom
 
6188         } else if (p.y > bounds.max.y) { // top
 
6195 // square distance (to avoid unnecessary Math.sqrt calls)
 
6196 function _sqDist(p1, p2) {
 
6197         var dx = p2.x - p1.x,
 
6199         return dx * dx + dy * dy;
 
6202 // return closest point on segment or distance to that point
 
6203 function _sqClosestPointOnSegment(p, p1, p2, sqDist) {
 
6208             dot = dx * dx + dy * dy,
 
6212                 t = ((p.x - x) * dx + (p.y - y) * dy) / dot;
 
6226         return sqDist ? dx * dx + dy * dy : new Point(x, y);
 
6230 // @function isFlat(latlngs: LatLng[]): Boolean
 
6231 // Returns true if `latlngs` is a flat array, false is nested.
 
6232 function isFlat(latlngs) {
 
6233         return !isArray(latlngs[0]) || (typeof latlngs[0][0] !== 'object' && typeof latlngs[0][0] !== 'undefined');
 
6236 function _flat(latlngs) {
 
6237         console.warn('Deprecated use of _flat, please use L.LineUtil.isFlat instead.');
 
6238         return isFlat(latlngs);
 
6242 var LineUtil = (Object.freeze || Object)({
 
6244         pointToSegmentDistance: pointToSegmentDistance,
 
6245         closestPointOnSegment: closestPointOnSegment,
 
6246         clipSegment: clipSegment,
 
6247         _getEdgeIntersection: _getEdgeIntersection,
 
6248         _getBitCode: _getBitCode,
 
6249         _sqClosestPointOnSegment: _sqClosestPointOnSegment,
 
6255  * @namespace PolyUtil
 
6256  * Various utility functions for polygon geometries.
 
6259 /* @function clipPolygon(points: Point[], bounds: Bounds, round?: Boolean): Point[]
 
6260  * 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)).
 
6261  * Used by Leaflet to only show polygon points that are on the screen or near, increasing
 
6262  * performance. Note that polygon points needs different algorithm for clipping
 
6263  * than polyline, so there's a separate method for it.
 
6265 function clipPolygon(points, bounds, round) {
 
6267             edges = [1, 4, 2, 8],
 
6272         for (i = 0, len = points.length; i < len; i++) {
 
6273                 points[i]._code = _getBitCode(points[i], bounds);
 
6276         // for each edge (left, bottom, right, top)
 
6277         for (k = 0; k < 4; k++) {
 
6281                 for (i = 0, len = points.length, j = len - 1; i < len; j = i++) {
 
6285                         // if a is inside the clip window
 
6286                         if (!(a._code & edge)) {
 
6287                                 // if b is outside the clip window (a->b goes out of screen)
 
6288                                 if (b._code & edge) {
 
6289                                         p = _getEdgeIntersection(b, a, edge, bounds, round);
 
6290                                         p._code = _getBitCode(p, bounds);
 
6291                                         clippedPoints.push(p);
 
6293                                 clippedPoints.push(a);
 
6295                         // else if b is inside the clip window (a->b enters the screen)
 
6296                         } else if (!(b._code & edge)) {
 
6297                                 p = _getEdgeIntersection(b, a, edge, bounds, round);
 
6298                                 p._code = _getBitCode(p, bounds);
 
6299                                 clippedPoints.push(p);
 
6302                 points = clippedPoints;
 
6309 var PolyUtil = (Object.freeze || Object)({
 
6310         clipPolygon: clipPolygon
 
6314  * @namespace Projection
 
6316  * Leaflet comes with a set of already defined Projections out of the box:
 
6318  * @projection L.Projection.LonLat
 
6320  * Equirectangular, or Plate Carree projection — the most simple projection,
 
6321  * mostly used by GIS enthusiasts. Directly maps `x` as longitude, and `y` as
 
6322  * latitude. Also suitable for flat worlds, e.g. game maps. Used by the
 
6323  * `EPSG:4326` and `Simple` CRS.
 
6327         project: function (latlng) {
 
6328                 return new Point(latlng.lng, latlng.lat);
 
6331         unproject: function (point) {
 
6332                 return new LatLng(point.y, point.x);
 
6335         bounds: new Bounds([-180, -90], [180, 90])
 
6339  * @namespace Projection
 
6340  * @projection L.Projection.Mercator
 
6342  * Elliptical Mercator projection — more complex than Spherical Mercator. Assumes that Earth is an ellipsoid. Used by the EPSG:3395 CRS.
 
6347         R_MINOR: 6356752.314245179,
 
6349         bounds: new Bounds([-20037508.34279, -15496570.73972], [20037508.34279, 18764656.23138]),
 
6351         project: function (latlng) {
 
6352                 var d = Math.PI / 180,
 
6355                     tmp = this.R_MINOR / r,
 
6356                     e = Math.sqrt(1 - tmp * tmp),
 
6357                     con = e * Math.sin(y);
 
6359                 var ts = Math.tan(Math.PI / 4 - y / 2) / Math.pow((1 - con) / (1 + con), e / 2);
 
6360                 y = -r * Math.log(Math.max(ts, 1E-10));
 
6362                 return new Point(latlng.lng * d * r, y);
 
6365         unproject: function (point) {
 
6366                 var d = 180 / Math.PI,
 
6368                     tmp = this.R_MINOR / r,
 
6369                     e = Math.sqrt(1 - tmp * tmp),
 
6370                     ts = Math.exp(-point.y / r),
 
6371                     phi = Math.PI / 2 - 2 * Math.atan(ts);
 
6373                 for (var i = 0, dphi = 0.1, con; i < 15 && Math.abs(dphi) > 1e-7; i++) {
 
6374                         con = e * Math.sin(phi);
 
6375                         con = Math.pow((1 - con) / (1 + con), e / 2);
 
6376                         dphi = Math.PI / 2 - 2 * Math.atan(ts * con) - phi;
 
6380                 return new LatLng(phi * d, point.x * d / r);
 
6387  * An object with methods for projecting geographical coordinates of the world onto
 
6388  * a flat surface (and back). See [Map projection](http://en.wikipedia.org/wiki/Map_projection).
 
6390  * @property bounds: Bounds
 
6391  * The bounds (specified in CRS units) where the projection is valid
 
6393  * @method project(latlng: LatLng): Point
 
6394  * Projects geographical coordinates into a 2D point.
 
6395  * Only accepts actual `L.LatLng` instances, not arrays.
 
6397  * @method unproject(point: Point): LatLng
 
6398  * The inverse of `project`. Projects a 2D point into a geographical location.
 
6399  * Only accepts actual `L.Point` instances, not arrays.
 
6401  * Note that the projection instances do not inherit from Leafet's `Class` object,
 
6402  * and can't be instantiated. Also, new classes can't inherit from them,
 
6403  * and methods can't be added to them with the `include` function.
 
6410 var index = (Object.freeze || Object)({
 
6413         SphericalMercator: SphericalMercator
 
6418  * @crs L.CRS.EPSG3395
 
6420  * Rarely used by some commercial tile providers. Uses Elliptical Mercator projection.
 
6422 var EPSG3395 = extend({}, Earth, {
 
6424         projection: Mercator,
 
6426         transformation: (function () {
 
6427                 var scale = 0.5 / (Math.PI * Mercator.R);
 
6428                 return toTransformation(scale, 0.5, -scale, 0.5);
 
6434  * @crs L.CRS.EPSG4326
 
6436  * A common CRS among GIS enthusiasts. Uses simple Equirectangular projection.
 
6438  * Leaflet 1.0.x complies with the [TMS coordinate scheme for EPSG:4326](https://wiki.osgeo.org/wiki/Tile_Map_Service_Specification#global-geodetic),
 
6439  * which is a breaking change from 0.7.x behaviour.  If you are using a `TileLayer`
 
6440  * with this CRS, ensure that there are two 256x256 pixel tiles covering the
 
6441  * whole earth at zoom level zero, and that the tile coordinate origin is (-180,+90),
 
6442  * or (-180,-90) for `TileLayer`s with [the `tms` option](#tilelayer-tms) set.
 
6445 var EPSG4326 = extend({}, Earth, {
 
6448         transformation: toTransformation(1 / 180, 1, -1 / 180, 0.5)
 
6455  * A simple CRS that maps longitude and latitude into `x` and `y` directly.
 
6456  * May be used for maps of flat surfaces (e.g. game maps). Note that the `y`
 
6457  * axis should still be inverted (going from bottom to top). `distance()` returns
 
6458  * simple euclidean distance.
 
6461 var Simple = extend({}, CRS, {
 
6463         transformation: toTransformation(1, 0, -1, 0),
 
6465         scale: function (zoom) {
 
6466                 return Math.pow(2, zoom);
 
6469         zoom: function (scale) {
 
6470                 return Math.log(scale) / Math.LN2;
 
6473         distance: function (latlng1, latlng2) {
 
6474                 var dx = latlng2.lng - latlng1.lng,
 
6475                     dy = latlng2.lat - latlng1.lat;
 
6477                 return Math.sqrt(dx * dx + dy * dy);
 
6484 CRS.EPSG3395 = EPSG3395;
 
6485 CRS.EPSG3857 = EPSG3857;
 
6486 CRS.EPSG900913 = EPSG900913;
 
6487 CRS.EPSG4326 = EPSG4326;
 
6488 CRS.Simple = Simple;
 
6496  * A set of methods from the Layer base class that all Leaflet layers use.
 
6497  * Inherits all methods, options and events from `L.Evented`.
 
6502  * var layer = L.marker(latlng).addTo(map);
 
6508  * Fired after the layer is added to a map
 
6510  * @event remove: Event
 
6511  * Fired after the layer is removed from a map
 
6515 var Layer = Evented.extend({
 
6517         // Classes extending `L.Layer` will inherit the following options:
 
6519                 // @option pane: String = 'overlayPane'
 
6520                 // 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.
 
6521                 pane: 'overlayPane',
 
6523                 // @option attribution: String = null
 
6524                 // 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.
 
6527                 bubblingMouseEvents: true
 
6531          * Classes extending `L.Layer` will inherit the following methods:
 
6533          * @method addTo(map: Map|LayerGroup): this
 
6534          * Adds the layer to the given map or layer group.
 
6536         addTo: function (map) {
 
6541         // @method remove: this
 
6542         // Removes the layer from the map it is currently active on.
 
6543         remove: function () {
 
6544                 return this.removeFrom(this._map || this._mapToAdd);
 
6547         // @method removeFrom(map: Map): this
 
6548         // Removes the layer from the given map
 
6549         removeFrom: function (obj) {
 
6551                         obj.removeLayer(this);
 
6556         // @method getPane(name? : String): HTMLElement
 
6557         // Returns the `HTMLElement` representing the named pane on the map. If `name` is omitted, returns the pane for this layer.
 
6558         getPane: function (name) {
 
6559                 return this._map.getPane(name ? (this.options[name] || name) : this.options.pane);
 
6562         addInteractiveTarget: function (targetEl) {
 
6563                 this._map._targets[stamp(targetEl)] = this;
 
6567         removeInteractiveTarget: function (targetEl) {
 
6568                 delete this._map._targets[stamp(targetEl)];
 
6572         // @method getAttribution: String
 
6573         // Used by the `attribution control`, returns the [attribution option](#gridlayer-attribution).
 
6574         getAttribution: function () {
 
6575                 return this.options.attribution;
 
6578         _layerAdd: function (e) {
 
6581                 // check in case layer gets added and then removed before the map is ready
 
6582                 if (!map.hasLayer(this)) { return; }
 
6585                 this._zoomAnimated = map._zoomAnimated;
 
6587                 if (this.getEvents) {
 
6588                         var events = this.getEvents();
 
6589                         map.on(events, this);
 
6590                         this.once('remove', function () {
 
6591                                 map.off(events, this);
 
6597                 if (this.getAttribution && map.attributionControl) {
 
6598                         map.attributionControl.addAttribution(this.getAttribution());
 
6602                 map.fire('layeradd', {layer: this});
 
6606 /* @section Extension methods
 
6609  * Every layer should extend from `L.Layer` and (re-)implement the following methods.
 
6611  * @method onAdd(map: Map): this
 
6612  * 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).
 
6614  * @method onRemove(map: Map): this
 
6615  * 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).
 
6617  * @method getEvents(): Object
 
6618  * 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.
 
6620  * @method getAttribution(): String
 
6621  * This optional method should return a string containing HTML to be shown on the `Attribution control` whenever the layer is visible.
 
6623  * @method beforeAdd(map: Map): this
 
6624  * 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.
 
6629  * @section Layer events
 
6631  * @event layeradd: LayerEvent
 
6632  * Fired when a new layer is added to the map.
 
6634  * @event layerremove: LayerEvent
 
6635  * Fired when some layer is removed from the map
 
6637  * @section Methods for Layers and Controls
 
6640         // @method addLayer(layer: Layer): this
 
6641         // Adds the given layer to the map
 
6642         addLayer: function (layer) {
 
6643                 if (!layer._layerAdd) {
 
6644                         throw new Error('The provided object is not a Layer.');
 
6647                 var id = stamp(layer);
 
6648                 if (this._layers[id]) { return this; }
 
6649                 this._layers[id] = layer;
 
6651                 layer._mapToAdd = this;
 
6653                 if (layer.beforeAdd) {
 
6654                         layer.beforeAdd(this);
 
6657                 this.whenReady(layer._layerAdd, layer);
 
6662         // @method removeLayer(layer: Layer): this
 
6663         // Removes the given layer from the map.
 
6664         removeLayer: function (layer) {
 
6665                 var id = stamp(layer);
 
6667                 if (!this._layers[id]) { return this; }
 
6670                         layer.onRemove(this);
 
6673                 if (layer.getAttribution && this.attributionControl) {
 
6674                         this.attributionControl.removeAttribution(layer.getAttribution());
 
6677                 delete this._layers[id];
 
6680                         this.fire('layerremove', {layer: layer});
 
6681                         layer.fire('remove');
 
6684                 layer._map = layer._mapToAdd = null;
 
6689         // @method hasLayer(layer: Layer): Boolean
 
6690         // Returns `true` if the given layer is currently added to the map
 
6691         hasLayer: function (layer) {
 
6692                 return !!layer && (stamp(layer) in this._layers);
 
6695         /* @method eachLayer(fn: Function, context?: Object): this
 
6696          * Iterates over the layers of the map, optionally specifying context of the iterator function.
 
6698          * map.eachLayer(function(layer){
 
6699          *     layer.bindPopup('Hello');
 
6703         eachLayer: function (method, context) {
 
6704                 for (var i in this._layers) {
 
6705                         method.call(context, this._layers[i]);
 
6710         _addLayers: function (layers) {
 
6711                 layers = layers ? (isArray(layers) ? layers : [layers]) : [];
 
6713                 for (var i = 0, len = layers.length; i < len; i++) {
 
6714                         this.addLayer(layers[i]);
 
6718         _addZoomLimit: function (layer) {
 
6719                 if (isNaN(layer.options.maxZoom) || !isNaN(layer.options.minZoom)) {
 
6720                         this._zoomBoundLayers[stamp(layer)] = layer;
 
6721                         this._updateZoomLevels();
 
6725         _removeZoomLimit: function (layer) {
 
6726                 var id = stamp(layer);
 
6728                 if (this._zoomBoundLayers[id]) {
 
6729                         delete this._zoomBoundLayers[id];
 
6730                         this._updateZoomLevels();
 
6734         _updateZoomLevels: function () {
 
6735                 var minZoom = Infinity,
 
6736                     maxZoom = -Infinity,
 
6737                     oldZoomSpan = this._getZoomSpan();
 
6739                 for (var i in this._zoomBoundLayers) {
 
6740                         var options = this._zoomBoundLayers[i].options;
 
6742                         minZoom = options.minZoom === undefined ? minZoom : Math.min(minZoom, options.minZoom);
 
6743                         maxZoom = options.maxZoom === undefined ? maxZoom : Math.max(maxZoom, options.maxZoom);
 
6746                 this._layersMaxZoom = maxZoom === -Infinity ? undefined : maxZoom;
 
6747                 this._layersMinZoom = minZoom === Infinity ? undefined : minZoom;
 
6749                 // @section Map state change events
 
6750                 // @event zoomlevelschange: Event
 
6751                 // Fired when the number of zoomlevels on the map is changed due
 
6752                 // to adding or removing a layer.
 
6753                 if (oldZoomSpan !== this._getZoomSpan()) {
 
6754                         this.fire('zoomlevelschange');
 
6757                 if (this.options.maxZoom === undefined && this._layersMaxZoom && this.getZoom() > this._layersMaxZoom) {
 
6758                         this.setZoom(this._layersMaxZoom);
 
6760                 if (this.options.minZoom === undefined && this._layersMinZoom && this.getZoom() < this._layersMinZoom) {
 
6761                         this.setZoom(this._layersMinZoom);
 
6771  * Used to group several layers and handle them as one. If you add it to the map,
 
6772  * any layers added or removed from the group will be added/removed on the map as
 
6773  * well. Extends `Layer`.
 
6778  * L.layerGroup([marker1, marker2])
 
6779  *      .addLayer(polyline)
 
6784 var LayerGroup = Layer.extend({
 
6786         initialize: function (layers, options) {
 
6787                 setOptions(this, options);
 
6794                         for (i = 0, len = layers.length; i < len; i++) {
 
6795                                 this.addLayer(layers[i]);
 
6800         // @method addLayer(layer: Layer): this
 
6801         // Adds the given layer to the group.
 
6802         addLayer: function (layer) {
 
6803                 var id = this.getLayerId(layer);
 
6805                 this._layers[id] = layer;
 
6808                         this._map.addLayer(layer);
 
6814         // @method removeLayer(layer: Layer): this
 
6815         // Removes the given layer from the group.
 
6817         // @method removeLayer(id: Number): this
 
6818         // Removes the layer with the given internal ID from the group.
 
6819         removeLayer: function (layer) {
 
6820                 var id = layer in this._layers ? layer : this.getLayerId(layer);
 
6822                 if (this._map && this._layers[id]) {
 
6823                         this._map.removeLayer(this._layers[id]);
 
6826                 delete this._layers[id];
 
6831         // @method hasLayer(layer: Layer): Boolean
 
6832         // Returns `true` if the given layer is currently added to the group.
 
6834         // @method hasLayer(id: Number): Boolean
 
6835         // Returns `true` if the given internal ID is currently added to the group.
 
6836         hasLayer: function (layer) {
 
6837                 return !!layer && (layer in this._layers || this.getLayerId(layer) in this._layers);
 
6840         // @method clearLayers(): this
 
6841         // Removes all the layers from the group.
 
6842         clearLayers: function () {
 
6843                 return this.eachLayer(this.removeLayer, this);
 
6846         // @method invoke(methodName: String, …): this
 
6847         // Calls `methodName` on every layer contained in this group, passing any
 
6848         // additional parameters. Has no effect if the layers contained do not
 
6849         // implement `methodName`.
 
6850         invoke: function (methodName) {
 
6851                 var args = Array.prototype.slice.call(arguments, 1),
 
6854                 for (i in this._layers) {
 
6855                         layer = this._layers[i];
 
6857                         if (layer[methodName]) {
 
6858                                 layer[methodName].apply(layer, args);
 
6865         onAdd: function (map) {
 
6866                 this.eachLayer(map.addLayer, map);
 
6869         onRemove: function (map) {
 
6870                 this.eachLayer(map.removeLayer, map);
 
6873         // @method eachLayer(fn: Function, context?: Object): this
 
6874         // Iterates over the layers of the group, optionally specifying context of the iterator function.
 
6876         // group.eachLayer(function (layer) {
 
6877         //      layer.bindPopup('Hello');
 
6880         eachLayer: function (method, context) {
 
6881                 for (var i in this._layers) {
 
6882                         method.call(context, this._layers[i]);
 
6887         // @method getLayer(id: Number): Layer
 
6888         // Returns the layer with the given internal ID.
 
6889         getLayer: function (id) {
 
6890                 return this._layers[id];
 
6893         // @method getLayers(): Layer[]
 
6894         // Returns an array of all the layers added to the group.
 
6895         getLayers: function () {
 
6897                 this.eachLayer(layers.push, layers);
 
6901         // @method setZIndex(zIndex: Number): this
 
6902         // Calls `setZIndex` on every layer contained in this group, passing the z-index.
 
6903         setZIndex: function (zIndex) {
 
6904                 return this.invoke('setZIndex', zIndex);
 
6907         // @method getLayerId(layer: Layer): Number
 
6908         // Returns the internal ID for a layer
 
6909         getLayerId: function (layer) {
 
6910                 return stamp(layer);
 
6915 // @factory L.layerGroup(layers?: Layer[], options?: Object)
 
6916 // Create a layer group, optionally given an initial set of layers and an `options` object.
 
6917 var layerGroup = function (layers, options) {
 
6918         return new LayerGroup(layers, options);
 
6922  * @class FeatureGroup
 
6923  * @aka L.FeatureGroup
 
6924  * @inherits LayerGroup
 
6926  * Extended `LayerGroup` that makes it easier to do the same thing to all its member layers:
 
6927  *  * [`bindPopup`](#layer-bindpopup) binds a popup to all of the layers at once (likewise with [`bindTooltip`](#layer-bindtooltip))
 
6928  *  * Events are propagated to the `FeatureGroup`, so if the group has an event
 
6929  * handler, it will handle events from any of the layers. This includes mouse events
 
6930  * and custom events.
 
6931  *  * Has `layeradd` and `layerremove` events
 
6936  * L.featureGroup([marker1, marker2, polyline])
 
6937  *      .bindPopup('Hello world!')
 
6938  *      .on('click', function() { alert('Clicked on a member of the group!'); })
 
6943 var FeatureGroup = LayerGroup.extend({
 
6945         addLayer: function (layer) {
 
6946                 if (this.hasLayer(layer)) {
 
6950                 layer.addEventParent(this);
 
6952                 LayerGroup.prototype.addLayer.call(this, layer);
 
6954                 // @event layeradd: LayerEvent
 
6955                 // Fired when a layer is added to this `FeatureGroup`
 
6956                 return this.fire('layeradd', {layer: layer});
 
6959         removeLayer: function (layer) {
 
6960                 if (!this.hasLayer(layer)) {
 
6963                 if (layer in this._layers) {
 
6964                         layer = this._layers[layer];
 
6967                 layer.removeEventParent(this);
 
6969                 LayerGroup.prototype.removeLayer.call(this, layer);
 
6971                 // @event layerremove: LayerEvent
 
6972                 // Fired when a layer is removed from this `FeatureGroup`
 
6973                 return this.fire('layerremove', {layer: layer});
 
6976         // @method setStyle(style: Path options): this
 
6977         // Sets the given path options to each layer of the group that has a `setStyle` method.
 
6978         setStyle: function (style) {
 
6979                 return this.invoke('setStyle', style);
 
6982         // @method bringToFront(): this
 
6983         // Brings the layer group to the top of all other layers
 
6984         bringToFront: function () {
 
6985                 return this.invoke('bringToFront');
 
6988         // @method bringToBack(): this
 
6989         // Brings the layer group to the back of all other layers
 
6990         bringToBack: function () {
 
6991                 return this.invoke('bringToBack');
 
6994         // @method getBounds(): LatLngBounds
 
6995         // Returns the LatLngBounds of the Feature Group (created from bounds and coordinates of its children).
 
6996         getBounds: function () {
 
6997                 var bounds = new LatLngBounds();
 
6999                 for (var id in this._layers) {
 
7000                         var layer = this._layers[id];
 
7001                         bounds.extend(layer.getBounds ? layer.getBounds() : layer.getLatLng());
 
7007 // @factory L.featureGroup(layers: Layer[])
 
7008 // Create a feature group, optionally given an initial set of layers.
 
7009 var featureGroup = function (layers) {
 
7010         return new FeatureGroup(layers);
 
7017  * Represents an icon to provide when creating a marker.
 
7022  * var myIcon = L.icon({
 
7023  *     iconUrl: 'my-icon.png',
 
7024  *     iconRetinaUrl: 'my-icon@2x.png',
 
7025  *     iconSize: [38, 95],
 
7026  *     iconAnchor: [22, 94],
 
7027  *     popupAnchor: [-3, -76],
 
7028  *     shadowUrl: 'my-icon-shadow.png',
 
7029  *     shadowRetinaUrl: 'my-icon-shadow@2x.png',
 
7030  *     shadowSize: [68, 95],
 
7031  *     shadowAnchor: [22, 94]
 
7034  * L.marker([50.505, 30.57], {icon: myIcon}).addTo(map);
 
7037  * `L.Icon.Default` extends `L.Icon` and is the blue icon Leaflet uses for markers by default.
 
7041 var Icon = Class.extend({
 
7046          * @option iconUrl: String = null
 
7047          * **(required)** The URL to the icon image (absolute or relative to your script path).
 
7049          * @option iconRetinaUrl: String = null
 
7050          * The URL to a retina sized version of the icon image (absolute or relative to your
 
7051          * script path). Used for Retina screen devices.
 
7053          * @option iconSize: Point = null
 
7054          * Size of the icon image in pixels.
 
7056          * @option iconAnchor: Point = null
 
7057          * The coordinates of the "tip" of the icon (relative to its top left corner). The icon
 
7058          * will be aligned so that this point is at the marker's geographical location. Centered
 
7059          * by default if size is specified, also can be set in CSS with negative margins.
 
7061          * @option popupAnchor: Point = [0, 0]
 
7062          * The coordinates of the point from which popups will "open", relative to the icon anchor.
 
7064          * @option tooltipAnchor: Point = [0, 0]
 
7065          * The coordinates of the point from which tooltips will "open", relative to the icon anchor.
 
7067          * @option shadowUrl: String = null
 
7068          * The URL to the icon shadow image. If not specified, no shadow image will be created.
 
7070          * @option shadowRetinaUrl: String = null
 
7072          * @option shadowSize: Point = null
 
7073          * Size of the shadow image in pixels.
 
7075          * @option shadowAnchor: Point = null
 
7076          * The coordinates of the "tip" of the shadow (relative to its top left corner) (the same
 
7077          * as iconAnchor if not specified).
 
7079          * @option className: String = ''
 
7080          * A custom class name to assign to both icon and shadow images. Empty by default.
 
7084                 popupAnchor: [0, 0],
 
7085                 tooltipAnchor: [0, 0]
 
7088         initialize: function (options) {
 
7089                 setOptions(this, options);
 
7092         // @method createIcon(oldIcon?: HTMLElement): HTMLElement
 
7093         // Called internally when the icon has to be shown, returns a `<img>` HTML element
 
7094         // styled according to the options.
 
7095         createIcon: function (oldIcon) {
 
7096                 return this._createIcon('icon', oldIcon);
 
7099         // @method createShadow(oldIcon?: HTMLElement): HTMLElement
 
7100         // As `createIcon`, but for the shadow beneath it.
 
7101         createShadow: function (oldIcon) {
 
7102                 return this._createIcon('shadow', oldIcon);
 
7105         _createIcon: function (name, oldIcon) {
 
7106                 var src = this._getIconUrl(name);
 
7109                         if (name === 'icon') {
 
7110                                 throw new Error('iconUrl not set in Icon options (see the docs).');
 
7115                 var img = this._createImg(src, oldIcon && oldIcon.tagName === 'IMG' ? oldIcon : null);
 
7116                 this._setIconStyles(img, name);
 
7121         _setIconStyles: function (img, name) {
 
7122                 var options = this.options;
 
7123                 var sizeOption = options[name + 'Size'];
 
7125                 if (typeof sizeOption === 'number') {
 
7126                         sizeOption = [sizeOption, sizeOption];
 
7129                 var size = toPoint(sizeOption),
 
7130                     anchor = toPoint(name === 'shadow' && options.shadowAnchor || options.iconAnchor ||
 
7131                             size && size.divideBy(2, true));
 
7133                 img.className = 'leaflet-marker-' + name + ' ' + (options.className || '');
 
7136                         img.style.marginLeft = (-anchor.x) + 'px';
 
7137                         img.style.marginTop  = (-anchor.y) + 'px';
 
7141                         img.style.width  = size.x + 'px';
 
7142                         img.style.height = size.y + 'px';
 
7146         _createImg: function (src, el) {
 
7147                 el = el || document.createElement('img');
 
7152         _getIconUrl: function (name) {
 
7153                 return retina && this.options[name + 'RetinaUrl'] || this.options[name + 'Url'];
 
7158 // @factory L.icon(options: Icon options)
 
7159 // Creates an icon instance with the given options.
 
7160 function icon(options) {
 
7161         return new Icon(options);
 
7165  * @miniclass Icon.Default (Icon)
 
7166  * @aka L.Icon.Default
 
7169  * A trivial subclass of `Icon`, represents the icon to use in `Marker`s when
 
7170  * no icon is specified. Points to the blue marker image distributed with Leaflet
 
7173  * In order to customize the default icon, just change the properties of `L.Icon.Default.prototype.options`
 
7174  * (which is a set of `Icon options`).
 
7176  * If you want to _completely_ replace the default icon, override the
 
7177  * `L.Marker.prototype.options.icon` with your own icon instead.
 
7180 var IconDefault = Icon.extend({
 
7183                 iconUrl:       'marker-icon.png',
 
7184                 iconRetinaUrl: 'marker-icon-2x.png',
 
7185                 shadowUrl:     'marker-shadow.png',
 
7187                 iconAnchor:  [12, 41],
 
7188                 popupAnchor: [1, -34],
 
7189                 tooltipAnchor: [16, -28],
 
7190                 shadowSize:  [41, 41]
 
7193         _getIconUrl: function (name) {
 
7194                 if (!IconDefault.imagePath) {   // Deprecated, backwards-compatibility only
 
7195                         IconDefault.imagePath = this._detectIconPath();
 
7198                 // @option imagePath: String
 
7199                 // `Icon.Default` will try to auto-detect the location of the
 
7200                 // blue icon images. If you are placing these images in a non-standard
 
7201                 // way, set this option to point to the right path.
 
7202                 return (this.options.imagePath || IconDefault.imagePath) + Icon.prototype._getIconUrl.call(this, name);
 
7205         _detectIconPath: function () {
 
7206                 var el = create$1('div',  'leaflet-default-icon-path', document.body);
 
7207                 var path = getStyle(el, 'background-image') ||
 
7208                            getStyle(el, 'backgroundImage');     // IE8
 
7210                 document.body.removeChild(el);
 
7212                 if (path === null || path.indexOf('url') !== 0) {
 
7215                         path = path.replace(/^url\(["']?/, '').replace(/marker-icon\.png["']?\)$/, '');
 
7223  * L.Handler.MarkerDrag is used internally by L.Marker to make the markers draggable.
 
7227 /* @namespace Marker
 
7228  * @section Interaction handlers
 
7230  * 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:
 
7233  * marker.dragging.disable();
 
7236  * @property dragging: Handler
 
7237  * Marker dragging handler (by both mouse and touch). Only valid when the marker is on the map (Otherwise set [`marker.options.draggable`](#marker-draggable)).
 
7240 var MarkerDrag = Handler.extend({
 
7241         initialize: function (marker) {
 
7242                 this._marker = marker;
 
7245         addHooks: function () {
 
7246                 var icon = this._marker._icon;
 
7248                 if (!this._draggable) {
 
7249                         this._draggable = new Draggable(icon, icon, true);
 
7252                 this._draggable.on({
 
7253                         dragstart: this._onDragStart,
 
7254                         predrag: this._onPreDrag,
 
7256                         dragend: this._onDragEnd
 
7259                 addClass(icon, 'leaflet-marker-draggable');
 
7262         removeHooks: function () {
 
7263                 this._draggable.off({
 
7264                         dragstart: this._onDragStart,
 
7265                         predrag: this._onPreDrag,
 
7267                         dragend: this._onDragEnd
 
7270                 if (this._marker._icon) {
 
7271                         removeClass(this._marker._icon, 'leaflet-marker-draggable');
 
7275         moved: function () {
 
7276                 return this._draggable && this._draggable._moved;
 
7279         _adjustPan: function (e) {
 
7280                 var marker = this._marker,
 
7282                     speed = this._marker.options.autoPanSpeed,
 
7283                     padding = this._marker.options.autoPanPadding,
 
7284                     iconPos = getPosition(marker._icon),
 
7285                     bounds = map.getPixelBounds(),
 
7286                     origin = map.getPixelOrigin();
 
7288                 var panBounds = toBounds(
 
7289                         bounds.min._subtract(origin).add(padding),
 
7290                         bounds.max._subtract(origin).subtract(padding)
 
7293                 if (!panBounds.contains(iconPos)) {
 
7294                         // Compute incremental movement
 
7295                         var movement = toPoint(
 
7296                                 (Math.max(panBounds.max.x, iconPos.x) - panBounds.max.x) / (bounds.max.x - panBounds.max.x) -
 
7297                                 (Math.min(panBounds.min.x, iconPos.x) - panBounds.min.x) / (bounds.min.x - panBounds.min.x),
 
7299                                 (Math.max(panBounds.max.y, iconPos.y) - panBounds.max.y) / (bounds.max.y - panBounds.max.y) -
 
7300                                 (Math.min(panBounds.min.y, iconPos.y) - panBounds.min.y) / (bounds.min.y - panBounds.min.y)
 
7301                         ).multiplyBy(speed);
 
7303                         map.panBy(movement, {animate: false});
 
7305                         this._draggable._newPos._add(movement);
 
7306                         this._draggable._startPos._add(movement);
 
7308                         setPosition(marker._icon, this._draggable._newPos);
 
7311                         this._panRequest = requestAnimFrame(this._adjustPan.bind(this, e));
 
7315         _onDragStart: function () {
 
7316                 // @section Dragging events
 
7317                 // @event dragstart: Event
 
7318                 // Fired when the user starts dragging the marker.
 
7320                 // @event movestart: Event
 
7321                 // Fired when the marker starts moving (because of dragging).
 
7323                 this._oldLatLng = this._marker.getLatLng();
 
7330         _onPreDrag: function (e) {
 
7331                 if (this._marker.options.autoPan) {
 
7332                         cancelAnimFrame(this._panRequest);
 
7333                         this._panRequest = requestAnimFrame(this._adjustPan.bind(this, e));
 
7337         _onDrag: function (e) {
 
7338                 var marker = this._marker,
 
7339                     shadow = marker._shadow,
 
7340                     iconPos = getPosition(marker._icon),
 
7341                     latlng = marker._map.layerPointToLatLng(iconPos);
 
7343                 // update shadow position
 
7345                         setPosition(shadow, iconPos);
 
7348                 marker._latlng = latlng;
 
7350                 e.oldLatLng = this._oldLatLng;
 
7352                 // @event drag: Event
 
7353                 // Fired repeatedly while the user drags the marker.
 
7359         _onDragEnd: function (e) {
 
7360                 // @event dragend: DragEndEvent
 
7361                 // Fired when the user stops dragging the marker.
 
7363                  cancelAnimFrame(this._panRequest);
 
7365                 // @event moveend: Event
 
7366                 // Fired when the marker stops moving (because of dragging).
 
7367                 delete this._oldLatLng;
 
7370                     .fire('dragend', e);
 
7376  * @inherits Interactive layer
 
7378  * L.Marker is used to display clickable/draggable icons on the map. Extends `Layer`.
 
7383  * L.marker([50.5, 30.5]).addTo(map);
 
7387 var Marker = Layer.extend({
 
7390         // @aka Marker options
 
7392                 // @option icon: Icon = *
 
7393                 // Icon instance to use for rendering the marker.
 
7394                 // See [Icon documentation](#L.Icon) for details on how to customize the marker icon.
 
7395                 // If not specified, a common instance of `L.Icon.Default` is used.
 
7396                 icon: new IconDefault(),
 
7398                 // Option inherited from "Interactive layer" abstract class
 
7401                 // @option keyboard: Boolean = true
 
7402                 // Whether the marker can be tabbed to with a keyboard and clicked by pressing enter.
 
7405                 // @option title: String = ''
 
7406                 // Text for the browser tooltip that appear on marker hover (no tooltip by default).
 
7409                 // @option alt: String = ''
 
7410                 // Text for the `alt` attribute of the icon image (useful for accessibility).
 
7413                 // @option zIndexOffset: Number = 0
 
7414                 // 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).
 
7417                 // @option opacity: Number = 1.0
 
7418                 // The opacity of the marker.
 
7421                 // @option riseOnHover: Boolean = false
 
7422                 // If `true`, the marker will get on top of others when you hover the mouse over it.
 
7425                 // @option riseOffset: Number = 250
 
7426                 // The z-index offset used for the `riseOnHover` feature.
 
7429                 // @option pane: String = 'markerPane'
 
7430                 // `Map pane` where the markers icon will be added.
 
7433                 // @option pane: String = 'shadowPane'
 
7434                 // `Map pane` where the markers shadow will be added.
 
7435                 shadowPane: 'shadowPane',
 
7437                 // @option bubblingMouseEvents: Boolean = false
 
7438                 // When `true`, a mouse event on this marker will trigger the same event on the map
 
7439                 // (unless [`L.DomEvent.stopPropagation`](#domevent-stoppropagation) is used).
 
7440                 bubblingMouseEvents: false,
 
7442                 // @section Draggable marker options
 
7443                 // @option draggable: Boolean = false
 
7444                 // Whether the marker is draggable with mouse/touch or not.
 
7447                 // @option autoPan: Boolean = false
 
7448                 // Whether to pan the map when dragging this marker near its edge or not.
 
7451                 // @option autoPanPadding: Point = Point(50, 50)
 
7452                 // Distance (in pixels to the left/right and to the top/bottom) of the
 
7453                 // map edge to start panning the map.
 
7454                 autoPanPadding: [50, 50],
 
7456                 // @option autoPanSpeed: Number = 10
 
7457                 // Number of pixels the map should pan by.
 
7463          * In addition to [shared layer methods](#Layer) like `addTo()` and `remove()` and [popup methods](#Popup) like bindPopup() you can also use the following methods:
 
7466         initialize: function (latlng, options) {
 
7467                 setOptions(this, options);
 
7468                 this._latlng = toLatLng(latlng);
 
7471         onAdd: function (map) {
 
7472                 this._zoomAnimated = this._zoomAnimated && map.options.markerZoomAnimation;
 
7474                 if (this._zoomAnimated) {
 
7475                         map.on('zoomanim', this._animateZoom, this);
 
7482         onRemove: function (map) {
 
7483                 if (this.dragging && this.dragging.enabled()) {
 
7484                         this.options.draggable = true;
 
7485                         this.dragging.removeHooks();
 
7487                 delete this.dragging;
 
7489                 if (this._zoomAnimated) {
 
7490                         map.off('zoomanim', this._animateZoom, this);
 
7494                 this._removeShadow();
 
7497         getEvents: function () {
 
7500                         viewreset: this.update
 
7504         // @method getLatLng: LatLng
 
7505         // Returns the current geographical position of the marker.
 
7506         getLatLng: function () {
 
7507                 return this._latlng;
 
7510         // @method setLatLng(latlng: LatLng): this
 
7511         // Changes the marker position to the given point.
 
7512         setLatLng: function (latlng) {
 
7513                 var oldLatLng = this._latlng;
 
7514                 this._latlng = toLatLng(latlng);
 
7517                 // @event move: Event
 
7518                 // 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`.
 
7519                 return this.fire('move', {oldLatLng: oldLatLng, latlng: this._latlng});
 
7522         // @method setZIndexOffset(offset: Number): this
 
7523         // Changes the [zIndex offset](#marker-zindexoffset) of the marker.
 
7524         setZIndexOffset: function (offset) {
 
7525                 this.options.zIndexOffset = offset;
 
7526                 return this.update();
 
7529         // @method getIcon: Icon
 
7530         // Returns the current icon used by the marker
 
7531         getIcon: function () {
 
7532                 return this.options.icon;
 
7535         // @method setIcon(icon: Icon): this
 
7536         // Changes the marker icon.
 
7537         setIcon: function (icon) {
 
7539                 this.options.icon = icon;
 
7547                         this.bindPopup(this._popup, this._popup.options);
 
7553         getElement: function () {
 
7557         update: function () {
 
7559                 if (this._icon && this._map) {
 
7560                         var pos = this._map.latLngToLayerPoint(this._latlng).round();
 
7567         _initIcon: function () {
 
7568                 var options = this.options,
 
7569                     classToAdd = 'leaflet-zoom-' + (this._zoomAnimated ? 'animated' : 'hide');
 
7571                 var icon = options.icon.createIcon(this._icon),
 
7574                 // if we're not reusing the icon, remove the old one and init new one
 
7575                 if (icon !== this._icon) {
 
7581                         if (options.title) {
 
7582                                 icon.title = options.title;
 
7585                         if (icon.tagName === 'IMG') {
 
7586                                 icon.alt = options.alt || '';
 
7590                 addClass(icon, classToAdd);
 
7592                 if (options.keyboard) {
 
7593                         icon.tabIndex = '0';
 
7598                 if (options.riseOnHover) {
 
7600                                 mouseover: this._bringToFront,
 
7601                                 mouseout: this._resetZIndex
 
7605                 var newShadow = options.icon.createShadow(this._shadow),
 
7608                 if (newShadow !== this._shadow) {
 
7609                         this._removeShadow();
 
7614                         addClass(newShadow, classToAdd);
 
7617                 this._shadow = newShadow;
 
7620                 if (options.opacity < 1) {
 
7621                         this._updateOpacity();
 
7626                         this.getPane().appendChild(this._icon);
 
7628                 this._initInteraction();
 
7629                 if (newShadow && addShadow) {
 
7630                         this.getPane(options.shadowPane).appendChild(this._shadow);
 
7634         _removeIcon: function () {
 
7635                 if (this.options.riseOnHover) {
 
7637                                 mouseover: this._bringToFront,
 
7638                                 mouseout: this._resetZIndex
 
7643                 this.removeInteractiveTarget(this._icon);
 
7648         _removeShadow: function () {
 
7650                         remove(this._shadow);
 
7652                 this._shadow = null;
 
7655         _setPos: function (pos) {
 
7656                 setPosition(this._icon, pos);
 
7659                         setPosition(this._shadow, pos);
 
7662                 this._zIndex = pos.y + this.options.zIndexOffset;
 
7664                 this._resetZIndex();
 
7667         _updateZIndex: function (offset) {
 
7668                 this._icon.style.zIndex = this._zIndex + offset;
 
7671         _animateZoom: function (opt) {
 
7672                 var pos = this._map._latLngToNewLayerPoint(this._latlng, opt.zoom, opt.center).round();
 
7677         _initInteraction: function () {
 
7679                 if (!this.options.interactive) { return; }
 
7681                 addClass(this._icon, 'leaflet-interactive');
 
7683                 this.addInteractiveTarget(this._icon);
 
7686                         var draggable = this.options.draggable;
 
7687                         if (this.dragging) {
 
7688                                 draggable = this.dragging.enabled();
 
7689                                 this.dragging.disable();
 
7692                         this.dragging = new MarkerDrag(this);
 
7695                                 this.dragging.enable();
 
7700         // @method setOpacity(opacity: Number): this
 
7701         // Changes the opacity of the marker.
 
7702         setOpacity: function (opacity) {
 
7703                 this.options.opacity = opacity;
 
7705                         this._updateOpacity();
 
7711         _updateOpacity: function () {
 
7712                 var opacity = this.options.opacity;
 
7715                         setOpacity(this._icon, opacity);
 
7719                         setOpacity(this._shadow, opacity);
 
7723         _bringToFront: function () {
 
7724                 this._updateZIndex(this.options.riseOffset);
 
7727         _resetZIndex: function () {
 
7728                 this._updateZIndex(0);
 
7731         _getPopupAnchor: function () {
 
7732                 return this.options.icon.options.popupAnchor;
 
7735         _getTooltipAnchor: function () {
 
7736                 return this.options.icon.options.tooltipAnchor;
 
7741 // factory L.marker(latlng: LatLng, options? : Marker options)
 
7743 // @factory L.marker(latlng: LatLng, options? : Marker options)
 
7744 // Instantiates a Marker object given a geographical point and optionally an options object.
 
7745 function marker(latlng, options) {
 
7746         return new Marker(latlng, options);
 
7752  * @inherits Interactive layer
 
7754  * An abstract class that contains options and constants shared between vector
 
7755  * overlays (Polygon, Polyline, Circle). Do not use it directly. Extends `Layer`.
 
7758 var Path = Layer.extend({
 
7761         // @aka Path options
 
7763                 // @option stroke: Boolean = true
 
7764                 // Whether to draw stroke along the path. Set it to `false` to disable borders on polygons or circles.
 
7767                 // @option color: String = '#3388ff'
 
7771                 // @option weight: Number = 3
 
7772                 // Stroke width in pixels
 
7775                 // @option opacity: Number = 1.0
 
7779                 // @option lineCap: String= 'round'
 
7780                 // A string that defines [shape to be used at the end](https://developer.mozilla.org/docs/Web/SVG/Attribute/stroke-linecap) of the stroke.
 
7783                 // @option lineJoin: String = 'round'
 
7784                 // A string that defines [shape to be used at the corners](https://developer.mozilla.org/docs/Web/SVG/Attribute/stroke-linejoin) of the stroke.
 
7787                 // @option dashArray: String = null
 
7788                 // 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).
 
7791                 // @option dashOffset: String = null
 
7792                 // 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).
 
7795                 // @option fill: Boolean = depends
 
7796                 // Whether to fill the path with color. Set it to `false` to disable filling on polygons or circles.
 
7799                 // @option fillColor: String = *
 
7800                 // Fill color. Defaults to the value of the [`color`](#path-color) option
 
7803                 // @option fillOpacity: Number = 0.2
 
7807                 // @option fillRule: String = 'evenodd'
 
7808                 // A string that defines [how the inside of a shape](https://developer.mozilla.org/docs/Web/SVG/Attribute/fill-rule) is determined.
 
7809                 fillRule: 'evenodd',
 
7813                 // Option inherited from "Interactive layer" abstract class
 
7816                 // @option bubblingMouseEvents: Boolean = true
 
7817                 // When `true`, a mouse event on this path will trigger the same event on the map
 
7818                 // (unless [`L.DomEvent.stopPropagation`](#domevent-stoppropagation) is used).
 
7819                 bubblingMouseEvents: true
 
7822         beforeAdd: function (map) {
 
7823                 // Renderer is set here because we need to call renderer.getEvents
 
7824                 // before this.getEvents.
 
7825                 this._renderer = map.getRenderer(this);
 
7828         onAdd: function () {
 
7829                 this._renderer._initPath(this);
 
7831                 this._renderer._addPath(this);
 
7834         onRemove: function () {
 
7835                 this._renderer._removePath(this);
 
7838         // @method redraw(): this
 
7839         // Redraws the layer. Sometimes useful after you changed the coordinates that the path uses.
 
7840         redraw: function () {
 
7842                         this._renderer._updatePath(this);
 
7847         // @method setStyle(style: Path options): this
 
7848         // Changes the appearance of a Path based on the options in the `Path options` object.
 
7849         setStyle: function (style) {
 
7850                 setOptions(this, style);
 
7851                 if (this._renderer) {
 
7852                         this._renderer._updateStyle(this);
 
7853                         if (this.options.stroke && style.hasOwnProperty('weight')) {
 
7854                                 this._updateBounds();
 
7860         // @method bringToFront(): this
 
7861         // Brings the layer to the top of all path layers.
 
7862         bringToFront: function () {
 
7863                 if (this._renderer) {
 
7864                         this._renderer._bringToFront(this);
 
7869         // @method bringToBack(): this
 
7870         // Brings the layer to the bottom of all path layers.
 
7871         bringToBack: function () {
 
7872                 if (this._renderer) {
 
7873                         this._renderer._bringToBack(this);
 
7878         getElement: function () {
 
7882         _reset: function () {
 
7883                 // defined in child classes
 
7888         _clickTolerance: function () {
 
7889                 // used when doing hit detection for Canvas layers
 
7890                 return (this.options.stroke ? this.options.weight / 2 : 0) + this._renderer.options.tolerance;
 
7895  * @class CircleMarker
 
7896  * @aka L.CircleMarker
 
7899  * A circle of a fixed size with radius specified in pixels. Extends `Path`.
 
7902 var CircleMarker = Path.extend({
 
7905         // @aka CircleMarker options
 
7909                 // @option radius: Number = 10
 
7910                 // Radius of the circle marker, in pixels
 
7914         initialize: function (latlng, options) {
 
7915                 setOptions(this, options);
 
7916                 this._latlng = toLatLng(latlng);
 
7917                 this._radius = this.options.radius;
 
7920         // @method setLatLng(latLng: LatLng): this
 
7921         // Sets the position of a circle marker to a new location.
 
7922         setLatLng: function (latlng) {
 
7923                 this._latlng = toLatLng(latlng);
 
7925                 return this.fire('move', {latlng: this._latlng});
 
7928         // @method getLatLng(): LatLng
 
7929         // Returns the current geographical position of the circle marker
 
7930         getLatLng: function () {
 
7931                 return this._latlng;
 
7934         // @method setRadius(radius: Number): this
 
7935         // Sets the radius of a circle marker. Units are in pixels.
 
7936         setRadius: function (radius) {
 
7937                 this.options.radius = this._radius = radius;
 
7938                 return this.redraw();
 
7941         // @method getRadius(): Number
 
7942         // Returns the current radius of the circle
 
7943         getRadius: function () {
 
7944                 return this._radius;
 
7947         setStyle : function (options) {
 
7948                 var radius = options && options.radius || this._radius;
 
7949                 Path.prototype.setStyle.call(this, options);
 
7950                 this.setRadius(radius);
 
7954         _project: function () {
 
7955                 this._point = this._map.latLngToLayerPoint(this._latlng);
 
7956                 this._updateBounds();
 
7959         _updateBounds: function () {
 
7960                 var r = this._radius,
 
7961                     r2 = this._radiusY || r,
 
7962                     w = this._clickTolerance(),
 
7963                     p = [r + w, r2 + w];
 
7964                 this._pxBounds = new Bounds(this._point.subtract(p), this._point.add(p));
 
7967         _update: function () {
 
7973         _updatePath: function () {
 
7974                 this._renderer._updateCircle(this);
 
7977         _empty: function () {
 
7978                 return this._radius && !this._renderer._bounds.intersects(this._pxBounds);
 
7981         // Needed by the `Canvas` renderer for interactivity
 
7982         _containsPoint: function (p) {
 
7983                 return p.distanceTo(this._point) <= this._radius + this._clickTolerance();
 
7988 // @factory L.circleMarker(latlng: LatLng, options?: CircleMarker options)
 
7989 // Instantiates a circle marker object given a geographical point, and an optional options object.
 
7990 function circleMarker(latlng, options) {
 
7991         return new CircleMarker(latlng, options);
 
7997  * @inherits CircleMarker
 
7999  * A class for drawing circle overlays on a map. Extends `CircleMarker`.
 
8001  * It's an approximation and starts to diverge from a real circle closer to poles (due to projection distortion).
 
8006  * L.circle([50.5, 30.5], {radius: 200}).addTo(map);
 
8010 var Circle = CircleMarker.extend({
 
8012         initialize: function (latlng, options, legacyOptions) {
 
8013                 if (typeof options === 'number') {
 
8014                         // Backwards compatibility with 0.7.x factory (latlng, radius, options?)
 
8015                         options = extend({}, legacyOptions, {radius: options});
 
8017                 setOptions(this, options);
 
8018                 this._latlng = toLatLng(latlng);
 
8020                 if (isNaN(this.options.radius)) { throw new Error('Circle radius cannot be NaN'); }
 
8023                 // @aka Circle options
 
8024                 // @option radius: Number; Radius of the circle, in meters.
 
8025                 this._mRadius = this.options.radius;
 
8028         // @method setRadius(radius: Number): this
 
8029         // Sets the radius of a circle. Units are in meters.
 
8030         setRadius: function (radius) {
 
8031                 this._mRadius = radius;
 
8032                 return this.redraw();
 
8035         // @method getRadius(): Number
 
8036         // Returns the current radius of a circle. Units are in meters.
 
8037         getRadius: function () {
 
8038                 return this._mRadius;
 
8041         // @method getBounds(): LatLngBounds
 
8042         // Returns the `LatLngBounds` of the path.
 
8043         getBounds: function () {
 
8044                 var half = [this._radius, this._radiusY || this._radius];
 
8046                 return new LatLngBounds(
 
8047                         this._map.layerPointToLatLng(this._point.subtract(half)),
 
8048                         this._map.layerPointToLatLng(this._point.add(half)));
 
8051         setStyle: Path.prototype.setStyle,
 
8053         _project: function () {
 
8055                 var lng = this._latlng.lng,
 
8056                     lat = this._latlng.lat,
 
8058                     crs = map.options.crs;
 
8060                 if (crs.distance === Earth.distance) {
 
8061                         var d = Math.PI / 180,
 
8062                             latR = (this._mRadius / Earth.R) / d,
 
8063                             top = map.project([lat + latR, lng]),
 
8064                             bottom = map.project([lat - latR, lng]),
 
8065                             p = top.add(bottom).divideBy(2),
 
8066                             lat2 = map.unproject(p).lat,
 
8067                             lngR = Math.acos((Math.cos(latR * d) - Math.sin(lat * d) * Math.sin(lat2 * d)) /
 
8068                                     (Math.cos(lat * d) * Math.cos(lat2 * d))) / d;
 
8070                         if (isNaN(lngR) || lngR === 0) {
 
8071                                 lngR = latR / Math.cos(Math.PI / 180 * lat); // Fallback for edge case, #2425
 
8074                         this._point = p.subtract(map.getPixelOrigin());
 
8075                         this._radius = isNaN(lngR) ? 0 : p.x - map.project([lat2, lng - lngR]).x;
 
8076                         this._radiusY = p.y - top.y;
 
8079                         var latlng2 = crs.unproject(crs.project(this._latlng).subtract([this._mRadius, 0]));
 
8081                         this._point = map.latLngToLayerPoint(this._latlng);
 
8082                         this._radius = this._point.x - map.latLngToLayerPoint(latlng2).x;
 
8085                 this._updateBounds();
 
8089 // @factory L.circle(latlng: LatLng, options?: Circle options)
 
8090 // Instantiates a circle object given a geographical point, and an options object
 
8091 // which contains the circle radius.
 
8093 // @factory L.circle(latlng: LatLng, radius: Number, options?: Circle options)
 
8094 // Obsolete way of instantiating a circle, for compatibility with 0.7.x code.
 
8095 // Do not use in new applications or plugins.
 
8096 function circle(latlng, options, legacyOptions) {
 
8097         return new Circle(latlng, options, legacyOptions);
 
8105  * A class for drawing polyline overlays on a map. Extends `Path`.
 
8110  * // create a red polyline from an array of LatLng points
 
8117  * var polyline = L.polyline(latlngs, {color: 'red'}).addTo(map);
 
8119  * // zoom the map to the polyline
 
8120  * map.fitBounds(polyline.getBounds());
 
8123  * You can also pass a multi-dimensional array to represent a `MultiPolyline` shape:
 
8126  * // create a red polyline from an array of arrays of LatLng points
 
8128  *      [[45.51, -122.68],
 
8139 var Polyline = Path.extend({
 
8142         // @aka Polyline options
 
8144                 // @option smoothFactor: Number = 1.0
 
8145                 // How much to simplify the polyline on each zoom level. More means
 
8146                 // better performance and smoother look, and less means more accurate representation.
 
8149                 // @option noClip: Boolean = false
 
8150                 // Disable polyline clipping.
 
8154         initialize: function (latlngs, options) {
 
8155                 setOptions(this, options);
 
8156                 this._setLatLngs(latlngs);
 
8159         // @method getLatLngs(): LatLng[]
 
8160         // Returns an array of the points in the path, or nested arrays of points in case of multi-polyline.
 
8161         getLatLngs: function () {
 
8162                 return this._latlngs;
 
8165         // @method setLatLngs(latlngs: LatLng[]): this
 
8166         // Replaces all the points in the polyline with the given array of geographical points.
 
8167         setLatLngs: function (latlngs) {
 
8168                 this._setLatLngs(latlngs);
 
8169                 return this.redraw();
 
8172         // @method isEmpty(): Boolean
 
8173         // Returns `true` if the Polyline has no LatLngs.
 
8174         isEmpty: function () {
 
8175                 return !this._latlngs.length;
 
8178         // @method closestLayerPoint(p: Point): Point
 
8179         // Returns the point closest to `p` on the Polyline.
 
8180         closestLayerPoint: function (p) {
 
8181                 var minDistance = Infinity,
 
8183                     closest = _sqClosestPointOnSegment,
 
8186                 for (var j = 0, jLen = this._parts.length; j < jLen; j++) {
 
8187                         var points = this._parts[j];
 
8189                         for (var i = 1, len = points.length; i < len; i++) {
 
8193                                 var sqDist = closest(p, p1, p2, true);
 
8195                                 if (sqDist < minDistance) {
 
8196                                         minDistance = sqDist;
 
8197                                         minPoint = closest(p, p1, p2);
 
8202                         minPoint.distance = Math.sqrt(minDistance);
 
8207         // @method getCenter(): LatLng
 
8208         // Returns the center ([centroid](http://en.wikipedia.org/wiki/Centroid)) of the polyline.
 
8209         getCenter: function () {
 
8210                 // throws error when not yet added to map as this center calculation requires projected coordinates
 
8212                         throw new Error('Must add layer to map before using getCenter()');
 
8215                 var i, halfDist, segDist, dist, p1, p2, ratio,
 
8216                     points = this._rings[0],
 
8217                     len = points.length;
 
8219                 if (!len) { return null; }
 
8221                 // polyline centroid algorithm; only uses the first ring if there are multiple
 
8223                 for (i = 0, halfDist = 0; i < len - 1; i++) {
 
8224                         halfDist += points[i].distanceTo(points[i + 1]) / 2;
 
8227                 // The line is so small in the current view that all points are on the same pixel.
 
8228                 if (halfDist === 0) {
 
8229                         return this._map.layerPointToLatLng(points[0]);
 
8232                 for (i = 0, dist = 0; i < len - 1; i++) {
 
8235                         segDist = p1.distanceTo(p2);
 
8238                         if (dist > halfDist) {
 
8239                                 ratio = (dist - halfDist) / segDist;
 
8240                                 return this._map.layerPointToLatLng([
 
8241                                         p2.x - ratio * (p2.x - p1.x),
 
8242                                         p2.y - ratio * (p2.y - p1.y)
 
8248         // @method getBounds(): LatLngBounds
 
8249         // Returns the `LatLngBounds` of the path.
 
8250         getBounds: function () {
 
8251                 return this._bounds;
 
8254         // @method addLatLng(latlng: LatLng, latlngs? LatLng[]): this
 
8255         // Adds a given point to the polyline. By default, adds to the first ring of
 
8256         // the polyline in case of a multi-polyline, but can be overridden by passing
 
8257         // a specific ring as a LatLng array (that you can earlier access with [`getLatLngs`](#polyline-getlatlngs)).
 
8258         addLatLng: function (latlng, latlngs) {
 
8259                 latlngs = latlngs || this._defaultShape();
 
8260                 latlng = toLatLng(latlng);
 
8261                 latlngs.push(latlng);
 
8262                 this._bounds.extend(latlng);
 
8263                 return this.redraw();
 
8266         _setLatLngs: function (latlngs) {
 
8267                 this._bounds = new LatLngBounds();
 
8268                 this._latlngs = this._convertLatLngs(latlngs);
 
8271         _defaultShape: function () {
 
8272                 return isFlat(this._latlngs) ? this._latlngs : this._latlngs[0];
 
8275         // recursively convert latlngs input into actual LatLng instances; calculate bounds along the way
 
8276         _convertLatLngs: function (latlngs) {
 
8278                     flat = isFlat(latlngs);
 
8280                 for (var i = 0, len = latlngs.length; i < len; i++) {
 
8282                                 result[i] = toLatLng(latlngs[i]);
 
8283                                 this._bounds.extend(result[i]);
 
8285                                 result[i] = this._convertLatLngs(latlngs[i]);
 
8292         _project: function () {
 
8293                 var pxBounds = new Bounds();
 
8295                 this._projectLatlngs(this._latlngs, this._rings, pxBounds);
 
8297                 if (this._bounds.isValid() && pxBounds.isValid()) {
 
8298                         this._rawPxBounds = pxBounds;
 
8299                         this._updateBounds();
 
8303         _updateBounds: function () {
 
8304                 var w = this._clickTolerance(),
 
8305                     p = new Point(w, w);
 
8306                 this._pxBounds = new Bounds([
 
8307                         this._rawPxBounds.min.subtract(p),
 
8308                         this._rawPxBounds.max.add(p)
 
8312         // recursively turns latlngs into a set of rings with projected coordinates
 
8313         _projectLatlngs: function (latlngs, result, projectedBounds) {
 
8314                 var flat = latlngs[0] instanceof LatLng,
 
8315                     len = latlngs.length,
 
8320                         for (i = 0; i < len; i++) {
 
8321                                 ring[i] = this._map.latLngToLayerPoint(latlngs[i]);
 
8322                                 projectedBounds.extend(ring[i]);
 
8326                         for (i = 0; i < len; i++) {
 
8327                                 this._projectLatlngs(latlngs[i], result, projectedBounds);
 
8332         // clip polyline by renderer bounds so that we have less to render for performance
 
8333         _clipPoints: function () {
 
8334                 var bounds = this._renderer._bounds;
 
8337                 if (!this._pxBounds || !this._pxBounds.intersects(bounds)) {
 
8341                 if (this.options.noClip) {
 
8342                         this._parts = this._rings;
 
8346                 var parts = this._parts,
 
8347                     i, j, k, len, len2, segment, points;
 
8349                 for (i = 0, k = 0, len = this._rings.length; i < len; i++) {
 
8350                         points = this._rings[i];
 
8352                         for (j = 0, len2 = points.length; j < len2 - 1; j++) {
 
8353                                 segment = clipSegment(points[j], points[j + 1], bounds, j, true);
 
8355                                 if (!segment) { continue; }
 
8357                                 parts[k] = parts[k] || [];
 
8358                                 parts[k].push(segment[0]);
 
8360                                 // if segment goes out of screen, or it's the last one, it's the end of the line part
 
8361                                 if ((segment[1] !== points[j + 1]) || (j === len2 - 2)) {
 
8362                                         parts[k].push(segment[1]);
 
8369         // simplify each clipped part of the polyline for performance
 
8370         _simplifyPoints: function () {
 
8371                 var parts = this._parts,
 
8372                     tolerance = this.options.smoothFactor;
 
8374                 for (var i = 0, len = parts.length; i < len; i++) {
 
8375                         parts[i] = simplify(parts[i], tolerance);
 
8379         _update: function () {
 
8380                 if (!this._map) { return; }
 
8383                 this._simplifyPoints();
 
8387         _updatePath: function () {
 
8388                 this._renderer._updatePoly(this);
 
8391         // Needed by the `Canvas` renderer for interactivity
 
8392         _containsPoint: function (p, closed) {
 
8393                 var i, j, k, len, len2, part,
 
8394                     w = this._clickTolerance();
 
8396                 if (!this._pxBounds || !this._pxBounds.contains(p)) { return false; }
 
8398                 // hit detection for polylines
 
8399                 for (i = 0, len = this._parts.length; i < len; i++) {
 
8400                         part = this._parts[i];
 
8402                         for (j = 0, len2 = part.length, k = len2 - 1; j < len2; k = j++) {
 
8403                                 if (!closed && (j === 0)) { continue; }
 
8405                                 if (pointToSegmentDistance(p, part[k], part[j]) <= w) {
 
8414 // @factory L.polyline(latlngs: LatLng[], options?: Polyline options)
 
8415 // Instantiates a polyline object given an array of geographical points and
 
8416 // optionally an options object. You can create a `Polyline` object with
 
8417 // multiple separate lines (`MultiPolyline`) by passing an array of arrays
 
8418 // of geographic points.
 
8419 function polyline(latlngs, options) {
 
8420         return new Polyline(latlngs, options);
 
8423 // Retrocompat. Allow plugins to support Leaflet versions before and after 1.1.
 
8424 Polyline._flat = _flat;
 
8429  * @inherits Polyline
 
8431  * A class for drawing polygon overlays on a map. Extends `Polyline`.
 
8433  * 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.
 
8439  * // create a red polygon from an array of LatLng points
 
8440  * var latlngs = [[37, -109.05],[41, -109.03],[41, -102.05],[37, -102.04]];
 
8442  * var polygon = L.polygon(latlngs, {color: 'red'}).addTo(map);
 
8444  * // zoom the map to the polygon
 
8445  * map.fitBounds(polygon.getBounds());
 
8448  * 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:
 
8452  *   [[37, -109.05],[41, -109.03],[41, -102.05],[37, -102.04]], // outer ring
 
8453  *   [[37.29, -108.58],[40.71, -108.58],[40.71, -102.50],[37.29, -102.50]] // hole
 
8457  * Additionally, you can pass a multi-dimensional array to represent a MultiPolygon shape.
 
8461  *   [ // first polygon
 
8462  *     [[37, -109.05],[41, -109.03],[41, -102.05],[37, -102.04]], // outer ring
 
8463  *     [[37.29, -108.58],[40.71, -108.58],[40.71, -102.50],[37.29, -102.50]] // hole
 
8465  *   [ // second polygon
 
8466  *     [[41, -111.03],[45, -111.04],[45, -104.05],[41, -104.05]]
 
8472 var Polygon = Polyline.extend({
 
8478         isEmpty: function () {
 
8479                 return !this._latlngs.length || !this._latlngs[0].length;
 
8482         getCenter: function () {
 
8483                 // throws error when not yet added to map as this center calculation requires projected coordinates
 
8485                         throw new Error('Must add layer to map before using getCenter()');
 
8488                 var i, j, p1, p2, f, area, x, y, center,
 
8489                     points = this._rings[0],
 
8490                     len = points.length;
 
8492                 if (!len) { return null; }
 
8494                 // polygon centroid algorithm; only uses the first ring if there are multiple
 
8498                 for (i = 0, j = len - 1; i < len; j = i++) {
 
8502                         f = p1.y * p2.x - p2.y * p1.x;
 
8503                         x += (p1.x + p2.x) * f;
 
8504                         y += (p1.y + p2.y) * f;
 
8509                         // Polygon is so small that all points are on same pixel.
 
8512                         center = [x / area, y / area];
 
8514                 return this._map.layerPointToLatLng(center);
 
8517         _convertLatLngs: function (latlngs) {
 
8518                 var result = Polyline.prototype._convertLatLngs.call(this, latlngs),
 
8519                     len = result.length;
 
8521                 // remove last point if it equals first one
 
8522                 if (len >= 2 && result[0] instanceof LatLng && result[0].equals(result[len - 1])) {
 
8528         _setLatLngs: function (latlngs) {
 
8529                 Polyline.prototype._setLatLngs.call(this, latlngs);
 
8530                 if (isFlat(this._latlngs)) {
 
8531                         this._latlngs = [this._latlngs];
 
8535         _defaultShape: function () {
 
8536                 return isFlat(this._latlngs[0]) ? this._latlngs[0] : this._latlngs[0][0];
 
8539         _clipPoints: function () {
 
8540                 // polygons need a different clipping algorithm so we redefine that
 
8542                 var bounds = this._renderer._bounds,
 
8543                     w = this.options.weight,
 
8544                     p = new Point(w, w);
 
8546                 // increase clip padding by stroke width to avoid stroke on clip edges
 
8547                 bounds = new Bounds(bounds.min.subtract(p), bounds.max.add(p));
 
8550                 if (!this._pxBounds || !this._pxBounds.intersects(bounds)) {
 
8554                 if (this.options.noClip) {
 
8555                         this._parts = this._rings;
 
8559                 for (var i = 0, len = this._rings.length, clipped; i < len; i++) {
 
8560                         clipped = clipPolygon(this._rings[i], bounds, true);
 
8561                         if (clipped.length) {
 
8562                                 this._parts.push(clipped);
 
8567         _updatePath: function () {
 
8568                 this._renderer._updatePoly(this, true);
 
8571         // Needed by the `Canvas` renderer for interactivity
 
8572         _containsPoint: function (p) {
 
8574                     part, p1, p2, i, j, k, len, len2;
 
8576                 if (!this._pxBounds || !this._pxBounds.contains(p)) { return false; }
 
8578                 // ray casting algorithm for detecting if point is in polygon
 
8579                 for (i = 0, len = this._parts.length; i < len; i++) {
 
8580                         part = this._parts[i];
 
8582                         for (j = 0, len2 = part.length, k = len2 - 1; j < len2; k = j++) {
 
8586                                 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)) {
 
8592                 // also check if it's on polygon stroke
 
8593                 return inside || Polyline.prototype._containsPoint.call(this, p, true);
 
8599 // @factory L.polygon(latlngs: LatLng[], options?: Polyline options)
 
8600 function polygon(latlngs, options) {
 
8601         return new Polygon(latlngs, options);
 
8607  * @inherits FeatureGroup
 
8609  * Represents a GeoJSON object or an array of GeoJSON objects. Allows you to parse
 
8610  * GeoJSON data and display it on the map. Extends `FeatureGroup`.
 
8616  *      style: function (feature) {
 
8617  *              return {color: feature.properties.color};
 
8619  * }).bindPopup(function (layer) {
 
8620  *      return layer.feature.properties.description;
 
8625 var GeoJSON = FeatureGroup.extend({
 
8628          * @aka GeoJSON options
 
8630          * @option pointToLayer: Function = *
 
8631          * A `Function` defining how GeoJSON points spawn Leaflet layers. It is internally
 
8632          * called when data is added, passing the GeoJSON point feature and its `LatLng`.
 
8633          * The default is to spawn a default `Marker`:
 
8635          * function(geoJsonPoint, latlng) {
 
8636          *      return L.marker(latlng);
 
8640          * @option style: Function = *
 
8641          * A `Function` defining the `Path options` for styling GeoJSON lines and polygons,
 
8642          * called internally when data is added.
 
8643          * The default value is to not override any defaults:
 
8645          * function (geoJsonFeature) {
 
8650          * @option onEachFeature: Function = *
 
8651          * A `Function` that will be called once for each created `Feature`, after it has
 
8652          * been created and styled. Useful for attaching events and popups to features.
 
8653          * The default is to do nothing with the newly created layers:
 
8655          * function (feature, layer) {}
 
8658          * @option filter: Function = *
 
8659          * A `Function` that will be used to decide whether to include a feature or not.
 
8660          * The default is to include all features:
 
8662          * function (geoJsonFeature) {
 
8666          * Note: dynamically changing the `filter` option will have effect only on newly
 
8667          * added data. It will _not_ re-evaluate already included features.
 
8669          * @option coordsToLatLng: Function = *
 
8670          * A `Function` that will be used for converting GeoJSON coordinates to `LatLng`s.
 
8671          * The default is the `coordsToLatLng` static method.
 
8674         initialize: function (geojson, options) {
 
8675                 setOptions(this, options);
 
8680                         this.addData(geojson);
 
8684         // @method addData( <GeoJSON> data ): this
 
8685         // Adds a GeoJSON object to the layer.
 
8686         addData: function (geojson) {
 
8687                 var features = isArray(geojson) ? geojson : geojson.features,
 
8691                         for (i = 0, len = features.length; i < len; i++) {
 
8692                                 // only add this if geometry or geometries are set and not null
 
8693                                 feature = features[i];
 
8694                                 if (feature.geometries || feature.geometry || feature.features || feature.coordinates) {
 
8695                                         this.addData(feature);
 
8701                 var options = this.options;
 
8703                 if (options.filter && !options.filter(geojson)) { return this; }
 
8705                 var layer = geometryToLayer(geojson, options);
 
8709                 layer.feature = asFeature(geojson);
 
8711                 layer.defaultOptions = layer.options;
 
8712                 this.resetStyle(layer);
 
8714                 if (options.onEachFeature) {
 
8715                         options.onEachFeature(geojson, layer);
 
8718                 return this.addLayer(layer);
 
8721         // @method resetStyle( <Path> layer ): this
 
8722         // Resets the given vector layer's style to the original GeoJSON style, useful for resetting style after hover events.
 
8723         resetStyle: function (layer) {
 
8724                 // reset any custom styles
 
8725                 layer.options = extend({}, layer.defaultOptions);
 
8726                 this._setLayerStyle(layer, this.options.style);
 
8730         // @method setStyle( <Function> style ): this
 
8731         // Changes styles of GeoJSON vector layers with the given style function.
 
8732         setStyle: function (style) {
 
8733                 return this.eachLayer(function (layer) {
 
8734                         this._setLayerStyle(layer, style);
 
8738         _setLayerStyle: function (layer, style) {
 
8739                 if (layer.setStyle) {
 
8740                         if (typeof style === 'function') {
 
8741                                 style = style(layer.feature);
 
8743                         layer.setStyle(style);
 
8749 // There are several static functions which can be called without instantiating L.GeoJSON:
 
8751 // @function geometryToLayer(featureData: Object, options?: GeoJSON options): Layer
 
8752 // Creates a `Layer` from a given GeoJSON feature. Can use a custom
 
8753 // [`pointToLayer`](#geojson-pointtolayer) and/or [`coordsToLatLng`](#geojson-coordstolatlng)
 
8754 // functions if provided as options.
 
8755 function geometryToLayer(geojson, options) {
 
8757         var geometry = geojson.type === 'Feature' ? geojson.geometry : geojson,
 
8758             coords = geometry ? geometry.coordinates : null,
 
8760             pointToLayer = options && options.pointToLayer,
 
8761             _coordsToLatLng = options && options.coordsToLatLng || coordsToLatLng,
 
8762             latlng, latlngs, i, len;
 
8764         if (!coords && !geometry) {
 
8768         switch (geometry.type) {
 
8770                 latlng = _coordsToLatLng(coords);
 
8771                 return pointToLayer ? pointToLayer(geojson, latlng) : new Marker(latlng);
 
8774                 for (i = 0, len = coords.length; i < len; i++) {
 
8775                         latlng = _coordsToLatLng(coords[i]);
 
8776                         layers.push(pointToLayer ? pointToLayer(geojson, latlng) : new Marker(latlng));
 
8778                 return new FeatureGroup(layers);
 
8781         case 'MultiLineString':
 
8782                 latlngs = coordsToLatLngs(coords, geometry.type === 'LineString' ? 0 : 1, _coordsToLatLng);
 
8783                 return new Polyline(latlngs, options);
 
8786         case 'MultiPolygon':
 
8787                 latlngs = coordsToLatLngs(coords, geometry.type === 'Polygon' ? 1 : 2, _coordsToLatLng);
 
8788                 return new Polygon(latlngs, options);
 
8790         case 'GeometryCollection':
 
8791                 for (i = 0, len = geometry.geometries.length; i < len; i++) {
 
8792                         var layer = geometryToLayer({
 
8793                                 geometry: geometry.geometries[i],
 
8795                                 properties: geojson.properties
 
8802                 return new FeatureGroup(layers);
 
8805                 throw new Error('Invalid GeoJSON object.');
 
8809 // @function coordsToLatLng(coords: Array): LatLng
 
8810 // Creates a `LatLng` object from an array of 2 numbers (longitude, latitude)
 
8811 // or 3 numbers (longitude, latitude, altitude) used in GeoJSON for points.
 
8812 function coordsToLatLng(coords) {
 
8813         return new LatLng(coords[1], coords[0], coords[2]);
 
8816 // @function coordsToLatLngs(coords: Array, levelsDeep?: Number, coordsToLatLng?: Function): Array
 
8817 // Creates a multidimensional array of `LatLng`s from a GeoJSON coordinates array.
 
8818 // `levelsDeep` specifies the nesting level (0 is for an array of points, 1 for an array of arrays of points, etc., 0 by default).
 
8819 // Can use a custom [`coordsToLatLng`](#geojson-coordstolatlng) function.
 
8820 function coordsToLatLngs(coords, levelsDeep, _coordsToLatLng) {
 
8823         for (var i = 0, len = coords.length, latlng; i < len; i++) {
 
8824                 latlng = levelsDeep ?
 
8825                         coordsToLatLngs(coords[i], levelsDeep - 1, _coordsToLatLng) :
 
8826                         (_coordsToLatLng || coordsToLatLng)(coords[i]);
 
8828                 latlngs.push(latlng);
 
8834 // @function latLngToCoords(latlng: LatLng, precision?: Number): Array
 
8835 // Reverse of [`coordsToLatLng`](#geojson-coordstolatlng)
 
8836 function latLngToCoords(latlng, precision) {
 
8837         precision = typeof precision === 'number' ? precision : 6;
 
8838         return latlng.alt !== undefined ?
 
8839                 [formatNum(latlng.lng, precision), formatNum(latlng.lat, precision), formatNum(latlng.alt, precision)] :
 
8840                 [formatNum(latlng.lng, precision), formatNum(latlng.lat, precision)];
 
8843 // @function latLngsToCoords(latlngs: Array, levelsDeep?: Number, closed?: Boolean): Array
 
8844 // Reverse of [`coordsToLatLngs`](#geojson-coordstolatlngs)
 
8845 // `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.
 
8846 function latLngsToCoords(latlngs, levelsDeep, closed, precision) {
 
8849         for (var i = 0, len = latlngs.length; i < len; i++) {
 
8850                 coords.push(levelsDeep ?
 
8851                         latLngsToCoords(latlngs[i], levelsDeep - 1, closed, precision) :
 
8852                         latLngToCoords(latlngs[i], precision));
 
8855         if (!levelsDeep && closed) {
 
8856                 coords.push(coords[0]);
 
8862 function getFeature(layer, newGeometry) {
 
8863         return layer.feature ?
 
8864                 extend({}, layer.feature, {geometry: newGeometry}) :
 
8865                 asFeature(newGeometry);
 
8868 // @function asFeature(geojson: Object): Object
 
8869 // Normalize GeoJSON geometries/features into GeoJSON features.
 
8870 function asFeature(geojson) {
 
8871         if (geojson.type === 'Feature' || geojson.type === 'FeatureCollection') {
 
8882 var PointToGeoJSON = {
 
8883         toGeoJSON: function (precision) {
 
8884                 return getFeature(this, {
 
8886                         coordinates: latLngToCoords(this.getLatLng(), precision)
 
8891 // @namespace Marker
 
8892 // @method toGeoJSON(precision?: Number): Object
 
8893 // `precision` is the number of decimal places for coordinates.
 
8894 // The default value is 6 places.
 
8895 // Returns a [`GeoJSON`](http://en.wikipedia.org/wiki/GeoJSON) representation of the marker (as a GeoJSON `Point` Feature).
 
8896 Marker.include(PointToGeoJSON);
 
8898 // @namespace CircleMarker
 
8899 // @method toGeoJSON(precision?: Number): Object
 
8900 // `precision` is the number of decimal places for coordinates.
 
8901 // The default value is 6 places.
 
8902 // Returns a [`GeoJSON`](http://en.wikipedia.org/wiki/GeoJSON) representation of the circle marker (as a GeoJSON `Point` Feature).
 
8903 Circle.include(PointToGeoJSON);
 
8904 CircleMarker.include(PointToGeoJSON);
 
8907 // @namespace Polyline
 
8908 // @method toGeoJSON(precision?: Number): Object
 
8909 // `precision` is the number of decimal places for coordinates.
 
8910 // The default value is 6 places.
 
8911 // Returns a [`GeoJSON`](http://en.wikipedia.org/wiki/GeoJSON) representation of the polyline (as a GeoJSON `LineString` or `MultiLineString` Feature).
 
8913         toGeoJSON: function (precision) {
 
8914                 var multi = !isFlat(this._latlngs);
 
8916                 var coords = latLngsToCoords(this._latlngs, multi ? 1 : 0, false, precision);
 
8918                 return getFeature(this, {
 
8919                         type: (multi ? 'Multi' : '') + 'LineString',
 
8925 // @namespace Polygon
 
8926 // @method toGeoJSON(precision?: Number): Object
 
8927 // `precision` is the number of decimal places for coordinates.
 
8928 // The default value is 6 places.
 
8929 // Returns a [`GeoJSON`](http://en.wikipedia.org/wiki/GeoJSON) representation of the polygon (as a GeoJSON `Polygon` or `MultiPolygon` Feature).
 
8931         toGeoJSON: function (precision) {
 
8932                 var holes = !isFlat(this._latlngs),
 
8933                     multi = holes && !isFlat(this._latlngs[0]);
 
8935                 var coords = latLngsToCoords(this._latlngs, multi ? 2 : holes ? 1 : 0, true, precision);
 
8941                 return getFeature(this, {
 
8942                         type: (multi ? 'Multi' : '') + 'Polygon',
 
8949 // @namespace LayerGroup
 
8950 LayerGroup.include({
 
8951         toMultiPoint: function (precision) {
 
8954                 this.eachLayer(function (layer) {
 
8955                         coords.push(layer.toGeoJSON(precision).geometry.coordinates);
 
8958                 return getFeature(this, {
 
8964         // @method toGeoJSON(precision?: Number): Object
 
8965         // `precision` is the number of decimal places for coordinates.
 
8966         // The default value is 6 places.
 
8967         // Returns a [`GeoJSON`](http://en.wikipedia.org/wiki/GeoJSON) representation of the layer group (as a GeoJSON `FeatureCollection`, `GeometryCollection`, or `MultiPoint`).
 
8968         toGeoJSON: function (precision) {
 
8970                 var type = this.feature && this.feature.geometry && this.feature.geometry.type;
 
8972                 if (type === 'MultiPoint') {
 
8973                         return this.toMultiPoint(precision);
 
8976                 var isGeometryCollection = type === 'GeometryCollection',
 
8979                 this.eachLayer(function (layer) {
 
8980                         if (layer.toGeoJSON) {
 
8981                                 var json = layer.toGeoJSON(precision);
 
8982                                 if (isGeometryCollection) {
 
8983                                         jsons.push(json.geometry);
 
8985                                         var feature = asFeature(json);
 
8986                                         // Squash nested feature collections
 
8987                                         if (feature.type === 'FeatureCollection') {
 
8988                                                 jsons.push.apply(jsons, feature.features);
 
8990                                                 jsons.push(feature);
 
8996                 if (isGeometryCollection) {
 
8997                         return getFeature(this, {
 
8999                                 type: 'GeometryCollection'
 
9004                         type: 'FeatureCollection',
 
9010 // @namespace GeoJSON
 
9011 // @factory L.geoJSON(geojson?: Object, options?: GeoJSON options)
 
9012 // Creates a GeoJSON layer. Optionally accepts an object in
 
9013 // [GeoJSON format](https://tools.ietf.org/html/rfc7946) to display on the map
 
9014 // (you can alternatively add it later with `addData` method) and an `options` object.
 
9015 function geoJSON(geojson, options) {
 
9016         return new GeoJSON(geojson, options);
 
9019 // Backward compatibility.
 
9020 var geoJson = geoJSON;
 
9023  * @class ImageOverlay
 
9024  * @aka L.ImageOverlay
 
9025  * @inherits Interactive layer
 
9027  * Used to load and display a single image over specific bounds of the map. Extends `Layer`.
 
9032  * var imageUrl = 'http://www.lib.utexas.edu/maps/historical/newark_nj_1922.jpg',
 
9033  *      imageBounds = [[40.712216, -74.22655], [40.773941, -74.12544]];
 
9034  * L.imageOverlay(imageUrl, imageBounds).addTo(map);
 
9038 var ImageOverlay = Layer.extend({
 
9041         // @aka ImageOverlay options
 
9043                 // @option opacity: Number = 1.0
 
9044                 // The opacity of the image overlay.
 
9047                 // @option alt: String = ''
 
9048                 // Text for the `alt` attribute of the image (useful for accessibility).
 
9051                 // @option interactive: Boolean = false
 
9052                 // If `true`, the image overlay will emit [mouse events](#interactive-layer) when clicked or hovered.
 
9055                 // @option crossOrigin: Boolean|String = false
 
9056                 // Whether the crossOrigin attribute will be added to the image.
 
9057                 // 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.
 
9058                 // Refer to [CORS Settings](https://developer.mozilla.org/en-US/docs/Web/HTML/CORS_settings_attributes) for valid String values.
 
9061                 // @option errorOverlayUrl: String = ''
 
9062                 // URL to the overlay image to show in place of the overlay that failed to load.
 
9063                 errorOverlayUrl: '',
 
9065                 // @option zIndex: Number = 1
 
9066                 // The explicit [zIndex](https://developer.mozilla.org/docs/Web/CSS/CSS_Positioning/Understanding_z_index) of the overlay layer.
 
9069                 // @option className: String = ''
 
9070                 // A custom class name to assign to the image. Empty by default.
 
9074         initialize: function (url, bounds, options) { // (String, LatLngBounds, Object)
 
9076                 this._bounds = toLatLngBounds(bounds);
 
9078                 setOptions(this, options);
 
9081         onAdd: function () {
 
9085                         if (this.options.opacity < 1) {
 
9086                                 this._updateOpacity();
 
9090                 if (this.options.interactive) {
 
9091                         addClass(this._image, 'leaflet-interactive');
 
9092                         this.addInteractiveTarget(this._image);
 
9095                 this.getPane().appendChild(this._image);
 
9099         onRemove: function () {
 
9100                 remove(this._image);
 
9101                 if (this.options.interactive) {
 
9102                         this.removeInteractiveTarget(this._image);
 
9106         // @method setOpacity(opacity: Number): this
 
9107         // Sets the opacity of the overlay.
 
9108         setOpacity: function (opacity) {
 
9109                 this.options.opacity = opacity;
 
9112                         this._updateOpacity();
 
9117         setStyle: function (styleOpts) {
 
9118                 if (styleOpts.opacity) {
 
9119                         this.setOpacity(styleOpts.opacity);
 
9124         // @method bringToFront(): this
 
9125         // Brings the layer to the top of all overlays.
 
9126         bringToFront: function () {
 
9128                         toFront(this._image);
 
9133         // @method bringToBack(): this
 
9134         // Brings the layer to the bottom of all overlays.
 
9135         bringToBack: function () {
 
9137                         toBack(this._image);
 
9142         // @method setUrl(url: String): this
 
9143         // Changes the URL of the image.
 
9144         setUrl: function (url) {
 
9148                         this._image.src = url;
 
9153         // @method setBounds(bounds: LatLngBounds): this
 
9154         // Update the bounds that this ImageOverlay covers
 
9155         setBounds: function (bounds) {
 
9156                 this._bounds = toLatLngBounds(bounds);
 
9164         getEvents: function () {
 
9167                         viewreset: this._reset
 
9170                 if (this._zoomAnimated) {
 
9171                         events.zoomanim = this._animateZoom;
 
9177         // @method setZIndex(value: Number): this
 
9178         // Changes the [zIndex](#imageoverlay-zindex) of the image overlay.
 
9179         setZIndex: function (value) {
 
9180                 this.options.zIndex = value;
 
9181                 this._updateZIndex();
 
9185         // @method getBounds(): LatLngBounds
 
9186         // Get the bounds that this ImageOverlay covers
 
9187         getBounds: function () {
 
9188                 return this._bounds;
 
9191         // @method getElement(): HTMLElement
 
9192         // Returns the instance of [`HTMLImageElement`](https://developer.mozilla.org/docs/Web/API/HTMLImageElement)
 
9193         // used by this overlay.
 
9194         getElement: function () {
 
9198         _initImage: function () {
 
9199                 var wasElementSupplied = this._url.tagName === 'IMG';
 
9200                 var img = this._image = wasElementSupplied ? this._url : create$1('img');
 
9202                 addClass(img, 'leaflet-image-layer');
 
9203                 if (this._zoomAnimated) { addClass(img, 'leaflet-zoom-animated'); }
 
9204                 if (this.options.className) { addClass(img, this.options.className); }
 
9206                 img.onselectstart = falseFn;
 
9207                 img.onmousemove = falseFn;
 
9209                 // @event load: Event
 
9210                 // Fired when the ImageOverlay layer has loaded its image
 
9211                 img.onload = bind(this.fire, this, 'load');
 
9212                 img.onerror = bind(this._overlayOnError, this, 'error');
 
9214                 if (this.options.crossOrigin || this.options.crossOrigin === '') {
 
9215                         img.crossOrigin = this.options.crossOrigin === true ? '' : this.options.crossOrigin;
 
9218                 if (this.options.zIndex) {
 
9219                         this._updateZIndex();
 
9222                 if (wasElementSupplied) {
 
9223                         this._url = img.src;
 
9227                 img.src = this._url;
 
9228                 img.alt = this.options.alt;
 
9231         _animateZoom: function (e) {
 
9232                 var scale = this._map.getZoomScale(e.zoom),
 
9233                     offset = this._map._latLngBoundsToNewLayerBounds(this._bounds, e.zoom, e.center).min;
 
9235                 setTransform(this._image, offset, scale);
 
9238         _reset: function () {
 
9239                 var image = this._image,
 
9240                     bounds = new Bounds(
 
9241                         this._map.latLngToLayerPoint(this._bounds.getNorthWest()),
 
9242                         this._map.latLngToLayerPoint(this._bounds.getSouthEast())),
 
9243                     size = bounds.getSize();
 
9245                 setPosition(image, bounds.min);
 
9247                 image.style.width  = size.x + 'px';
 
9248                 image.style.height = size.y + 'px';
 
9251         _updateOpacity: function () {
 
9252                 setOpacity(this._image, this.options.opacity);
 
9255         _updateZIndex: function () {
 
9256                 if (this._image && this.options.zIndex !== undefined && this.options.zIndex !== null) {
 
9257                         this._image.style.zIndex = this.options.zIndex;
 
9261         _overlayOnError: function () {
 
9262                 // @event error: Event
 
9263                 // Fired when the ImageOverlay layer fails to load its image
 
9266                 var errorUrl = this.options.errorOverlayUrl;
 
9267                 if (errorUrl && this._url !== errorUrl) {
 
9268                         this._url = errorUrl;
 
9269                         this._image.src = errorUrl;
 
9274 // @factory L.imageOverlay(imageUrl: String, bounds: LatLngBounds, options?: ImageOverlay options)
 
9275 // Instantiates an image overlay object given the URL of the image and the
 
9276 // geographical bounds it is tied to.
 
9277 var imageOverlay = function (url, bounds, options) {
 
9278         return new ImageOverlay(url, bounds, options);
 
9282  * @class VideoOverlay
 
9283  * @aka L.VideoOverlay
 
9284  * @inherits ImageOverlay
 
9286  * Used to load and display a video player over specific bounds of the map. Extends `ImageOverlay`.
 
9288  * A video overlay uses the [`<video>`](https://developer.mozilla.org/docs/Web/HTML/Element/video)
 
9294  * var videoUrl = 'https://www.mapbox.com/bites/00188/patricia_nasa.webm',
 
9295  *      videoBounds = [[ 32, -130], [ 13, -100]];
 
9296  * L.videoOverlay(videoUrl, videoBounds ).addTo(map);
 
9300 var VideoOverlay = ImageOverlay.extend({
 
9303         // @aka VideoOverlay options
 
9305                 // @option autoplay: Boolean = true
 
9306                 // Whether the video starts playing automatically when loaded.
 
9309                 // @option loop: Boolean = true
 
9310                 // Whether the video will loop back to the beginning when played.
 
9313                 // @option keepAspectRatio: Boolean = true
 
9314                 // Whether the video will save aspect ratio after the projection.
 
9315                 // Relevant for supported browsers. Browser compatibility- https://developer.mozilla.org/en-US/docs/Web/CSS/object-fit
 
9316                 keepAspectRatio: true
 
9319         _initImage: function () {
 
9320                 var wasElementSupplied = this._url.tagName === 'VIDEO';
 
9321                 var vid = this._image = wasElementSupplied ? this._url : create$1('video');
 
9323                 addClass(vid, 'leaflet-image-layer');
 
9324                 if (this._zoomAnimated) { addClass(vid, 'leaflet-zoom-animated'); }
 
9326                 vid.onselectstart = falseFn;
 
9327                 vid.onmousemove = falseFn;
 
9329                 // @event load: Event
 
9330                 // Fired when the video has finished loading the first frame
 
9331                 vid.onloadeddata = bind(this.fire, this, 'load');
 
9333                 if (wasElementSupplied) {
 
9334                         var sourceElements = vid.getElementsByTagName('source');
 
9336                         for (var j = 0; j < sourceElements.length; j++) {
 
9337                                 sources.push(sourceElements[j].src);
 
9340                         this._url = (sourceElements.length > 0) ? sources : [vid.src];
 
9344                 if (!isArray(this._url)) { this._url = [this._url]; }
 
9346                 if (!this.options.keepAspectRatio && vid.style.hasOwnProperty('objectFit')) { vid.style['objectFit'] = 'fill'; }
 
9347                 vid.autoplay = !!this.options.autoplay;
 
9348                 vid.loop = !!this.options.loop;
 
9349                 for (var i = 0; i < this._url.length; i++) {
 
9350                         var source = create$1('source');
 
9351                         source.src = this._url[i];
 
9352                         vid.appendChild(source);
 
9356         // @method getElement(): HTMLVideoElement
 
9357         // Returns the instance of [`HTMLVideoElement`](https://developer.mozilla.org/docs/Web/API/HTMLVideoElement)
 
9358         // used by this overlay.
 
9362 // @factory L.videoOverlay(video: String|Array|HTMLVideoElement, bounds: LatLngBounds, options?: VideoOverlay options)
 
9363 // Instantiates an image overlay object given the URL of the video (or array of URLs, or even a video element) and the
 
9364 // geographical bounds it is tied to.
 
9366 function videoOverlay(video, bounds, options) {
 
9367         return new VideoOverlay(video, bounds, options);
 
9373  * @inherits ImageOverlay
 
9375  * Used to load, display and provide DOM access to an SVG file over specific bounds of the map. Extends `ImageOverlay`.
 
9377  * An SVG overlay uses the [`<svg>`](https://developer.mozilla.org/docs/Web/SVG/Element/svg) element.
 
9382  * var element = '<svg width="200" height="200" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"><image xlink:href="https://mdn.mozillademos.org/files/6457/mdn_logo_only_color.png" height="200" width="200"/></svg>',
 
9383  *               elementBounds = [ [ 32, -130 ], [ 13, -100 ] ];
 
9384  * L.svgOverlay(element, elementBounds).addTo(map);
 
9388 var SVGOverlay = ImageOverlay.extend({
 
9389         _initImage: function () {
 
9390                 var el = this._image = this._url;
 
9392                 addClass(el, 'leaflet-image-layer');
 
9393                 if (this._zoomAnimated) { addClass(el, 'leaflet-zoom-animated'); }
 
9395                 el.onselectstart = falseFn;
 
9396                 el.onmousemove = falseFn;
 
9399         // @method getElement(): SVGElement
 
9400         // Returns the instance of [`SVGElement`](https://developer.mozilla.org/docs/Web/API/SVGElement)
 
9401         // used by this overlay.
 
9405 // @factory L.svgOverlay(svg: String|SVGElement, bounds: LatLngBounds, options?: SVGOverlay options)
 
9406 // Instantiates an image overlay object given an SVG element and the geographical bounds it is tied to.
 
9407 // A viewBox attribute is required on the SVG element to zoom in and out properly.
 
9409 function svgOverlay(el, bounds, options) {
 
9410         return new SVGOverlay(el, bounds, options);
 
9417  * Base model for L.Popup and L.Tooltip. Inherit from it for custom popup like plugins.
 
9420 // @namespace DivOverlay
 
9421 var DivOverlay = Layer.extend({
 
9424         // @aka DivOverlay options
 
9426                 // @option offset: Point = Point(0, 7)
 
9427                 // The offset of the popup position. Useful to control the anchor
 
9428                 // of the popup when opening it on some overlays.
 
9431                 // @option className: String = ''
 
9432                 // A custom CSS class name to assign to the popup.
 
9435                 // @option pane: String = 'popupPane'
 
9436                 // `Map pane` where the popup will be added.
 
9440         initialize: function (options, source) {
 
9441                 setOptions(this, options);
 
9443                 this._source = source;
 
9446         onAdd: function (map) {
 
9447                 this._zoomAnimated = map._zoomAnimated;
 
9449                 if (!this._container) {
 
9453                 if (map._fadeAnimated) {
 
9454                         setOpacity(this._container, 0);
 
9457                 clearTimeout(this._removeTimeout);
 
9458                 this.getPane().appendChild(this._container);
 
9461                 if (map._fadeAnimated) {
 
9462                         setOpacity(this._container, 1);
 
9465                 this.bringToFront();
 
9468         onRemove: function (map) {
 
9469                 if (map._fadeAnimated) {
 
9470                         setOpacity(this._container, 0);
 
9471                         this._removeTimeout = setTimeout(bind(remove, undefined, this._container), 200);
 
9473                         remove(this._container);
 
9478         // @method getLatLng: LatLng
 
9479         // Returns the geographical point of popup.
 
9480         getLatLng: function () {
 
9481                 return this._latlng;
 
9484         // @method setLatLng(latlng: LatLng): this
 
9485         // Sets the geographical point where the popup will open.
 
9486         setLatLng: function (latlng) {
 
9487                 this._latlng = toLatLng(latlng);
 
9489                         this._updatePosition();
 
9495         // @method getContent: String|HTMLElement
 
9496         // Returns the content of the popup.
 
9497         getContent: function () {
 
9498                 return this._content;
 
9501         // @method setContent(htmlContent: String|HTMLElement|Function): this
 
9502         // Sets the HTML content of the popup. If a function is passed the source layer will be passed to the function. The function should return a `String` or `HTMLElement` to be used in the popup.
 
9503         setContent: function (content) {
 
9504                 this._content = content;
 
9509         // @method getElement: String|HTMLElement
 
9510         // Alias for [getContent()](#popup-getcontent)
 
9511         getElement: function () {
 
9512                 return this._container;
 
9515         // @method update: null
 
9516         // Updates the popup content, layout and position. Useful for updating the popup after something inside changed, e.g. image loaded.
 
9517         update: function () {
 
9518                 if (!this._map) { return; }
 
9520                 this._container.style.visibility = 'hidden';
 
9522                 this._updateContent();
 
9523                 this._updateLayout();
 
9524                 this._updatePosition();
 
9526                 this._container.style.visibility = '';
 
9531         getEvents: function () {
 
9533                         zoom: this._updatePosition,
 
9534                         viewreset: this._updatePosition
 
9537                 if (this._zoomAnimated) {
 
9538                         events.zoomanim = this._animateZoom;
 
9543         // @method isOpen: Boolean
 
9544         // Returns `true` when the popup is visible on the map.
 
9545         isOpen: function () {
 
9546                 return !!this._map && this._map.hasLayer(this);
 
9549         // @method bringToFront: this
 
9550         // Brings this popup in front of other popups (in the same map pane).
 
9551         bringToFront: function () {
 
9553                         toFront(this._container);
 
9558         // @method bringToBack: this
 
9559         // Brings this popup to the back of other popups (in the same map pane).
 
9560         bringToBack: function () {
 
9562                         toBack(this._container);
 
9567         _prepareOpen: function (parent, layer, latlng) {
 
9568                 if (!(layer instanceof Layer)) {
 
9573                 if (layer instanceof FeatureGroup) {
 
9574                         for (var id in parent._layers) {
 
9575                                 layer = parent._layers[id];
 
9581                         if (layer.getCenter) {
 
9582                                 latlng = layer.getCenter();
 
9583                         } else if (layer.getLatLng) {
 
9584                                 latlng = layer.getLatLng();
 
9586                                 throw new Error('Unable to get source layer LatLng.');
 
9590                 // set overlay source to this layer
 
9591                 this._source = layer;
 
9593                 // update the overlay (content, layout, ect...)
 
9599         _updateContent: function () {
 
9600                 if (!this._content) { return; }
 
9602                 var node = this._contentNode;
 
9603                 var content = (typeof this._content === 'function') ? this._content(this._source || this) : this._content;
 
9605                 if (typeof content === 'string') {
 
9606                         node.innerHTML = content;
 
9608                         while (node.hasChildNodes()) {
 
9609                                 node.removeChild(node.firstChild);
 
9611                         node.appendChild(content);
 
9613                 this.fire('contentupdate');
 
9616         _updatePosition: function () {
 
9617                 if (!this._map) { return; }
 
9619                 var pos = this._map.latLngToLayerPoint(this._latlng),
 
9620                     offset = toPoint(this.options.offset),
 
9621                     anchor = this._getAnchor();
 
9623                 if (this._zoomAnimated) {
 
9624                         setPosition(this._container, pos.add(anchor));
 
9626                         offset = offset.add(pos).add(anchor);
 
9629                 var bottom = this._containerBottom = -offset.y,
 
9630                     left = this._containerLeft = -Math.round(this._containerWidth / 2) + offset.x;
 
9632                 // bottom position the popup in case the height of the popup changes (images loading etc)
 
9633                 this._container.style.bottom = bottom + 'px';
 
9634                 this._container.style.left = left + 'px';
 
9637         _getAnchor: function () {
 
9645  * @inherits DivOverlay
 
9647  * Used to open popups in certain places of the map. Use [Map.openPopup](#map-openpopup) to
 
9648  * open popups while making sure that only one popup is open at one time
 
9649  * (recommended for usability), or use [Map.addLayer](#map-addlayer) to open as many as you want.
 
9653  * If you want to just bind a popup to marker click and then open it, it's really easy:
 
9656  * marker.bindPopup(popupContent).openPopup();
 
9658  * Path overlays like polylines also have a `bindPopup` method.
 
9659  * Here's a more complicated way to open a popup on a map:
 
9662  * var popup = L.popup()
 
9663  *      .setLatLng(latlng)
 
9664  *      .setContent('<p>Hello world!<br />This is a nice popup.</p>')
 
9671 var Popup = DivOverlay.extend({
 
9674         // @aka Popup options
 
9676                 // @option maxWidth: Number = 300
 
9677                 // Max width of the popup, in pixels.
 
9680                 // @option minWidth: Number = 50
 
9681                 // Min width of the popup, in pixels.
 
9684                 // @option maxHeight: Number = null
 
9685                 // If set, creates a scrollable container of the given height
 
9686                 // inside a popup if its content exceeds it.
 
9689                 // @option autoPan: Boolean = true
 
9690                 // Set it to `false` if you don't want the map to do panning animation
 
9691                 // to fit the opened popup.
 
9694                 // @option autoPanPaddingTopLeft: Point = null
 
9695                 // The margin between the popup and the top left corner of the map
 
9696                 // view after autopanning was performed.
 
9697                 autoPanPaddingTopLeft: null,
 
9699                 // @option autoPanPaddingBottomRight: Point = null
 
9700                 // The margin between the popup and the bottom right corner of the map
 
9701                 // view after autopanning was performed.
 
9702                 autoPanPaddingBottomRight: null,
 
9704                 // @option autoPanPadding: Point = Point(5, 5)
 
9705                 // Equivalent of setting both top left and bottom right autopan padding to the same value.
 
9706                 autoPanPadding: [5, 5],
 
9708                 // @option keepInView: Boolean = false
 
9709                 // Set it to `true` if you want to prevent users from panning the popup
 
9710                 // off of the screen while it is open.
 
9713                 // @option closeButton: Boolean = true
 
9714                 // Controls the presence of a close button in the popup.
 
9717                 // @option autoClose: Boolean = true
 
9718                 // Set it to `false` if you want to override the default behavior of
 
9719                 // the popup closing when another popup is opened.
 
9722                 // @option closeOnEscapeKey: Boolean = true
 
9723                 // Set it to `false` if you want to override the default behavior of
 
9724                 // the ESC key for closing of the popup.
 
9725                 closeOnEscapeKey: true,
 
9727                 // @option closeOnClick: Boolean = *
 
9728                 // Set it if you want to override the default behavior of the popup closing when user clicks
 
9729                 // on the map. Defaults to the map's [`closePopupOnClick`](#map-closepopuponclick) option.
 
9731                 // @option className: String = ''
 
9732                 // A custom CSS class name to assign to the popup.
 
9737         // @method openOn(map: Map): this
 
9738         // Adds the popup to the map and closes the previous one. The same as `map.openPopup(popup)`.
 
9739         openOn: function (map) {
 
9740                 map.openPopup(this);
 
9744         onAdd: function (map) {
 
9745                 DivOverlay.prototype.onAdd.call(this, map);
 
9748                 // @section Popup events
 
9749                 // @event popupopen: PopupEvent
 
9750                 // Fired when a popup is opened in the map
 
9751                 map.fire('popupopen', {popup: this});
 
9755                         // @section Popup events
 
9756                         // @event popupopen: PopupEvent
 
9757                         // Fired when a popup bound to this layer is opened
 
9758                         this._source.fire('popupopen', {popup: this}, true);
 
9759                         // For non-path layers, we toggle the popup when clicking
 
9760                         // again the layer, so prevent the map to reopen it.
 
9761                         if (!(this._source instanceof Path)) {
 
9762                                 this._source.on('preclick', stopPropagation);
 
9767         onRemove: function (map) {
 
9768                 DivOverlay.prototype.onRemove.call(this, map);
 
9771                 // @section Popup events
 
9772                 // @event popupclose: PopupEvent
 
9773                 // Fired when a popup in the map is closed
 
9774                 map.fire('popupclose', {popup: this});
 
9778                         // @section Popup events
 
9779                         // @event popupclose: PopupEvent
 
9780                         // Fired when a popup bound to this layer is closed
 
9781                         this._source.fire('popupclose', {popup: this}, true);
 
9782                         if (!(this._source instanceof Path)) {
 
9783                                 this._source.off('preclick', stopPropagation);
 
9788         getEvents: function () {
 
9789                 var events = DivOverlay.prototype.getEvents.call(this);
 
9791                 if (this.options.closeOnClick !== undefined ? this.options.closeOnClick : this._map.options.closePopupOnClick) {
 
9792                         events.preclick = this._close;
 
9795                 if (this.options.keepInView) {
 
9796                         events.moveend = this._adjustPan;
 
9802         _close: function () {
 
9804                         this._map.closePopup(this);
 
9808         _initLayout: function () {
 
9809                 var prefix = 'leaflet-popup',
 
9810                     container = this._container = create$1('div',
 
9811                         prefix + ' ' + (this.options.className || '') +
 
9812                         ' leaflet-zoom-animated');
 
9814                 var wrapper = this._wrapper = create$1('div', prefix + '-content-wrapper', container);
 
9815                 this._contentNode = create$1('div', prefix + '-content', wrapper);
 
9817                 disableClickPropagation(wrapper);
 
9818                 disableScrollPropagation(this._contentNode);
 
9819                 on(wrapper, 'contextmenu', stopPropagation);
 
9821                 this._tipContainer = create$1('div', prefix + '-tip-container', container);
 
9822                 this._tip = create$1('div', prefix + '-tip', this._tipContainer);
 
9824                 if (this.options.closeButton) {
 
9825                         var closeButton = this._closeButton = create$1('a', prefix + '-close-button', container);
 
9826                         closeButton.href = '#close';
 
9827                         closeButton.innerHTML = '×';
 
9829                         on(closeButton, 'click', this._onCloseButtonClick, this);
 
9833         _updateLayout: function () {
 
9834                 var container = this._contentNode,
 
9835                     style = container.style;
 
9838                 style.whiteSpace = 'nowrap';
 
9840                 var width = container.offsetWidth;
 
9841                 width = Math.min(width, this.options.maxWidth);
 
9842                 width = Math.max(width, this.options.minWidth);
 
9844                 style.width = (width + 1) + 'px';
 
9845                 style.whiteSpace = '';
 
9849                 var height = container.offsetHeight,
 
9850                     maxHeight = this.options.maxHeight,
 
9851                     scrolledClass = 'leaflet-popup-scrolled';
 
9853                 if (maxHeight && height > maxHeight) {
 
9854                         style.height = maxHeight + 'px';
 
9855                         addClass(container, scrolledClass);
 
9857                         removeClass(container, scrolledClass);
 
9860                 this._containerWidth = this._container.offsetWidth;
 
9863         _animateZoom: function (e) {
 
9864                 var pos = this._map._latLngToNewLayerPoint(this._latlng, e.zoom, e.center),
 
9865                     anchor = this._getAnchor();
 
9866                 setPosition(this._container, pos.add(anchor));
 
9869         _adjustPan: function () {
 
9870                 if (!this.options.autoPan) { return; }
 
9871                 if (this._map._panAnim) { this._map._panAnim.stop(); }
 
9873                 var map = this._map,
 
9874                     marginBottom = parseInt(getStyle(this._container, 'marginBottom'), 10) || 0,
 
9875                     containerHeight = this._container.offsetHeight + marginBottom,
 
9876                     containerWidth = this._containerWidth,
 
9877                     layerPos = new Point(this._containerLeft, -containerHeight - this._containerBottom);
 
9879                 layerPos._add(getPosition(this._container));
 
9881                 var containerPos = map.layerPointToContainerPoint(layerPos),
 
9882                     padding = toPoint(this.options.autoPanPadding),
 
9883                     paddingTL = toPoint(this.options.autoPanPaddingTopLeft || padding),
 
9884                     paddingBR = toPoint(this.options.autoPanPaddingBottomRight || padding),
 
9885                     size = map.getSize(),
 
9889                 if (containerPos.x + containerWidth + paddingBR.x > size.x) { // right
 
9890                         dx = containerPos.x + containerWidth - size.x + paddingBR.x;
 
9892                 if (containerPos.x - dx - paddingTL.x < 0) { // left
 
9893                         dx = containerPos.x - paddingTL.x;
 
9895                 if (containerPos.y + containerHeight + paddingBR.y > size.y) { // bottom
 
9896                         dy = containerPos.y + containerHeight - size.y + paddingBR.y;
 
9898                 if (containerPos.y - dy - paddingTL.y < 0) { // top
 
9899                         dy = containerPos.y - paddingTL.y;
 
9903                 // @section Popup events
 
9904                 // @event autopanstart: Event
 
9905                 // Fired when the map starts autopanning when opening a popup.
 
9908                             .fire('autopanstart')
 
9913         _onCloseButtonClick: function (e) {
 
9918         _getAnchor: function () {
 
9919                 // Where should we anchor the popup on the source layer?
 
9920                 return toPoint(this._source && this._source._getPopupAnchor ? this._source._getPopupAnchor() : [0, 0]);
 
9926 // @factory L.popup(options?: Popup options, source?: Layer)
 
9927 // Instantiates a `Popup` object given an optional `options` object that describes its appearance and location and an optional `source` object that is used to tag the popup with a reference to the Layer to which it refers.
 
9928 var popup = function (options, source) {
 
9929         return new Popup(options, source);
 
9934  * @section Interaction Options
 
9935  * @option closePopupOnClick: Boolean = true
 
9936  * Set it to `false` if you don't want popups to close when user clicks the map.
 
9939         closePopupOnClick: true
 
9944 // @section Methods for Layers and Controls
 
9946         // @method openPopup(popup: Popup): this
 
9947         // Opens the specified popup while closing the previously opened (to make sure only one is opened at one time for usability).
 
9949         // @method openPopup(content: String|HTMLElement, latlng: LatLng, options?: Popup options): this
 
9950         // Creates a popup with the specified content and options and opens it in the given point on a map.
 
9951         openPopup: function (popup, latlng, options) {
 
9952                 if (!(popup instanceof Popup)) {
 
9953                         popup = new Popup(options).setContent(popup);
 
9957                         popup.setLatLng(latlng);
 
9960                 if (this.hasLayer(popup)) {
 
9964                 if (this._popup && this._popup.options.autoClose) {
 
9968                 this._popup = popup;
 
9969                 return this.addLayer(popup);
 
9972         // @method closePopup(popup?: Popup): this
 
9973         // Closes the popup previously opened with [openPopup](#map-openpopup) (or the given one).
 
9974         closePopup: function (popup) {
 
9975                 if (!popup || popup === this._popup) {
 
9976                         popup = this._popup;
 
9980                         this.removeLayer(popup);
 
9988  * @section Popup methods example
 
9990  * All layers share a set of methods convenient for binding popups to it.
 
9993  * var layer = L.Polygon(latlngs).bindPopup('Hi There!').addTo(map);
 
9994  * layer.openPopup();
 
9995  * layer.closePopup();
 
9998  * Popups will also be automatically opened when the layer is clicked on and closed when the layer is removed from the map or another popup is opened.
 
10001 // @section Popup methods
 
10004         // @method bindPopup(content: String|HTMLElement|Function|Popup, options?: Popup options): this
 
10005         // Binds a popup to the layer with the passed `content` and sets up the
 
10006         // necessary event listeners. If a `Function` is passed it will receive
 
10007         // the layer as the first argument and should return a `String` or `HTMLElement`.
 
10008         bindPopup: function (content, options) {
 
10010                 if (content instanceof Popup) {
 
10011                         setOptions(content, options);
 
10012                         this._popup = content;
 
10013                         content._source = this;
 
10015                         if (!this._popup || options) {
 
10016                                 this._popup = new Popup(options, this);
 
10018                         this._popup.setContent(content);
 
10021                 if (!this._popupHandlersAdded) {
 
10023                                 click: this._openPopup,
 
10024                                 keypress: this._onKeyPress,
 
10025                                 remove: this.closePopup,
 
10026                                 move: this._movePopup
 
10028                         this._popupHandlersAdded = true;
 
10034         // @method unbindPopup(): this
 
10035         // Removes the popup previously bound with `bindPopup`.
 
10036         unbindPopup: function () {
 
10039                                 click: this._openPopup,
 
10040                                 keypress: this._onKeyPress,
 
10041                                 remove: this.closePopup,
 
10042                                 move: this._movePopup
 
10044                         this._popupHandlersAdded = false;
 
10045                         this._popup = null;
 
10050         // @method openPopup(latlng?: LatLng): this
 
10051         // Opens the bound popup at the specified `latlng` or at the default popup anchor if no `latlng` is passed.
 
10052         openPopup: function (layer, latlng) {
 
10053                 if (this._popup && this._map) {
 
10054                         latlng = this._popup._prepareOpen(this, layer, latlng);
 
10056                         // open the popup on the map
 
10057                         this._map.openPopup(this._popup, latlng);
 
10063         // @method closePopup(): this
 
10064         // Closes the popup bound to this layer if it is open.
 
10065         closePopup: function () {
 
10067                         this._popup._close();
 
10072         // @method togglePopup(): this
 
10073         // Opens or closes the popup bound to this layer depending on its current state.
 
10074         togglePopup: function (target) {
 
10076                         if (this._popup._map) {
 
10079                                 this.openPopup(target);
 
10085         // @method isPopupOpen(): boolean
 
10086         // Returns `true` if the popup bound to this layer is currently open.
 
10087         isPopupOpen: function () {
 
10088                 return (this._popup ? this._popup.isOpen() : false);
 
10091         // @method setPopupContent(content: String|HTMLElement|Popup): this
 
10092         // Sets the content of the popup bound to this layer.
 
10093         setPopupContent: function (content) {
 
10095                         this._popup.setContent(content);
 
10100         // @method getPopup(): Popup
 
10101         // Returns the popup bound to this layer.
 
10102         getPopup: function () {
 
10103                 return this._popup;
 
10106         _openPopup: function (e) {
 
10107                 var layer = e.layer || e.target;
 
10109                 if (!this._popup) {
 
10117                 // prevent map click
 
10120                 // if this inherits from Path its a vector and we can just
 
10121                 // open the popup at the new location
 
10122                 if (layer instanceof Path) {
 
10123                         this.openPopup(e.layer || e.target, e.latlng);
 
10127                 // otherwise treat it like a marker and figure out
 
10128                 // if we should toggle it open/closed
 
10129                 if (this._map.hasLayer(this._popup) && this._popup._source === layer) {
 
10132                         this.openPopup(layer, e.latlng);
 
10136         _movePopup: function (e) {
 
10137                 this._popup.setLatLng(e.latlng);
 
10140         _onKeyPress: function (e) {
 
10141                 if (e.originalEvent.keyCode === 13) {
 
10142                         this._openPopup(e);
 
10149  * @inherits DivOverlay
 
10151  * Used to display small texts on top of map layers.
 
10156  * marker.bindTooltip("my tooltip text").openTooltip();
 
10158  * Note about tooltip offset. Leaflet takes two options in consideration
 
10159  * for computing tooltip offsetting:
 
10160  * - the `offset` Tooltip option: it defaults to [0, 0], and it's specific to one tooltip.
 
10161  *   Add a positive x offset to move the tooltip to the right, and a positive y offset to
 
10162  *   move it to the bottom. Negatives will move to the left and top.
 
10163  * - the `tooltipAnchor` Icon option: this will only be considered for Marker. You
 
10164  *   should adapt this value if you use a custom icon.
 
10168 // @namespace Tooltip
 
10169 var Tooltip = DivOverlay.extend({
 
10172         // @aka Tooltip options
 
10174                 // @option pane: String = 'tooltipPane'
 
10175                 // `Map pane` where the tooltip will be added.
 
10176                 pane: 'tooltipPane',
 
10178                 // @option offset: Point = Point(0, 0)
 
10179                 // Optional offset of the tooltip position.
 
10182                 // @option direction: String = 'auto'
 
10183                 // Direction where to open the tooltip. Possible values are: `right`, `left`,
 
10184                 // `top`, `bottom`, `center`, `auto`.
 
10185                 // `auto` will dynamically switch between `right` and `left` according to the tooltip
 
10186                 // position on the map.
 
10189                 // @option permanent: Boolean = false
 
10190                 // Whether to open the tooltip permanently or only on mouseover.
 
10193                 // @option sticky: Boolean = false
 
10194                 // If true, the tooltip will follow the mouse instead of being fixed at the feature center.
 
10197                 // @option interactive: Boolean = false
 
10198                 // If true, the tooltip will listen to the feature events.
 
10199                 interactive: false,
 
10201                 // @option opacity: Number = 0.9
 
10202                 // Tooltip container opacity.
 
10206         onAdd: function (map) {
 
10207                 DivOverlay.prototype.onAdd.call(this, map);
 
10208                 this.setOpacity(this.options.opacity);
 
10211                 // @section Tooltip events
 
10212                 // @event tooltipopen: TooltipEvent
 
10213                 // Fired when a tooltip is opened in the map.
 
10214                 map.fire('tooltipopen', {tooltip: this});
 
10216                 if (this._source) {
 
10217                         // @namespace Layer
 
10218                         // @section Tooltip events
 
10219                         // @event tooltipopen: TooltipEvent
 
10220                         // Fired when a tooltip bound to this layer is opened.
 
10221                         this._source.fire('tooltipopen', {tooltip: this}, true);
 
10225         onRemove: function (map) {
 
10226                 DivOverlay.prototype.onRemove.call(this, map);
 
10229                 // @section Tooltip events
 
10230                 // @event tooltipclose: TooltipEvent
 
10231                 // Fired when a tooltip in the map is closed.
 
10232                 map.fire('tooltipclose', {tooltip: this});
 
10234                 if (this._source) {
 
10235                         // @namespace Layer
 
10236                         // @section Tooltip events
 
10237                         // @event tooltipclose: TooltipEvent
 
10238                         // Fired when a tooltip bound to this layer is closed.
 
10239                         this._source.fire('tooltipclose', {tooltip: this}, true);
 
10243         getEvents: function () {
 
10244                 var events = DivOverlay.prototype.getEvents.call(this);
 
10246                 if (touch && !this.options.permanent) {
 
10247                         events.preclick = this._close;
 
10253         _close: function () {
 
10255                         this._map.closeTooltip(this);
 
10259         _initLayout: function () {
 
10260                 var prefix = 'leaflet-tooltip',
 
10261                     className = prefix + ' ' + (this.options.className || '') + ' leaflet-zoom-' + (this._zoomAnimated ? 'animated' : 'hide');
 
10263                 this._contentNode = this._container = create$1('div', className);
 
10266         _updateLayout: function () {},
 
10268         _adjustPan: function () {},
 
10270         _setPosition: function (pos) {
 
10271                 var map = this._map,
 
10272                     container = this._container,
 
10273                     centerPoint = map.latLngToContainerPoint(map.getCenter()),
 
10274                     tooltipPoint = map.layerPointToContainerPoint(pos),
 
10275                     direction = this.options.direction,
 
10276                     tooltipWidth = container.offsetWidth,
 
10277                     tooltipHeight = container.offsetHeight,
 
10278                     offset = toPoint(this.options.offset),
 
10279                     anchor = this._getAnchor();
 
10281                 if (direction === 'top') {
 
10282                         pos = pos.add(toPoint(-tooltipWidth / 2 + offset.x, -tooltipHeight + offset.y + anchor.y, true));
 
10283                 } else if (direction === 'bottom') {
 
10284                         pos = pos.subtract(toPoint(tooltipWidth / 2 - offset.x, -offset.y, true));
 
10285                 } else if (direction === 'center') {
 
10286                         pos = pos.subtract(toPoint(tooltipWidth / 2 + offset.x, tooltipHeight / 2 - anchor.y + offset.y, true));
 
10287                 } else if (direction === 'right' || direction === 'auto' && tooltipPoint.x < centerPoint.x) {
 
10288                         direction = 'right';
 
10289                         pos = pos.add(toPoint(offset.x + anchor.x, anchor.y - tooltipHeight / 2 + offset.y, true));
 
10291                         direction = 'left';
 
10292                         pos = pos.subtract(toPoint(tooltipWidth + anchor.x - offset.x, tooltipHeight / 2 - anchor.y - offset.y, true));
 
10295                 removeClass(container, 'leaflet-tooltip-right');
 
10296                 removeClass(container, 'leaflet-tooltip-left');
 
10297                 removeClass(container, 'leaflet-tooltip-top');
 
10298                 removeClass(container, 'leaflet-tooltip-bottom');
 
10299                 addClass(container, 'leaflet-tooltip-' + direction);
 
10300                 setPosition(container, pos);
 
10303         _updatePosition: function () {
 
10304                 var pos = this._map.latLngToLayerPoint(this._latlng);
 
10305                 this._setPosition(pos);
 
10308         setOpacity: function (opacity) {
 
10309                 this.options.opacity = opacity;
 
10311                 if (this._container) {
 
10312                         setOpacity(this._container, opacity);
 
10316         _animateZoom: function (e) {
 
10317                 var pos = this._map._latLngToNewLayerPoint(this._latlng, e.zoom, e.center);
 
10318                 this._setPosition(pos);
 
10321         _getAnchor: function () {
 
10322                 // Where should we anchor the tooltip on the source layer?
 
10323                 return toPoint(this._source && this._source._getTooltipAnchor && !this.options.sticky ? this._source._getTooltipAnchor() : [0, 0]);
 
10328 // @namespace Tooltip
 
10329 // @factory L.tooltip(options?: Tooltip options, source?: Layer)
 
10330 // Instantiates a Tooltip object given an optional `options` object that describes its appearance and location and an optional `source` object that is used to tag the tooltip with a reference to the Layer to which it refers.
 
10331 var tooltip = function (options, source) {
 
10332         return new Tooltip(options, source);
 
10336 // @section Methods for Layers and Controls
 
10339         // @method openTooltip(tooltip: Tooltip): this
 
10340         // Opens the specified tooltip.
 
10342         // @method openTooltip(content: String|HTMLElement, latlng: LatLng, options?: Tooltip options): this
 
10343         // Creates a tooltip with the specified content and options and open it.
 
10344         openTooltip: function (tooltip, latlng, options) {
 
10345                 if (!(tooltip instanceof Tooltip)) {
 
10346                         tooltip = new Tooltip(options).setContent(tooltip);
 
10350                         tooltip.setLatLng(latlng);
 
10353                 if (this.hasLayer(tooltip)) {
 
10357                 return this.addLayer(tooltip);
 
10360         // @method closeTooltip(tooltip?: Tooltip): this
 
10361         // Closes the tooltip given as parameter.
 
10362         closeTooltip: function (tooltip) {
 
10364                         this.removeLayer(tooltip);
 
10373  * @section Tooltip methods example
 
10375  * All layers share a set of methods convenient for binding tooltips to it.
 
10378  * var layer = L.Polygon(latlngs).bindTooltip('Hi There!').addTo(map);
 
10379  * layer.openTooltip();
 
10380  * layer.closeTooltip();
 
10384 // @section Tooltip methods
 
10387         // @method bindTooltip(content: String|HTMLElement|Function|Tooltip, options?: Tooltip options): this
 
10388         // Binds a tooltip to the layer with the passed `content` and sets up the
 
10389         // necessary event listeners. If a `Function` is passed it will receive
 
10390         // the layer as the first argument and should return a `String` or `HTMLElement`.
 
10391         bindTooltip: function (content, options) {
 
10393                 if (content instanceof Tooltip) {
 
10394                         setOptions(content, options);
 
10395                         this._tooltip = content;
 
10396                         content._source = this;
 
10398                         if (!this._tooltip || options) {
 
10399                                 this._tooltip = new Tooltip(options, this);
 
10401                         this._tooltip.setContent(content);
 
10405                 this._initTooltipInteractions();
 
10407                 if (this._tooltip.options.permanent && this._map && this._map.hasLayer(this)) {
 
10408                         this.openTooltip();
 
10414         // @method unbindTooltip(): this
 
10415         // Removes the tooltip previously bound with `bindTooltip`.
 
10416         unbindTooltip: function () {
 
10417                 if (this._tooltip) {
 
10418                         this._initTooltipInteractions(true);
 
10419                         this.closeTooltip();
 
10420                         this._tooltip = null;
 
10425         _initTooltipInteractions: function (remove$$1) {
 
10426                 if (!remove$$1 && this._tooltipHandlersAdded) { return; }
 
10427                 var onOff = remove$$1 ? 'off' : 'on',
 
10429                         remove: this.closeTooltip,
 
10430                         move: this._moveTooltip
 
10432                 if (!this._tooltip.options.permanent) {
 
10433                         events.mouseover = this._openTooltip;
 
10434                         events.mouseout = this.closeTooltip;
 
10435                         if (this._tooltip.options.sticky) {
 
10436                                 events.mousemove = this._moveTooltip;
 
10439                                 events.click = this._openTooltip;
 
10442                         events.add = this._openTooltip;
 
10444                 this[onOff](events);
 
10445                 this._tooltipHandlersAdded = !remove$$1;
 
10448         // @method openTooltip(latlng?: LatLng): this
 
10449         // Opens the bound tooltip at the specified `latlng` or at the default tooltip anchor if no `latlng` is passed.
 
10450         openTooltip: function (layer, latlng) {
 
10451                 if (this._tooltip && this._map) {
 
10452                         latlng = this._tooltip._prepareOpen(this, layer, latlng);
 
10454                         // open the tooltip on the map
 
10455                         this._map.openTooltip(this._tooltip, latlng);
 
10457                         // Tooltip container may not be defined if not permanent and never
 
10459                         if (this._tooltip.options.interactive && this._tooltip._container) {
 
10460                                 addClass(this._tooltip._container, 'leaflet-clickable');
 
10461                                 this.addInteractiveTarget(this._tooltip._container);
 
10468         // @method closeTooltip(): this
 
10469         // Closes the tooltip bound to this layer if it is open.
 
10470         closeTooltip: function () {
 
10471                 if (this._tooltip) {
 
10472                         this._tooltip._close();
 
10473                         if (this._tooltip.options.interactive && this._tooltip._container) {
 
10474                                 removeClass(this._tooltip._container, 'leaflet-clickable');
 
10475                                 this.removeInteractiveTarget(this._tooltip._container);
 
10481         // @method toggleTooltip(): this
 
10482         // Opens or closes the tooltip bound to this layer depending on its current state.
 
10483         toggleTooltip: function (target) {
 
10484                 if (this._tooltip) {
 
10485                         if (this._tooltip._map) {
 
10486                                 this.closeTooltip();
 
10488                                 this.openTooltip(target);
 
10494         // @method isTooltipOpen(): boolean
 
10495         // Returns `true` if the tooltip bound to this layer is currently open.
 
10496         isTooltipOpen: function () {
 
10497                 return this._tooltip.isOpen();
 
10500         // @method setTooltipContent(content: String|HTMLElement|Tooltip): this
 
10501         // Sets the content of the tooltip bound to this layer.
 
10502         setTooltipContent: function (content) {
 
10503                 if (this._tooltip) {
 
10504                         this._tooltip.setContent(content);
 
10509         // @method getTooltip(): Tooltip
 
10510         // Returns the tooltip bound to this layer.
 
10511         getTooltip: function () {
 
10512                 return this._tooltip;
 
10515         _openTooltip: function (e) {
 
10516                 var layer = e.layer || e.target;
 
10518                 if (!this._tooltip || !this._map) {
 
10521                 this.openTooltip(layer, this._tooltip.options.sticky ? e.latlng : undefined);
 
10524         _moveTooltip: function (e) {
 
10525                 var latlng = e.latlng, containerPoint, layerPoint;
 
10526                 if (this._tooltip.options.sticky && e.originalEvent) {
 
10527                         containerPoint = this._map.mouseEventToContainerPoint(e.originalEvent);
 
10528                         layerPoint = this._map.containerPointToLayerPoint(containerPoint);
 
10529                         latlng = this._map.layerPointToLatLng(layerPoint);
 
10531                 this._tooltip.setLatLng(latlng);
 
10540  * Represents a lightweight icon for markers that uses a simple `<div>`
 
10541  * element instead of an image. Inherits from `Icon` but ignores the `iconUrl` and shadow options.
 
10545  * var myIcon = L.divIcon({className: 'my-div-icon'});
 
10546  * // you can set .my-div-icon styles in CSS
 
10548  * L.marker([50.505, 30.57], {icon: myIcon}).addTo(map);
 
10551  * By default, it has a 'leaflet-div-icon' CSS class and is styled as a little white square with a shadow.
 
10554 var DivIcon = Icon.extend({
 
10557                 // @aka DivIcon options
 
10558                 iconSize: [12, 12], // also can be set through CSS
 
10560                 // iconAnchor: (Point),
 
10561                 // popupAnchor: (Point),
 
10563                 // @option html: String|HTMLElement = ''
 
10564                 // Custom HTML code to put inside the div element, empty by default. Alternatively,
 
10565                 // an instance of `HTMLElement`.
 
10568                 // @option bgPos: Point = [0, 0]
 
10569                 // Optional relative position of the background, in pixels
 
10572                 className: 'leaflet-div-icon'
 
10575         createIcon: function (oldIcon) {
 
10576                 var div = (oldIcon && oldIcon.tagName === 'DIV') ? oldIcon : document.createElement('div'),
 
10577                     options = this.options;
 
10579                 if (options.html instanceof Element) {
 
10581                         div.appendChild(options.html);
 
10583                         div.innerHTML = options.html !== false ? options.html : '';
 
10586                 if (options.bgPos) {
 
10587                         var bgPos = toPoint(options.bgPos);
 
10588                         div.style.backgroundPosition = (-bgPos.x) + 'px ' + (-bgPos.y) + 'px';
 
10590                 this._setIconStyles(div, 'icon');
 
10595         createShadow: function () {
 
10600 // @factory L.divIcon(options: DivIcon options)
 
10601 // Creates a `DivIcon` instance with the given options.
 
10602 function divIcon(options) {
 
10603         return new DivIcon(options);
 
10606 Icon.Default = IconDefault;
 
10613  * Generic class for handling a tiled grid of HTML elements. This is the base class for all tile layers and replaces `TileLayer.Canvas`.
 
10614  * GridLayer can be extended to create a tiled grid of HTML elements like `<canvas>`, `<img>` or `<div>`. GridLayer will handle creating and animating these DOM elements for you.
 
10617  * @section Synchronous usage
 
10620  * To create a custom layer, extend GridLayer and implement the `createTile()` method, which will be passed a `Point` object with the `x`, `y`, and `z` (zoom level) coordinates to draw your tile.
 
10623  * var CanvasLayer = L.GridLayer.extend({
 
10624  *     createTile: function(coords){
 
10625  *         // create a <canvas> element for drawing
 
10626  *         var tile = L.DomUtil.create('canvas', 'leaflet-tile');
 
10628  *         // setup tile width and height according to the options
 
10629  *         var size = this.getTileSize();
 
10630  *         tile.width = size.x;
 
10631  *         tile.height = size.y;
 
10633  *         // get a canvas context and draw something on it using coords.x, coords.y and coords.z
 
10634  *         var ctx = tile.getContext('2d');
 
10636  *         // return the tile so it can be rendered on screen
 
10642  * @section Asynchronous usage
 
10645  * Tile creation can also be asynchronous, this is useful when using a third-party drawing library. Once the tile is finished drawing it can be passed to the `done()` callback.
 
10648  * var CanvasLayer = L.GridLayer.extend({
 
10649  *     createTile: function(coords, done){
 
10652  *         // create a <canvas> element for drawing
 
10653  *         var tile = L.DomUtil.create('canvas', 'leaflet-tile');
 
10655  *         // setup tile width and height according to the options
 
10656  *         var size = this.getTileSize();
 
10657  *         tile.width = size.x;
 
10658  *         tile.height = size.y;
 
10660  *         // draw something asynchronously and pass the tile to the done() callback
 
10661  *         setTimeout(function() {
 
10662  *             done(error, tile);
 
10674 var GridLayer = Layer.extend({
 
10677         // @aka GridLayer options
 
10679                 // @option tileSize: Number|Point = 256
 
10680                 // Width and height of tiles in the grid. Use a number if width and height are equal, or `L.point(width, height)` otherwise.
 
10683                 // @option opacity: Number = 1.0
 
10684                 // Opacity of the tiles. Can be used in the `createTile()` function.
 
10687                 // @option updateWhenIdle: Boolean = (depends)
 
10688                 // Load new tiles only when panning ends.
 
10689                 // `true` by default on mobile browsers, in order to avoid too many requests and keep smooth navigation.
 
10690                 // `false` otherwise in order to display new tiles _during_ panning, since it is easy to pan outside the
 
10691                 // [`keepBuffer`](#gridlayer-keepbuffer) option in desktop browsers.
 
10692                 updateWhenIdle: mobile,
 
10694                 // @option updateWhenZooming: Boolean = true
 
10695                 // By default, a smooth zoom animation (during a [touch zoom](#map-touchzoom) or a [`flyTo()`](#map-flyto)) will update grid layers every integer zoom level. Setting this option to `false` will update the grid layer only when the smooth animation ends.
 
10696                 updateWhenZooming: true,
 
10698                 // @option updateInterval: Number = 200
 
10699                 // Tiles will not update more than once every `updateInterval` milliseconds when panning.
 
10700                 updateInterval: 200,
 
10702                 // @option zIndex: Number = 1
 
10703                 // The explicit zIndex of the tile layer.
 
10706                 // @option bounds: LatLngBounds = undefined
 
10707                 // If set, tiles will only be loaded inside the set `LatLngBounds`.
 
10710                 // @option minZoom: Number = 0
 
10711                 // The minimum zoom level down to which this layer will be displayed (inclusive).
 
10714                 // @option maxZoom: Number = undefined
 
10715                 // The maximum zoom level up to which this layer will be displayed (inclusive).
 
10716                 maxZoom: undefined,
 
10718                 // @option maxNativeZoom: Number = undefined
 
10719                 // Maximum zoom number the tile source has available. If it is specified,
 
10720                 // the tiles on all zoom levels higher than `maxNativeZoom` will be loaded
 
10721                 // from `maxNativeZoom` level and auto-scaled.
 
10722                 maxNativeZoom: undefined,
 
10724                 // @option minNativeZoom: Number = undefined
 
10725                 // Minimum zoom number the tile source has available. If it is specified,
 
10726                 // the tiles on all zoom levels lower than `minNativeZoom` will be loaded
 
10727                 // from `minNativeZoom` level and auto-scaled.
 
10728                 minNativeZoom: undefined,
 
10730                 // @option noWrap: Boolean = false
 
10731                 // Whether the layer is wrapped around the antimeridian. If `true`, the
 
10732                 // GridLayer will only be displayed once at low zoom levels. Has no
 
10733                 // effect when the [map CRS](#map-crs) doesn't wrap around. Can be used
 
10734                 // in combination with [`bounds`](#gridlayer-bounds) to prevent requesting
 
10735                 // tiles outside the CRS limits.
 
10738                 // @option pane: String = 'tilePane'
 
10739                 // `Map pane` where the grid layer will be added.
 
10742                 // @option className: String = ''
 
10743                 // A custom class name to assign to the tile layer. Empty by default.
 
10746                 // @option keepBuffer: Number = 2
 
10747                 // When panning the map, keep this many rows and columns of tiles before unloading them.
 
10751         initialize: function (options) {
 
10752                 setOptions(this, options);
 
10755         onAdd: function () {
 
10756                 this._initContainer();
 
10765         beforeAdd: function (map) {
 
10766                 map._addZoomLimit(this);
 
10769         onRemove: function (map) {
 
10770                 this._removeAllTiles();
 
10771                 remove(this._container);
 
10772                 map._removeZoomLimit(this);
 
10773                 this._container = null;
 
10774                 this._tileZoom = undefined;
 
10777         // @method bringToFront: this
 
10778         // Brings the tile layer to the top of all tile layers.
 
10779         bringToFront: function () {
 
10781                         toFront(this._container);
 
10782                         this._setAutoZIndex(Math.max);
 
10787         // @method bringToBack: this
 
10788         // Brings the tile layer to the bottom of all tile layers.
 
10789         bringToBack: function () {
 
10791                         toBack(this._container);
 
10792                         this._setAutoZIndex(Math.min);
 
10797         // @method getContainer: HTMLElement
 
10798         // Returns the HTML element that contains the tiles for this layer.
 
10799         getContainer: function () {
 
10800                 return this._container;
 
10803         // @method setOpacity(opacity: Number): this
 
10804         // Changes the [opacity](#gridlayer-opacity) of the grid layer.
 
10805         setOpacity: function (opacity) {
 
10806                 this.options.opacity = opacity;
 
10807                 this._updateOpacity();
 
10811         // @method setZIndex(zIndex: Number): this
 
10812         // Changes the [zIndex](#gridlayer-zindex) of the grid layer.
 
10813         setZIndex: function (zIndex) {
 
10814                 this.options.zIndex = zIndex;
 
10815                 this._updateZIndex();
 
10820         // @method isLoading: Boolean
 
10821         // Returns `true` if any tile in the grid layer has not finished loading.
 
10822         isLoading: function () {
 
10823                 return this._loading;
 
10826         // @method redraw: this
 
10827         // Causes the layer to clear all the tiles and request them again.
 
10828         redraw: function () {
 
10830                         this._removeAllTiles();
 
10836         getEvents: function () {
 
10838                         viewprereset: this._invalidateAll,
 
10839                         viewreset: this._resetView,
 
10840                         zoom: this._resetView,
 
10841                         moveend: this._onMoveEnd
 
10844                 if (!this.options.updateWhenIdle) {
 
10845                         // update tiles on move, but not more often than once per given interval
 
10846                         if (!this._onMove) {
 
10847                                 this._onMove = throttle(this._onMoveEnd, this.options.updateInterval, this);
 
10850                         events.move = this._onMove;
 
10853                 if (this._zoomAnimated) {
 
10854                         events.zoomanim = this._animateZoom;
 
10860         // @section Extension methods
 
10861         // Layers extending `GridLayer` shall reimplement the following method.
 
10862         // @method createTile(coords: Object, done?: Function): HTMLElement
 
10863         // Called only internally, must be overridden by classes extending `GridLayer`.
 
10864         // Returns the `HTMLElement` corresponding to the given `coords`. If the `done` callback
 
10865         // is specified, it must be called when the tile has finished loading and drawing.
 
10866         createTile: function () {
 
10867                 return document.createElement('div');
 
10871         // @method getTileSize: Point
 
10872         // Normalizes the [tileSize option](#gridlayer-tilesize) into a point. Used by the `createTile()` method.
 
10873         getTileSize: function () {
 
10874                 var s = this.options.tileSize;
 
10875                 return s instanceof Point ? s : new Point(s, s);
 
10878         _updateZIndex: function () {
 
10879                 if (this._container && this.options.zIndex !== undefined && this.options.zIndex !== null) {
 
10880                         this._container.style.zIndex = this.options.zIndex;
 
10884         _setAutoZIndex: function (compare) {
 
10885                 // go through all other layers of the same pane, set zIndex to max + 1 (front) or min - 1 (back)
 
10887                 var layers = this.getPane().children,
 
10888                     edgeZIndex = -compare(-Infinity, Infinity); // -Infinity for max, Infinity for min
 
10890                 for (var i = 0, len = layers.length, zIndex; i < len; i++) {
 
10892                         zIndex = layers[i].style.zIndex;
 
10894                         if (layers[i] !== this._container && zIndex) {
 
10895                                 edgeZIndex = compare(edgeZIndex, +zIndex);
 
10899                 if (isFinite(edgeZIndex)) {
 
10900                         this.options.zIndex = edgeZIndex + compare(-1, 1);
 
10901                         this._updateZIndex();
 
10905         _updateOpacity: function () {
 
10906                 if (!this._map) { return; }
 
10908                 // IE doesn't inherit filter opacity properly, so we're forced to set it on tiles
 
10909                 if (ielt9) { return; }
 
10911                 setOpacity(this._container, this.options.opacity);
 
10913                 var now = +new Date(),
 
10917                 for (var key in this._tiles) {
 
10918                         var tile = this._tiles[key];
 
10919                         if (!tile.current || !tile.loaded) { continue; }
 
10921                         var fade = Math.min(1, (now - tile.loaded) / 200);
 
10923                         setOpacity(tile.el, fade);
 
10930                                         this._onOpaqueTile(tile);
 
10932                                 tile.active = true;
 
10936                 if (willPrune && !this._noPrune) { this._pruneTiles(); }
 
10939                         cancelAnimFrame(this._fadeFrame);
 
10940                         this._fadeFrame = requestAnimFrame(this._updateOpacity, this);
 
10944         _onOpaqueTile: falseFn,
 
10946         _initContainer: function () {
 
10947                 if (this._container) { return; }
 
10949                 this._container = create$1('div', 'leaflet-layer ' + (this.options.className || ''));
 
10950                 this._updateZIndex();
 
10952                 if (this.options.opacity < 1) {
 
10953                         this._updateOpacity();
 
10956                 this.getPane().appendChild(this._container);
 
10959         _updateLevels: function () {
 
10961                 var zoom = this._tileZoom,
 
10962                     maxZoom = this.options.maxZoom;
 
10964                 if (zoom === undefined) { return undefined; }
 
10966                 for (var z in this._levels) {
 
10967                         if (this._levels[z].el.children.length || z === zoom) {
 
10968                                 this._levels[z].el.style.zIndex = maxZoom - Math.abs(zoom - z);
 
10969                                 this._onUpdateLevel(z);
 
10971                                 remove(this._levels[z].el);
 
10972                                 this._removeTilesAtZoom(z);
 
10973                                 this._onRemoveLevel(z);
 
10974                                 delete this._levels[z];
 
10978                 var level = this._levels[zoom],
 
10982                         level = this._levels[zoom] = {};
 
10984                         level.el = create$1('div', 'leaflet-tile-container leaflet-zoom-animated', this._container);
 
10985                         level.el.style.zIndex = maxZoom;
 
10987                         level.origin = map.project(map.unproject(map.getPixelOrigin()), zoom).round();
 
10990                         this._setZoomTransform(level, map.getCenter(), map.getZoom());
 
10992                         // force the browser to consider the newly added element for transition
 
10993                         falseFn(level.el.offsetWidth);
 
10995                         this._onCreateLevel(level);
 
10998                 this._level = level;
 
11003         _onUpdateLevel: falseFn,
 
11005         _onRemoveLevel: falseFn,
 
11007         _onCreateLevel: falseFn,
 
11009         _pruneTiles: function () {
 
11016                 var zoom = this._map.getZoom();
 
11017                 if (zoom > this.options.maxZoom ||
 
11018                         zoom < this.options.minZoom) {
 
11019                         this._removeAllTiles();
 
11023                 for (key in this._tiles) {
 
11024                         tile = this._tiles[key];
 
11025                         tile.retain = tile.current;
 
11028                 for (key in this._tiles) {
 
11029                         tile = this._tiles[key];
 
11030                         if (tile.current && !tile.active) {
 
11031                                 var coords = tile.coords;
 
11032                                 if (!this._retainParent(coords.x, coords.y, coords.z, coords.z - 5)) {
 
11033                                         this._retainChildren(coords.x, coords.y, coords.z, coords.z + 2);
 
11038                 for (key in this._tiles) {
 
11039                         if (!this._tiles[key].retain) {
 
11040                                 this._removeTile(key);
 
11045         _removeTilesAtZoom: function (zoom) {
 
11046                 for (var key in this._tiles) {
 
11047                         if (this._tiles[key].coords.z !== zoom) {
 
11050                         this._removeTile(key);
 
11054         _removeAllTiles: function () {
 
11055                 for (var key in this._tiles) {
 
11056                         this._removeTile(key);
 
11060         _invalidateAll: function () {
 
11061                 for (var z in this._levels) {
 
11062                         remove(this._levels[z].el);
 
11063                         this._onRemoveLevel(z);
 
11064                         delete this._levels[z];
 
11066                 this._removeAllTiles();
 
11068                 this._tileZoom = undefined;
 
11071         _retainParent: function (x, y, z, minZoom) {
 
11072                 var x2 = Math.floor(x / 2),
 
11073                     y2 = Math.floor(y / 2),
 
11075                     coords2 = new Point(+x2, +y2);
 
11078                 var key = this._tileCoordsToKey(coords2),
 
11079                     tile = this._tiles[key];
 
11081                 if (tile && tile.active) {
 
11082                         tile.retain = true;
 
11085                 } else if (tile && tile.loaded) {
 
11086                         tile.retain = true;
 
11089                 if (z2 > minZoom) {
 
11090                         return this._retainParent(x2, y2, z2, minZoom);
 
11096         _retainChildren: function (x, y, z, maxZoom) {
 
11098                 for (var i = 2 * x; i < 2 * x + 2; i++) {
 
11099                         for (var j = 2 * y; j < 2 * y + 2; j++) {
 
11101                                 var coords = new Point(i, j);
 
11104                                 var key = this._tileCoordsToKey(coords),
 
11105                                     tile = this._tiles[key];
 
11107                                 if (tile && tile.active) {
 
11108                                         tile.retain = true;
 
11111                                 } else if (tile && tile.loaded) {
 
11112                                         tile.retain = true;
 
11115                                 if (z + 1 < maxZoom) {
 
11116                                         this._retainChildren(i, j, z + 1, maxZoom);
 
11122         _resetView: function (e) {
 
11123                 var animating = e && (e.pinch || e.flyTo);
 
11124                 this._setView(this._map.getCenter(), this._map.getZoom(), animating, animating);
 
11127         _animateZoom: function (e) {
 
11128                 this._setView(e.center, e.zoom, true, e.noUpdate);
 
11131         _clampZoom: function (zoom) {
 
11132                 var options = this.options;
 
11134                 if (undefined !== options.minNativeZoom && zoom < options.minNativeZoom) {
 
11135                         return options.minNativeZoom;
 
11138                 if (undefined !== options.maxNativeZoom && options.maxNativeZoom < zoom) {
 
11139                         return options.maxNativeZoom;
 
11145         _setView: function (center, zoom, noPrune, noUpdate) {
 
11146                 var tileZoom = this._clampZoom(Math.round(zoom));
 
11147                 if ((this.options.maxZoom !== undefined && tileZoom > this.options.maxZoom) ||
 
11148                     (this.options.minZoom !== undefined && tileZoom < this.options.minZoom)) {
 
11149                         tileZoom = undefined;
 
11152                 var tileZoomChanged = this.options.updateWhenZooming && (tileZoom !== this._tileZoom);
 
11154                 if (!noUpdate || tileZoomChanged) {
 
11156                         this._tileZoom = tileZoom;
 
11158                         if (this._abortLoading) {
 
11159                                 this._abortLoading();
 
11162                         this._updateLevels();
 
11165                         if (tileZoom !== undefined) {
 
11166                                 this._update(center);
 
11170                                 this._pruneTiles();
 
11173                         // Flag to prevent _updateOpacity from pruning tiles during
 
11174                         // a zoom anim or a pinch gesture
 
11175                         this._noPrune = !!noPrune;
 
11178                 this._setZoomTransforms(center, zoom);
 
11181         _setZoomTransforms: function (center, zoom) {
 
11182                 for (var i in this._levels) {
 
11183                         this._setZoomTransform(this._levels[i], center, zoom);
 
11187         _setZoomTransform: function (level, center, zoom) {
 
11188                 var scale = this._map.getZoomScale(zoom, level.zoom),
 
11189                     translate = level.origin.multiplyBy(scale)
 
11190                         .subtract(this._map._getNewPixelOrigin(center, zoom)).round();
 
11193                         setTransform(level.el, translate, scale);
 
11195                         setPosition(level.el, translate);
 
11199         _resetGrid: function () {
 
11200                 var map = this._map,
 
11201                     crs = map.options.crs,
 
11202                     tileSize = this._tileSize = this.getTileSize(),
 
11203                     tileZoom = this._tileZoom;
 
11205                 var bounds = this._map.getPixelWorldBounds(this._tileZoom);
 
11207                         this._globalTileRange = this._pxBoundsToTileRange(bounds);
 
11210                 this._wrapX = crs.wrapLng && !this.options.noWrap && [
 
11211                         Math.floor(map.project([0, crs.wrapLng[0]], tileZoom).x / tileSize.x),
 
11212                         Math.ceil(map.project([0, crs.wrapLng[1]], tileZoom).x / tileSize.y)
 
11214                 this._wrapY = crs.wrapLat && !this.options.noWrap && [
 
11215                         Math.floor(map.project([crs.wrapLat[0], 0], tileZoom).y / tileSize.x),
 
11216                         Math.ceil(map.project([crs.wrapLat[1], 0], tileZoom).y / tileSize.y)
 
11220         _onMoveEnd: function () {
 
11221                 if (!this._map || this._map._animatingZoom) { return; }
 
11226         _getTiledPixelBounds: function (center) {
 
11227                 var map = this._map,
 
11228                     mapZoom = map._animatingZoom ? Math.max(map._animateToZoom, map.getZoom()) : map.getZoom(),
 
11229                     scale = map.getZoomScale(mapZoom, this._tileZoom),
 
11230                     pixelCenter = map.project(center, this._tileZoom).floor(),
 
11231                     halfSize = map.getSize().divideBy(scale * 2);
 
11233                 return new Bounds(pixelCenter.subtract(halfSize), pixelCenter.add(halfSize));
 
11236         // Private method to load tiles in the grid's active zoom level according to map bounds
 
11237         _update: function (center) {
 
11238                 var map = this._map;
 
11239                 if (!map) { return; }
 
11240                 var zoom = this._clampZoom(map.getZoom());
 
11242                 if (center === undefined) { center = map.getCenter(); }
 
11243                 if (this._tileZoom === undefined) { return; }   // if out of minzoom/maxzoom
 
11245                 var pixelBounds = this._getTiledPixelBounds(center),
 
11246                     tileRange = this._pxBoundsToTileRange(pixelBounds),
 
11247                     tileCenter = tileRange.getCenter(),
 
11249                     margin = this.options.keepBuffer,
 
11250                     noPruneRange = new Bounds(tileRange.getBottomLeft().subtract([margin, -margin]),
 
11251                                               tileRange.getTopRight().add([margin, -margin]));
 
11253                 // Sanity check: panic if the tile range contains Infinity somewhere.
 
11254                 if (!(isFinite(tileRange.min.x) &&
 
11255                       isFinite(tileRange.min.y) &&
 
11256                       isFinite(tileRange.max.x) &&
 
11257                       isFinite(tileRange.max.y))) { throw new Error('Attempted to load an infinite number of tiles'); }
 
11259                 for (var key in this._tiles) {
 
11260                         var c = this._tiles[key].coords;
 
11261                         if (c.z !== this._tileZoom || !noPruneRange.contains(new Point(c.x, c.y))) {
 
11262                                 this._tiles[key].current = false;
 
11266                 // _update just loads more tiles. If the tile zoom level differs too much
 
11267                 // from the map's, let _setView reset levels and prune old tiles.
 
11268                 if (Math.abs(zoom - this._tileZoom) > 1) { this._setView(center, zoom); return; }
 
11270                 // create a queue of coordinates to load tiles from
 
11271                 for (var j = tileRange.min.y; j <= tileRange.max.y; j++) {
 
11272                         for (var i = tileRange.min.x; i <= tileRange.max.x; i++) {
 
11273                                 var coords = new Point(i, j);
 
11274                                 coords.z = this._tileZoom;
 
11276                                 if (!this._isValidTile(coords)) { continue; }
 
11278                                 var tile = this._tiles[this._tileCoordsToKey(coords)];
 
11280                                         tile.current = true;
 
11282                                         queue.push(coords);
 
11287                 // sort tile queue to load tiles in order of their distance to center
 
11288                 queue.sort(function (a, b) {
 
11289                         return a.distanceTo(tileCenter) - b.distanceTo(tileCenter);
 
11292                 if (queue.length !== 0) {
 
11293                         // if it's the first batch of tiles to load
 
11294                         if (!this._loading) {
 
11295                                 this._loading = true;
 
11296                                 // @event loading: Event
 
11297                                 // Fired when the grid layer starts loading tiles.
 
11298                                 this.fire('loading');
 
11301                         // create DOM fragment to append tiles in one batch
 
11302                         var fragment = document.createDocumentFragment();
 
11304                         for (i = 0; i < queue.length; i++) {
 
11305                                 this._addTile(queue[i], fragment);
 
11308                         this._level.el.appendChild(fragment);
 
11312         _isValidTile: function (coords) {
 
11313                 var crs = this._map.options.crs;
 
11315                 if (!crs.infinite) {
 
11316                         // don't load tile if it's out of bounds and not wrapped
 
11317                         var bounds = this._globalTileRange;
 
11318                         if ((!crs.wrapLng && (coords.x < bounds.min.x || coords.x > bounds.max.x)) ||
 
11319                             (!crs.wrapLat && (coords.y < bounds.min.y || coords.y > bounds.max.y))) { return false; }
 
11322                 if (!this.options.bounds) { return true; }
 
11324                 // don't load tile if it doesn't intersect the bounds in options
 
11325                 var tileBounds = this._tileCoordsToBounds(coords);
 
11326                 return toLatLngBounds(this.options.bounds).overlaps(tileBounds);
 
11329         _keyToBounds: function (key) {
 
11330                 return this._tileCoordsToBounds(this._keyToTileCoords(key));
 
11333         _tileCoordsToNwSe: function (coords) {
 
11334                 var map = this._map,
 
11335                     tileSize = this.getTileSize(),
 
11336                     nwPoint = coords.scaleBy(tileSize),
 
11337                     sePoint = nwPoint.add(tileSize),
 
11338                     nw = map.unproject(nwPoint, coords.z),
 
11339                     se = map.unproject(sePoint, coords.z);
 
11343         // converts tile coordinates to its geographical bounds
 
11344         _tileCoordsToBounds: function (coords) {
 
11345                 var bp = this._tileCoordsToNwSe(coords),
 
11346                     bounds = new LatLngBounds(bp[0], bp[1]);
 
11348                 if (!this.options.noWrap) {
 
11349                         bounds = this._map.wrapLatLngBounds(bounds);
 
11353         // converts tile coordinates to key for the tile cache
 
11354         _tileCoordsToKey: function (coords) {
 
11355                 return coords.x + ':' + coords.y + ':' + coords.z;
 
11358         // converts tile cache key to coordinates
 
11359         _keyToTileCoords: function (key) {
 
11360                 var k = key.split(':'),
 
11361                     coords = new Point(+k[0], +k[1]);
 
11366         _removeTile: function (key) {
 
11367                 var tile = this._tiles[key];
 
11368                 if (!tile) { return; }
 
11372                 delete this._tiles[key];
 
11374                 // @event tileunload: TileEvent
 
11375                 // Fired when a tile is removed (e.g. when a tile goes off the screen).
 
11376                 this.fire('tileunload', {
 
11378                         coords: this._keyToTileCoords(key)
 
11382         _initTile: function (tile) {
 
11383                 addClass(tile, 'leaflet-tile');
 
11385                 var tileSize = this.getTileSize();
 
11386                 tile.style.width = tileSize.x + 'px';
 
11387                 tile.style.height = tileSize.y + 'px';
 
11389                 tile.onselectstart = falseFn;
 
11390                 tile.onmousemove = falseFn;
 
11392                 // update opacity on tiles in IE7-8 because of filter inheritance problems
 
11393                 if (ielt9 && this.options.opacity < 1) {
 
11394                         setOpacity(tile, this.options.opacity);
 
11397                 // without this hack, tiles disappear after zoom on Chrome for Android
 
11398                 // https://github.com/Leaflet/Leaflet/issues/2078
 
11399                 if (android && !android23) {
 
11400                         tile.style.WebkitBackfaceVisibility = 'hidden';
 
11404         _addTile: function (coords, container) {
 
11405                 var tilePos = this._getTilePos(coords),
 
11406                     key = this._tileCoordsToKey(coords);
 
11408                 var tile = this.createTile(this._wrapCoords(coords), bind(this._tileReady, this, coords));
 
11410                 this._initTile(tile);
 
11412                 // if createTile is defined with a second argument ("done" callback),
 
11413                 // we know that tile is async and will be ready later; otherwise
 
11414                 if (this.createTile.length < 2) {
 
11415                         // mark tile as ready, but delay one frame for opacity animation to happen
 
11416                         requestAnimFrame(bind(this._tileReady, this, coords, null, tile));
 
11419                 setPosition(tile, tilePos);
 
11421                 // save tile in cache
 
11422                 this._tiles[key] = {
 
11428                 container.appendChild(tile);
 
11429                 // @event tileloadstart: TileEvent
 
11430                 // Fired when a tile is requested and starts loading.
 
11431                 this.fire('tileloadstart', {
 
11437         _tileReady: function (coords, err, tile) {
 
11439                         // @event tileerror: TileErrorEvent
 
11440                         // Fired when there is an error loading a tile.
 
11441                         this.fire('tileerror', {
 
11448                 var key = this._tileCoordsToKey(coords);
 
11450                 tile = this._tiles[key];
 
11451                 if (!tile) { return; }
 
11453                 tile.loaded = +new Date();
 
11454                 if (this._map._fadeAnimated) {
 
11455                         setOpacity(tile.el, 0);
 
11456                         cancelAnimFrame(this._fadeFrame);
 
11457                         this._fadeFrame = requestAnimFrame(this._updateOpacity, this);
 
11459                         tile.active = true;
 
11460                         this._pruneTiles();
 
11464                         addClass(tile.el, 'leaflet-tile-loaded');
 
11466                         // @event tileload: TileEvent
 
11467                         // Fired when a tile loads.
 
11468                         this.fire('tileload', {
 
11474                 if (this._noTilesToLoad()) {
 
11475                         this._loading = false;
 
11476                         // @event load: Event
 
11477                         // Fired when the grid layer loaded all visible tiles.
 
11480                         if (ielt9 || !this._map._fadeAnimated) {
 
11481                                 requestAnimFrame(this._pruneTiles, this);
 
11483                                 // Wait a bit more than 0.2 secs (the duration of the tile fade-in)
 
11484                                 // to trigger a pruning.
 
11485                                 setTimeout(bind(this._pruneTiles, this), 250);
 
11490         _getTilePos: function (coords) {
 
11491                 return coords.scaleBy(this.getTileSize()).subtract(this._level.origin);
 
11494         _wrapCoords: function (coords) {
 
11495                 var newCoords = new Point(
 
11496                         this._wrapX ? wrapNum(coords.x, this._wrapX) : coords.x,
 
11497                         this._wrapY ? wrapNum(coords.y, this._wrapY) : coords.y);
 
11498                 newCoords.z = coords.z;
 
11502         _pxBoundsToTileRange: function (bounds) {
 
11503                 var tileSize = this.getTileSize();
 
11505                         bounds.min.unscaleBy(tileSize).floor(),
 
11506                         bounds.max.unscaleBy(tileSize).ceil().subtract([1, 1]));
 
11509         _noTilesToLoad: function () {
 
11510                 for (var key in this._tiles) {
 
11511                         if (!this._tiles[key].loaded) { return false; }
 
11517 // @factory L.gridLayer(options?: GridLayer options)
 
11518 // Creates a new instance of GridLayer with the supplied options.
 
11519 function gridLayer(options) {
 
11520         return new GridLayer(options);
 
11525  * @inherits GridLayer
 
11527  * Used to load and display tile layers on the map. Note that most tile servers require attribution, which you can set under `Layer`. Extends `GridLayer`.
 
11532  * L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png?{foo}', {foo: 'bar', attribution: 'Map data © <a href="https://www.openstreetmap.org/">OpenStreetMap</a> contributors, <a href="https://creativecommons.org/licenses/by-sa/2.0/">CC-BY-SA</a>'}).addTo(map);
 
11535  * @section URL template
 
11538  * A string of the following form:
 
11541  * 'http://{s}.somedomain.com/blabla/{z}/{x}/{y}{r}.png'
 
11544  * `{s}` means one of the available subdomains (used sequentially to help with browser parallel requests per domain limitation; subdomain values are specified in options; `a`, `b` or `c` by default, can be omitted), `{z}` — zoom level, `{x}` and `{y}` — tile coordinates. `{r}` can be used to add "@2x" to the URL to load retina tiles.
 
11546  * You can use custom keys in the template, which will be [evaluated](#util-template) from TileLayer options, like this:
 
11549  * L.tileLayer('http://{s}.somedomain.com/{foo}/{z}/{x}/{y}.png', {foo: 'bar'});
 
11554 var TileLayer = GridLayer.extend({
 
11557         // @aka TileLayer options
 
11559                 // @option minZoom: Number = 0
 
11560                 // The minimum zoom level down to which this layer will be displayed (inclusive).
 
11563                 // @option maxZoom: Number = 18
 
11564                 // The maximum zoom level up to which this layer will be displayed (inclusive).
 
11567                 // @option subdomains: String|String[] = 'abc'
 
11568                 // Subdomains of the tile service. Can be passed in the form of one string (where each letter is a subdomain name) or an array of strings.
 
11571                 // @option errorTileUrl: String = ''
 
11572                 // URL to the tile image to show in place of the tile that failed to load.
 
11575                 // @option zoomOffset: Number = 0
 
11576                 // The zoom number used in tile URLs will be offset with this value.
 
11579                 // @option tms: Boolean = false
 
11580                 // If `true`, inverses Y axis numbering for tiles (turn this on for [TMS](https://en.wikipedia.org/wiki/Tile_Map_Service) services).
 
11583                 // @option zoomReverse: Boolean = false
 
11584                 // If set to true, the zoom number used in tile URLs will be reversed (`maxZoom - zoom` instead of `zoom`)
 
11585                 zoomReverse: false,
 
11587                 // @option detectRetina: Boolean = false
 
11588                 // If `true` and user is on a retina display, it will request four tiles of half the specified size and a bigger zoom level in place of one to utilize the high resolution.
 
11589                 detectRetina: false,
 
11591                 // @option crossOrigin: Boolean|String = false
 
11592                 // Whether the crossOrigin attribute will be added to the tiles.
 
11593                 // If a String is provided, all tiles will have their crossOrigin attribute set to the String provided. This is needed if you want to access tile pixel data.
 
11594                 // Refer to [CORS Settings](https://developer.mozilla.org/en-US/docs/Web/HTML/CORS_settings_attributes) for valid String values.
 
11598         initialize: function (url, options) {
 
11602                 options = setOptions(this, options);
 
11604                 // detecting retina displays, adjusting tileSize and zoom levels
 
11605                 if (options.detectRetina && retina && options.maxZoom > 0) {
 
11607                         options.tileSize = Math.floor(options.tileSize / 2);
 
11609                         if (!options.zoomReverse) {
 
11610                                 options.zoomOffset++;
 
11613                                 options.zoomOffset--;
 
11617                         options.minZoom = Math.max(0, options.minZoom);
 
11620                 if (typeof options.subdomains === 'string') {
 
11621                         options.subdomains = options.subdomains.split('');
 
11624                 // for https://github.com/Leaflet/Leaflet/issues/137
 
11626                         this.on('tileunload', this._onTileRemove);
 
11630         // @method setUrl(url: String, noRedraw?: Boolean): this
 
11631         // Updates the layer's URL template and redraws it (unless `noRedraw` is set to `true`).
 
11632         // If the URL does not change, the layer will not be redrawn unless
 
11633         // the noRedraw parameter is set to false.
 
11634         setUrl: function (url, noRedraw) {
 
11635                 if (this._url === url && noRedraw === undefined) {
 
11647         // @method createTile(coords: Object, done?: Function): HTMLElement
 
11648         // Called only internally, overrides GridLayer's [`createTile()`](#gridlayer-createtile)
 
11649         // to return an `<img>` HTML element with the appropriate image URL given `coords`. The `done`
 
11650         // callback is called when the tile has been loaded.
 
11651         createTile: function (coords, done) {
 
11652                 var tile = document.createElement('img');
 
11654                 on(tile, 'load', bind(this._tileOnLoad, this, done, tile));
 
11655                 on(tile, 'error', bind(this._tileOnError, this, done, tile));
 
11657                 if (this.options.crossOrigin || this.options.crossOrigin === '') {
 
11658                         tile.crossOrigin = this.options.crossOrigin === true ? '' : this.options.crossOrigin;
 
11662                  Alt tag is set to empty string to keep screen readers from reading URL and for compliance reasons
 
11663                  http://www.w3.org/TR/WCAG20-TECHS/H67
 
11668                  Set role="presentation" to force screen readers to ignore this
 
11669                  https://www.w3.org/TR/wai-aria/roles#textalternativecomputation
 
11671                 tile.setAttribute('role', 'presentation');
 
11673                 tile.src = this.getTileUrl(coords);
 
11678         // @section Extension methods
 
11680         // Layers extending `TileLayer` might reimplement the following method.
 
11681         // @method getTileUrl(coords: Object): String
 
11682         // Called only internally, returns the URL for a tile given its coordinates.
 
11683         // Classes extending `TileLayer` can override this function to provide custom tile URL naming schemes.
 
11684         getTileUrl: function (coords) {
 
11686                         r: retina ? '@2x' : '',
 
11687                         s: this._getSubdomain(coords),
 
11690                         z: this._getZoomForUrl()
 
11692                 if (this._map && !this._map.options.crs.infinite) {
 
11693                         var invertedY = this._globalTileRange.max.y - coords.y;
 
11694                         if (this.options.tms) {
 
11695                                 data['y'] = invertedY;
 
11697                         data['-y'] = invertedY;
 
11700                 return template(this._url, extend(data, this.options));
 
11703         _tileOnLoad: function (done, tile) {
 
11704                 // For https://github.com/Leaflet/Leaflet/issues/3332
 
11706                         setTimeout(bind(done, this, null, tile), 0);
 
11712         _tileOnError: function (done, tile, e) {
 
11713                 var errorUrl = this.options.errorTileUrl;
 
11714                 if (errorUrl && tile.getAttribute('src') !== errorUrl) {
 
11715                         tile.src = errorUrl;
 
11720         _onTileRemove: function (e) {
 
11721                 e.tile.onload = null;
 
11724         _getZoomForUrl: function () {
 
11725                 var zoom = this._tileZoom,
 
11726                 maxZoom = this.options.maxZoom,
 
11727                 zoomReverse = this.options.zoomReverse,
 
11728                 zoomOffset = this.options.zoomOffset;
 
11731                         zoom = maxZoom - zoom;
 
11734                 return zoom + zoomOffset;
 
11737         _getSubdomain: function (tilePoint) {
 
11738                 var index = Math.abs(tilePoint.x + tilePoint.y) % this.options.subdomains.length;
 
11739                 return this.options.subdomains[index];
 
11742         // stops loading all tiles in the background layer
 
11743         _abortLoading: function () {
 
11745                 for (i in this._tiles) {
 
11746                         if (this._tiles[i].coords.z !== this._tileZoom) {
 
11747                                 tile = this._tiles[i].el;
 
11749                                 tile.onload = falseFn;
 
11750                                 tile.onerror = falseFn;
 
11752                                 if (!tile.complete) {
 
11753                                         tile.src = emptyImageUrl;
 
11755                                         delete this._tiles[i];
 
11761         _removeTile: function (key) {
 
11762                 var tile = this._tiles[key];
 
11763                 if (!tile) { return; }
 
11765                 // Cancels any pending http requests associated with the tile
 
11766                 // unless we're on Android's stock browser,
 
11767                 // see https://github.com/Leaflet/Leaflet/issues/137
 
11768                 if (!androidStock) {
 
11769                         tile.el.setAttribute('src', emptyImageUrl);
 
11772                 return GridLayer.prototype._removeTile.call(this, key);
 
11775         _tileReady: function (coords, err, tile) {
 
11776                 if (!this._map || (tile && tile.getAttribute('src') === emptyImageUrl)) {
 
11780                 return GridLayer.prototype._tileReady.call(this, coords, err, tile);
 
11785 // @factory L.tilelayer(urlTemplate: String, options?: TileLayer options)
 
11786 // Instantiates a tile layer object given a `URL template` and optionally an options object.
 
11788 function tileLayer(url, options) {
 
11789         return new TileLayer(url, options);
 
11793  * @class TileLayer.WMS
 
11794  * @inherits TileLayer
 
11795  * @aka L.TileLayer.WMS
 
11796  * Used to display [WMS](https://en.wikipedia.org/wiki/Web_Map_Service) services as tile layers on the map. Extends `TileLayer`.
 
11801  * var nexrad = L.tileLayer.wms("http://mesonet.agron.iastate.edu/cgi-bin/wms/nexrad/n0r.cgi", {
 
11802  *      layers: 'nexrad-n0r-900913',
 
11803  *      format: 'image/png',
 
11804  *      transparent: true,
 
11805  *      attribution: "Weather data © 2012 IEM Nexrad"
 
11810 var TileLayerWMS = TileLayer.extend({
 
11813         // @aka TileLayer.WMS options
 
11814         // If any custom options not documented here are used, they will be sent to the
 
11815         // WMS server as extra parameters in each request URL. This can be useful for
 
11816         // [non-standard vendor WMS parameters](http://docs.geoserver.org/stable/en/user/services/wms/vendor.html).
 
11817         defaultWmsParams: {
 
11821                 // @option layers: String = ''
 
11822                 // **(required)** Comma-separated list of WMS layers to show.
 
11825                 // @option styles: String = ''
 
11826                 // Comma-separated list of WMS styles.
 
11829                 // @option format: String = 'image/jpeg'
 
11830                 // WMS image format (use `'image/png'` for layers with transparency).
 
11831                 format: 'image/jpeg',
 
11833                 // @option transparent: Boolean = false
 
11834                 // If `true`, the WMS service will return images with transparency.
 
11835                 transparent: false,
 
11837                 // @option version: String = '1.1.1'
 
11838                 // Version of the WMS service to use
 
11843                 // @option crs: CRS = null
 
11844                 // Coordinate Reference System to use for the WMS requests, defaults to
 
11845                 // map CRS. Don't change this if you're not sure what it means.
 
11848                 // @option uppercase: Boolean = false
 
11849                 // If `true`, WMS request parameter keys will be uppercase.
 
11853         initialize: function (url, options) {
 
11857                 var wmsParams = extend({}, this.defaultWmsParams);
 
11859                 // all keys that are not TileLayer options go to WMS params
 
11860                 for (var i in options) {
 
11861                         if (!(i in this.options)) {
 
11862                                 wmsParams[i] = options[i];
 
11866                 options = setOptions(this, options);
 
11868                 var realRetina = options.detectRetina && retina ? 2 : 1;
 
11869                 var tileSize = this.getTileSize();
 
11870                 wmsParams.width = tileSize.x * realRetina;
 
11871                 wmsParams.height = tileSize.y * realRetina;
 
11873                 this.wmsParams = wmsParams;
 
11876         onAdd: function (map) {
 
11878                 this._crs = this.options.crs || map.options.crs;
 
11879                 this._wmsVersion = parseFloat(this.wmsParams.version);
 
11881                 var projectionKey = this._wmsVersion >= 1.3 ? 'crs' : 'srs';
 
11882                 this.wmsParams[projectionKey] = this._crs.code;
 
11884                 TileLayer.prototype.onAdd.call(this, map);
 
11887         getTileUrl: function (coords) {
 
11889                 var tileBounds = this._tileCoordsToNwSe(coords),
 
11891                     bounds = toBounds(crs.project(tileBounds[0]), crs.project(tileBounds[1])),
 
11894                     bbox = (this._wmsVersion >= 1.3 && this._crs === EPSG4326 ?
 
11895                     [min.y, min.x, max.y, max.x] :
 
11896                     [min.x, min.y, max.x, max.y]).join(','),
 
11897                     url = TileLayer.prototype.getTileUrl.call(this, coords);
 
11899                         getParamString(this.wmsParams, url, this.options.uppercase) +
 
11900                         (this.options.uppercase ? '&BBOX=' : '&bbox=') + bbox;
 
11903         // @method setParams(params: Object, noRedraw?: Boolean): this
 
11904         // Merges an object with the new parameters and re-requests tiles on the current screen (unless `noRedraw` was set to true).
 
11905         setParams: function (params, noRedraw) {
 
11907                 extend(this.wmsParams, params);
 
11918 // @factory L.tileLayer.wms(baseUrl: String, options: TileLayer.WMS options)
 
11919 // Instantiates a WMS tile layer object given a base URL of the WMS service and a WMS parameters/options object.
 
11920 function tileLayerWMS(url, options) {
 
11921         return new TileLayerWMS(url, options);
 
11924 TileLayer.WMS = TileLayerWMS;
 
11925 tileLayer.wms = tileLayerWMS;
 
11932  * Base class for vector renderer implementations (`SVG`, `Canvas`). Handles the
 
11933  * DOM container of the renderer, its bounds, and its zoom animation.
 
11935  * A `Renderer` works as an implicit layer group for all `Path`s - the renderer
 
11936  * itself can be added or removed to the map. All paths use a renderer, which can
 
11937  * be implicit (the map will decide the type of renderer and use it automatically)
 
11938  * or explicit (using the [`renderer`](#path-renderer) option of the path).
 
11940  * Do not use this class directly, use `SVG` and `Canvas` instead.
 
11942  * @event update: Event
 
11943  * Fired when the renderer updates its bounds, center and zoom, for example when
 
11944  * its map has moved
 
11947 var Renderer = Layer.extend({
 
11950         // @aka Renderer options
 
11952                 // @option padding: Number = 0.1
 
11953                 // How much to extend the clip area around the map view (relative to its size)
 
11954                 // e.g. 0.1 would be 10% of map view in each direction
 
11957                 // @option tolerance: Number = 0
 
11958                 // How much to extend click tolerance round a path/object on the map
 
11962         initialize: function (options) {
 
11963                 setOptions(this, options);
 
11965                 this._layers = this._layers || {};
 
11968         onAdd: function () {
 
11969                 if (!this._container) {
 
11970                         this._initContainer(); // defined by renderer implementations
 
11972                         if (this._zoomAnimated) {
 
11973                                 addClass(this._container, 'leaflet-zoom-animated');
 
11977                 this.getPane().appendChild(this._container);
 
11979                 this.on('update', this._updatePaths, this);
 
11982         onRemove: function () {
 
11983                 this.off('update', this._updatePaths, this);
 
11984                 this._destroyContainer();
 
11987         getEvents: function () {
 
11989                         viewreset: this._reset,
 
11990                         zoom: this._onZoom,
 
11991                         moveend: this._update,
 
11992                         zoomend: this._onZoomEnd
 
11994                 if (this._zoomAnimated) {
 
11995                         events.zoomanim = this._onAnimZoom;
 
12000         _onAnimZoom: function (ev) {
 
12001                 this._updateTransform(ev.center, ev.zoom);
 
12004         _onZoom: function () {
 
12005                 this._updateTransform(this._map.getCenter(), this._map.getZoom());
 
12008         _updateTransform: function (center, zoom) {
 
12009                 var scale = this._map.getZoomScale(zoom, this._zoom),
 
12010                     position = getPosition(this._container),
 
12011                     viewHalf = this._map.getSize().multiplyBy(0.5 + this.options.padding),
 
12012                     currentCenterPoint = this._map.project(this._center, zoom),
 
12013                     destCenterPoint = this._map.project(center, zoom),
 
12014                     centerOffset = destCenterPoint.subtract(currentCenterPoint),
 
12016                     topLeftOffset = viewHalf.multiplyBy(-scale).add(position).add(viewHalf).subtract(centerOffset);
 
12019                         setTransform(this._container, topLeftOffset, scale);
 
12021                         setPosition(this._container, topLeftOffset);
 
12025         _reset: function () {
 
12027                 this._updateTransform(this._center, this._zoom);
 
12029                 for (var id in this._layers) {
 
12030                         this._layers[id]._reset();
 
12034         _onZoomEnd: function () {
 
12035                 for (var id in this._layers) {
 
12036                         this._layers[id]._project();
 
12040         _updatePaths: function () {
 
12041                 for (var id in this._layers) {
 
12042                         this._layers[id]._update();
 
12046         _update: function () {
 
12047                 // Update pixel bounds of renderer container (for positioning/sizing/clipping later)
 
12048                 // Subclasses are responsible of firing the 'update' event.
 
12049                 var p = this.options.padding,
 
12050                     size = this._map.getSize(),
 
12051                     min = this._map.containerPointToLayerPoint(size.multiplyBy(-p)).round();
 
12053                 this._bounds = new Bounds(min, min.add(size.multiplyBy(1 + p * 2)).round());
 
12055                 this._center = this._map.getCenter();
 
12056                 this._zoom = this._map.getZoom();
 
12062  * @inherits Renderer
 
12065  * Allows vector layers to be displayed with [`<canvas>`](https://developer.mozilla.org/docs/Web/API/Canvas_API).
 
12066  * Inherits `Renderer`.
 
12068  * Due to [technical limitations](http://caniuse.com/#search=canvas), Canvas is not
 
12069  * available in all web browsers, notably IE8, and overlapping geometries might
 
12070  * not display properly in some edge cases.
 
12074  * Use Canvas by default for all paths in the map:
 
12077  * var map = L.map('map', {
 
12078  *      renderer: L.canvas()
 
12082  * Use a Canvas renderer with extra padding for specific vector geometries:
 
12085  * var map = L.map('map');
 
12086  * var myRenderer = L.canvas({ padding: 0.5 });
 
12087  * var line = L.polyline( coordinates, { renderer: myRenderer } );
 
12088  * var circle = L.circle( center, { renderer: myRenderer } );
 
12092 var Canvas = Renderer.extend({
 
12093         getEvents: function () {
 
12094                 var events = Renderer.prototype.getEvents.call(this);
 
12095                 events.viewprereset = this._onViewPreReset;
 
12099         _onViewPreReset: function () {
 
12100                 // Set a flag so that a viewprereset+moveend+viewreset only updates&redraws once
 
12101                 this._postponeUpdatePaths = true;
 
12104         onAdd: function () {
 
12105                 Renderer.prototype.onAdd.call(this);
 
12107                 // Redraw vectors since canvas is cleared upon removal,
 
12108                 // in case of removing the renderer itself from the map.
 
12112         _initContainer: function () {
 
12113                 var container = this._container = document.createElement('canvas');
 
12115                 on(container, 'mousemove', throttle(this._onMouseMove, 32, this), this);
 
12116                 on(container, 'click dblclick mousedown mouseup contextmenu', this._onClick, this);
 
12117                 on(container, 'mouseout', this._handleMouseOut, this);
 
12119                 this._ctx = container.getContext('2d');
 
12122         _destroyContainer: function () {
 
12123                 cancelAnimFrame(this._redrawRequest);
 
12125                 remove(this._container);
 
12126                 off(this._container);
 
12127                 delete this._container;
 
12130         _updatePaths: function () {
 
12131                 if (this._postponeUpdatePaths) { return; }
 
12134                 this._redrawBounds = null;
 
12135                 for (var id in this._layers) {
 
12136                         layer = this._layers[id];
 
12142         _update: function () {
 
12143                 if (this._map._animatingZoom && this._bounds) { return; }
 
12145                 Renderer.prototype._update.call(this);
 
12147                 var b = this._bounds,
 
12148                     container = this._container,
 
12149                     size = b.getSize(),
 
12150                     m = retina ? 2 : 1;
 
12152                 setPosition(container, b.min);
 
12154                 // set canvas size (also clearing it); use double size on retina
 
12155                 container.width = m * size.x;
 
12156                 container.height = m * size.y;
 
12157                 container.style.width = size.x + 'px';
 
12158                 container.style.height = size.y + 'px';
 
12161                         this._ctx.scale(2, 2);
 
12164                 // translate so we use the same path coordinates after canvas element moves
 
12165                 this._ctx.translate(-b.min.x, -b.min.y);
 
12167                 // Tell paths to redraw themselves
 
12168                 this.fire('update');
 
12171         _reset: function () {
 
12172                 Renderer.prototype._reset.call(this);
 
12174                 if (this._postponeUpdatePaths) {
 
12175                         this._postponeUpdatePaths = false;
 
12176                         this._updatePaths();
 
12180         _initPath: function (layer) {
 
12181                 this._updateDashArray(layer);
 
12182                 this._layers[stamp(layer)] = layer;
 
12184                 var order = layer._order = {
 
12186                         prev: this._drawLast,
 
12189                 if (this._drawLast) { this._drawLast.next = order; }
 
12190                 this._drawLast = order;
 
12191                 this._drawFirst = this._drawFirst || this._drawLast;
 
12194         _addPath: function (layer) {
 
12195                 this._requestRedraw(layer);
 
12198         _removePath: function (layer) {
 
12199                 var order = layer._order;
 
12200                 var next = order.next;
 
12201                 var prev = order.prev;
 
12206                         this._drawLast = prev;
 
12211                         this._drawFirst = next;
 
12214                 delete layer._order;
 
12216                 delete this._layers[stamp(layer)];
 
12218                 this._requestRedraw(layer);
 
12221         _updatePath: function (layer) {
 
12222                 // Redraw the union of the layer's old pixel
 
12223                 // bounds and the new pixel bounds.
 
12224                 this._extendRedrawBounds(layer);
 
12227                 // The redraw will extend the redraw bounds
 
12228                 // with the new pixel bounds.
 
12229                 this._requestRedraw(layer);
 
12232         _updateStyle: function (layer) {
 
12233                 this._updateDashArray(layer);
 
12234                 this._requestRedraw(layer);
 
12237         _updateDashArray: function (layer) {
 
12238                 if (typeof layer.options.dashArray === 'string') {
 
12239                         var parts = layer.options.dashArray.split(/[, ]+/),
 
12243                         for (i = 0; i < parts.length; i++) {
 
12244                                 dashValue = Number(parts[i]);
 
12245                                 // Ignore dash array containing invalid lengths
 
12246                                 if (isNaN(dashValue)) { return; }
 
12247                                 dashArray.push(dashValue);
 
12249                         layer.options._dashArray = dashArray;
 
12251                         layer.options._dashArray = layer.options.dashArray;
 
12255         _requestRedraw: function (layer) {
 
12256                 if (!this._map) { return; }
 
12258                 this._extendRedrawBounds(layer);
 
12259                 this._redrawRequest = this._redrawRequest || requestAnimFrame(this._redraw, this);
 
12262         _extendRedrawBounds: function (layer) {
 
12263                 if (layer._pxBounds) {
 
12264                         var padding = (layer.options.weight || 0) + 1;
 
12265                         this._redrawBounds = this._redrawBounds || new Bounds();
 
12266                         this._redrawBounds.extend(layer._pxBounds.min.subtract([padding, padding]));
 
12267                         this._redrawBounds.extend(layer._pxBounds.max.add([padding, padding]));
 
12271         _redraw: function () {
 
12272                 this._redrawRequest = null;
 
12274                 if (this._redrawBounds) {
 
12275                         this._redrawBounds.min._floor();
 
12276                         this._redrawBounds.max._ceil();
 
12279                 this._clear(); // clear layers in redraw bounds
 
12280                 this._draw(); // draw layers
 
12282                 this._redrawBounds = null;
 
12285         _clear: function () {
 
12286                 var bounds = this._redrawBounds;
 
12288                         var size = bounds.getSize();
 
12289                         this._ctx.clearRect(bounds.min.x, bounds.min.y, size.x, size.y);
 
12291                         this._ctx.clearRect(0, 0, this._container.width, this._container.height);
 
12295         _draw: function () {
 
12296                 var layer, bounds = this._redrawBounds;
 
12299                         var size = bounds.getSize();
 
12300                         this._ctx.beginPath();
 
12301                         this._ctx.rect(bounds.min.x, bounds.min.y, size.x, size.y);
 
12305                 this._drawing = true;
 
12307                 for (var order = this._drawFirst; order; order = order.next) {
 
12308                         layer = order.layer;
 
12309                         if (!bounds || (layer._pxBounds && layer._pxBounds.intersects(bounds))) {
 
12310                                 layer._updatePath();
 
12314                 this._drawing = false;
 
12316                 this._ctx.restore();  // Restore state before clipping.
 
12319         _updatePoly: function (layer, closed) {
 
12320                 if (!this._drawing) { return; }
 
12323                     parts = layer._parts,
 
12324                     len = parts.length,
 
12327                 if (!len) { return; }
 
12331                 for (i = 0; i < len; i++) {
 
12332                         for (j = 0, len2 = parts[i].length; j < len2; j++) {
 
12334                                 ctx[j ? 'lineTo' : 'moveTo'](p.x, p.y);
 
12341                 this._fillStroke(ctx, layer);
 
12343                 // TODO optimization: 1 fill/stroke for all features with equal style instead of 1 for each feature
 
12346         _updateCircle: function (layer) {
 
12348                 if (!this._drawing || layer._empty()) { return; }
 
12350                 var p = layer._point,
 
12352                     r = Math.max(Math.round(layer._radius), 1),
 
12353                     s = (Math.max(Math.round(layer._radiusY), 1) || r) / r;
 
12361                 ctx.arc(p.x, p.y / s, r, 0, Math.PI * 2, false);
 
12367                 this._fillStroke(ctx, layer);
 
12370         _fillStroke: function (ctx, layer) {
 
12371                 var options = layer.options;
 
12373                 if (options.fill) {
 
12374                         ctx.globalAlpha = options.fillOpacity;
 
12375                         ctx.fillStyle = options.fillColor || options.color;
 
12376                         ctx.fill(options.fillRule || 'evenodd');
 
12379                 if (options.stroke && options.weight !== 0) {
 
12380                         if (ctx.setLineDash) {
 
12381                                 ctx.setLineDash(layer.options && layer.options._dashArray || []);
 
12383                         ctx.globalAlpha = options.opacity;
 
12384                         ctx.lineWidth = options.weight;
 
12385                         ctx.strokeStyle = options.color;
 
12386                         ctx.lineCap = options.lineCap;
 
12387                         ctx.lineJoin = options.lineJoin;
 
12392         // Canvas obviously doesn't have mouse events for individual drawn objects,
 
12393         // so we emulate that by calculating what's under the mouse on mousemove/click manually
 
12395         _onClick: function (e) {
 
12396                 var point = this._map.mouseEventToLayerPoint(e), layer, clickedLayer;
 
12398                 for (var order = this._drawFirst; order; order = order.next) {
 
12399                         layer = order.layer;
 
12400                         if (layer.options.interactive && layer._containsPoint(point) && !this._map._draggableMoved(layer)) {
 
12401                                 clickedLayer = layer;
 
12404                 if (clickedLayer)  {
 
12406                         this._fireEvent([clickedLayer], e);
 
12410         _onMouseMove: function (e) {
 
12411                 if (!this._map || this._map.dragging.moving() || this._map._animatingZoom) { return; }
 
12413                 var point = this._map.mouseEventToLayerPoint(e);
 
12414                 this._handleMouseHover(e, point);
 
12418         _handleMouseOut: function (e) {
 
12419                 var layer = this._hoveredLayer;
 
12421                         // if we're leaving the layer, fire mouseout
 
12422                         removeClass(this._container, 'leaflet-interactive');
 
12423                         this._fireEvent([layer], e, 'mouseout');
 
12424                         this._hoveredLayer = null;
 
12428         _handleMouseHover: function (e, point) {
 
12429                 var layer, candidateHoveredLayer;
 
12431                 for (var order = this._drawFirst; order; order = order.next) {
 
12432                         layer = order.layer;
 
12433                         if (layer.options.interactive && layer._containsPoint(point)) {
 
12434                                 candidateHoveredLayer = layer;
 
12438                 if (candidateHoveredLayer !== this._hoveredLayer) {
 
12439                         this._handleMouseOut(e);
 
12441                         if (candidateHoveredLayer) {
 
12442                                 addClass(this._container, 'leaflet-interactive'); // change cursor
 
12443                                 this._fireEvent([candidateHoveredLayer], e, 'mouseover');
 
12444                                 this._hoveredLayer = candidateHoveredLayer;
 
12448                 if (this._hoveredLayer) {
 
12449                         this._fireEvent([this._hoveredLayer], e);
 
12453         _fireEvent: function (layers, e, type) {
 
12454                 this._map._fireDOMEvent(e, type || e.type, layers);
 
12457         _bringToFront: function (layer) {
 
12458                 var order = layer._order;
 
12460                 if (!order) { return; }
 
12462                 var next = order.next;
 
12463                 var prev = order.prev;
 
12474                         // Update first entry unless this is the
 
12476                         this._drawFirst = next;
 
12479                 order.prev = this._drawLast;
 
12480                 this._drawLast.next = order;
 
12483                 this._drawLast = order;
 
12485                 this._requestRedraw(layer);
 
12488         _bringToBack: function (layer) {
 
12489                 var order = layer._order;
 
12491                 if (!order) { return; }
 
12493                 var next = order.next;
 
12494                 var prev = order.prev;
 
12505                         // Update last entry unless this is the
 
12507                         this._drawLast = prev;
 
12512                 order.next = this._drawFirst;
 
12513                 this._drawFirst.prev = order;
 
12514                 this._drawFirst = order;
 
12516                 this._requestRedraw(layer);
 
12520 // @factory L.canvas(options?: Renderer options)
 
12521 // Creates a Canvas renderer with the given options.
 
12522 function canvas$1(options) {
 
12523         return canvas ? new Canvas(options) : null;
 
12527  * Thanks to Dmitry Baranovsky and his Raphael library for inspiration!
 
12531 var vmlCreate = (function () {
 
12533                 document.namespaces.add('lvml', 'urn:schemas-microsoft-com:vml');
 
12534                 return function (name) {
 
12535                         return document.createElement('<lvml:' + name + ' class="lvml">');
 
12538                 return function (name) {
 
12539                         return document.createElement('<' + name + ' xmlns="urn:schemas-microsoft.com:vml" class="lvml">');
 
12549  * VML was deprecated in 2012, which means VML functionality exists only for backwards compatibility
 
12550  * with old versions of Internet Explorer.
 
12553 // mixin to redefine some SVG methods to handle VML syntax which is similar but with some differences
 
12556         _initContainer: function () {
 
12557                 this._container = create$1('div', 'leaflet-vml-container');
 
12560         _update: function () {
 
12561                 if (this._map._animatingZoom) { return; }
 
12562                 Renderer.prototype._update.call(this);
 
12563                 this.fire('update');
 
12566         _initPath: function (layer) {
 
12567                 var container = layer._container = vmlCreate('shape');
 
12569                 addClass(container, 'leaflet-vml-shape ' + (this.options.className || ''));
 
12571                 container.coordsize = '1 1';
 
12573                 layer._path = vmlCreate('path');
 
12574                 container.appendChild(layer._path);
 
12576                 this._updateStyle(layer);
 
12577                 this._layers[stamp(layer)] = layer;
 
12580         _addPath: function (layer) {
 
12581                 var container = layer._container;
 
12582                 this._container.appendChild(container);
 
12584                 if (layer.options.interactive) {
 
12585                         layer.addInteractiveTarget(container);
 
12589         _removePath: function (layer) {
 
12590                 var container = layer._container;
 
12592                 layer.removeInteractiveTarget(container);
 
12593                 delete this._layers[stamp(layer)];
 
12596         _updateStyle: function (layer) {
 
12597                 var stroke = layer._stroke,
 
12598                     fill = layer._fill,
 
12599                     options = layer.options,
 
12600                     container = layer._container;
 
12602                 container.stroked = !!options.stroke;
 
12603                 container.filled = !!options.fill;
 
12605                 if (options.stroke) {
 
12607                                 stroke = layer._stroke = vmlCreate('stroke');
 
12609                         container.appendChild(stroke);
 
12610                         stroke.weight = options.weight + 'px';
 
12611                         stroke.color = options.color;
 
12612                         stroke.opacity = options.opacity;
 
12614                         if (options.dashArray) {
 
12615                                 stroke.dashStyle = isArray(options.dashArray) ?
 
12616                                     options.dashArray.join(' ') :
 
12617                                     options.dashArray.replace(/( *, *)/g, ' ');
 
12619                                 stroke.dashStyle = '';
 
12621                         stroke.endcap = options.lineCap.replace('butt', 'flat');
 
12622                         stroke.joinstyle = options.lineJoin;
 
12624                 } else if (stroke) {
 
12625                         container.removeChild(stroke);
 
12626                         layer._stroke = null;
 
12629                 if (options.fill) {
 
12631                                 fill = layer._fill = vmlCreate('fill');
 
12633                         container.appendChild(fill);
 
12634                         fill.color = options.fillColor || options.color;
 
12635                         fill.opacity = options.fillOpacity;
 
12638                         container.removeChild(fill);
 
12639                         layer._fill = null;
 
12643         _updateCircle: function (layer) {
 
12644                 var p = layer._point.round(),
 
12645                     r = Math.round(layer._radius),
 
12646                     r2 = Math.round(layer._radiusY || r);
 
12648                 this._setPath(layer, layer._empty() ? 'M0 0' :
 
12649                         'AL ' + p.x + ',' + p.y + ' ' + r + ',' + r2 + ' 0,' + (65535 * 360));
 
12652         _setPath: function (layer, path) {
 
12653                 layer._path.v = path;
 
12656         _bringToFront: function (layer) {
 
12657                 toFront(layer._container);
 
12660         _bringToBack: function (layer) {
 
12661                 toBack(layer._container);
 
12665 var create$2 = vml ? vmlCreate : svgCreate;
 
12669  * @inherits Renderer
 
12672  * Allows vector layers to be displayed with [SVG](https://developer.mozilla.org/docs/Web/SVG).
 
12673  * Inherits `Renderer`.
 
12675  * Due to [technical limitations](http://caniuse.com/#search=svg), SVG is not
 
12676  * available in all web browsers, notably Android 2.x and 3.x.
 
12678  * Although SVG is not available on IE7 and IE8, these browsers support
 
12679  * [VML](https://en.wikipedia.org/wiki/Vector_Markup_Language)
 
12680  * (a now deprecated technology), and the SVG renderer will fall back to VML in
 
12685  * Use SVG by default for all paths in the map:
 
12688  * var map = L.map('map', {
 
12689  *      renderer: L.svg()
 
12693  * Use a SVG renderer with extra padding for specific vector geometries:
 
12696  * var map = L.map('map');
 
12697  * var myRenderer = L.svg({ padding: 0.5 });
 
12698  * var line = L.polyline( coordinates, { renderer: myRenderer } );
 
12699  * var circle = L.circle( center, { renderer: myRenderer } );
 
12703 var SVG = Renderer.extend({
 
12705         getEvents: function () {
 
12706                 var events = Renderer.prototype.getEvents.call(this);
 
12707                 events.zoomstart = this._onZoomStart;
 
12711         _initContainer: function () {
 
12712                 this._container = create$2('svg');
 
12714                 // makes it possible to click through svg root; we'll reset it back in individual paths
 
12715                 this._container.setAttribute('pointer-events', 'none');
 
12717                 this._rootGroup = create$2('g');
 
12718                 this._container.appendChild(this._rootGroup);
 
12721         _destroyContainer: function () {
 
12722                 remove(this._container);
 
12723                 off(this._container);
 
12724                 delete this._container;
 
12725                 delete this._rootGroup;
 
12726                 delete this._svgSize;
 
12729         _onZoomStart: function () {
 
12730                 // Drag-then-pinch interactions might mess up the center and zoom.
 
12731                 // In this case, the easiest way to prevent this is re-do the renderer
 
12732                 //   bounds and padding when the zooming starts.
 
12736         _update: function () {
 
12737                 if (this._map._animatingZoom && this._bounds) { return; }
 
12739                 Renderer.prototype._update.call(this);
 
12741                 var b = this._bounds,
 
12742                     size = b.getSize(),
 
12743                     container = this._container;
 
12745                 // set size of svg-container if changed
 
12746                 if (!this._svgSize || !this._svgSize.equals(size)) {
 
12747                         this._svgSize = size;
 
12748                         container.setAttribute('width', size.x);
 
12749                         container.setAttribute('height', size.y);
 
12752                 // movement: update container viewBox so that we don't have to change coordinates of individual layers
 
12753                 setPosition(container, b.min);
 
12754                 container.setAttribute('viewBox', [b.min.x, b.min.y, size.x, size.y].join(' '));
 
12756                 this.fire('update');
 
12759         // methods below are called by vector layers implementations
 
12761         _initPath: function (layer) {
 
12762                 var path = layer._path = create$2('path');
 
12765                 // @option className: String = null
 
12766                 // Custom class name set on an element. Only for SVG renderer.
 
12767                 if (layer.options.className) {
 
12768                         addClass(path, layer.options.className);
 
12771                 if (layer.options.interactive) {
 
12772                         addClass(path, 'leaflet-interactive');
 
12775                 this._updateStyle(layer);
 
12776                 this._layers[stamp(layer)] = layer;
 
12779         _addPath: function (layer) {
 
12780                 if (!this._rootGroup) { this._initContainer(); }
 
12781                 this._rootGroup.appendChild(layer._path);
 
12782                 layer.addInteractiveTarget(layer._path);
 
12785         _removePath: function (layer) {
 
12786                 remove(layer._path);
 
12787                 layer.removeInteractiveTarget(layer._path);
 
12788                 delete this._layers[stamp(layer)];
 
12791         _updatePath: function (layer) {
 
12796         _updateStyle: function (layer) {
 
12797                 var path = layer._path,
 
12798                     options = layer.options;
 
12800                 if (!path) { return; }
 
12802                 if (options.stroke) {
 
12803                         path.setAttribute('stroke', options.color);
 
12804                         path.setAttribute('stroke-opacity', options.opacity);
 
12805                         path.setAttribute('stroke-width', options.weight);
 
12806                         path.setAttribute('stroke-linecap', options.lineCap);
 
12807                         path.setAttribute('stroke-linejoin', options.lineJoin);
 
12809                         if (options.dashArray) {
 
12810                                 path.setAttribute('stroke-dasharray', options.dashArray);
 
12812                                 path.removeAttribute('stroke-dasharray');
 
12815                         if (options.dashOffset) {
 
12816                                 path.setAttribute('stroke-dashoffset', options.dashOffset);
 
12818                                 path.removeAttribute('stroke-dashoffset');
 
12821                         path.setAttribute('stroke', 'none');
 
12824                 if (options.fill) {
 
12825                         path.setAttribute('fill', options.fillColor || options.color);
 
12826                         path.setAttribute('fill-opacity', options.fillOpacity);
 
12827                         path.setAttribute('fill-rule', options.fillRule || 'evenodd');
 
12829                         path.setAttribute('fill', 'none');
 
12833         _updatePoly: function (layer, closed) {
 
12834                 this._setPath(layer, pointsToPath(layer._parts, closed));
 
12837         _updateCircle: function (layer) {
 
12838                 var p = layer._point,
 
12839                     r = Math.max(Math.round(layer._radius), 1),
 
12840                     r2 = Math.max(Math.round(layer._radiusY), 1) || r,
 
12841                     arc = 'a' + r + ',' + r2 + ' 0 1,0 ';
 
12843                 // drawing a circle with two half-arcs
 
12844                 var d = layer._empty() ? 'M0 0' :
 
12845                         'M' + (p.x - r) + ',' + p.y +
 
12846                         arc + (r * 2) + ',0 ' +
 
12847                         arc + (-r * 2) + ',0 ';
 
12849                 this._setPath(layer, d);
 
12852         _setPath: function (layer, path) {
 
12853                 layer._path.setAttribute('d', path);
 
12856         // SVG does not have the concept of zIndex so we resort to changing the DOM order of elements
 
12857         _bringToFront: function (layer) {
 
12858                 toFront(layer._path);
 
12861         _bringToBack: function (layer) {
 
12862                 toBack(layer._path);
 
12867         SVG.include(vmlMixin);
 
12871 // @factory L.svg(options?: Renderer options)
 
12872 // Creates a SVG renderer with the given options.
 
12873 function svg$1(options) {
 
12874         return svg || vml ? new SVG(options) : null;
 
12878         // @namespace Map; @method getRenderer(layer: Path): Renderer
 
12879         // Returns the instance of `Renderer` that should be used to render the given
 
12880         // `Path`. It will ensure that the `renderer` options of the map and paths
 
12881         // are respected, and that the renderers do exist on the map.
 
12882         getRenderer: function (layer) {
 
12883                 // @namespace Path; @option renderer: Renderer
 
12884                 // Use this specific instance of `Renderer` for this path. Takes
 
12885                 // precedence over the map's [default renderer](#map-renderer).
 
12886                 var renderer = layer.options.renderer || this._getPaneRenderer(layer.options.pane) || this.options.renderer || this._renderer;
 
12889                         renderer = this._renderer = this._createRenderer();
 
12892                 if (!this.hasLayer(renderer)) {
 
12893                         this.addLayer(renderer);
 
12898         _getPaneRenderer: function (name) {
 
12899                 if (name === 'overlayPane' || name === undefined) {
 
12903                 var renderer = this._paneRenderers[name];
 
12904                 if (renderer === undefined) {
 
12905                         renderer = this._createRenderer({pane: name});
 
12906                         this._paneRenderers[name] = renderer;
 
12911         _createRenderer: function (options) {
 
12912                 // @namespace Map; @option preferCanvas: Boolean = false
 
12913                 // Whether `Path`s should be rendered on a `Canvas` renderer.
 
12914                 // By default, all `Path`s are rendered in a `SVG` renderer.
 
12915                 return (this.options.preferCanvas && canvas$1(options)) || svg$1(options);
 
12920  * L.Rectangle extends Polygon and creates a rectangle when passed a LatLngBounds object.
 
12926  * @inherits Polygon
 
12928  * A class for drawing rectangle overlays on a map. Extends `Polygon`.
 
12933  * // define rectangle geographical bounds
 
12934  * var bounds = [[54.559322, -5.767822], [56.1210604, -3.021240]];
 
12936  * // create an orange rectangle
 
12937  * L.rectangle(bounds, {color: "#ff7800", weight: 1}).addTo(map);
 
12939  * // zoom the map to the rectangle bounds
 
12940  * map.fitBounds(bounds);
 
12946 var Rectangle = Polygon.extend({
 
12947         initialize: function (latLngBounds, options) {
 
12948                 Polygon.prototype.initialize.call(this, this._boundsToLatLngs(latLngBounds), options);
 
12951         // @method setBounds(latLngBounds: LatLngBounds): this
 
12952         // Redraws the rectangle with the passed bounds.
 
12953         setBounds: function (latLngBounds) {
 
12954                 return this.setLatLngs(this._boundsToLatLngs(latLngBounds));
 
12957         _boundsToLatLngs: function (latLngBounds) {
 
12958                 latLngBounds = toLatLngBounds(latLngBounds);
 
12960                         latLngBounds.getSouthWest(),
 
12961                         latLngBounds.getNorthWest(),
 
12962                         latLngBounds.getNorthEast(),
 
12963                         latLngBounds.getSouthEast()
 
12969 // @factory L.rectangle(latLngBounds: LatLngBounds, options?: Polyline options)
 
12970 function rectangle(latLngBounds, options) {
 
12971         return new Rectangle(latLngBounds, options);
 
12974 SVG.create = create$2;
 
12975 SVG.pointsToPath = pointsToPath;
 
12977 GeoJSON.geometryToLayer = geometryToLayer;
 
12978 GeoJSON.coordsToLatLng = coordsToLatLng;
 
12979 GeoJSON.coordsToLatLngs = coordsToLatLngs;
 
12980 GeoJSON.latLngToCoords = latLngToCoords;
 
12981 GeoJSON.latLngsToCoords = latLngsToCoords;
 
12982 GeoJSON.getFeature = getFeature;
 
12983 GeoJSON.asFeature = asFeature;
 
12986  * L.Handler.BoxZoom is used to add shift-drag zoom interaction to the map
 
12987  * (zoom to a selected bounding box), enabled by default.
 
12991 // @section Interaction Options
 
12993         // @option boxZoom: Boolean = true
 
12994         // Whether the map can be zoomed to a rectangular area specified by
 
12995         // dragging the mouse while pressing the shift key.
 
12999 var BoxZoom = Handler.extend({
 
13000         initialize: function (map) {
 
13002                 this._container = map._container;
 
13003                 this._pane = map._panes.overlayPane;
 
13004                 this._resetStateTimeout = 0;
 
13005                 map.on('unload', this._destroy, this);
 
13008         addHooks: function () {
 
13009                 on(this._container, 'mousedown', this._onMouseDown, this);
 
13012         removeHooks: function () {
 
13013                 off(this._container, 'mousedown', this._onMouseDown, this);
 
13016         moved: function () {
 
13017                 return this._moved;
 
13020         _destroy: function () {
 
13021                 remove(this._pane);
 
13025         _resetState: function () {
 
13026                 this._resetStateTimeout = 0;
 
13027                 this._moved = false;
 
13030         _clearDeferredResetState: function () {
 
13031                 if (this._resetStateTimeout !== 0) {
 
13032                         clearTimeout(this._resetStateTimeout);
 
13033                         this._resetStateTimeout = 0;
 
13037         _onMouseDown: function (e) {
 
13038                 if (!e.shiftKey || ((e.which !== 1) && (e.button !== 1))) { return false; }
 
13040                 // Clear the deferred resetState if it hasn't executed yet, otherwise it
 
13041                 // will interrupt the interaction and orphan a box element in the container.
 
13042                 this._clearDeferredResetState();
 
13043                 this._resetState();
 
13045                 disableTextSelection();
 
13046                 disableImageDrag();
 
13048                 this._startPoint = this._map.mouseEventToContainerPoint(e);
 
13052                         mousemove: this._onMouseMove,
 
13053                         mouseup: this._onMouseUp,
 
13054                         keydown: this._onKeyDown
 
13058         _onMouseMove: function (e) {
 
13059                 if (!this._moved) {
 
13060                         this._moved = true;
 
13062                         this._box = create$1('div', 'leaflet-zoom-box', this._container);
 
13063                         addClass(this._container, 'leaflet-crosshair');
 
13065                         this._map.fire('boxzoomstart');
 
13068                 this._point = this._map.mouseEventToContainerPoint(e);
 
13070                 var bounds = new Bounds(this._point, this._startPoint),
 
13071                     size = bounds.getSize();
 
13073                 setPosition(this._box, bounds.min);
 
13075                 this._box.style.width  = size.x + 'px';
 
13076                 this._box.style.height = size.y + 'px';
 
13079         _finish: function () {
 
13082                         removeClass(this._container, 'leaflet-crosshair');
 
13085                 enableTextSelection();
 
13090                         mousemove: this._onMouseMove,
 
13091                         mouseup: this._onMouseUp,
 
13092                         keydown: this._onKeyDown
 
13096         _onMouseUp: function (e) {
 
13097                 if ((e.which !== 1) && (e.button !== 1)) { return; }
 
13101                 if (!this._moved) { return; }
 
13102                 // Postpone to next JS tick so internal click event handling
 
13103                 // still see it as "moved".
 
13104                 this._clearDeferredResetState();
 
13105                 this._resetStateTimeout = setTimeout(bind(this._resetState, this), 0);
 
13107                 var bounds = new LatLngBounds(
 
13108                         this._map.containerPointToLatLng(this._startPoint),
 
13109                         this._map.containerPointToLatLng(this._point));
 
13113                         .fire('boxzoomend', {boxZoomBounds: bounds});
 
13116         _onKeyDown: function (e) {
 
13117                 if (e.keyCode === 27) {
 
13123 // @section Handlers
 
13124 // @property boxZoom: Handler
 
13125 // Box (shift-drag with mouse) zoom handler.
 
13126 Map.addInitHook('addHandler', 'boxZoom', BoxZoom);
 
13129  * L.Handler.DoubleClickZoom is used to handle double-click zoom on the map, enabled by default.
 
13133 // @section Interaction Options
 
13136         // @option doubleClickZoom: Boolean|String = true
 
13137         // Whether the map can be zoomed in by double clicking on it and
 
13138         // zoomed out by double clicking while holding shift. If passed
 
13139         // `'center'`, double-click zoom will zoom to the center of the
 
13140         //  view regardless of where the mouse was.
 
13141         doubleClickZoom: true
 
13144 var DoubleClickZoom = Handler.extend({
 
13145         addHooks: function () {
 
13146                 this._map.on('dblclick', this._onDoubleClick, this);
 
13149         removeHooks: function () {
 
13150                 this._map.off('dblclick', this._onDoubleClick, this);
 
13153         _onDoubleClick: function (e) {
 
13154                 var map = this._map,
 
13155                     oldZoom = map.getZoom(),
 
13156                     delta = map.options.zoomDelta,
 
13157                     zoom = e.originalEvent.shiftKey ? oldZoom - delta : oldZoom + delta;
 
13159                 if (map.options.doubleClickZoom === 'center') {
 
13162                         map.setZoomAround(e.containerPoint, zoom);
 
13167 // @section Handlers
 
13169 // Map properties include interaction handlers that allow you to control
 
13170 // interaction behavior in runtime, enabling or disabling certain features such
 
13171 // as dragging or touch zoom (see `Handler` methods). For example:
 
13174 // map.doubleClickZoom.disable();
 
13177 // @property doubleClickZoom: Handler
 
13178 // Double click zoom handler.
 
13179 Map.addInitHook('addHandler', 'doubleClickZoom', DoubleClickZoom);
 
13182  * L.Handler.MapDrag is used to make the map draggable (with panning inertia), enabled by default.
 
13186 // @section Interaction Options
 
13188         // @option dragging: Boolean = true
 
13189         // Whether the map be draggable with mouse/touch or not.
 
13192         // @section Panning Inertia Options
 
13193         // @option inertia: Boolean = *
 
13194         // If enabled, panning of the map will have an inertia effect where
 
13195         // the map builds momentum while dragging and continues moving in
 
13196         // the same direction for some time. Feels especially nice on touch
 
13197         // devices. Enabled by default unless running on old Android devices.
 
13198         inertia: !android23,
 
13200         // @option inertiaDeceleration: Number = 3000
 
13201         // The rate with which the inertial movement slows down, in pixels/second².
 
13202         inertiaDeceleration: 3400, // px/s^2
 
13204         // @option inertiaMaxSpeed: Number = Infinity
 
13205         // Max speed of the inertial movement, in pixels/second.
 
13206         inertiaMaxSpeed: Infinity, // px/s
 
13208         // @option easeLinearity: Number = 0.2
 
13209         easeLinearity: 0.2,
 
13211         // TODO refactor, move to CRS
 
13212         // @option worldCopyJump: Boolean = false
 
13213         // With this option enabled, the map tracks when you pan to another "copy"
 
13214         // of the world and seamlessly jumps to the original one so that all overlays
 
13215         // like markers and vector layers are still visible.
 
13216         worldCopyJump: false,
 
13218         // @option maxBoundsViscosity: Number = 0.0
 
13219         // If `maxBounds` is set, this option will control how solid the bounds
 
13220         // are when dragging the map around. The default value of `0.0` allows the
 
13221         // user to drag outside the bounds at normal speed, higher values will
 
13222         // slow down map dragging outside bounds, and `1.0` makes the bounds fully
 
13223         // solid, preventing the user from dragging outside the bounds.
 
13224         maxBoundsViscosity: 0.0
 
13227 var Drag = Handler.extend({
 
13228         addHooks: function () {
 
13229                 if (!this._draggable) {
 
13230                         var map = this._map;
 
13232                         this._draggable = new Draggable(map._mapPane, map._container);
 
13234                         this._draggable.on({
 
13235                                 dragstart: this._onDragStart,
 
13236                                 drag: this._onDrag,
 
13237                                 dragend: this._onDragEnd
 
13240                         this._draggable.on('predrag', this._onPreDragLimit, this);
 
13241                         if (map.options.worldCopyJump) {
 
13242                                 this._draggable.on('predrag', this._onPreDragWrap, this);
 
13243                                 map.on('zoomend', this._onZoomEnd, this);
 
13245                                 map.whenReady(this._onZoomEnd, this);
 
13248                 addClass(this._map._container, 'leaflet-grab leaflet-touch-drag');
 
13249                 this._draggable.enable();
 
13250                 this._positions = [];
 
13254         removeHooks: function () {
 
13255                 removeClass(this._map._container, 'leaflet-grab');
 
13256                 removeClass(this._map._container, 'leaflet-touch-drag');
 
13257                 this._draggable.disable();
 
13260         moved: function () {
 
13261                 return this._draggable && this._draggable._moved;
 
13264         moving: function () {
 
13265                 return this._draggable && this._draggable._moving;
 
13268         _onDragStart: function () {
 
13269                 var map = this._map;
 
13272                 if (this._map.options.maxBounds && this._map.options.maxBoundsViscosity) {
 
13273                         var bounds = toLatLngBounds(this._map.options.maxBounds);
 
13275                         this._offsetLimit = toBounds(
 
13276                                 this._map.latLngToContainerPoint(bounds.getNorthWest()).multiplyBy(-1),
 
13277                                 this._map.latLngToContainerPoint(bounds.getSouthEast()).multiplyBy(-1)
 
13278                                         .add(this._map.getSize()));
 
13280                         this._viscosity = Math.min(1.0, Math.max(0.0, this._map.options.maxBoundsViscosity));
 
13282                         this._offsetLimit = null;
 
13287                     .fire('dragstart');
 
13289                 if (map.options.inertia) {
 
13290                         this._positions = [];
 
13295         _onDrag: function (e) {
 
13296                 if (this._map.options.inertia) {
 
13297                         var time = this._lastTime = +new Date(),
 
13298                             pos = this._lastPos = this._draggable._absPos || this._draggable._newPos;
 
13300                         this._positions.push(pos);
 
13301                         this._times.push(time);
 
13303                         this._prunePositions(time);
 
13311         _prunePositions: function (time) {
 
13312                 while (this._positions.length > 1 && time - this._times[0] > 50) {
 
13313                         this._positions.shift();
 
13314                         this._times.shift();
 
13318         _onZoomEnd: function () {
 
13319                 var pxCenter = this._map.getSize().divideBy(2),
 
13320                     pxWorldCenter = this._map.latLngToLayerPoint([0, 0]);
 
13322                 this._initialWorldOffset = pxWorldCenter.subtract(pxCenter).x;
 
13323                 this._worldWidth = this._map.getPixelWorldBounds().getSize().x;
 
13326         _viscousLimit: function (value, threshold) {
 
13327                 return value - (value - threshold) * this._viscosity;
 
13330         _onPreDragLimit: function () {
 
13331                 if (!this._viscosity || !this._offsetLimit) { return; }
 
13333                 var offset = this._draggable._newPos.subtract(this._draggable._startPos);
 
13335                 var limit = this._offsetLimit;
 
13336                 if (offset.x < limit.min.x) { offset.x = this._viscousLimit(offset.x, limit.min.x); }
 
13337                 if (offset.y < limit.min.y) { offset.y = this._viscousLimit(offset.y, limit.min.y); }
 
13338                 if (offset.x > limit.max.x) { offset.x = this._viscousLimit(offset.x, limit.max.x); }
 
13339                 if (offset.y > limit.max.y) { offset.y = this._viscousLimit(offset.y, limit.max.y); }
 
13341                 this._draggable._newPos = this._draggable._startPos.add(offset);
 
13344         _onPreDragWrap: function () {
 
13345                 // TODO refactor to be able to adjust map pane position after zoom
 
13346                 var worldWidth = this._worldWidth,
 
13347                     halfWidth = Math.round(worldWidth / 2),
 
13348                     dx = this._initialWorldOffset,
 
13349                     x = this._draggable._newPos.x,
 
13350                     newX1 = (x - halfWidth + dx) % worldWidth + halfWidth - dx,
 
13351                     newX2 = (x + halfWidth + dx) % worldWidth - halfWidth - dx,
 
13352                     newX = Math.abs(newX1 + dx) < Math.abs(newX2 + dx) ? newX1 : newX2;
 
13354                 this._draggable._absPos = this._draggable._newPos.clone();
 
13355                 this._draggable._newPos.x = newX;
 
13358         _onDragEnd: function (e) {
 
13359                 var map = this._map,
 
13360                     options = map.options,
 
13362                     noInertia = !options.inertia || this._times.length < 2;
 
13364                 map.fire('dragend', e);
 
13367                         map.fire('moveend');
 
13370                         this._prunePositions(+new Date());
 
13372                         var direction = this._lastPos.subtract(this._positions[0]),
 
13373                             duration = (this._lastTime - this._times[0]) / 1000,
 
13374                             ease = options.easeLinearity,
 
13376                             speedVector = direction.multiplyBy(ease / duration),
 
13377                             speed = speedVector.distanceTo([0, 0]),
 
13379                             limitedSpeed = Math.min(options.inertiaMaxSpeed, speed),
 
13380                             limitedSpeedVector = speedVector.multiplyBy(limitedSpeed / speed),
 
13382                             decelerationDuration = limitedSpeed / (options.inertiaDeceleration * ease),
 
13383                             offset = limitedSpeedVector.multiplyBy(-decelerationDuration / 2).round();
 
13385                         if (!offset.x && !offset.y) {
 
13386                                 map.fire('moveend');
 
13389                                 offset = map._limitOffset(offset, map.options.maxBounds);
 
13391                                 requestAnimFrame(function () {
 
13392                                         map.panBy(offset, {
 
13393                                                 duration: decelerationDuration,
 
13394                                                 easeLinearity: ease,
 
13404 // @section Handlers
 
13405 // @property dragging: Handler
 
13406 // Map dragging handler (by both mouse and touch).
 
13407 Map.addInitHook('addHandler', 'dragging', Drag);
 
13410  * L.Map.Keyboard is handling keyboard interaction with the map, enabled by default.
 
13414 // @section Keyboard Navigation Options
 
13416         // @option keyboard: Boolean = true
 
13417         // Makes the map focusable and allows users to navigate the map with keyboard
 
13418         // arrows and `+`/`-` keys.
 
13421         // @option keyboardPanDelta: Number = 80
 
13422         // Amount of pixels to pan when pressing an arrow key.
 
13423         keyboardPanDelta: 80
 
13426 var Keyboard = Handler.extend({
 
13433                 zoomIn:  [187, 107, 61, 171],
 
13434                 zoomOut: [189, 109, 54, 173]
 
13437         initialize: function (map) {
 
13440                 this._setPanDelta(map.options.keyboardPanDelta);
 
13441                 this._setZoomDelta(map.options.zoomDelta);
 
13444         addHooks: function () {
 
13445                 var container = this._map._container;
 
13447                 // make the container focusable by tabbing
 
13448                 if (container.tabIndex <= 0) {
 
13449                         container.tabIndex = '0';
 
13453                         focus: this._onFocus,
 
13454                         blur: this._onBlur,
 
13455                         mousedown: this._onMouseDown
 
13459                         focus: this._addHooks,
 
13460                         blur: this._removeHooks
 
13464         removeHooks: function () {
 
13465                 this._removeHooks();
 
13467                 off(this._map._container, {
 
13468                         focus: this._onFocus,
 
13469                         blur: this._onBlur,
 
13470                         mousedown: this._onMouseDown
 
13474                         focus: this._addHooks,
 
13475                         blur: this._removeHooks
 
13479         _onMouseDown: function () {
 
13480                 if (this._focused) { return; }
 
13482                 var body = document.body,
 
13483                     docEl = document.documentElement,
 
13484                     top = body.scrollTop || docEl.scrollTop,
 
13485                     left = body.scrollLeft || docEl.scrollLeft;
 
13487                 this._map._container.focus();
 
13489                 window.scrollTo(left, top);
 
13492         _onFocus: function () {
 
13493                 this._focused = true;
 
13494                 this._map.fire('focus');
 
13497         _onBlur: function () {
 
13498                 this._focused = false;
 
13499                 this._map.fire('blur');
 
13502         _setPanDelta: function (panDelta) {
 
13503                 var keys = this._panKeys = {},
 
13504                     codes = this.keyCodes,
 
13507                 for (i = 0, len = codes.left.length; i < len; i++) {
 
13508                         keys[codes.left[i]] = [-1 * panDelta, 0];
 
13510                 for (i = 0, len = codes.right.length; i < len; i++) {
 
13511                         keys[codes.right[i]] = [panDelta, 0];
 
13513                 for (i = 0, len = codes.down.length; i < len; i++) {
 
13514                         keys[codes.down[i]] = [0, panDelta];
 
13516                 for (i = 0, len = codes.up.length; i < len; i++) {
 
13517                         keys[codes.up[i]] = [0, -1 * panDelta];
 
13521         _setZoomDelta: function (zoomDelta) {
 
13522                 var keys = this._zoomKeys = {},
 
13523                     codes = this.keyCodes,
 
13526                 for (i = 0, len = codes.zoomIn.length; i < len; i++) {
 
13527                         keys[codes.zoomIn[i]] = zoomDelta;
 
13529                 for (i = 0, len = codes.zoomOut.length; i < len; i++) {
 
13530                         keys[codes.zoomOut[i]] = -zoomDelta;
 
13534         _addHooks: function () {
 
13535                 on(document, 'keydown', this._onKeyDown, this);
 
13538         _removeHooks: function () {
 
13539                 off(document, 'keydown', this._onKeyDown, this);
 
13542         _onKeyDown: function (e) {
 
13543                 if (e.altKey || e.ctrlKey || e.metaKey) { return; }
 
13545                 var key = e.keyCode,
 
13549                 if (key in this._panKeys) {
 
13550                         if (!map._panAnim || !map._panAnim._inProgress) {
 
13551                                 offset = this._panKeys[key];
 
13553                                         offset = toPoint(offset).multiplyBy(3);
 
13558                                 if (map.options.maxBounds) {
 
13559                                         map.panInsideBounds(map.options.maxBounds);
 
13562                 } else if (key in this._zoomKeys) {
 
13563                         map.setZoom(map.getZoom() + (e.shiftKey ? 3 : 1) * this._zoomKeys[key]);
 
13565                 } else if (key === 27 && map._popup && map._popup.options.closeOnEscapeKey) {
 
13576 // @section Handlers
 
13577 // @section Handlers
 
13578 // @property keyboard: Handler
 
13579 // Keyboard navigation handler.
 
13580 Map.addInitHook('addHandler', 'keyboard', Keyboard);
 
13583  * L.Handler.ScrollWheelZoom is used by L.Map to enable mouse scroll wheel zoom on the map.
 
13587 // @section Interaction Options
 
13589         // @section Mousewheel options
 
13590         // @option scrollWheelZoom: Boolean|String = true
 
13591         // Whether the map can be zoomed by using the mouse wheel. If passed `'center'`,
 
13592         // it will zoom to the center of the view regardless of where the mouse was.
 
13593         scrollWheelZoom: true,
 
13595         // @option wheelDebounceTime: Number = 40
 
13596         // Limits the rate at which a wheel can fire (in milliseconds). By default
 
13597         // user can't zoom via wheel more often than once per 40 ms.
 
13598         wheelDebounceTime: 40,
 
13600         // @option wheelPxPerZoomLevel: Number = 60
 
13601         // How many scroll pixels (as reported by [L.DomEvent.getWheelDelta](#domevent-getwheeldelta))
 
13602         // mean a change of one full zoom level. Smaller values will make wheel-zooming
 
13603         // faster (and vice versa).
 
13604         wheelPxPerZoomLevel: 60
 
13607 var ScrollWheelZoom = Handler.extend({
 
13608         addHooks: function () {
 
13609                 on(this._map._container, 'mousewheel', this._onWheelScroll, this);
 
13614         removeHooks: function () {
 
13615                 off(this._map._container, 'mousewheel', this._onWheelScroll, this);
 
13618         _onWheelScroll: function (e) {
 
13619                 var delta = getWheelDelta(e);
 
13621                 var debounce = this._map.options.wheelDebounceTime;
 
13623                 this._delta += delta;
 
13624                 this._lastMousePos = this._map.mouseEventToContainerPoint(e);
 
13626                 if (!this._startTime) {
 
13627                         this._startTime = +new Date();
 
13630                 var left = Math.max(debounce - (+new Date() - this._startTime), 0);
 
13632                 clearTimeout(this._timer);
 
13633                 this._timer = setTimeout(bind(this._performZoom, this), left);
 
13638         _performZoom: function () {
 
13639                 var map = this._map,
 
13640                     zoom = map.getZoom(),
 
13641                     snap = this._map.options.zoomSnap || 0;
 
13643                 map._stop(); // stop panning and fly animations if any
 
13645                 // map the delta with a sigmoid function to -4..4 range leaning on -1..1
 
13646                 var d2 = this._delta / (this._map.options.wheelPxPerZoomLevel * 4),
 
13647                     d3 = 4 * Math.log(2 / (1 + Math.exp(-Math.abs(d2)))) / Math.LN2,
 
13648                     d4 = snap ? Math.ceil(d3 / snap) * snap : d3,
 
13649                     delta = map._limitZoom(zoom + (this._delta > 0 ? d4 : -d4)) - zoom;
 
13652                 this._startTime = null;
 
13654                 if (!delta) { return; }
 
13656                 if (map.options.scrollWheelZoom === 'center') {
 
13657                         map.setZoom(zoom + delta);
 
13659                         map.setZoomAround(this._lastMousePos, zoom + delta);
 
13664 // @section Handlers
 
13665 // @property scrollWheelZoom: Handler
 
13666 // Scroll wheel zoom handler.
 
13667 Map.addInitHook('addHandler', 'scrollWheelZoom', ScrollWheelZoom);
 
13670  * L.Map.Tap is used to enable mobile hacks like quick taps and long hold.
 
13674 // @section Interaction Options
 
13676         // @section Touch interaction options
 
13677         // @option tap: Boolean = true
 
13678         // Enables mobile hacks for supporting instant taps (fixing 200ms click
 
13679         // delay on iOS/Android) and touch holds (fired as `contextmenu` events).
 
13682         // @option tapTolerance: Number = 15
 
13683         // The max number of pixels a user can shift his finger during touch
 
13684         // for it to be considered a valid tap.
 
13688 var Tap = Handler.extend({
 
13689         addHooks: function () {
 
13690                 on(this._map._container, 'touchstart', this._onDown, this);
 
13693         removeHooks: function () {
 
13694                 off(this._map._container, 'touchstart', this._onDown, this);
 
13697         _onDown: function (e) {
 
13698                 if (!e.touches) { return; }
 
13702                 this._fireClick = true;
 
13704                 // don't simulate click or track longpress if more than 1 touch
 
13705                 if (e.touches.length > 1) {
 
13706                         this._fireClick = false;
 
13707                         clearTimeout(this._holdTimeout);
 
13711                 var first = e.touches[0],
 
13714                 this._startPos = this._newPos = new Point(first.clientX, first.clientY);
 
13716                 // if touching a link, highlight it
 
13717                 if (el.tagName && el.tagName.toLowerCase() === 'a') {
 
13718                         addClass(el, 'leaflet-active');
 
13721                 // simulate long hold but setting a timeout
 
13722                 this._holdTimeout = setTimeout(bind(function () {
 
13723                         if (this._isTapValid()) {
 
13724                                 this._fireClick = false;
 
13726                                 this._simulateEvent('contextmenu', first);
 
13730                 this._simulateEvent('mousedown', first);
 
13733                         touchmove: this._onMove,
 
13734                         touchend: this._onUp
 
13738         _onUp: function (e) {
 
13739                 clearTimeout(this._holdTimeout);
 
13742                         touchmove: this._onMove,
 
13743                         touchend: this._onUp
 
13746                 if (this._fireClick && e && e.changedTouches) {
 
13748                         var first = e.changedTouches[0],
 
13751                         if (el && el.tagName && el.tagName.toLowerCase() === 'a') {
 
13752                                 removeClass(el, 'leaflet-active');
 
13755                         this._simulateEvent('mouseup', first);
 
13757                         // simulate click if the touch didn't move too much
 
13758                         if (this._isTapValid()) {
 
13759                                 this._simulateEvent('click', first);
 
13764         _isTapValid: function () {
 
13765                 return this._newPos.distanceTo(this._startPos) <= this._map.options.tapTolerance;
 
13768         _onMove: function (e) {
 
13769                 var first = e.touches[0];
 
13770                 this._newPos = new Point(first.clientX, first.clientY);
 
13771                 this._simulateEvent('mousemove', first);
 
13774         _simulateEvent: function (type, e) {
 
13775                 var simulatedEvent = document.createEvent('MouseEvents');
 
13777                 simulatedEvent._simulated = true;
 
13778                 e.target._simulatedClick = true;
 
13780                 simulatedEvent.initMouseEvent(
 
13781                         type, true, true, window, 1,
 
13782                         e.screenX, e.screenY,
 
13783                         e.clientX, e.clientY,
 
13784                         false, false, false, false, 0, null);
 
13786                 e.target.dispatchEvent(simulatedEvent);
 
13790 // @section Handlers
 
13791 // @property tap: Handler
 
13792 // Mobile touch hacks (quick tap and touch hold) handler.
 
13793 if (touch && !pointer) {
 
13794         Map.addInitHook('addHandler', 'tap', Tap);
 
13798  * L.Handler.TouchZoom is used by L.Map to add pinch zoom on supported mobile browsers.
 
13802 // @section Interaction Options
 
13804         // @section Touch interaction options
 
13805         // @option touchZoom: Boolean|String = *
 
13806         // Whether the map can be zoomed by touch-dragging with two fingers. If
 
13807         // passed `'center'`, it will zoom to the center of the view regardless of
 
13808         // where the touch events (fingers) were. Enabled for touch-capable web
 
13809         // browsers except for old Androids.
 
13810         touchZoom: touch && !android23,
 
13812         // @option bounceAtZoomLimits: Boolean = true
 
13813         // Set it to false if you don't want the map to zoom beyond min/max zoom
 
13814         // and then bounce back when pinch-zooming.
 
13815         bounceAtZoomLimits: true
 
13818 var TouchZoom = Handler.extend({
 
13819         addHooks: function () {
 
13820                 addClass(this._map._container, 'leaflet-touch-zoom');
 
13821                 on(this._map._container, 'touchstart', this._onTouchStart, this);
 
13824         removeHooks: function () {
 
13825                 removeClass(this._map._container, 'leaflet-touch-zoom');
 
13826                 off(this._map._container, 'touchstart', this._onTouchStart, this);
 
13829         _onTouchStart: function (e) {
 
13830                 var map = this._map;
 
13831                 if (!e.touches || e.touches.length !== 2 || map._animatingZoom || this._zooming) { return; }
 
13833                 var p1 = map.mouseEventToContainerPoint(e.touches[0]),
 
13834                     p2 = map.mouseEventToContainerPoint(e.touches[1]);
 
13836                 this._centerPoint = map.getSize()._divideBy(2);
 
13837                 this._startLatLng = map.containerPointToLatLng(this._centerPoint);
 
13838                 if (map.options.touchZoom !== 'center') {
 
13839                         this._pinchStartLatLng = map.containerPointToLatLng(p1.add(p2)._divideBy(2));
 
13842                 this._startDist = p1.distanceTo(p2);
 
13843                 this._startZoom = map.getZoom();
 
13845                 this._moved = false;
 
13846                 this._zooming = true;
 
13850                 on(document, 'touchmove', this._onTouchMove, this);
 
13851                 on(document, 'touchend', this._onTouchEnd, this);
 
13856         _onTouchMove: function (e) {
 
13857                 if (!e.touches || e.touches.length !== 2 || !this._zooming) { return; }
 
13859                 var map = this._map,
 
13860                     p1 = map.mouseEventToContainerPoint(e.touches[0]),
 
13861                     p2 = map.mouseEventToContainerPoint(e.touches[1]),
 
13862                     scale = p1.distanceTo(p2) / this._startDist;
 
13864                 this._zoom = map.getScaleZoom(scale, this._startZoom);
 
13866                 if (!map.options.bounceAtZoomLimits && (
 
13867                         (this._zoom < map.getMinZoom() && scale < 1) ||
 
13868                         (this._zoom > map.getMaxZoom() && scale > 1))) {
 
13869                         this._zoom = map._limitZoom(this._zoom);
 
13872                 if (map.options.touchZoom === 'center') {
 
13873                         this._center = this._startLatLng;
 
13874                         if (scale === 1) { return; }
 
13876                         // Get delta from pinch to center, so centerLatLng is delta applied to initial pinchLatLng
 
13877                         var delta = p1._add(p2)._divideBy(2)._subtract(this._centerPoint);
 
13878                         if (scale === 1 && delta.x === 0 && delta.y === 0) { return; }
 
13879                         this._center = map.unproject(map.project(this._pinchStartLatLng, this._zoom).subtract(delta), this._zoom);
 
13882                 if (!this._moved) {
 
13883                         map._moveStart(true, false);
 
13884                         this._moved = true;
 
13887                 cancelAnimFrame(this._animRequest);
 
13889                 var moveFn = bind(map._move, map, this._center, this._zoom, {pinch: true, round: false});
 
13890                 this._animRequest = requestAnimFrame(moveFn, this, true);
 
13895         _onTouchEnd: function () {
 
13896                 if (!this._moved || !this._zooming) {
 
13897                         this._zooming = false;
 
13901                 this._zooming = false;
 
13902                 cancelAnimFrame(this._animRequest);
 
13904                 off(document, 'touchmove', this._onTouchMove);
 
13905                 off(document, 'touchend', this._onTouchEnd);
 
13907                 // Pinch updates GridLayers' levels only when zoomSnap is off, so zoomSnap becomes noUpdate.
 
13908                 if (this._map.options.zoomAnimation) {
 
13909                         this._map._animateZoom(this._center, this._map._limitZoom(this._zoom), true, this._map.options.zoomSnap);
 
13911                         this._map._resetView(this._center, this._map._limitZoom(this._zoom));
 
13916 // @section Handlers
 
13917 // @property touchZoom: Handler
 
13918 // Touch zoom handler.
 
13919 Map.addInitHook('addHandler', 'touchZoom', TouchZoom);
 
13921 Map.BoxZoom = BoxZoom;
 
13922 Map.DoubleClickZoom = DoubleClickZoom;
 
13924 Map.Keyboard = Keyboard;
 
13925 Map.ScrollWheelZoom = ScrollWheelZoom;
 
13927 Map.TouchZoom = TouchZoom;
 
13929 Object.freeze = freeze;
 
13931 exports.version = version;
 
13932 exports.Control = Control;
 
13933 exports.control = control;
 
13934 exports.Browser = Browser;
 
13935 exports.Evented = Evented;
 
13936 exports.Mixin = Mixin;
 
13937 exports.Util = Util;
 
13938 exports.Class = Class;
 
13939 exports.Handler = Handler;
 
13940 exports.extend = extend;
 
13941 exports.bind = bind;
 
13942 exports.stamp = stamp;
 
13943 exports.setOptions = setOptions;
 
13944 exports.DomEvent = DomEvent;
 
13945 exports.DomUtil = DomUtil;
 
13946 exports.PosAnimation = PosAnimation;
 
13947 exports.Draggable = Draggable;
 
13948 exports.LineUtil = LineUtil;
 
13949 exports.PolyUtil = PolyUtil;
 
13950 exports.Point = Point;
 
13951 exports.point = toPoint;
 
13952 exports.Bounds = Bounds;
 
13953 exports.bounds = toBounds;
 
13954 exports.Transformation = Transformation;
 
13955 exports.transformation = toTransformation;
 
13956 exports.Projection = index;
 
13957 exports.LatLng = LatLng;
 
13958 exports.latLng = toLatLng;
 
13959 exports.LatLngBounds = LatLngBounds;
 
13960 exports.latLngBounds = toLatLngBounds;
 
13962 exports.GeoJSON = GeoJSON;
 
13963 exports.geoJSON = geoJSON;
 
13964 exports.geoJson = geoJson;
 
13965 exports.Layer = Layer;
 
13966 exports.LayerGroup = LayerGroup;
 
13967 exports.layerGroup = layerGroup;
 
13968 exports.FeatureGroup = FeatureGroup;
 
13969 exports.featureGroup = featureGroup;
 
13970 exports.ImageOverlay = ImageOverlay;
 
13971 exports.imageOverlay = imageOverlay;
 
13972 exports.VideoOverlay = VideoOverlay;
 
13973 exports.videoOverlay = videoOverlay;
 
13974 exports.SVGOverlay = SVGOverlay;
 
13975 exports.svgOverlay = svgOverlay;
 
13976 exports.DivOverlay = DivOverlay;
 
13977 exports.Popup = Popup;
 
13978 exports.popup = popup;
 
13979 exports.Tooltip = Tooltip;
 
13980 exports.tooltip = tooltip;
 
13981 exports.Icon = Icon;
 
13982 exports.icon = icon;
 
13983 exports.DivIcon = DivIcon;
 
13984 exports.divIcon = divIcon;
 
13985 exports.Marker = Marker;
 
13986 exports.marker = marker;
 
13987 exports.TileLayer = TileLayer;
 
13988 exports.tileLayer = tileLayer;
 
13989 exports.GridLayer = GridLayer;
 
13990 exports.gridLayer = gridLayer;
 
13992 exports.svg = svg$1;
 
13993 exports.Renderer = Renderer;
 
13994 exports.Canvas = Canvas;
 
13995 exports.canvas = canvas$1;
 
13996 exports.Path = Path;
 
13997 exports.CircleMarker = CircleMarker;
 
13998 exports.circleMarker = circleMarker;
 
13999 exports.Circle = Circle;
 
14000 exports.circle = circle;
 
14001 exports.Polyline = Polyline;
 
14002 exports.polyline = polyline;
 
14003 exports.Polygon = Polygon;
 
14004 exports.polygon = polygon;
 
14005 exports.Rectangle = Rectangle;
 
14006 exports.rectangle = rectangle;
 
14008 exports.map = createMap;
 
14010 var oldL = window.L;
 
14011 exports.noConflict = function() {
 
14016 // Always export us to window global (see #2364)
 
14017 window.L = exports;
 
14020 //# sourceMappingURL=leaflet-src.js.map