2  * Leaflet 1.6.0, a JS library for interactive maps. http://leafletjs.com
 
   3  * (c) 2010-2019 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.6.0";
 
  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         var pow = Math.pow(10, (digits === undefined ? 6 : digits));
 
 131         return Math.round(num * pow) / pow;
 
 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 = !webkit && !!(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;
 
1930 // @property passiveEvents: Boolean
 
1931 // `true` for browsers that support passive events.
 
1932 var passiveEvents = (function () {
 
1933         var supportsPassiveOption = false;
 
1935                 var opts = Object.defineProperty({}, 'passive', {
 
1937                                 supportsPassiveOption = true;
 
1940                 window.addEventListener('testPassiveEventSupport', falseFn, opts);
 
1941                 window.removeEventListener('testPassiveEventSupport', falseFn, opts);
 
1943                 // Errors can safely be ignored since this is only a browser support test.
 
1945         return supportsPassiveOption;
 
1948 // @property canvas: Boolean
 
1949 // `true` when the browser supports [`<canvas>`](https://developer.mozilla.org/docs/Web/API/Canvas_API).
 
1950 var canvas = (function () {
 
1951         return !!document.createElement('canvas').getContext;
 
1954 // @property svg: Boolean
 
1955 // `true` when the browser supports [SVG](https://developer.mozilla.org/docs/Web/SVG).
 
1956 var svg = !!(document.createElementNS && svgCreate('svg').createSVGRect);
 
1958 // @property vml: Boolean
 
1959 // `true` if the browser supports [VML](https://en.wikipedia.org/wiki/Vector_Markup_Language).
 
1960 var vml = !svg && (function () {
 
1962                 var div = document.createElement('div');
 
1963                 div.innerHTML = '<v:shape adj="1"/>';
 
1965                 var shape = div.firstChild;
 
1966                 shape.style.behavior = 'url(#default#VML)';
 
1968                 return shape && (typeof shape.adj === 'object');
 
1976 function userAgentContains(str) {
 
1977         return navigator.userAgent.toLowerCase().indexOf(str) >= 0;
 
1981 var Browser = (Object.freeze || Object)({
 
1987         android23: android23,
 
1988         androidStock: androidStock,
 
2001         mobileWebkit: mobileWebkit,
 
2002         mobileWebkit3d: mobileWebkit3d,
 
2003         msPointer: msPointer,
 
2006         mobileOpera: mobileOpera,
 
2007         mobileGecko: mobileGecko,
 
2009         passiveEvents: passiveEvents,
 
2016  * Extends L.DomEvent to provide touch support for Internet Explorer and Windows-based devices.
 
2020 var POINTER_DOWN =   msPointer ? 'MSPointerDown'   : 'pointerdown';
 
2021 var POINTER_MOVE =   msPointer ? 'MSPointerMove'   : 'pointermove';
 
2022 var POINTER_UP =     msPointer ? 'MSPointerUp'     : 'pointerup';
 
2023 var POINTER_CANCEL = msPointer ? 'MSPointerCancel' : 'pointercancel';
 
2024 var TAG_WHITE_LIST = ['INPUT', 'SELECT', 'OPTION'];
 
2027 var _pointerDocListener = false;
 
2029 // DomEvent.DoubleTap needs to know about this
 
2030 var _pointersCount = 0;
 
2032 // Provides a touch events wrapper for (ms)pointer events.
 
2033 // ref http://www.w3.org/TR/pointerevents/ https://www.w3.org/Bugs/Public/show_bug.cgi?id=22890
 
2035 function addPointerListener(obj, type, handler, id) {
 
2036         if (type === 'touchstart') {
 
2037                 _addPointerStart(obj, handler, id);
 
2039         } else if (type === 'touchmove') {
 
2040                 _addPointerMove(obj, handler, id);
 
2042         } else if (type === 'touchend') {
 
2043                 _addPointerEnd(obj, handler, id);
 
2049 function removePointerListener(obj, type, id) {
 
2050         var handler = obj['_leaflet_' + type + id];
 
2052         if (type === 'touchstart') {
 
2053                 obj.removeEventListener(POINTER_DOWN, handler, false);
 
2055         } else if (type === 'touchmove') {
 
2056                 obj.removeEventListener(POINTER_MOVE, handler, false);
 
2058         } else if (type === 'touchend') {
 
2059                 obj.removeEventListener(POINTER_UP, handler, false);
 
2060                 obj.removeEventListener(POINTER_CANCEL, handler, false);
 
2066 function _addPointerStart(obj, handler, id) {
 
2067         var onDown = bind(function (e) {
 
2068                 if (e.pointerType !== 'mouse' && e.MSPOINTER_TYPE_MOUSE && e.pointerType !== e.MSPOINTER_TYPE_MOUSE) {
 
2069                         // In IE11, some touch events needs to fire for form controls, or
 
2070                         // the controls will stop working. We keep a whitelist of tag names that
 
2071                         // need these events. For other target tags, we prevent default on the event.
 
2072                         if (TAG_WHITE_LIST.indexOf(e.target.tagName) < 0) {
 
2079                 _handlePointer(e, handler);
 
2082         obj['_leaflet_touchstart' + id] = onDown;
 
2083         obj.addEventListener(POINTER_DOWN, onDown, false);
 
2085         // need to keep track of what pointers and how many are active to provide e.touches emulation
 
2086         if (!_pointerDocListener) {
 
2087                 // we listen documentElement as any drags that end by moving the touch off the screen get fired there
 
2088                 document.documentElement.addEventListener(POINTER_DOWN, _globalPointerDown, true);
 
2089                 document.documentElement.addEventListener(POINTER_MOVE, _globalPointerMove, true);
 
2090                 document.documentElement.addEventListener(POINTER_UP, _globalPointerUp, true);
 
2091                 document.documentElement.addEventListener(POINTER_CANCEL, _globalPointerUp, true);
 
2093                 _pointerDocListener = true;
 
2097 function _globalPointerDown(e) {
 
2098         _pointers[e.pointerId] = e;
 
2102 function _globalPointerMove(e) {
 
2103         if (_pointers[e.pointerId]) {
 
2104                 _pointers[e.pointerId] = e;
 
2108 function _globalPointerUp(e) {
 
2109         delete _pointers[e.pointerId];
 
2113 function _handlePointer(e, handler) {
 
2115         for (var i in _pointers) {
 
2116                 e.touches.push(_pointers[i]);
 
2118         e.changedTouches = [e];
 
2123 function _addPointerMove(obj, handler, id) {
 
2124         var onMove = function (e) {
 
2125                 // don't fire touch moves when mouse isn't down
 
2126                 if ((e.pointerType === e.MSPOINTER_TYPE_MOUSE || e.pointerType === 'mouse') && e.buttons === 0) { return; }
 
2128                 _handlePointer(e, handler);
 
2131         obj['_leaflet_touchmove' + id] = onMove;
 
2132         obj.addEventListener(POINTER_MOVE, onMove, false);
 
2135 function _addPointerEnd(obj, handler, id) {
 
2136         var onUp = function (e) {
 
2137                 _handlePointer(e, handler);
 
2140         obj['_leaflet_touchend' + id] = onUp;
 
2141         obj.addEventListener(POINTER_UP, onUp, false);
 
2142         obj.addEventListener(POINTER_CANCEL, onUp, false);
 
2146  * Extends the event handling code with double tap support for mobile browsers.
 
2149 var _touchstart = msPointer ? 'MSPointerDown' : pointer ? 'pointerdown' : 'touchstart';
 
2150 var _touchend = msPointer ? 'MSPointerUp' : pointer ? 'pointerup' : 'touchend';
 
2151 var _pre = '_leaflet_';
 
2153 // inspired by Zepto touch code by Thomas Fuchs
 
2154 function addDoubleTapListener(obj, handler, id) {
 
2159         function onTouchStart(e) {
 
2163                         if ((!edge) || e.pointerType === 'mouse') { return; }
 
2164                         count = _pointersCount;
 
2166                         count = e.touches.length;
 
2169                 if (count > 1) { return; }
 
2171                 var now = Date.now(),
 
2172                     delta = now - (last || now);
 
2174                 touch$$1 = e.touches ? e.touches[0] : e;
 
2175                 doubleTap = (delta > 0 && delta <= delay);
 
2179         function onTouchEnd(e) {
 
2180                 if (doubleTap && !touch$$1.cancelBubble) {
 
2182                                 if ((!edge) || e.pointerType === 'mouse') { return; }
 
2183                                 // work around .type being readonly with MSPointer* events
 
2187                                 for (i in touch$$1) {
 
2189                                         newTouch[i] = prop && prop.bind ? prop.bind(touch$$1) : prop;
 
2191                                 touch$$1 = newTouch;
 
2193                         touch$$1.type = 'dblclick';
 
2194                         touch$$1.button = 0;
 
2200         obj[_pre + _touchstart + id] = onTouchStart;
 
2201         obj[_pre + _touchend + id] = onTouchEnd;
 
2202         obj[_pre + 'dblclick' + id] = handler;
 
2204         obj.addEventListener(_touchstart, onTouchStart, passiveEvents ? {passive: false} : false);
 
2205         obj.addEventListener(_touchend, onTouchEnd, passiveEvents ? {passive: false} : false);
 
2207         // On some platforms (notably, chrome<55 on win10 + touchscreen + mouse),
 
2208         // the browser doesn't fire touchend/pointerup events but does fire
 
2209         // native dblclicks. See #4127.
 
2210         // Edge 14 also fires native dblclicks, but only for pointerType mouse, see #5180.
 
2211         obj.addEventListener('dblclick', handler, false);
 
2216 function removeDoubleTapListener(obj, id) {
 
2217         var touchstart = obj[_pre + _touchstart + id],
 
2218             touchend = obj[_pre + _touchend + id],
 
2219             dblclick = obj[_pre + 'dblclick' + id];
 
2221         obj.removeEventListener(_touchstart, touchstart, passiveEvents ? {passive: false} : false);
 
2222         obj.removeEventListener(_touchend, touchend, passiveEvents ? {passive: false} : false);
 
2224                 obj.removeEventListener('dblclick', dblclick, false);
 
2231  * @namespace DomUtil
 
2233  * Utility functions to work with the [DOM](https://developer.mozilla.org/docs/Web/API/Document_Object_Model)
 
2234  * tree, used by Leaflet internally.
 
2236  * Most functions expecting or returning a `HTMLElement` also work for
 
2237  * SVG elements. The only difference is that classes refer to CSS classes
 
2238  * in HTML and SVG classes in SVG.
 
2242 // @property TRANSFORM: String
 
2243 // Vendor-prefixed transform style name (e.g. `'webkitTransform'` for WebKit).
 
2244 var TRANSFORM = testProp(
 
2245         ['transform', 'webkitTransform', 'OTransform', 'MozTransform', 'msTransform']);
 
2247 // webkitTransition comes first because some browser versions that drop vendor prefix don't do
 
2248 // the same for the transitionend event, in particular the Android 4.1 stock browser
 
2250 // @property TRANSITION: String
 
2251 // Vendor-prefixed transition style name.
 
2252 var TRANSITION = testProp(
 
2253         ['webkitTransition', 'transition', 'OTransition', 'MozTransition', 'msTransition']);
 
2255 // @property TRANSITION_END: String
 
2256 // Vendor-prefixed transitionend event name.
 
2257 var TRANSITION_END =
 
2258         TRANSITION === 'webkitTransition' || TRANSITION === 'OTransition' ? TRANSITION + 'End' : 'transitionend';
 
2261 // @function get(id: String|HTMLElement): HTMLElement
 
2262 // Returns an element given its DOM id, or returns the element itself
 
2263 // if it was passed directly.
 
2265         return typeof id === 'string' ? document.getElementById(id) : id;
 
2268 // @function getStyle(el: HTMLElement, styleAttrib: String): String
 
2269 // Returns the value for a certain style attribute on an element,
 
2270 // including computed values or values set through CSS.
 
2271 function getStyle(el, style) {
 
2272         var value = el.style[style] || (el.currentStyle && el.currentStyle[style]);
 
2274         if ((!value || value === 'auto') && document.defaultView) {
 
2275                 var css = document.defaultView.getComputedStyle(el, null);
 
2276                 value = css ? css[style] : null;
 
2278         return value === 'auto' ? null : value;
 
2281 // @function create(tagName: String, className?: String, container?: HTMLElement): HTMLElement
 
2282 // Creates an HTML element with `tagName`, sets its class to `className`, and optionally appends it to `container` element.
 
2283 function create$1(tagName, className, container) {
 
2284         var el = document.createElement(tagName);
 
2285         el.className = className || '';
 
2288                 container.appendChild(el);
 
2293 // @function remove(el: HTMLElement)
 
2294 // Removes `el` from its parent element
 
2295 function remove(el) {
 
2296         var parent = el.parentNode;
 
2298                 parent.removeChild(el);
 
2302 // @function empty(el: HTMLElement)
 
2303 // Removes all of `el`'s children elements from `el`
 
2304 function empty(el) {
 
2305         while (el.firstChild) {
 
2306                 el.removeChild(el.firstChild);
 
2310 // @function toFront(el: HTMLElement)
 
2311 // Makes `el` the last child of its parent, so it renders in front of the other children.
 
2312 function toFront(el) {
 
2313         var parent = el.parentNode;
 
2314         if (parent && parent.lastChild !== el) {
 
2315                 parent.appendChild(el);
 
2319 // @function toBack(el: HTMLElement)
 
2320 // Makes `el` the first child of its parent, so it renders behind the other children.
 
2321 function toBack(el) {
 
2322         var parent = el.parentNode;
 
2323         if (parent && parent.firstChild !== el) {
 
2324                 parent.insertBefore(el, parent.firstChild);
 
2328 // @function hasClass(el: HTMLElement, name: String): Boolean
 
2329 // Returns `true` if the element's class attribute contains `name`.
 
2330 function hasClass(el, name) {
 
2331         if (el.classList !== undefined) {
 
2332                 return el.classList.contains(name);
 
2334         var className = getClass(el);
 
2335         return className.length > 0 && new RegExp('(^|\\s)' + name + '(\\s|$)').test(className);
 
2338 // @function addClass(el: HTMLElement, name: String)
 
2339 // Adds `name` to the element's class attribute.
 
2340 function addClass(el, name) {
 
2341         if (el.classList !== undefined) {
 
2342                 var classes = splitWords(name);
 
2343                 for (var i = 0, len = classes.length; i < len; i++) {
 
2344                         el.classList.add(classes[i]);
 
2346         } else if (!hasClass(el, name)) {
 
2347                 var className = getClass(el);
 
2348                 setClass(el, (className ? className + ' ' : '') + name);
 
2352 // @function removeClass(el: HTMLElement, name: String)
 
2353 // Removes `name` from the element's class attribute.
 
2354 function removeClass(el, name) {
 
2355         if (el.classList !== undefined) {
 
2356                 el.classList.remove(name);
 
2358                 setClass(el, trim((' ' + getClass(el) + ' ').replace(' ' + name + ' ', ' ')));
 
2362 // @function setClass(el: HTMLElement, name: String)
 
2363 // Sets the element's class.
 
2364 function setClass(el, name) {
 
2365         if (el.className.baseVal === undefined) {
 
2366                 el.className = name;
 
2368                 // in case of SVG element
 
2369                 el.className.baseVal = name;
 
2373 // @function getClass(el: HTMLElement): String
 
2374 // Returns the element's class.
 
2375 function getClass(el) {
 
2376         // Check if the element is an SVGElementInstance and use the correspondingElement instead
 
2377         // (Required for linked SVG elements in IE11.)
 
2378         if (el.correspondingElement) {
 
2379                 el = el.correspondingElement;
 
2381         return el.className.baseVal === undefined ? el.className : el.className.baseVal;
 
2384 // @function setOpacity(el: HTMLElement, opacity: Number)
 
2385 // Set the opacity of an element (including old IE support).
 
2386 // `opacity` must be a number from `0` to `1`.
 
2387 function setOpacity(el, value) {
 
2388         if ('opacity' in el.style) {
 
2389                 el.style.opacity = value;
 
2390         } else if ('filter' in el.style) {
 
2391                 _setOpacityIE(el, value);
 
2395 function _setOpacityIE(el, value) {
 
2397             filterName = 'DXImageTransform.Microsoft.Alpha';
 
2399         // filters collection throws an error if we try to retrieve a filter that doesn't exist
 
2401                 filter = el.filters.item(filterName);
 
2403                 // don't set opacity to 1 if we haven't already set an opacity,
 
2404                 // it isn't needed and breaks transparent pngs.
 
2405                 if (value === 1) { return; }
 
2408         value = Math.round(value * 100);
 
2411                 filter.Enabled = (value !== 100);
 
2412                 filter.Opacity = value;
 
2414                 el.style.filter += ' progid:' + filterName + '(opacity=' + value + ')';
 
2418 // @function testProp(props: String[]): String|false
 
2419 // Goes through the array of style names and returns the first name
 
2420 // that is a valid style name for an element. If no such name is found,
 
2421 // it returns false. Useful for vendor-prefixed styles like `transform`.
 
2422 function testProp(props) {
 
2423         var style = document.documentElement.style;
 
2425         for (var i = 0; i < props.length; i++) {
 
2426                 if (props[i] in style) {
 
2433 // @function setTransform(el: HTMLElement, offset: Point, scale?: Number)
 
2434 // Resets the 3D CSS transform of `el` so it is translated by `offset` pixels
 
2435 // and optionally scaled by `scale`. Does not have an effect if the
 
2436 // browser doesn't support 3D CSS transforms.
 
2437 function setTransform(el, offset, scale) {
 
2438         var pos = offset || new Point(0, 0);
 
2440         el.style[TRANSFORM] =
 
2442                         'translate(' + pos.x + 'px,' + pos.y + 'px)' :
 
2443                         'translate3d(' + pos.x + 'px,' + pos.y + 'px,0)') +
 
2444                 (scale ? ' scale(' + scale + ')' : '');
 
2447 // @function setPosition(el: HTMLElement, position: Point)
 
2448 // Sets the position of `el` to coordinates specified by `position`,
 
2449 // using CSS translate or top/left positioning depending on the browser
 
2450 // (used by Leaflet internally to position its layers).
 
2451 function setPosition(el, point) {
 
2454         el._leaflet_pos = point;
 
2458                 setTransform(el, point);
 
2460                 el.style.left = point.x + 'px';
 
2461                 el.style.top = point.y + 'px';
 
2465 // @function getPosition(el: HTMLElement): Point
 
2466 // Returns the coordinates of an element previously positioned with setPosition.
 
2467 function getPosition(el) {
 
2468         // this method is only used for elements previously positioned using setPosition,
 
2469         // so it's safe to cache the position for performance
 
2471         return el._leaflet_pos || new Point(0, 0);
 
2474 // @function disableTextSelection()
 
2475 // Prevents the user from generating `selectstart` DOM events, usually generated
 
2476 // when the user drags the mouse through a page with text. Used internally
 
2477 // by Leaflet to override the behaviour of any click-and-drag interaction on
 
2478 // the map. Affects drag interactions on the whole document.
 
2480 // @function enableTextSelection()
 
2481 // Cancels the effects of a previous [`L.DomUtil.disableTextSelection`](#domutil-disabletextselection).
 
2482 var disableTextSelection;
 
2483 var enableTextSelection;
 
2485 if ('onselectstart' in document) {
 
2486         disableTextSelection = function () {
 
2487                 on(window, 'selectstart', preventDefault);
 
2489         enableTextSelection = function () {
 
2490                 off(window, 'selectstart', preventDefault);
 
2493         var userSelectProperty = testProp(
 
2494                 ['userSelect', 'WebkitUserSelect', 'OUserSelect', 'MozUserSelect', 'msUserSelect']);
 
2496         disableTextSelection = function () {
 
2497                 if (userSelectProperty) {
 
2498                         var style = document.documentElement.style;
 
2499                         _userSelect = style[userSelectProperty];
 
2500                         style[userSelectProperty] = 'none';
 
2503         enableTextSelection = function () {
 
2504                 if (userSelectProperty) {
 
2505                         document.documentElement.style[userSelectProperty] = _userSelect;
 
2506                         _userSelect = undefined;
 
2511 // @function disableImageDrag()
 
2512 // As [`L.DomUtil.disableTextSelection`](#domutil-disabletextselection), but
 
2513 // for `dragstart` DOM events, usually generated when the user drags an image.
 
2514 function disableImageDrag() {
 
2515         on(window, 'dragstart', preventDefault);
 
2518 // @function enableImageDrag()
 
2519 // Cancels the effects of a previous [`L.DomUtil.disableImageDrag`](#domutil-disabletextselection).
 
2520 function enableImageDrag() {
 
2521         off(window, 'dragstart', preventDefault);
 
2524 var _outlineElement;
 
2526 // @function preventOutline(el: HTMLElement)
 
2527 // Makes the [outline](https://developer.mozilla.org/docs/Web/CSS/outline)
 
2528 // of the element `el` invisible. Used internally by Leaflet to prevent
 
2529 // focusable elements from displaying an outline when the user performs a
 
2530 // drag interaction on them.
 
2531 function preventOutline(element) {
 
2532         while (element.tabIndex === -1) {
 
2533                 element = element.parentNode;
 
2535         if (!element.style) { return; }
 
2537         _outlineElement = element;
 
2538         _outlineStyle = element.style.outline;
 
2539         element.style.outline = 'none';
 
2540         on(window, 'keydown', restoreOutline);
 
2543 // @function restoreOutline()
 
2544 // Cancels the effects of a previous [`L.DomUtil.preventOutline`]().
 
2545 function restoreOutline() {
 
2546         if (!_outlineElement) { return; }
 
2547         _outlineElement.style.outline = _outlineStyle;
 
2548         _outlineElement = undefined;
 
2549         _outlineStyle = undefined;
 
2550         off(window, 'keydown', restoreOutline);
 
2553 // @function getSizedParentNode(el: HTMLElement): HTMLElement
 
2554 // Finds the closest parent node which size (width and height) is not null.
 
2555 function getSizedParentNode(element) {
 
2557                 element = element.parentNode;
 
2558         } while ((!element.offsetWidth || !element.offsetHeight) && element !== document.body);
 
2562 // @function getScale(el: HTMLElement): Object
 
2563 // Computes the CSS scale currently applied on the element.
 
2564 // Returns an object with `x` and `y` members as horizontal and vertical scales respectively,
 
2565 // and `boundingClientRect` as the result of [`getBoundingClientRect()`](https://developer.mozilla.org/en-US/docs/Web/API/Element/getBoundingClientRect).
 
2566 function getScale(element) {
 
2567         var rect = element.getBoundingClientRect(); // Read-only in old browsers.
 
2570                 x: rect.width / element.offsetWidth || 1,
 
2571                 y: rect.height / element.offsetHeight || 1,
 
2572                 boundingClientRect: rect
 
2577 var DomUtil = (Object.freeze || Object)({
 
2578         TRANSFORM: TRANSFORM,
 
2579         TRANSITION: TRANSITION,
 
2580         TRANSITION_END: TRANSITION_END,
 
2590         removeClass: removeClass,
 
2593         setOpacity: setOpacity,
 
2595         setTransform: setTransform,
 
2596         setPosition: setPosition,
 
2597         getPosition: getPosition,
 
2598         disableTextSelection: disableTextSelection,
 
2599         enableTextSelection: enableTextSelection,
 
2600         disableImageDrag: disableImageDrag,
 
2601         enableImageDrag: enableImageDrag,
 
2602         preventOutline: preventOutline,
 
2603         restoreOutline: restoreOutline,
 
2604         getSizedParentNode: getSizedParentNode,
 
2609  * @namespace DomEvent
 
2610  * Utility functions to work with the [DOM events](https://developer.mozilla.org/docs/Web/API/Event), used by Leaflet internally.
 
2613 // Inspired by John Resig, Dean Edwards and YUI addEvent implementations.
 
2615 // @function on(el: HTMLElement, types: String, fn: Function, context?: Object): this
 
2616 // Adds a listener function (`fn`) to a particular DOM event type of the
 
2617 // element `el`. You can optionally specify the context of the listener
 
2618 // (object the `this` keyword will point to). You can also pass several
 
2619 // space-separated types (e.g. `'click dblclick'`).
 
2622 // @function on(el: HTMLElement, eventMap: Object, context?: Object): this
 
2623 // Adds a set of type/listener pairs, e.g. `{click: onClick, mousemove: onMouseMove}`
 
2624 function on(obj, types, fn, context) {
 
2626         if (typeof types === 'object') {
 
2627                 for (var type in types) {
 
2628                         addOne(obj, type, types[type], fn);
 
2631                 types = splitWords(types);
 
2633                 for (var i = 0, len = types.length; i < len; i++) {
 
2634                         addOne(obj, types[i], fn, context);
 
2641 var eventsKey = '_leaflet_events';
 
2643 // @function off(el: HTMLElement, types: String, fn: Function, context?: Object): this
 
2644 // Removes a previously added listener function.
 
2645 // Note that if you passed a custom context to on, you must pass the same
 
2646 // context to `off` in order to remove the listener.
 
2649 // @function off(el: HTMLElement, eventMap: Object, context?: Object): this
 
2650 // Removes a set of type/listener pairs, e.g. `{click: onClick, mousemove: onMouseMove}`
 
2651 function off(obj, types, fn, context) {
 
2653         if (typeof types === 'object') {
 
2654                 for (var type in types) {
 
2655                         removeOne(obj, type, types[type], fn);
 
2658                 types = splitWords(types);
 
2660                 for (var i = 0, len = types.length; i < len; i++) {
 
2661                         removeOne(obj, types[i], fn, context);
 
2664                 for (var j in obj[eventsKey]) {
 
2665                         removeOne(obj, j, obj[eventsKey][j]);
 
2667                 delete obj[eventsKey];
 
2673 function addOne(obj, type, fn, context) {
 
2674         var id = type + stamp(fn) + (context ? '_' + stamp(context) : '');
 
2676         if (obj[eventsKey] && obj[eventsKey][id]) { return this; }
 
2678         var handler = function (e) {
 
2679                 return fn.call(context || obj, e || window.event);
 
2682         var originalHandler = handler;
 
2684         if (pointer && type.indexOf('touch') === 0) {
 
2685                 // Needs DomEvent.Pointer.js
 
2686                 addPointerListener(obj, type, handler, id);
 
2688         } else if (touch && (type === 'dblclick') && addDoubleTapListener &&
 
2689                    !(pointer && chrome)) {
 
2690                 // Chrome >55 does not need the synthetic dblclicks from addDoubleTapListener
 
2692                 addDoubleTapListener(obj, handler, id);
 
2694         } else if ('addEventListener' in obj) {
 
2696                 if (type === 'mousewheel') {
 
2697                         obj.addEventListener('onwheel' in obj ? 'wheel' : 'mousewheel', handler, passiveEvents ? {passive: false} : false);
 
2699                 } else if ((type === 'mouseenter') || (type === 'mouseleave')) {
 
2700                         handler = function (e) {
 
2701                                 e = e || window.event;
 
2702                                 if (isExternalTarget(obj, e)) {
 
2706                         obj.addEventListener(type === 'mouseenter' ? 'mouseover' : 'mouseout', handler, false);
 
2709                         if (type === 'click' && android) {
 
2710                                 handler = function (e) {
 
2711                                         filterClick(e, originalHandler);
 
2714                         obj.addEventListener(type, handler, false);
 
2717         } else if ('attachEvent' in obj) {
 
2718                 obj.attachEvent('on' + type, handler);
 
2721         obj[eventsKey] = obj[eventsKey] || {};
 
2722         obj[eventsKey][id] = handler;
 
2725 function removeOne(obj, type, fn, context) {
 
2727         var id = type + stamp(fn) + (context ? '_' + stamp(context) : ''),
 
2728             handler = obj[eventsKey] && obj[eventsKey][id];
 
2730         if (!handler) { return this; }
 
2732         if (pointer && type.indexOf('touch') === 0) {
 
2733                 removePointerListener(obj, type, id);
 
2735         } else if (touch && (type === 'dblclick') && removeDoubleTapListener &&
 
2736                    !(pointer && chrome)) {
 
2737                 removeDoubleTapListener(obj, id);
 
2739         } else if ('removeEventListener' in obj) {
 
2741                 if (type === 'mousewheel') {
 
2742                         obj.removeEventListener('onwheel' in obj ? 'wheel' : 'mousewheel', handler, passiveEvents ? {passive: false} : false);
 
2745                         obj.removeEventListener(
 
2746                                 type === 'mouseenter' ? 'mouseover' :
 
2747                                 type === 'mouseleave' ? 'mouseout' : type, handler, false);
 
2750         } else if ('detachEvent' in obj) {
 
2751                 obj.detachEvent('on' + type, handler);
 
2754         obj[eventsKey][id] = null;
 
2757 // @function stopPropagation(ev: DOMEvent): this
 
2758 // Stop the given event from propagation to parent elements. Used inside the listener functions:
 
2760 // L.DomEvent.on(div, 'click', function (ev) {
 
2761 //      L.DomEvent.stopPropagation(ev);
 
2764 function stopPropagation(e) {
 
2766         if (e.stopPropagation) {
 
2767                 e.stopPropagation();
 
2768         } else if (e.originalEvent) {  // In case of Leaflet event.
 
2769                 e.originalEvent._stopped = true;
 
2771                 e.cancelBubble = true;
 
2778 // @function disableScrollPropagation(el: HTMLElement): this
 
2779 // Adds `stopPropagation` to the element's `'mousewheel'` events (plus browser variants).
 
2780 function disableScrollPropagation(el) {
 
2781         addOne(el, 'mousewheel', stopPropagation);
 
2785 // @function disableClickPropagation(el: HTMLElement): this
 
2786 // Adds `stopPropagation` to the element's `'click'`, `'doubleclick'`,
 
2787 // `'mousedown'` and `'touchstart'` events (plus browser variants).
 
2788 function disableClickPropagation(el) {
 
2789         on(el, 'mousedown touchstart dblclick', stopPropagation);
 
2790         addOne(el, 'click', fakeStop);
 
2794 // @function preventDefault(ev: DOMEvent): this
 
2795 // Prevents the default action of the DOM Event `ev` from happening (such as
 
2796 // following a link in the href of the a element, or doing a POST request
 
2797 // with page reload when a `<form>` is submitted).
 
2798 // Use it inside listener functions.
 
2799 function preventDefault(e) {
 
2800         if (e.preventDefault) {
 
2803                 e.returnValue = false;
 
2808 // @function stop(ev: DOMEvent): this
 
2809 // Does `stopPropagation` and `preventDefault` at the same time.
 
2816 // @function getMousePosition(ev: DOMEvent, container?: HTMLElement): Point
 
2817 // Gets normalized mouse position from a DOM event relative to the
 
2818 // `container` (border excluded) or to the whole page if not specified.
 
2819 function getMousePosition(e, container) {
 
2821                 return new Point(e.clientX, e.clientY);
 
2824         var scale = getScale(container),
 
2825             offset = scale.boundingClientRect; // left and top  values are in page scale (like the event clientX/Y)
 
2828                 // offset.left/top values are in page scale (like clientX/Y),
 
2829                 // whereas clientLeft/Top (border width) values are the original values (before CSS scale applies).
 
2830                 (e.clientX - offset.left) / scale.x - container.clientLeft,
 
2831                 (e.clientY - offset.top) / scale.y - container.clientTop
 
2835 // Chrome on Win scrolls double the pixels as in other platforms (see #4538),
 
2836 // and Firefox scrolls device pixels, not CSS pixels
 
2838         (win && chrome) ? 2 * window.devicePixelRatio :
 
2839         gecko ? window.devicePixelRatio : 1;
 
2841 // @function getWheelDelta(ev: DOMEvent): Number
 
2842 // Gets normalized wheel delta from a mousewheel DOM event, in vertical
 
2843 // pixels scrolled (negative if scrolling down).
 
2844 // Events from pointing devices without precise scrolling are mapped to
 
2845 // a best guess of 60 pixels.
 
2846 function getWheelDelta(e) {
 
2847         return (edge) ? e.wheelDeltaY / 2 : // Don't trust window-geometry-based delta
 
2848                (e.deltaY && e.deltaMode === 0) ? -e.deltaY / wheelPxFactor : // Pixels
 
2849                (e.deltaY && e.deltaMode === 1) ? -e.deltaY * 20 : // Lines
 
2850                (e.deltaY && e.deltaMode === 2) ? -e.deltaY * 60 : // Pages
 
2851                (e.deltaX || e.deltaZ) ? 0 :     // Skip horizontal/depth wheel events
 
2852                e.wheelDelta ? (e.wheelDeltaY || e.wheelDelta) / 2 : // Legacy IE pixels
 
2853                (e.detail && Math.abs(e.detail) < 32765) ? -e.detail * 20 : // Legacy Moz lines
 
2854                e.detail ? e.detail / -32765 * 60 : // Legacy Moz pages
 
2858 var skipEvents = {};
 
2860 function fakeStop(e) {
 
2861         // fakes stopPropagation by setting a special event flag, checked/reset with skipped(e)
 
2862         skipEvents[e.type] = true;
 
2865 function skipped(e) {
 
2866         var events = skipEvents[e.type];
 
2867         // reset when checking, as it's only used in map container and propagates outside of the map
 
2868         skipEvents[e.type] = false;
 
2872 // check if element really left/entered the event target (for mouseenter/mouseleave)
 
2873 function isExternalTarget(el, e) {
 
2875         var related = e.relatedTarget;
 
2877         if (!related) { return true; }
 
2880                 while (related && (related !== el)) {
 
2881                         related = related.parentNode;
 
2886         return (related !== el);
 
2891 // this is a horrible workaround for a bug in Android where a single touch triggers two click events
 
2892 function filterClick(e, handler) {
 
2893         var timeStamp = (e.timeStamp || (e.originalEvent && e.originalEvent.timeStamp)),
 
2894             elapsed = lastClick && (timeStamp - lastClick);
 
2896         // are they closer together than 500ms yet more than 100ms?
 
2897         // Android typically triggers them ~300ms apart while multiple listeners
 
2898         // on the same event should be triggered far faster;
 
2899         // or check if click is simulated on the element, and if it is, reject any non-simulated events
 
2901         if ((elapsed && elapsed > 100 && elapsed < 500) || (e.target._simulatedClick && !e._simulated)) {
 
2905         lastClick = timeStamp;
 
2913 var DomEvent = (Object.freeze || Object)({
 
2916         stopPropagation: stopPropagation,
 
2917         disableScrollPropagation: disableScrollPropagation,
 
2918         disableClickPropagation: disableClickPropagation,
 
2919         preventDefault: preventDefault,
 
2921         getMousePosition: getMousePosition,
 
2922         getWheelDelta: getWheelDelta,
 
2925         isExternalTarget: isExternalTarget,
 
2931  * @class PosAnimation
 
2932  * @aka L.PosAnimation
 
2934  * Used internally for panning animations, utilizing CSS3 Transitions for modern browsers and a timer fallback for IE6-9.
 
2938  * var fx = new L.PosAnimation();
 
2939  * fx.run(el, [300, 500], 0.5);
 
2942  * @constructor L.PosAnimation()
 
2943  * Creates a `PosAnimation` object.
 
2947 var PosAnimation = Evented.extend({
 
2949         // @method run(el: HTMLElement, newPos: Point, duration?: Number, easeLinearity?: Number)
 
2950         // Run an animation of a given element to a new position, optionally setting
 
2951         // duration in seconds (`0.25` by default) and easing linearity factor (3rd
 
2952         // argument of the [cubic bezier curve](http://cubic-bezier.com/#0,0,.5,1),
 
2953         // `0.5` by default).
 
2954         run: function (el, newPos, duration, easeLinearity) {
 
2958                 this._inProgress = true;
 
2959                 this._duration = duration || 0.25;
 
2960                 this._easeOutPower = 1 / Math.max(easeLinearity || 0.5, 0.2);
 
2962                 this._startPos = getPosition(el);
 
2963                 this._offset = newPos.subtract(this._startPos);
 
2964                 this._startTime = +new Date();
 
2966                 // @event start: Event
 
2967                 // Fired when the animation starts
 
2974         // Stops the animation (if currently running).
 
2976                 if (!this._inProgress) { return; }
 
2982         _animate: function () {
 
2984                 this._animId = requestAnimFrame(this._animate, this);
 
2988         _step: function (round) {
 
2989                 var elapsed = (+new Date()) - this._startTime,
 
2990                     duration = this._duration * 1000;
 
2992                 if (elapsed < duration) {
 
2993                         this._runFrame(this._easeOut(elapsed / duration), round);
 
3000         _runFrame: function (progress, round) {
 
3001                 var pos = this._startPos.add(this._offset.multiplyBy(progress));
 
3005                 setPosition(this._el, pos);
 
3007                 // @event step: Event
 
3008                 // Fired continuously during the animation.
 
3012         _complete: function () {
 
3013                 cancelAnimFrame(this._animId);
 
3015                 this._inProgress = false;
 
3016                 // @event end: Event
 
3017                 // Fired when the animation ends.
 
3021         _easeOut: function (t) {
 
3022                 return 1 - Math.pow(1 - t, this._easeOutPower);
 
3031  * The central class of the API — it is used to create a map on a page and manipulate it.
 
3036  * // initialize the map on the "map" div with a given center and zoom
 
3037  * var map = L.map('map', {
 
3038  *      center: [51.505, -0.09],
 
3045 var Map = Evented.extend({
 
3048                 // @section Map State Options
 
3049                 // @option crs: CRS = L.CRS.EPSG3857
 
3050                 // The [Coordinate Reference System](#crs) to use. Don't change this if you're not
 
3051                 // sure what it means.
 
3054                 // @option center: LatLng = undefined
 
3055                 // Initial geographic center of the map
 
3058                 // @option zoom: Number = undefined
 
3059                 // Initial map zoom level
 
3062                 // @option minZoom: Number = *
 
3063                 // Minimum zoom level of the map.
 
3064                 // If not specified and at least one `GridLayer` or `TileLayer` is in the map,
 
3065                 // the lowest of their `minZoom` options will be used instead.
 
3068                 // @option maxZoom: Number = *
 
3069                 // Maximum zoom level of the map.
 
3070                 // If not specified and at least one `GridLayer` or `TileLayer` is in the map,
 
3071                 // the highest of their `maxZoom` options will be used instead.
 
3074                 // @option layers: Layer[] = []
 
3075                 // Array of layers that will be added to the map initially
 
3078                 // @option maxBounds: LatLngBounds = null
 
3079                 // When this option is set, the map restricts the view to the given
 
3080                 // geographical bounds, bouncing the user back if the user tries to pan
 
3081                 // outside the view. To set the restriction dynamically, use
 
3082                 // [`setMaxBounds`](#map-setmaxbounds) method.
 
3083                 maxBounds: undefined,
 
3085                 // @option renderer: Renderer = *
 
3086                 // The default method for drawing vector layers on the map. `L.SVG`
 
3087                 // or `L.Canvas` by default depending on browser support.
 
3088                 renderer: undefined,
 
3091                 // @section Animation Options
 
3092                 // @option zoomAnimation: Boolean = true
 
3093                 // Whether the map zoom animation is enabled. By default it's enabled
 
3094                 // in all browsers that support CSS3 Transitions except Android.
 
3095                 zoomAnimation: true,
 
3097                 // @option zoomAnimationThreshold: Number = 4
 
3098                 // Won't animate zoom if the zoom difference exceeds this value.
 
3099                 zoomAnimationThreshold: 4,
 
3101                 // @option fadeAnimation: Boolean = true
 
3102                 // Whether the tile fade animation is enabled. By default it's enabled
 
3103                 // in all browsers that support CSS3 Transitions except Android.
 
3104                 fadeAnimation: true,
 
3106                 // @option markerZoomAnimation: Boolean = true
 
3107                 // Whether markers animate their zoom with the zoom animation, if disabled
 
3108                 // they will disappear for the length of the animation. By default it's
 
3109                 // enabled in all browsers that support CSS3 Transitions except Android.
 
3110                 markerZoomAnimation: true,
 
3112                 // @option transform3DLimit: Number = 2^23
 
3113                 // Defines the maximum size of a CSS translation transform. The default
 
3114                 // value should not be changed unless a web browser positions layers in
 
3115                 // the wrong place after doing a large `panBy`.
 
3116                 transform3DLimit: 8388608, // Precision limit of a 32-bit float
 
3118                 // @section Interaction Options
 
3119                 // @option zoomSnap: Number = 1
 
3120                 // Forces the map's zoom level to always be a multiple of this, particularly
 
3121                 // right after a [`fitBounds()`](#map-fitbounds) or a pinch-zoom.
 
3122                 // By default, the zoom level snaps to the nearest integer; lower values
 
3123                 // (e.g. `0.5` or `0.1`) allow for greater granularity. A value of `0`
 
3124                 // means the zoom level will not be snapped after `fitBounds` or a pinch-zoom.
 
3127                 // @option zoomDelta: Number = 1
 
3128                 // Controls how much the map's zoom level will change after a
 
3129                 // [`zoomIn()`](#map-zoomin), [`zoomOut()`](#map-zoomout), pressing `+`
 
3130                 // or `-` on the keyboard, or using the [zoom controls](#control-zoom).
 
3131                 // Values smaller than `1` (e.g. `0.5`) allow for greater granularity.
 
3134                 // @option trackResize: Boolean = true
 
3135                 // Whether the map automatically handles browser window resize to update itself.
 
3139         initialize: function (id, options) { // (HTMLElement or String, Object)
 
3140                 options = setOptions(this, options);
 
3142                 // Make sure to assign internal flags at the beginning,
 
3143                 // to avoid inconsistent state in some edge cases.
 
3144                 this._handlers = [];
 
3146                 this._zoomBoundLayers = {};
 
3147                 this._sizeChanged = true;
 
3149                 this._initContainer(id);
 
3152                 // hack for https://github.com/Leaflet/Leaflet/issues/1980
 
3153                 this._onResize = bind(this._onResize, this);
 
3157                 if (options.maxBounds) {
 
3158                         this.setMaxBounds(options.maxBounds);
 
3161                 if (options.zoom !== undefined) {
 
3162                         this._zoom = this._limitZoom(options.zoom);
 
3165                 if (options.center && options.zoom !== undefined) {
 
3166                         this.setView(toLatLng(options.center), options.zoom, {reset: true});
 
3169                 this.callInitHooks();
 
3171                 // don't animate on browsers without hardware-accelerated transitions or old Android/Opera
 
3172                 this._zoomAnimated = TRANSITION && any3d && !mobileOpera &&
 
3173                                 this.options.zoomAnimation;
 
3175                 // zoom transitions run with the same duration for all layers, so if one of transitionend events
 
3176                 // happens after starting zoom animation (propagating to the map pane), we know that it ended globally
 
3177                 if (this._zoomAnimated) {
 
3178                         this._createAnimProxy();
 
3179                         on(this._proxy, TRANSITION_END, this._catchTransitionEnd, this);
 
3182                 this._addLayers(this.options.layers);
 
3186         // @section Methods for modifying map state
 
3188         // @method setView(center: LatLng, zoom: Number, options?: Zoom/pan options): this
 
3189         // Sets the view of the map (geographical center and zoom) with the given
 
3190         // animation options.
 
3191         setView: function (center, zoom, options) {
 
3193                 zoom = zoom === undefined ? this._zoom : this._limitZoom(zoom);
 
3194                 center = this._limitCenter(toLatLng(center), zoom, this.options.maxBounds);
 
3195                 options = options || {};
 
3199                 if (this._loaded && !options.reset && options !== true) {
 
3201                         if (options.animate !== undefined) {
 
3202                                 options.zoom = extend({animate: options.animate}, options.zoom);
 
3203                                 options.pan = extend({animate: options.animate, duration: options.duration}, options.pan);
 
3206                         // try animating pan or zoom
 
3207                         var moved = (this._zoom !== zoom) ?
 
3208                                 this._tryAnimatedZoom && this._tryAnimatedZoom(center, zoom, options.zoom) :
 
3209                                 this._tryAnimatedPan(center, options.pan);
 
3212                                 // prevent resize handler call, the view will refresh after animation anyway
 
3213                                 clearTimeout(this._sizeTimer);
 
3218                 // animation didn't start, just reset the map view
 
3219                 this._resetView(center, zoom);
 
3224         // @method setZoom(zoom: Number, options?: Zoom/pan options): this
 
3225         // Sets the zoom of the map.
 
3226         setZoom: function (zoom, options) {
 
3227                 if (!this._loaded) {
 
3231                 return this.setView(this.getCenter(), zoom, {zoom: options});
 
3234         // @method zoomIn(delta?: Number, options?: Zoom options): this
 
3235         // Increases the zoom of the map by `delta` ([`zoomDelta`](#map-zoomdelta) by default).
 
3236         zoomIn: function (delta, options) {
 
3237                 delta = delta || (any3d ? this.options.zoomDelta : 1);
 
3238                 return this.setZoom(this._zoom + delta, options);
 
3241         // @method zoomOut(delta?: Number, options?: Zoom options): this
 
3242         // Decreases the zoom of the map by `delta` ([`zoomDelta`](#map-zoomdelta) by default).
 
3243         zoomOut: function (delta, options) {
 
3244                 delta = delta || (any3d ? this.options.zoomDelta : 1);
 
3245                 return this.setZoom(this._zoom - delta, options);
 
3248         // @method setZoomAround(latlng: LatLng, zoom: Number, options: Zoom options): this
 
3249         // Zooms the map while keeping a specified geographical point on the map
 
3250         // stationary (e.g. used internally for scroll zoom and double-click zoom).
 
3252         // @method setZoomAround(offset: Point, zoom: Number, options: Zoom options): this
 
3253         // Zooms the map while keeping a specified pixel on the map (relative to the top-left corner) stationary.
 
3254         setZoomAround: function (latlng, zoom, options) {
 
3255                 var scale = this.getZoomScale(zoom),
 
3256                     viewHalf = this.getSize().divideBy(2),
 
3257                     containerPoint = latlng instanceof Point ? latlng : this.latLngToContainerPoint(latlng),
 
3259                     centerOffset = containerPoint.subtract(viewHalf).multiplyBy(1 - 1 / scale),
 
3260                     newCenter = this.containerPointToLatLng(viewHalf.add(centerOffset));
 
3262                 return this.setView(newCenter, zoom, {zoom: options});
 
3265         _getBoundsCenterZoom: function (bounds, options) {
 
3267                 options = options || {};
 
3268                 bounds = bounds.getBounds ? bounds.getBounds() : toLatLngBounds(bounds);
 
3270                 var paddingTL = toPoint(options.paddingTopLeft || options.padding || [0, 0]),
 
3271                     paddingBR = toPoint(options.paddingBottomRight || options.padding || [0, 0]),
 
3273                     zoom = this.getBoundsZoom(bounds, false, paddingTL.add(paddingBR));
 
3275                 zoom = (typeof options.maxZoom === 'number') ? Math.min(options.maxZoom, zoom) : zoom;
 
3277                 if (zoom === Infinity) {
 
3279                                 center: bounds.getCenter(),
 
3284                 var paddingOffset = paddingBR.subtract(paddingTL).divideBy(2),
 
3286                     swPoint = this.project(bounds.getSouthWest(), zoom),
 
3287                     nePoint = this.project(bounds.getNorthEast(), zoom),
 
3288                     center = this.unproject(swPoint.add(nePoint).divideBy(2).add(paddingOffset), zoom);
 
3296         // @method fitBounds(bounds: LatLngBounds, options?: fitBounds options): this
 
3297         // Sets a map view that contains the given geographical bounds with the
 
3298         // maximum zoom level possible.
 
3299         fitBounds: function (bounds, options) {
 
3301                 bounds = toLatLngBounds(bounds);
 
3303                 if (!bounds.isValid()) {
 
3304                         throw new Error('Bounds are not valid.');
 
3307                 var target = this._getBoundsCenterZoom(bounds, options);
 
3308                 return this.setView(target.center, target.zoom, options);
 
3311         // @method fitWorld(options?: fitBounds options): this
 
3312         // Sets a map view that mostly contains the whole world with the maximum
 
3313         // zoom level possible.
 
3314         fitWorld: function (options) {
 
3315                 return this.fitBounds([[-90, -180], [90, 180]], options);
 
3318         // @method panTo(latlng: LatLng, options?: Pan options): this
 
3319         // Pans the map to a given center.
 
3320         panTo: function (center, options) { // (LatLng)
 
3321                 return this.setView(center, this._zoom, {pan: options});
 
3324         // @method panBy(offset: Point, options?: Pan options): this
 
3325         // Pans the map by a given number of pixels (animated).
 
3326         panBy: function (offset, options) {
 
3327                 offset = toPoint(offset).round();
 
3328                 options = options || {};
 
3330                 if (!offset.x && !offset.y) {
 
3331                         return this.fire('moveend');
 
3333                 // If we pan too far, Chrome gets issues with tiles
 
3334                 // and makes them disappear or appear in the wrong place (slightly offset) #2602
 
3335                 if (options.animate !== true && !this.getSize().contains(offset)) {
 
3336                         this._resetView(this.unproject(this.project(this.getCenter()).add(offset)), this.getZoom());
 
3340                 if (!this._panAnim) {
 
3341                         this._panAnim = new PosAnimation();
 
3344                                 'step': this._onPanTransitionStep,
 
3345                                 'end': this._onPanTransitionEnd
 
3349                 // don't fire movestart if animating inertia
 
3350                 if (!options.noMoveStart) {
 
3351                         this.fire('movestart');
 
3354                 // animate pan unless animate: false specified
 
3355                 if (options.animate !== false) {
 
3356                         addClass(this._mapPane, 'leaflet-pan-anim');
 
3358                         var newPos = this._getMapPanePos().subtract(offset).round();
 
3359                         this._panAnim.run(this._mapPane, newPos, options.duration || 0.25, options.easeLinearity);
 
3361                         this._rawPanBy(offset);
 
3362                         this.fire('move').fire('moveend');
 
3368         // @method flyTo(latlng: LatLng, zoom?: Number, options?: Zoom/pan options): this
 
3369         // Sets the view of the map (geographical center and zoom) performing a smooth
 
3370         // pan-zoom animation.
 
3371         flyTo: function (targetCenter, targetZoom, options) {
 
3373                 options = options || {};
 
3374                 if (options.animate === false || !any3d) {
 
3375                         return this.setView(targetCenter, targetZoom, options);
 
3380                 var from = this.project(this.getCenter()),
 
3381                     to = this.project(targetCenter),
 
3382                     size = this.getSize(),
 
3383                     startZoom = this._zoom;
 
3385                 targetCenter = toLatLng(targetCenter);
 
3386                 targetZoom = targetZoom === undefined ? startZoom : targetZoom;
 
3388                 var w0 = Math.max(size.x, size.y),
 
3389                     w1 = w0 * this.getZoomScale(startZoom, targetZoom),
 
3390                     u1 = (to.distanceTo(from)) || 1,
 
3395                         var s1 = i ? -1 : 1,
 
3397                             t1 = w1 * w1 - w0 * w0 + s1 * rho2 * rho2 * u1 * u1,
 
3398                             b1 = 2 * s2 * rho2 * u1,
 
3400                             sq = Math.sqrt(b * b + 1) - b;
 
3402                             // workaround for floating point precision bug when sq = 0, log = -Infinite,
 
3403                             // thus triggering an infinite loop in flyTo
 
3404                             var log = sq < 0.000000001 ? -18 : Math.log(sq);
 
3409                 function sinh(n) { return (Math.exp(n) - Math.exp(-n)) / 2; }
 
3410                 function cosh(n) { return (Math.exp(n) + Math.exp(-n)) / 2; }
 
3411                 function tanh(n) { return sinh(n) / cosh(n); }
 
3415                 function w(s) { return w0 * (cosh(r0) / cosh(r0 + rho * s)); }
 
3416                 function u(s) { return w0 * (cosh(r0) * tanh(r0 + rho * s) - sinh(r0)) / rho2; }
 
3418                 function easeOut(t) { return 1 - Math.pow(1 - t, 1.5); }
 
3420                 var start = Date.now(),
 
3421                     S = (r(1) - r0) / rho,
 
3422                     duration = options.duration ? 1000 * options.duration : 1000 * S * 0.8;
 
3425                         var t = (Date.now() - start) / duration,
 
3429                                 this._flyToFrame = requestAnimFrame(frame, this);
 
3432                                         this.unproject(from.add(to.subtract(from).multiplyBy(u(s) / u1)), startZoom),
 
3433                                         this.getScaleZoom(w0 / w(s), startZoom),
 
3438                                         ._move(targetCenter, targetZoom)
 
3443                 this._moveStart(true, options.noMoveStart);
 
3449         // @method flyToBounds(bounds: LatLngBounds, options?: fitBounds options): this
 
3450         // Sets the view of the map with a smooth animation like [`flyTo`](#map-flyto),
 
3451         // but takes a bounds parameter like [`fitBounds`](#map-fitbounds).
 
3452         flyToBounds: function (bounds, options) {
 
3453                 var target = this._getBoundsCenterZoom(bounds, options);
 
3454                 return this.flyTo(target.center, target.zoom, options);
 
3457         // @method setMaxBounds(bounds: Bounds): this
 
3458         // Restricts the map view to the given bounds (see the [maxBounds](#map-maxbounds) option).
 
3459         setMaxBounds: function (bounds) {
 
3460                 bounds = toLatLngBounds(bounds);
 
3462                 if (!bounds.isValid()) {
 
3463                         this.options.maxBounds = null;
 
3464                         return this.off('moveend', this._panInsideMaxBounds);
 
3465                 } else if (this.options.maxBounds) {
 
3466                         this.off('moveend', this._panInsideMaxBounds);
 
3469                 this.options.maxBounds = bounds;
 
3472                         this._panInsideMaxBounds();
 
3475                 return this.on('moveend', this._panInsideMaxBounds);
 
3478         // @method setMinZoom(zoom: Number): this
 
3479         // Sets the lower limit for the available zoom levels (see the [minZoom](#map-minzoom) option).
 
3480         setMinZoom: function (zoom) {
 
3481                 var oldZoom = this.options.minZoom;
 
3482                 this.options.minZoom = zoom;
 
3484                 if (this._loaded && oldZoom !== zoom) {
 
3485                         this.fire('zoomlevelschange');
 
3487                         if (this.getZoom() < this.options.minZoom) {
 
3488                                 return this.setZoom(zoom);
 
3495         // @method setMaxZoom(zoom: Number): this
 
3496         // Sets the upper limit for the available zoom levels (see the [maxZoom](#map-maxzoom) option).
 
3497         setMaxZoom: function (zoom) {
 
3498                 var oldZoom = this.options.maxZoom;
 
3499                 this.options.maxZoom = zoom;
 
3501                 if (this._loaded && oldZoom !== zoom) {
 
3502                         this.fire('zoomlevelschange');
 
3504                         if (this.getZoom() > this.options.maxZoom) {
 
3505                                 return this.setZoom(zoom);
 
3512         // @method panInsideBounds(bounds: LatLngBounds, options?: Pan options): this
 
3513         // Pans the map to the closest view that would lie inside the given bounds (if it's not already), controlling the animation using the options specific, if any.
 
3514         panInsideBounds: function (bounds, options) {
 
3515                 this._enforcingBounds = true;
 
3516                 var center = this.getCenter(),
 
3517                     newCenter = this._limitCenter(center, this._zoom, toLatLngBounds(bounds));
 
3519                 if (!center.equals(newCenter)) {
 
3520                         this.panTo(newCenter, options);
 
3523                 this._enforcingBounds = false;
 
3527         // @method panInside(latlng: LatLng, options?: options): this
 
3528         // Pans the map the minimum amount to make the `latlng` visible. Use
 
3529         // `padding`, `paddingTopLeft` and `paddingTopRight` options to fit
 
3530         // the display to more restricted bounds, like [`fitBounds`](#map-fitbounds).
 
3531         // If `latlng` is already within the (optionally padded) display bounds,
 
3532         // the map will not be panned.
 
3533         panInside: function (latlng, options) {
 
3534                 options = options || {};
 
3536                 var paddingTL = toPoint(options.paddingTopLeft || options.padding || [0, 0]),
 
3537                     paddingBR = toPoint(options.paddingBottomRight || options.padding || [0, 0]),
 
3538                     center = this.getCenter(),
 
3539                     pixelCenter = this.project(center),
 
3540                     pixelPoint = this.project(latlng),
 
3541                     pixelBounds = this.getPixelBounds(),
 
3542                     halfPixelBounds = pixelBounds.getSize().divideBy(2),
 
3543                     paddedBounds = toBounds([pixelBounds.min.add(paddingTL), pixelBounds.max.subtract(paddingBR)]);
 
3545                 if (!paddedBounds.contains(pixelPoint)) {
 
3546                         this._enforcingBounds = true;
 
3547                         var diff = pixelCenter.subtract(pixelPoint),
 
3548                             newCenter = toPoint(pixelPoint.x + diff.x, pixelPoint.y + diff.y);
 
3550                         if (pixelPoint.x < paddedBounds.min.x || pixelPoint.x > paddedBounds.max.x) {
 
3551                                 newCenter.x = pixelCenter.x - diff.x;
 
3553                                         newCenter.x += halfPixelBounds.x - paddingTL.x;
 
3555                                         newCenter.x -= halfPixelBounds.x - paddingBR.x;
 
3558                         if (pixelPoint.y < paddedBounds.min.y || pixelPoint.y > paddedBounds.max.y) {
 
3559                                 newCenter.y = pixelCenter.y - diff.y;
 
3561                                         newCenter.y += halfPixelBounds.y - paddingTL.y;
 
3563                                         newCenter.y -= halfPixelBounds.y - paddingBR.y;
 
3566                         this.panTo(this.unproject(newCenter), options);
 
3567                         this._enforcingBounds = false;
 
3572         // @method invalidateSize(options: Zoom/pan options): this
 
3573         // Checks if the map container size changed and updates the map if so —
 
3574         // call it after you've changed the map size dynamically, also animating
 
3575         // pan by default. If `options.pan` is `false`, panning will not occur.
 
3576         // If `options.debounceMoveend` is `true`, it will delay `moveend` event so
 
3577         // that it doesn't happen often even if the method is called many
 
3581         // @method invalidateSize(animate: Boolean): this
 
3582         // Checks if the map container size changed and updates the map if so —
 
3583         // call it after you've changed the map size dynamically, also animating
 
3585         invalidateSize: function (options) {
 
3586                 if (!this._loaded) { return this; }
 
3591                 }, options === true ? {animate: true} : options);
 
3593                 var oldSize = this.getSize();
 
3594                 this._sizeChanged = true;
 
3595                 this._lastCenter = null;
 
3597                 var newSize = this.getSize(),
 
3598                     oldCenter = oldSize.divideBy(2).round(),
 
3599                     newCenter = newSize.divideBy(2).round(),
 
3600                     offset = oldCenter.subtract(newCenter);
 
3602                 if (!offset.x && !offset.y) { return this; }
 
3604                 if (options.animate && options.pan) {
 
3609                                 this._rawPanBy(offset);
 
3614                         if (options.debounceMoveend) {
 
3615                                 clearTimeout(this._sizeTimer);
 
3616                                 this._sizeTimer = setTimeout(bind(this.fire, this, 'moveend'), 200);
 
3618                                 this.fire('moveend');
 
3622                 // @section Map state change events
 
3623                 // @event resize: ResizeEvent
 
3624                 // Fired when the map is resized.
 
3625                 return this.fire('resize', {
 
3631         // @section Methods for modifying map state
 
3632         // @method stop(): this
 
3633         // Stops the currently running `panTo` or `flyTo` animation, if any.
 
3635                 this.setZoom(this._limitZoom(this._zoom));
 
3636                 if (!this.options.zoomSnap) {
 
3637                         this.fire('viewreset');
 
3639                 return this._stop();
 
3642         // @section Geolocation methods
 
3643         // @method locate(options?: Locate options): this
 
3644         // Tries to locate the user using the Geolocation API, firing a [`locationfound`](#map-locationfound)
 
3645         // event with location data on success or a [`locationerror`](#map-locationerror) event on failure,
 
3646         // and optionally sets the map view to the user's location with respect to
 
3647         // detection accuracy (or to the world view if geolocation failed).
 
3648         // Note that, if your page doesn't use HTTPS, this method will fail in
 
3649         // modern browsers ([Chrome 50 and newer](https://sites.google.com/a/chromium.org/dev/Home/chromium-security/deprecating-powerful-features-on-insecure-origins))
 
3650         // See `Locate options` for more details.
 
3651         locate: function (options) {
 
3653                 options = this._locateOptions = extend({
 
3657                         // maxZoom: <Number>
 
3659                         // enableHighAccuracy: false
 
3662                 if (!('geolocation' in navigator)) {
 
3663                         this._handleGeolocationError({
 
3665                                 message: 'Geolocation not supported.'
 
3670                 var onResponse = bind(this._handleGeolocationResponse, this),
 
3671                     onError = bind(this._handleGeolocationError, this);
 
3673                 if (options.watch) {
 
3674                         this._locationWatchId =
 
3675                                 navigator.geolocation.watchPosition(onResponse, onError, options);
 
3677                         navigator.geolocation.getCurrentPosition(onResponse, onError, options);
 
3682         // @method stopLocate(): this
 
3683         // Stops watching location previously initiated by `map.locate({watch: true})`
 
3684         // and aborts resetting the map view if map.locate was called with
 
3685         // `{setView: true}`.
 
3686         stopLocate: function () {
 
3687                 if (navigator.geolocation && navigator.geolocation.clearWatch) {
 
3688                         navigator.geolocation.clearWatch(this._locationWatchId);
 
3690                 if (this._locateOptions) {
 
3691                         this._locateOptions.setView = false;
 
3696         _handleGeolocationError: function (error) {
 
3698                     message = error.message ||
 
3699                             (c === 1 ? 'permission denied' :
 
3700                             (c === 2 ? 'position unavailable' : 'timeout'));
 
3702                 if (this._locateOptions.setView && !this._loaded) {
 
3706                 // @section Location events
 
3707                 // @event locationerror: ErrorEvent
 
3708                 // Fired when geolocation (using the [`locate`](#map-locate) method) failed.
 
3709                 this.fire('locationerror', {
 
3711                         message: 'Geolocation error: ' + message + '.'
 
3715         _handleGeolocationResponse: function (pos) {
 
3716                 var lat = pos.coords.latitude,
 
3717                     lng = pos.coords.longitude,
 
3718                     latlng = new LatLng(lat, lng),
 
3719                     bounds = latlng.toBounds(pos.coords.accuracy * 2),
 
3720                     options = this._locateOptions;
 
3722                 if (options.setView) {
 
3723                         var zoom = this.getBoundsZoom(bounds);
 
3724                         this.setView(latlng, options.maxZoom ? Math.min(zoom, options.maxZoom) : zoom);
 
3730                         timestamp: pos.timestamp
 
3733                 for (var i in pos.coords) {
 
3734                         if (typeof pos.coords[i] === 'number') {
 
3735                                 data[i] = pos.coords[i];
 
3739                 // @event locationfound: LocationEvent
 
3740                 // Fired when geolocation (using the [`locate`](#map-locate) method)
 
3741                 // went successfully.
 
3742                 this.fire('locationfound', data);
 
3745         // TODO Appropriate docs section?
 
3746         // @section Other Methods
 
3747         // @method addHandler(name: String, HandlerClass: Function): this
 
3748         // Adds a new `Handler` to the map, given its name and constructor function.
 
3749         addHandler: function (name, HandlerClass) {
 
3750                 if (!HandlerClass) { return this; }
 
3752                 var handler = this[name] = new HandlerClass(this);
 
3754                 this._handlers.push(handler);
 
3756                 if (this.options[name]) {
 
3763         // @method remove(): this
 
3764         // Destroys the map and clears all related event listeners.
 
3765         remove: function () {
 
3767                 this._initEvents(true);
 
3769                 if (this._containerId !== this._container._leaflet_id) {
 
3770                         throw new Error('Map container is being reused by another instance');
 
3774                         // throws error in IE6-8
 
3775                         delete this._container._leaflet_id;
 
3776                         delete this._containerId;
 
3779                         this._container._leaflet_id = undefined;
 
3781                         this._containerId = undefined;
 
3784                 if (this._locationWatchId !== undefined) {
 
3790                 remove(this._mapPane);
 
3792                 if (this._clearControlPos) {
 
3793                         this._clearControlPos();
 
3795                 if (this._resizeRequest) {
 
3796                         cancelAnimFrame(this._resizeRequest);
 
3797                         this._resizeRequest = null;
 
3800                 this._clearHandlers();
 
3803                         // @section Map state change events
 
3804                         // @event unload: Event
 
3805                         // Fired when the map is destroyed with [remove](#map-remove) method.
 
3806                         this.fire('unload');
 
3810                 for (i in this._layers) {
 
3811                         this._layers[i].remove();
 
3813                 for (i in this._panes) {
 
3814                         remove(this._panes[i]);
 
3819                 delete this._mapPane;
 
3820                 delete this._renderer;
 
3825         // @section Other Methods
 
3826         // @method createPane(name: String, container?: HTMLElement): HTMLElement
 
3827         // Creates a new [map pane](#map-pane) with the given name if it doesn't exist already,
 
3828         // then returns it. The pane is created as a child of `container`, or
 
3829         // as a child of the main map pane if not set.
 
3830         createPane: function (name, container) {
 
3831                 var className = 'leaflet-pane' + (name ? ' leaflet-' + name.replace('Pane', '') + '-pane' : ''),
 
3832                     pane = create$1('div', className, container || this._mapPane);
 
3835                         this._panes[name] = pane;
 
3840         // @section Methods for Getting Map State
 
3842         // @method getCenter(): LatLng
 
3843         // Returns the geographical center of the map view
 
3844         getCenter: function () {
 
3845                 this._checkIfLoaded();
 
3847                 if (this._lastCenter && !this._moved()) {
 
3848                         return this._lastCenter;
 
3850                 return this.layerPointToLatLng(this._getCenterLayerPoint());
 
3853         // @method getZoom(): Number
 
3854         // Returns the current zoom level of the map view
 
3855         getZoom: function () {
 
3859         // @method getBounds(): LatLngBounds
 
3860         // Returns the geographical bounds visible in the current map view
 
3861         getBounds: function () {
 
3862                 var bounds = this.getPixelBounds(),
 
3863                     sw = this.unproject(bounds.getBottomLeft()),
 
3864                     ne = this.unproject(bounds.getTopRight());
 
3866                 return new LatLngBounds(sw, ne);
 
3869         // @method getMinZoom(): Number
 
3870         // Returns the minimum zoom level of the map (if set in the `minZoom` option of the map or of any layers), or `0` by default.
 
3871         getMinZoom: function () {
 
3872                 return this.options.minZoom === undefined ? this._layersMinZoom || 0 : this.options.minZoom;
 
3875         // @method getMaxZoom(): Number
 
3876         // Returns the maximum zoom level of the map (if set in the `maxZoom` option of the map or of any layers).
 
3877         getMaxZoom: function () {
 
3878                 return this.options.maxZoom === undefined ?
 
3879                         (this._layersMaxZoom === undefined ? Infinity : this._layersMaxZoom) :
 
3880                         this.options.maxZoom;
 
3883         // @method getBoundsZoom(bounds: LatLngBounds, inside?: Boolean, padding?: Point): Number
 
3884         // Returns the maximum zoom level on which the given bounds fit to the map
 
3885         // view in its entirety. If `inside` (optional) is set to `true`, the method
 
3886         // instead returns the minimum zoom level on which the map view fits into
 
3887         // the given bounds in its entirety.
 
3888         getBoundsZoom: function (bounds, inside, padding) { // (LatLngBounds[, Boolean, Point]) -> Number
 
3889                 bounds = toLatLngBounds(bounds);
 
3890                 padding = toPoint(padding || [0, 0]);
 
3892                 var zoom = this.getZoom() || 0,
 
3893                     min = this.getMinZoom(),
 
3894                     max = this.getMaxZoom(),
 
3895                     nw = bounds.getNorthWest(),
 
3896                     se = bounds.getSouthEast(),
 
3897                     size = this.getSize().subtract(padding),
 
3898                     boundsSize = toBounds(this.project(se, zoom), this.project(nw, zoom)).getSize(),
 
3899                     snap = any3d ? this.options.zoomSnap : 1,
 
3900                     scalex = size.x / boundsSize.x,
 
3901                     scaley = size.y / boundsSize.y,
 
3902                     scale = inside ? Math.max(scalex, scaley) : Math.min(scalex, scaley);
 
3904                 zoom = this.getScaleZoom(scale, zoom);
 
3907                         zoom = Math.round(zoom / (snap / 100)) * (snap / 100); // don't jump if within 1% of a snap level
 
3908                         zoom = inside ? Math.ceil(zoom / snap) * snap : Math.floor(zoom / snap) * snap;
 
3911                 return Math.max(min, Math.min(max, zoom));
 
3914         // @method getSize(): Point
 
3915         // Returns the current size of the map container (in pixels).
 
3916         getSize: function () {
 
3917                 if (!this._size || this._sizeChanged) {
 
3918                         this._size = new Point(
 
3919                                 this._container.clientWidth || 0,
 
3920                                 this._container.clientHeight || 0);
 
3922                         this._sizeChanged = false;
 
3924                 return this._size.clone();
 
3927         // @method getPixelBounds(): Bounds
 
3928         // Returns the bounds of the current map view in projected pixel
 
3929         // coordinates (sometimes useful in layer and overlay implementations).
 
3930         getPixelBounds: function (center, zoom) {
 
3931                 var topLeftPoint = this._getTopLeftPoint(center, zoom);
 
3932                 return new Bounds(topLeftPoint, topLeftPoint.add(this.getSize()));
 
3935         // TODO: Check semantics - isn't the pixel origin the 0,0 coord relative to
 
3936         // the map pane? "left point of the map layer" can be confusing, specially
 
3937         // since there can be negative offsets.
 
3938         // @method getPixelOrigin(): Point
 
3939         // Returns the projected pixel coordinates of the top left point of
 
3940         // the map layer (useful in custom layer and overlay implementations).
 
3941         getPixelOrigin: function () {
 
3942                 this._checkIfLoaded();
 
3943                 return this._pixelOrigin;
 
3946         // @method getPixelWorldBounds(zoom?: Number): Bounds
 
3947         // Returns the world's bounds in pixel coordinates for zoom level `zoom`.
 
3948         // If `zoom` is omitted, the map's current zoom level is used.
 
3949         getPixelWorldBounds: function (zoom) {
 
3950                 return this.options.crs.getProjectedBounds(zoom === undefined ? this.getZoom() : zoom);
 
3953         // @section Other Methods
 
3955         // @method getPane(pane: String|HTMLElement): HTMLElement
 
3956         // Returns a [map pane](#map-pane), given its name or its HTML element (its identity).
 
3957         getPane: function (pane) {
 
3958                 return typeof pane === 'string' ? this._panes[pane] : pane;
 
3961         // @method getPanes(): Object
 
3962         // Returns a plain object containing the names of all [panes](#map-pane) as keys and
 
3963         // the panes as values.
 
3964         getPanes: function () {
 
3968         // @method getContainer: HTMLElement
 
3969         // Returns the HTML element that contains the map.
 
3970         getContainer: function () {
 
3971                 return this._container;
 
3975         // @section Conversion Methods
 
3977         // @method getZoomScale(toZoom: Number, fromZoom: Number): Number
 
3978         // Returns the scale factor to be applied to a map transition from zoom level
 
3979         // `fromZoom` to `toZoom`. Used internally to help with zoom animations.
 
3980         getZoomScale: function (toZoom, fromZoom) {
 
3981                 // TODO replace with universal implementation after refactoring projections
 
3982                 var crs = this.options.crs;
 
3983                 fromZoom = fromZoom === undefined ? this._zoom : fromZoom;
 
3984                 return crs.scale(toZoom) / crs.scale(fromZoom);
 
3987         // @method getScaleZoom(scale: Number, fromZoom: Number): Number
 
3988         // Returns the zoom level that the map would end up at, if it is at `fromZoom`
 
3989         // level and everything is scaled by a factor of `scale`. Inverse of
 
3990         // [`getZoomScale`](#map-getZoomScale).
 
3991         getScaleZoom: function (scale, fromZoom) {
 
3992                 var crs = this.options.crs;
 
3993                 fromZoom = fromZoom === undefined ? this._zoom : fromZoom;
 
3994                 var zoom = crs.zoom(scale * crs.scale(fromZoom));
 
3995                 return isNaN(zoom) ? Infinity : zoom;
 
3998         // @method project(latlng: LatLng, zoom: Number): Point
 
3999         // Projects a geographical coordinate `LatLng` according to the projection
 
4000         // of the map's CRS, then scales it according to `zoom` and the CRS's
 
4001         // `Transformation`. The result is pixel coordinate relative to
 
4003         project: function (latlng, zoom) {
 
4004                 zoom = zoom === undefined ? this._zoom : zoom;
 
4005                 return this.options.crs.latLngToPoint(toLatLng(latlng), zoom);
 
4008         // @method unproject(point: Point, zoom: Number): LatLng
 
4009         // Inverse of [`project`](#map-project).
 
4010         unproject: function (point, zoom) {
 
4011                 zoom = zoom === undefined ? this._zoom : zoom;
 
4012                 return this.options.crs.pointToLatLng(toPoint(point), zoom);
 
4015         // @method layerPointToLatLng(point: Point): LatLng
 
4016         // Given a pixel coordinate relative to the [origin pixel](#map-getpixelorigin),
 
4017         // returns the corresponding geographical coordinate (for the current zoom level).
 
4018         layerPointToLatLng: function (point) {
 
4019                 var projectedPoint = toPoint(point).add(this.getPixelOrigin());
 
4020                 return this.unproject(projectedPoint);
 
4023         // @method latLngToLayerPoint(latlng: LatLng): Point
 
4024         // Given a geographical coordinate, returns the corresponding pixel coordinate
 
4025         // relative to the [origin pixel](#map-getpixelorigin).
 
4026         latLngToLayerPoint: function (latlng) {
 
4027                 var projectedPoint = this.project(toLatLng(latlng))._round();
 
4028                 return projectedPoint._subtract(this.getPixelOrigin());
 
4031         // @method wrapLatLng(latlng: LatLng): LatLng
 
4032         // Returns a `LatLng` where `lat` and `lng` has been wrapped according to the
 
4033         // map's CRS's `wrapLat` and `wrapLng` properties, if they are outside the
 
4035         // By default this means longitude is wrapped around the dateline so its
 
4036         // value is between -180 and +180 degrees.
 
4037         wrapLatLng: function (latlng) {
 
4038                 return this.options.crs.wrapLatLng(toLatLng(latlng));
 
4041         // @method wrapLatLngBounds(bounds: LatLngBounds): LatLngBounds
 
4042         // Returns a `LatLngBounds` with the same size as the given one, ensuring that
 
4043         // its center is within the CRS's bounds.
 
4044         // By default this means the center longitude is wrapped around the dateline so its
 
4045         // value is between -180 and +180 degrees, and the majority of the bounds
 
4046         // overlaps the CRS's bounds.
 
4047         wrapLatLngBounds: function (latlng) {
 
4048                 return this.options.crs.wrapLatLngBounds(toLatLngBounds(latlng));
 
4051         // @method distance(latlng1: LatLng, latlng2: LatLng): Number
 
4052         // Returns the distance between two geographical coordinates according to
 
4053         // the map's CRS. By default this measures distance in meters.
 
4054         distance: function (latlng1, latlng2) {
 
4055                 return this.options.crs.distance(toLatLng(latlng1), toLatLng(latlng2));
 
4058         // @method containerPointToLayerPoint(point: Point): Point
 
4059         // Given a pixel coordinate relative to the map container, returns the corresponding
 
4060         // pixel coordinate relative to the [origin pixel](#map-getpixelorigin).
 
4061         containerPointToLayerPoint: function (point) { // (Point)
 
4062                 return toPoint(point).subtract(this._getMapPanePos());
 
4065         // @method layerPointToContainerPoint(point: Point): Point
 
4066         // Given a pixel coordinate relative to the [origin pixel](#map-getpixelorigin),
 
4067         // returns the corresponding pixel coordinate relative to the map container.
 
4068         layerPointToContainerPoint: function (point) { // (Point)
 
4069                 return toPoint(point).add(this._getMapPanePos());
 
4072         // @method containerPointToLatLng(point: Point): LatLng
 
4073         // Given a pixel coordinate relative to the map container, returns
 
4074         // the corresponding geographical coordinate (for the current zoom level).
 
4075         containerPointToLatLng: function (point) {
 
4076                 var layerPoint = this.containerPointToLayerPoint(toPoint(point));
 
4077                 return this.layerPointToLatLng(layerPoint);
 
4080         // @method latLngToContainerPoint(latlng: LatLng): Point
 
4081         // Given a geographical coordinate, returns the corresponding pixel coordinate
 
4082         // relative to the map container.
 
4083         latLngToContainerPoint: function (latlng) {
 
4084                 return this.layerPointToContainerPoint(this.latLngToLayerPoint(toLatLng(latlng)));
 
4087         // @method mouseEventToContainerPoint(ev: MouseEvent): Point
 
4088         // Given a MouseEvent object, returns the pixel coordinate relative to the
 
4089         // map container where the event took place.
 
4090         mouseEventToContainerPoint: function (e) {
 
4091                 return getMousePosition(e, this._container);
 
4094         // @method mouseEventToLayerPoint(ev: MouseEvent): Point
 
4095         // Given a MouseEvent object, returns the pixel coordinate relative to
 
4096         // the [origin pixel](#map-getpixelorigin) where the event took place.
 
4097         mouseEventToLayerPoint: function (e) {
 
4098                 return this.containerPointToLayerPoint(this.mouseEventToContainerPoint(e));
 
4101         // @method mouseEventToLatLng(ev: MouseEvent): LatLng
 
4102         // Given a MouseEvent object, returns geographical coordinate where the
 
4103         // event took place.
 
4104         mouseEventToLatLng: function (e) { // (MouseEvent)
 
4105                 return this.layerPointToLatLng(this.mouseEventToLayerPoint(e));
 
4109         // map initialization methods
 
4111         _initContainer: function (id) {
 
4112                 var container = this._container = get(id);
 
4115                         throw new Error('Map container not found.');
 
4116                 } else if (container._leaflet_id) {
 
4117                         throw new Error('Map container is already initialized.');
 
4120                 on(container, 'scroll', this._onScroll, this);
 
4121                 this._containerId = stamp(container);
 
4124         _initLayout: function () {
 
4125                 var container = this._container;
 
4127                 this._fadeAnimated = this.options.fadeAnimation && any3d;
 
4129                 addClass(container, 'leaflet-container' +
 
4130                         (touch ? ' leaflet-touch' : '') +
 
4131                         (retina ? ' leaflet-retina' : '') +
 
4132                         (ielt9 ? ' leaflet-oldie' : '') +
 
4133                         (safari ? ' leaflet-safari' : '') +
 
4134                         (this._fadeAnimated ? ' leaflet-fade-anim' : ''));
 
4136                 var position = getStyle(container, 'position');
 
4138                 if (position !== 'absolute' && position !== 'relative' && position !== 'fixed') {
 
4139                         container.style.position = 'relative';
 
4144                 if (this._initControlPos) {
 
4145                         this._initControlPos();
 
4149         _initPanes: function () {
 
4150                 var panes = this._panes = {};
 
4151                 this._paneRenderers = {};
 
4155                 // Panes are DOM elements used to control the ordering of layers on the map. You
 
4156                 // can access panes with [`map.getPane`](#map-getpane) or
 
4157                 // [`map.getPanes`](#map-getpanes) methods. New panes can be created with the
 
4158                 // [`map.createPane`](#map-createpane) method.
 
4160                 // Every map has the following default panes that differ only in zIndex.
 
4162                 // @pane mapPane: HTMLElement = 'auto'
 
4163                 // Pane that contains all other map panes
 
4165                 this._mapPane = this.createPane('mapPane', this._container);
 
4166                 setPosition(this._mapPane, new Point(0, 0));
 
4168                 // @pane tilePane: HTMLElement = 200
 
4169                 // Pane for `GridLayer`s and `TileLayer`s
 
4170                 this.createPane('tilePane');
 
4171                 // @pane overlayPane: HTMLElement = 400
 
4172                 // Pane for vectors (`Path`s, like `Polyline`s and `Polygon`s), `ImageOverlay`s and `VideoOverlay`s
 
4173                 this.createPane('shadowPane');
 
4174                 // @pane shadowPane: HTMLElement = 500
 
4175                 // Pane for overlay shadows (e.g. `Marker` shadows)
 
4176                 this.createPane('overlayPane');
 
4177                 // @pane markerPane: HTMLElement = 600
 
4178                 // Pane for `Icon`s of `Marker`s
 
4179                 this.createPane('markerPane');
 
4180                 // @pane tooltipPane: HTMLElement = 650
 
4181                 // Pane for `Tooltip`s.
 
4182                 this.createPane('tooltipPane');
 
4183                 // @pane popupPane: HTMLElement = 700
 
4184                 // Pane for `Popup`s.
 
4185                 this.createPane('popupPane');
 
4187                 if (!this.options.markerZoomAnimation) {
 
4188                         addClass(panes.markerPane, 'leaflet-zoom-hide');
 
4189                         addClass(panes.shadowPane, 'leaflet-zoom-hide');
 
4194         // private methods that modify map state
 
4196         // @section Map state change events
 
4197         _resetView: function (center, zoom) {
 
4198                 setPosition(this._mapPane, new Point(0, 0));
 
4200                 var loading = !this._loaded;
 
4201                 this._loaded = true;
 
4202                 zoom = this._limitZoom(zoom);
 
4204                 this.fire('viewprereset');
 
4206                 var zoomChanged = this._zoom !== zoom;
 
4208                         ._moveStart(zoomChanged, false)
 
4209                         ._move(center, zoom)
 
4210                         ._moveEnd(zoomChanged);
 
4212                 // @event viewreset: Event
 
4213                 // Fired when the map needs to redraw its content (this usually happens
 
4214                 // on map zoom or load). Very useful for creating custom overlays.
 
4215                 this.fire('viewreset');
 
4217                 // @event load: Event
 
4218                 // Fired when the map is initialized (when its center and zoom are set
 
4219                 // for the first time).
 
4225         _moveStart: function (zoomChanged, noMoveStart) {
 
4226                 // @event zoomstart: Event
 
4227                 // Fired when the map zoom is about to change (e.g. before zoom animation).
 
4228                 // @event movestart: Event
 
4229                 // Fired when the view of the map starts changing (e.g. user starts dragging the map).
 
4231                         this.fire('zoomstart');
 
4234                         this.fire('movestart');
 
4239         _move: function (center, zoom, data) {
 
4240                 if (zoom === undefined) {
 
4243                 var zoomChanged = this._zoom !== zoom;
 
4246                 this._lastCenter = center;
 
4247                 this._pixelOrigin = this._getNewPixelOrigin(center);
 
4249                 // @event zoom: Event
 
4250                 // Fired repeatedly during any change in zoom level, including zoom
 
4251                 // and fly animations.
 
4252                 if (zoomChanged || (data && data.pinch)) {      // Always fire 'zoom' if pinching because #3530
 
4253                         this.fire('zoom', data);
 
4256                 // @event move: Event
 
4257                 // Fired repeatedly during any movement of the map, including pan and
 
4259                 return this.fire('move', data);
 
4262         _moveEnd: function (zoomChanged) {
 
4263                 // @event zoomend: Event
 
4264                 // Fired when the map has changed, after any animations.
 
4266                         this.fire('zoomend');
 
4269                 // @event moveend: Event
 
4270                 // Fired when the center of the map stops changing (e.g. user stopped
 
4271                 // dragging the map).
 
4272                 return this.fire('moveend');
 
4275         _stop: function () {
 
4276                 cancelAnimFrame(this._flyToFrame);
 
4277                 if (this._panAnim) {
 
4278                         this._panAnim.stop();
 
4283         _rawPanBy: function (offset) {
 
4284                 setPosition(this._mapPane, this._getMapPanePos().subtract(offset));
 
4287         _getZoomSpan: function () {
 
4288                 return this.getMaxZoom() - this.getMinZoom();
 
4291         _panInsideMaxBounds: function () {
 
4292                 if (!this._enforcingBounds) {
 
4293                         this.panInsideBounds(this.options.maxBounds);
 
4297         _checkIfLoaded: function () {
 
4298                 if (!this._loaded) {
 
4299                         throw new Error('Set map center and zoom first.');
 
4303         // DOM event handling
 
4305         // @section Interaction events
 
4306         _initEvents: function (remove$$1) {
 
4308                 this._targets[stamp(this._container)] = this;
 
4310                 var onOff = remove$$1 ? off : on;
 
4312                 // @event click: MouseEvent
 
4313                 // Fired when the user clicks (or taps) the map.
 
4314                 // @event dblclick: MouseEvent
 
4315                 // Fired when the user double-clicks (or double-taps) the map.
 
4316                 // @event mousedown: MouseEvent
 
4317                 // Fired when the user pushes the mouse button on the map.
 
4318                 // @event mouseup: MouseEvent
 
4319                 // Fired when the user releases the mouse button on the map.
 
4320                 // @event mouseover: MouseEvent
 
4321                 // Fired when the mouse enters the map.
 
4322                 // @event mouseout: MouseEvent
 
4323                 // Fired when the mouse leaves the map.
 
4324                 // @event mousemove: MouseEvent
 
4325                 // Fired while the mouse moves over the map.
 
4326                 // @event contextmenu: MouseEvent
 
4327                 // Fired when the user pushes the right mouse button on the map, prevents
 
4328                 // default browser context menu from showing if there are listeners on
 
4329                 // this event. Also fired on mobile when the user holds a single touch
 
4330                 // for a second (also called long press).
 
4331                 // @event keypress: KeyboardEvent
 
4332                 // Fired when the user presses a key from the keyboard that produces a character value while the map is focused.
 
4333                 // @event keydown: KeyboardEvent
 
4334                 // Fired when the user presses a key from the keyboard while the map is focused. Unlike the `keypress` event,
 
4335                 // the `keydown` event is fired for keys that produce a character value and for keys
 
4336                 // that do not produce a character value.
 
4337                 // @event keyup: KeyboardEvent
 
4338                 // Fired when the user releases a key from the keyboard while the map is focused.
 
4339                 onOff(this._container, 'click dblclick mousedown mouseup ' +
 
4340                         'mouseover mouseout mousemove contextmenu keypress keydown keyup', this._handleDOMEvent, this);
 
4342                 if (this.options.trackResize) {
 
4343                         onOff(window, 'resize', this._onResize, this);
 
4346                 if (any3d && this.options.transform3DLimit) {
 
4347                         (remove$$1 ? this.off : this.on).call(this, 'moveend', this._onMoveEnd);
 
4351         _onResize: function () {
 
4352                 cancelAnimFrame(this._resizeRequest);
 
4353                 this._resizeRequest = requestAnimFrame(
 
4354                         function () { this.invalidateSize({debounceMoveend: true}); }, this);
 
4357         _onScroll: function () {
 
4358                 this._container.scrollTop  = 0;
 
4359                 this._container.scrollLeft = 0;
 
4362         _onMoveEnd: function () {
 
4363                 var pos = this._getMapPanePos();
 
4364                 if (Math.max(Math.abs(pos.x), Math.abs(pos.y)) >= this.options.transform3DLimit) {
 
4365                         // https://bugzilla.mozilla.org/show_bug.cgi?id=1203873 but Webkit also have
 
4366                         // a pixel offset on very high values, see: http://jsfiddle.net/dg6r5hhb/
 
4367                         this._resetView(this.getCenter(), this.getZoom());
 
4371         _findEventTargets: function (e, type) {
 
4374                     isHover = type === 'mouseout' || type === 'mouseover',
 
4375                     src = e.target || e.srcElement,
 
4379                         target = this._targets[stamp(src)];
 
4380                         if (target && (type === 'click' || type === 'preclick') && !e._simulated && this._draggableMoved(target)) {
 
4381                                 // Prevent firing click after you just dragged an object.
 
4385                         if (target && target.listens(type, true)) {
 
4386                                 if (isHover && !isExternalTarget(src, e)) { break; }
 
4387                                 targets.push(target);
 
4388                                 if (isHover) { break; }
 
4390                         if (src === this._container) { break; }
 
4391                         src = src.parentNode;
 
4393                 if (!targets.length && !dragging && !isHover && isExternalTarget(src, e)) {
 
4399         _handleDOMEvent: function (e) {
 
4400                 if (!this._loaded || skipped(e)) { return; }
 
4404                 if (type === 'mousedown' || type === 'keypress' || type === 'keyup' || type === 'keydown') {
 
4405                         // prevents outline when clicking on keyboard-focusable element
 
4406                         preventOutline(e.target || e.srcElement);
 
4409                 this._fireDOMEvent(e, type);
 
4412         _mouseEvents: ['click', 'dblclick', 'mouseover', 'mouseout', 'contextmenu'],
 
4414         _fireDOMEvent: function (e, type, targets) {
 
4416                 if (e.type === 'click') {
 
4417                         // Fire a synthetic 'preclick' event which propagates up (mainly for closing popups).
 
4418                         // @event preclick: MouseEvent
 
4419                         // Fired before mouse click on the map (sometimes useful when you
 
4420                         // want something to happen on click before any existing click
 
4421                         // handlers start running).
 
4422                         var synth = extend({}, e);
 
4423                         synth.type = 'preclick';
 
4424                         this._fireDOMEvent(synth, synth.type, targets);
 
4427                 if (e._stopped) { return; }
 
4429                 // Find the layer the event is propagating from and its parents.
 
4430                 targets = (targets || []).concat(this._findEventTargets(e, type));
 
4432                 if (!targets.length) { return; }
 
4434                 var target = targets[0];
 
4435                 if (type === 'contextmenu' && target.listens(type, true)) {
 
4443                 if (e.type !== 'keypress' && e.type !== 'keydown' && e.type !== 'keyup') {
 
4444                         var isMarker = target.getLatLng && (!target._radius || target._radius <= 10);
 
4445                         data.containerPoint = isMarker ?
 
4446                                 this.latLngToContainerPoint(target.getLatLng()) : this.mouseEventToContainerPoint(e);
 
4447                         data.layerPoint = this.containerPointToLayerPoint(data.containerPoint);
 
4448                         data.latlng = isMarker ? target.getLatLng() : this.layerPointToLatLng(data.layerPoint);
 
4451                 for (var i = 0; i < targets.length; i++) {
 
4452                         targets[i].fire(type, data, true);
 
4453                         if (data.originalEvent._stopped ||
 
4454                                 (targets[i].options.bubblingMouseEvents === false && indexOf(this._mouseEvents, type) !== -1)) { return; }
 
4458         _draggableMoved: function (obj) {
 
4459                 obj = obj.dragging && obj.dragging.enabled() ? obj : this;
 
4460                 return (obj.dragging && obj.dragging.moved()) || (this.boxZoom && this.boxZoom.moved());
 
4463         _clearHandlers: function () {
 
4464                 for (var i = 0, len = this._handlers.length; i < len; i++) {
 
4465                         this._handlers[i].disable();
 
4469         // @section Other Methods
 
4471         // @method whenReady(fn: Function, context?: Object): this
 
4472         // Runs the given function `fn` when the map gets initialized with
 
4473         // a view (center and zoom) and at least one layer, or immediately
 
4474         // if it's already initialized, optionally passing a function context.
 
4475         whenReady: function (callback, context) {
 
4477                         callback.call(context || this, {target: this});
 
4479                         this.on('load', callback, context);
 
4485         // private methods for getting map state
 
4487         _getMapPanePos: function () {
 
4488                 return getPosition(this._mapPane) || new Point(0, 0);
 
4491         _moved: function () {
 
4492                 var pos = this._getMapPanePos();
 
4493                 return pos && !pos.equals([0, 0]);
 
4496         _getTopLeftPoint: function (center, zoom) {
 
4497                 var pixelOrigin = center && zoom !== undefined ?
 
4498                         this._getNewPixelOrigin(center, zoom) :
 
4499                         this.getPixelOrigin();
 
4500                 return pixelOrigin.subtract(this._getMapPanePos());
 
4503         _getNewPixelOrigin: function (center, zoom) {
 
4504                 var viewHalf = this.getSize()._divideBy(2);
 
4505                 return this.project(center, zoom)._subtract(viewHalf)._add(this._getMapPanePos())._round();
 
4508         _latLngToNewLayerPoint: function (latlng, zoom, center) {
 
4509                 var topLeft = this._getNewPixelOrigin(center, zoom);
 
4510                 return this.project(latlng, zoom)._subtract(topLeft);
 
4513         _latLngBoundsToNewLayerBounds: function (latLngBounds, zoom, center) {
 
4514                 var topLeft = this._getNewPixelOrigin(center, zoom);
 
4516                         this.project(latLngBounds.getSouthWest(), zoom)._subtract(topLeft),
 
4517                         this.project(latLngBounds.getNorthWest(), zoom)._subtract(topLeft),
 
4518                         this.project(latLngBounds.getSouthEast(), zoom)._subtract(topLeft),
 
4519                         this.project(latLngBounds.getNorthEast(), zoom)._subtract(topLeft)
 
4523         // layer point of the current center
 
4524         _getCenterLayerPoint: function () {
 
4525                 return this.containerPointToLayerPoint(this.getSize()._divideBy(2));
 
4528         // offset of the specified place to the current center in pixels
 
4529         _getCenterOffset: function (latlng) {
 
4530                 return this.latLngToLayerPoint(latlng).subtract(this._getCenterLayerPoint());
 
4533         // adjust center for view to get inside bounds
 
4534         _limitCenter: function (center, zoom, bounds) {
 
4536                 if (!bounds) { return center; }
 
4538                 var centerPoint = this.project(center, zoom),
 
4539                     viewHalf = this.getSize().divideBy(2),
 
4540                     viewBounds = new Bounds(centerPoint.subtract(viewHalf), centerPoint.add(viewHalf)),
 
4541                     offset = this._getBoundsOffset(viewBounds, bounds, zoom);
 
4543                 // If offset is less than a pixel, ignore.
 
4544                 // This prevents unstable projections from getting into
 
4545                 // an infinite loop of tiny offsets.
 
4546                 if (offset.round().equals([0, 0])) {
 
4550                 return this.unproject(centerPoint.add(offset), zoom);
 
4553         // adjust offset for view to get inside bounds
 
4554         _limitOffset: function (offset, bounds) {
 
4555                 if (!bounds) { return offset; }
 
4557                 var viewBounds = this.getPixelBounds(),
 
4558                     newBounds = new Bounds(viewBounds.min.add(offset), viewBounds.max.add(offset));
 
4560                 return offset.add(this._getBoundsOffset(newBounds, bounds));
 
4563         // returns offset needed for pxBounds to get inside maxBounds at a specified zoom
 
4564         _getBoundsOffset: function (pxBounds, maxBounds, zoom) {
 
4565                 var projectedMaxBounds = toBounds(
 
4566                         this.project(maxBounds.getNorthEast(), zoom),
 
4567                         this.project(maxBounds.getSouthWest(), zoom)
 
4569                     minOffset = projectedMaxBounds.min.subtract(pxBounds.min),
 
4570                     maxOffset = projectedMaxBounds.max.subtract(pxBounds.max),
 
4572                     dx = this._rebound(minOffset.x, -maxOffset.x),
 
4573                     dy = this._rebound(minOffset.y, -maxOffset.y);
 
4575                 return new Point(dx, dy);
 
4578         _rebound: function (left, right) {
 
4579                 return left + right > 0 ?
 
4580                         Math.round(left - right) / 2 :
 
4581                         Math.max(0, Math.ceil(left)) - Math.max(0, Math.floor(right));
 
4584         _limitZoom: function (zoom) {
 
4585                 var min = this.getMinZoom(),
 
4586                     max = this.getMaxZoom(),
 
4587                     snap = any3d ? this.options.zoomSnap : 1;
 
4589                         zoom = Math.round(zoom / snap) * snap;
 
4591                 return Math.max(min, Math.min(max, zoom));
 
4594         _onPanTransitionStep: function () {
 
4598         _onPanTransitionEnd: function () {
 
4599                 removeClass(this._mapPane, 'leaflet-pan-anim');
 
4600                 this.fire('moveend');
 
4603         _tryAnimatedPan: function (center, options) {
 
4604                 // difference between the new and current centers in pixels
 
4605                 var offset = this._getCenterOffset(center)._trunc();
 
4607                 // don't animate too far unless animate: true specified in options
 
4608                 if ((options && options.animate) !== true && !this.getSize().contains(offset)) { return false; }
 
4610                 this.panBy(offset, options);
 
4615         _createAnimProxy: function () {
 
4617                 var proxy = this._proxy = create$1('div', 'leaflet-proxy leaflet-zoom-animated');
 
4618                 this._panes.mapPane.appendChild(proxy);
 
4620                 this.on('zoomanim', function (e) {
 
4621                         var prop = TRANSFORM,
 
4622                             transform = this._proxy.style[prop];
 
4624                         setTransform(this._proxy, this.project(e.center, e.zoom), this.getZoomScale(e.zoom, 1));
 
4626                         // workaround for case when transform is the same and so transitionend event is not fired
 
4627                         if (transform === this._proxy.style[prop] && this._animatingZoom) {
 
4628                                 this._onZoomTransitionEnd();
 
4632                 this.on('load moveend', this._animMoveEnd, this);
 
4634                 this._on('unload', this._destroyAnimProxy, this);
 
4637         _destroyAnimProxy: function () {
 
4638                 remove(this._proxy);
 
4639                 this.off('load moveend', this._animMoveEnd, this);
 
4643         _animMoveEnd: function () {
 
4644                 var c = this.getCenter(),
 
4646                 setTransform(this._proxy, this.project(c, z), this.getZoomScale(z, 1));
 
4649         _catchTransitionEnd: function (e) {
 
4650                 if (this._animatingZoom && e.propertyName.indexOf('transform') >= 0) {
 
4651                         this._onZoomTransitionEnd();
 
4655         _nothingToAnimate: function () {
 
4656                 return !this._container.getElementsByClassName('leaflet-zoom-animated').length;
 
4659         _tryAnimatedZoom: function (center, zoom, options) {
 
4661                 if (this._animatingZoom) { return true; }
 
4663                 options = options || {};
 
4665                 // don't animate if disabled, not supported or zoom difference is too large
 
4666                 if (!this._zoomAnimated || options.animate === false || this._nothingToAnimate() ||
 
4667                         Math.abs(zoom - this._zoom) > this.options.zoomAnimationThreshold) { return false; }
 
4669                 // offset is the pixel coords of the zoom origin relative to the current center
 
4670                 var scale = this.getZoomScale(zoom),
 
4671                     offset = this._getCenterOffset(center)._divideBy(1 - 1 / scale);
 
4673                 // don't animate if the zoom origin isn't within one screen from the current center, unless forced
 
4674                 if (options.animate !== true && !this.getSize().contains(offset)) { return false; }
 
4676                 requestAnimFrame(function () {
 
4678                             ._moveStart(true, false)
 
4679                             ._animateZoom(center, zoom, true);
 
4685         _animateZoom: function (center, zoom, startAnim, noUpdate) {
 
4686                 if (!this._mapPane) { return; }
 
4689                         this._animatingZoom = true;
 
4691                         // remember what center/zoom to set after animation
 
4692                         this._animateToCenter = center;
 
4693                         this._animateToZoom = zoom;
 
4695                         addClass(this._mapPane, 'leaflet-zoom-anim');
 
4698                 // @section Other Events
 
4699                 // @event zoomanim: ZoomAnimEvent
 
4700                 // Fired at least once per zoom animation. For continuous zoom, like pinch zooming, fired once per frame during zoom.
 
4701                 this.fire('zoomanim', {
 
4707                 // Work around webkit not firing 'transitionend', see https://github.com/Leaflet/Leaflet/issues/3689, 2693
 
4708                 setTimeout(bind(this._onZoomTransitionEnd, this), 250);
 
4711         _onZoomTransitionEnd: function () {
 
4712                 if (!this._animatingZoom) { return; }
 
4714                 if (this._mapPane) {
 
4715                         removeClass(this._mapPane, 'leaflet-zoom-anim');
 
4718                 this._animatingZoom = false;
 
4720                 this._move(this._animateToCenter, this._animateToZoom);
 
4722                 // This anim frame should prevent an obscure iOS webkit tile loading race condition.
 
4723                 requestAnimFrame(function () {
 
4724                         this._moveEnd(true);
 
4731 // @factory L.map(id: String, options?: Map options)
 
4732 // Instantiates a map object given the DOM ID of a `<div>` element
 
4733 // and optionally an object literal with `Map options`.
 
4736 // @factory L.map(el: HTMLElement, options?: Map options)
 
4737 // Instantiates a map object given an instance of a `<div>` HTML element
 
4738 // and optionally an object literal with `Map options`.
 
4739 function createMap(id, options) {
 
4740         return new Map(id, options);
 
4748  * L.Control is a base class for implementing map controls. Handles positioning.
 
4749  * All other controls extend from this class.
 
4752 var Control = Class.extend({
 
4754         // @aka Control options
 
4756                 // @option position: String = 'topright'
 
4757                 // The position of the control (one of the map corners). Possible values are `'topleft'`,
 
4758                 // `'topright'`, `'bottomleft'` or `'bottomright'`
 
4759                 position: 'topright'
 
4762         initialize: function (options) {
 
4763                 setOptions(this, options);
 
4767          * Classes extending L.Control will inherit the following methods:
 
4769          * @method getPosition: string
 
4770          * Returns the position of the control.
 
4772         getPosition: function () {
 
4773                 return this.options.position;
 
4776         // @method setPosition(position: string): this
 
4777         // Sets the position of the control.
 
4778         setPosition: function (position) {
 
4779                 var map = this._map;
 
4782                         map.removeControl(this);
 
4785                 this.options.position = position;
 
4788                         map.addControl(this);
 
4794         // @method getContainer: HTMLElement
 
4795         // Returns the HTMLElement that contains the control.
 
4796         getContainer: function () {
 
4797                 return this._container;
 
4800         // @method addTo(map: Map): this
 
4801         // Adds the control to the given map.
 
4802         addTo: function (map) {
 
4806                 var container = this._container = this.onAdd(map),
 
4807                     pos = this.getPosition(),
 
4808                     corner = map._controlCorners[pos];
 
4810                 addClass(container, 'leaflet-control');
 
4812                 if (pos.indexOf('bottom') !== -1) {
 
4813                         corner.insertBefore(container, corner.firstChild);
 
4815                         corner.appendChild(container);
 
4818                 this._map.on('unload', this.remove, this);
 
4823         // @method remove: this
 
4824         // Removes the control from the map it is currently active on.
 
4825         remove: function () {
 
4830                 remove(this._container);
 
4832                 if (this.onRemove) {
 
4833                         this.onRemove(this._map);
 
4836                 this._map.off('unload', this.remove, this);
 
4842         _refocusOnMap: function (e) {
 
4843                 // if map exists and event is not a keyboard event
 
4844                 if (this._map && e && e.screenX > 0 && e.screenY > 0) {
 
4845                         this._map.getContainer().focus();
 
4850 var control = function (options) {
 
4851         return new Control(options);
 
4854 /* @section Extension methods
 
4857  * Every control should extend from `L.Control` and (re-)implement the following methods.
 
4859  * @method onAdd(map: Map): HTMLElement
 
4860  * Should return the container DOM element for the control and add listeners on relevant map events. Called on [`control.addTo(map)`](#control-addTo).
 
4862  * @method onRemove(map: Map)
 
4863  * Optional method. Should contain all clean up code that removes the listeners previously added in [`onAdd`](#control-onadd). Called on [`control.remove()`](#control-remove).
 
4867  * @section Methods for Layers and Controls
 
4870         // @method addControl(control: Control): this
 
4871         // Adds the given control to the map
 
4872         addControl: function (control) {
 
4873                 control.addTo(this);
 
4877         // @method removeControl(control: Control): this
 
4878         // Removes the given control from the map
 
4879         removeControl: function (control) {
 
4884         _initControlPos: function () {
 
4885                 var corners = this._controlCorners = {},
 
4887                     container = this._controlContainer =
 
4888                             create$1('div', l + 'control-container', this._container);
 
4890                 function createCorner(vSide, hSide) {
 
4891                         var className = l + vSide + ' ' + l + hSide;
 
4893                         corners[vSide + hSide] = create$1('div', className, container);
 
4896                 createCorner('top', 'left');
 
4897                 createCorner('top', 'right');
 
4898                 createCorner('bottom', 'left');
 
4899                 createCorner('bottom', 'right');
 
4902         _clearControlPos: function () {
 
4903                 for (var i in this._controlCorners) {
 
4904                         remove(this._controlCorners[i]);
 
4906                 remove(this._controlContainer);
 
4907                 delete this._controlCorners;
 
4908                 delete this._controlContainer;
 
4913  * @class Control.Layers
 
4914  * @aka L.Control.Layers
 
4917  * The layers control gives users the ability to switch between different base layers and switch overlays on/off (check out the [detailed example](http://leafletjs.com/examples/layers-control/)). Extends `Control`.
 
4922  * var baseLayers = {
 
4924  *      "OpenStreetMap": osm
 
4929  *      "Roads": roadsLayer
 
4932  * L.control.layers(baseLayers, overlays).addTo(map);
 
4935  * The `baseLayers` and `overlays` parameters are object literals with layer names as keys and `Layer` objects as values:
 
4939  *     "<someName1>": layer1,
 
4940  *     "<someName2>": layer2
 
4944  * The layer names can contain HTML, which allows you to add additional styling to the items:
 
4947  * {"<img src='my-layer-icon' /> <span class='my-layer-item'>My Layer</span>": myLayer}
 
4951 var Layers = Control.extend({
 
4953         // @aka Control.Layers options
 
4955                 // @option collapsed: Boolean = true
 
4956                 // If `true`, the control will be collapsed into an icon and expanded on mouse hover or touch.
 
4958                 position: 'topright',
 
4960                 // @option autoZIndex: Boolean = true
 
4961                 // If `true`, the control will assign zIndexes in increasing order to all of its layers so that the order is preserved when switching them on/off.
 
4964                 // @option hideSingleBase: Boolean = false
 
4965                 // If `true`, the base layers in the control will be hidden when there is only one.
 
4966                 hideSingleBase: false,
 
4968                 // @option sortLayers: Boolean = false
 
4969                 // Whether to sort the layers. When `false`, layers will keep the order
 
4970                 // in which they were added to the control.
 
4973                 // @option sortFunction: Function = *
 
4974                 // A [compare function](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Array/sort)
 
4975                 // that will be used for sorting the layers, when `sortLayers` is `true`.
 
4976                 // The function receives both the `L.Layer` instances and their names, as in
 
4977                 // `sortFunction(layerA, layerB, nameA, nameB)`.
 
4978                 // By default, it sorts layers alphabetically by their name.
 
4979                 sortFunction: function (layerA, layerB, nameA, nameB) {
 
4980                         return nameA < nameB ? -1 : (nameB < nameA ? 1 : 0);
 
4984         initialize: function (baseLayers, overlays, options) {
 
4985                 setOptions(this, options);
 
4987                 this._layerControlInputs = [];
 
4989                 this._lastZIndex = 0;
 
4990                 this._handlingClick = false;
 
4992                 for (var i in baseLayers) {
 
4993                         this._addLayer(baseLayers[i], i);
 
4996                 for (i in overlays) {
 
4997                         this._addLayer(overlays[i], i, true);
 
5001         onAdd: function (map) {
 
5006                 map.on('zoomend', this._checkDisabledLayers, this);
 
5008                 for (var i = 0; i < this._layers.length; i++) {
 
5009                         this._layers[i].layer.on('add remove', this._onLayerChange, this);
 
5012                 return this._container;
 
5015         addTo: function (map) {
 
5016                 Control.prototype.addTo.call(this, map);
 
5017                 // Trigger expand after Layers Control has been inserted into DOM so that is now has an actual height.
 
5018                 return this._expandIfNotCollapsed();
 
5021         onRemove: function () {
 
5022                 this._map.off('zoomend', this._checkDisabledLayers, this);
 
5024                 for (var i = 0; i < this._layers.length; i++) {
 
5025                         this._layers[i].layer.off('add remove', this._onLayerChange, this);
 
5029         // @method addBaseLayer(layer: Layer, name: String): this
 
5030         // Adds a base layer (radio button entry) with the given name to the control.
 
5031         addBaseLayer: function (layer, name) {
 
5032                 this._addLayer(layer, name);
 
5033                 return (this._map) ? this._update() : this;
 
5036         // @method addOverlay(layer: Layer, name: String): this
 
5037         // Adds an overlay (checkbox entry) with the given name to the control.
 
5038         addOverlay: function (layer, name) {
 
5039                 this._addLayer(layer, name, true);
 
5040                 return (this._map) ? this._update() : this;
 
5043         // @method removeLayer(layer: Layer): this
 
5044         // Remove the given layer from the control.
 
5045         removeLayer: function (layer) {
 
5046                 layer.off('add remove', this._onLayerChange, this);
 
5048                 var obj = this._getLayer(stamp(layer));
 
5050                         this._layers.splice(this._layers.indexOf(obj), 1);
 
5052                 return (this._map) ? this._update() : this;
 
5055         // @method expand(): this
 
5056         // Expand the control container if collapsed.
 
5057         expand: function () {
 
5058                 addClass(this._container, 'leaflet-control-layers-expanded');
 
5059                 this._section.style.height = null;
 
5060                 var acceptableHeight = this._map.getSize().y - (this._container.offsetTop + 50);
 
5061                 if (acceptableHeight < this._section.clientHeight) {
 
5062                         addClass(this._section, 'leaflet-control-layers-scrollbar');
 
5063                         this._section.style.height = acceptableHeight + 'px';
 
5065                         removeClass(this._section, 'leaflet-control-layers-scrollbar');
 
5067                 this._checkDisabledLayers();
 
5071         // @method collapse(): this
 
5072         // Collapse the control container if expanded.
 
5073         collapse: function () {
 
5074                 removeClass(this._container, 'leaflet-control-layers-expanded');
 
5078         _initLayout: function () {
 
5079                 var className = 'leaflet-control-layers',
 
5080                     container = this._container = create$1('div', className),
 
5081                     collapsed = this.options.collapsed;
 
5083                 // makes this work on IE touch devices by stopping it from firing a mouseout event when the touch is released
 
5084                 container.setAttribute('aria-haspopup', true);
 
5086                 disableClickPropagation(container);
 
5087                 disableScrollPropagation(container);
 
5089                 var section = this._section = create$1('section', className + '-list');
 
5092                         this._map.on('click', this.collapse, this);
 
5096                                         mouseenter: this.expand,
 
5097                                         mouseleave: this.collapse
 
5102                 var link = this._layersLink = create$1('a', className + '-toggle', container);
 
5104                 link.title = 'Layers';
 
5107                         on(link, 'click', stop);
 
5108                         on(link, 'click', this.expand, this);
 
5110                         on(link, 'focus', this.expand, this);
 
5117                 this._baseLayersList = create$1('div', className + '-base', section);
 
5118                 this._separator = create$1('div', className + '-separator', section);
 
5119                 this._overlaysList = create$1('div', className + '-overlays', section);
 
5121                 container.appendChild(section);
 
5124         _getLayer: function (id) {
 
5125                 for (var i = 0; i < this._layers.length; i++) {
 
5127                         if (this._layers[i] && stamp(this._layers[i].layer) === id) {
 
5128                                 return this._layers[i];
 
5133         _addLayer: function (layer, name, overlay) {
 
5135                         layer.on('add remove', this._onLayerChange, this);
 
5144                 if (this.options.sortLayers) {
 
5145                         this._layers.sort(bind(function (a, b) {
 
5146                                 return this.options.sortFunction(a.layer, b.layer, a.name, b.name);
 
5150                 if (this.options.autoZIndex && layer.setZIndex) {
 
5152                         layer.setZIndex(this._lastZIndex);
 
5155                 this._expandIfNotCollapsed();
 
5158         _update: function () {
 
5159                 if (!this._container) { return this; }
 
5161                 empty(this._baseLayersList);
 
5162                 empty(this._overlaysList);
 
5164                 this._layerControlInputs = [];
 
5165                 var baseLayersPresent, overlaysPresent, i, obj, baseLayersCount = 0;
 
5167                 for (i = 0; i < this._layers.length; i++) {
 
5168                         obj = this._layers[i];
 
5170                         overlaysPresent = overlaysPresent || obj.overlay;
 
5171                         baseLayersPresent = baseLayersPresent || !obj.overlay;
 
5172                         baseLayersCount += !obj.overlay ? 1 : 0;
 
5175                 // Hide base layers section if there's only one layer.
 
5176                 if (this.options.hideSingleBase) {
 
5177                         baseLayersPresent = baseLayersPresent && baseLayersCount > 1;
 
5178                         this._baseLayersList.style.display = baseLayersPresent ? '' : 'none';
 
5181                 this._separator.style.display = overlaysPresent && baseLayersPresent ? '' : 'none';
 
5186         _onLayerChange: function (e) {
 
5187                 if (!this._handlingClick) {
 
5191                 var obj = this._getLayer(stamp(e.target));
 
5194                 // @section Layer events
 
5195                 // @event baselayerchange: LayersControlEvent
 
5196                 // Fired when the base layer is changed through the [layer control](#control-layers).
 
5197                 // @event overlayadd: LayersControlEvent
 
5198                 // Fired when an overlay is selected through the [layer control](#control-layers).
 
5199                 // @event overlayremove: LayersControlEvent
 
5200                 // Fired when an overlay is deselected through the [layer control](#control-layers).
 
5201                 // @namespace Control.Layers
 
5202                 var type = obj.overlay ?
 
5203                         (e.type === 'add' ? 'overlayadd' : 'overlayremove') :
 
5204                         (e.type === 'add' ? 'baselayerchange' : null);
 
5207                         this._map.fire(type, obj);
 
5211         // IE7 bugs out if you create a radio dynamically, so you have to do it this hacky way (see http://bit.ly/PqYLBe)
 
5212         _createRadioElement: function (name, checked) {
 
5214                 var radioHtml = '<input type="radio" class="leaflet-control-layers-selector" name="' +
 
5215                                 name + '"' + (checked ? ' checked="checked"' : '') + '/>';
 
5217                 var radioFragment = document.createElement('div');
 
5218                 radioFragment.innerHTML = radioHtml;
 
5220                 return radioFragment.firstChild;
 
5223         _addItem: function (obj) {
 
5224                 var label = document.createElement('label'),
 
5225                     checked = this._map.hasLayer(obj.layer),
 
5229                         input = document.createElement('input');
 
5230                         input.type = 'checkbox';
 
5231                         input.className = 'leaflet-control-layers-selector';
 
5232                         input.defaultChecked = checked;
 
5234                         input = this._createRadioElement('leaflet-base-layers_' + stamp(this), checked);
 
5237                 this._layerControlInputs.push(input);
 
5238                 input.layerId = stamp(obj.layer);
 
5240                 on(input, 'click', this._onInputClick, this);
 
5242                 var name = document.createElement('span');
 
5243                 name.innerHTML = ' ' + obj.name;
 
5245                 // Helps from preventing layer control flicker when checkboxes are disabled
 
5246                 // https://github.com/Leaflet/Leaflet/issues/2771
 
5247                 var holder = document.createElement('div');
 
5249                 label.appendChild(holder);
 
5250                 holder.appendChild(input);
 
5251                 holder.appendChild(name);
 
5253                 var container = obj.overlay ? this._overlaysList : this._baseLayersList;
 
5254                 container.appendChild(label);
 
5256                 this._checkDisabledLayers();
 
5260         _onInputClick: function () {
 
5261                 var inputs = this._layerControlInputs,
 
5263                 var addedLayers = [],
 
5266                 this._handlingClick = true;
 
5268                 for (var i = inputs.length - 1; i >= 0; i--) {
 
5270                         layer = this._getLayer(input.layerId).layer;
 
5272                         if (input.checked) {
 
5273                                 addedLayers.push(layer);
 
5274                         } else if (!input.checked) {
 
5275                                 removedLayers.push(layer);
 
5279                 // Bugfix issue 2318: Should remove all old layers before readding new ones
 
5280                 for (i = 0; i < removedLayers.length; i++) {
 
5281                         if (this._map.hasLayer(removedLayers[i])) {
 
5282                                 this._map.removeLayer(removedLayers[i]);
 
5285                 for (i = 0; i < addedLayers.length; i++) {
 
5286                         if (!this._map.hasLayer(addedLayers[i])) {
 
5287                                 this._map.addLayer(addedLayers[i]);
 
5291                 this._handlingClick = false;
 
5293                 this._refocusOnMap();
 
5296         _checkDisabledLayers: function () {
 
5297                 var inputs = this._layerControlInputs,
 
5300                     zoom = this._map.getZoom();
 
5302                 for (var i = inputs.length - 1; i >= 0; i--) {
 
5304                         layer = this._getLayer(input.layerId).layer;
 
5305                         input.disabled = (layer.options.minZoom !== undefined && zoom < layer.options.minZoom) ||
 
5306                                          (layer.options.maxZoom !== undefined && zoom > layer.options.maxZoom);
 
5311         _expandIfNotCollapsed: function () {
 
5312                 if (this._map && !this.options.collapsed) {
 
5318         _expand: function () {
 
5319                 // Backward compatibility, remove me in 1.1.
 
5320                 return this.expand();
 
5323         _collapse: function () {
 
5324                 // Backward compatibility, remove me in 1.1.
 
5325                 return this.collapse();
 
5331 // @factory L.control.layers(baselayers?: Object, overlays?: Object, options?: Control.Layers options)
 
5332 // Creates a layers control with the given layers. Base layers will be switched with radio buttons, while overlays will be switched with checkboxes. Note that all base layers should be passed in the base layers object, but only one should be added to the map during map instantiation.
 
5333 var layers = function (baseLayers, overlays, options) {
 
5334         return new Layers(baseLayers, overlays, options);
 
5338  * @class Control.Zoom
 
5339  * @aka L.Control.Zoom
 
5342  * A basic zoom control with two buttons (zoom in and zoom out). It is put on the map by default unless you set its [`zoomControl` option](#map-zoomcontrol) to `false`. Extends `Control`.
 
5345 var Zoom = Control.extend({
 
5347         // @aka Control.Zoom options
 
5349                 position: 'topleft',
 
5351                 // @option zoomInText: String = '+'
 
5352                 // The text set on the 'zoom in' button.
 
5355                 // @option zoomInTitle: String = 'Zoom in'
 
5356                 // The title set on the 'zoom in' button.
 
5357                 zoomInTitle: 'Zoom in',
 
5359                 // @option zoomOutText: String = '−'
 
5360                 // The text set on the 'zoom out' button.
 
5361                 zoomOutText: '−',
 
5363                 // @option zoomOutTitle: String = 'Zoom out'
 
5364                 // The title set on the 'zoom out' button.
 
5365                 zoomOutTitle: 'Zoom out'
 
5368         onAdd: function (map) {
 
5369                 var zoomName = 'leaflet-control-zoom',
 
5370                     container = create$1('div', zoomName + ' leaflet-bar'),
 
5371                     options = this.options;
 
5373                 this._zoomInButton  = this._createButton(options.zoomInText, options.zoomInTitle,
 
5374                         zoomName + '-in',  container, this._zoomIn);
 
5375                 this._zoomOutButton = this._createButton(options.zoomOutText, options.zoomOutTitle,
 
5376                         zoomName + '-out', container, this._zoomOut);
 
5378                 this._updateDisabled();
 
5379                 map.on('zoomend zoomlevelschange', this._updateDisabled, this);
 
5384         onRemove: function (map) {
 
5385                 map.off('zoomend zoomlevelschange', this._updateDisabled, this);
 
5388         disable: function () {
 
5389                 this._disabled = true;
 
5390                 this._updateDisabled();
 
5394         enable: function () {
 
5395                 this._disabled = false;
 
5396                 this._updateDisabled();
 
5400         _zoomIn: function (e) {
 
5401                 if (!this._disabled && this._map._zoom < this._map.getMaxZoom()) {
 
5402                         this._map.zoomIn(this._map.options.zoomDelta * (e.shiftKey ? 3 : 1));
 
5406         _zoomOut: function (e) {
 
5407                 if (!this._disabled && this._map._zoom > this._map.getMinZoom()) {
 
5408                         this._map.zoomOut(this._map.options.zoomDelta * (e.shiftKey ? 3 : 1));
 
5412         _createButton: function (html, title, className, container, fn) {
 
5413                 var link = create$1('a', className, container);
 
5414                 link.innerHTML = html;
 
5419                  * Will force screen readers like VoiceOver to read this as "Zoom in - button"
 
5421                 link.setAttribute('role', 'button');
 
5422                 link.setAttribute('aria-label', title);
 
5424                 disableClickPropagation(link);
 
5425                 on(link, 'click', stop);
 
5426                 on(link, 'click', fn, this);
 
5427                 on(link, 'click', this._refocusOnMap, this);
 
5432         _updateDisabled: function () {
 
5433                 var map = this._map,
 
5434                     className = 'leaflet-disabled';
 
5436                 removeClass(this._zoomInButton, className);
 
5437                 removeClass(this._zoomOutButton, className);
 
5439                 if (this._disabled || map._zoom === map.getMinZoom()) {
 
5440                         addClass(this._zoomOutButton, className);
 
5442                 if (this._disabled || map._zoom === map.getMaxZoom()) {
 
5443                         addClass(this._zoomInButton, className);
 
5449 // @section Control options
 
5450 // @option zoomControl: Boolean = true
 
5451 // Whether a [zoom control](#control-zoom) is added to the map by default.
 
5456 Map.addInitHook(function () {
 
5457         if (this.options.zoomControl) {
 
5458                 // @section Controls
 
5459                 // @property zoomControl: Control.Zoom
 
5460                 // The default zoom control (only available if the
 
5461                 // [`zoomControl` option](#map-zoomcontrol) was `true` when creating the map).
 
5462                 this.zoomControl = new Zoom();
 
5463                 this.addControl(this.zoomControl);
 
5467 // @namespace Control.Zoom
 
5468 // @factory L.control.zoom(options: Control.Zoom options)
 
5469 // Creates a zoom control
 
5470 var zoom = function (options) {
 
5471         return new Zoom(options);
 
5475  * @class Control.Scale
 
5476  * @aka L.Control.Scale
 
5479  * A simple scale control that shows the scale of the current center of screen in metric (m/km) and imperial (mi/ft) systems. Extends `Control`.
 
5484  * L.control.scale().addTo(map);
 
5488 var Scale = Control.extend({
 
5490         // @aka Control.Scale options
 
5492                 position: 'bottomleft',
 
5494                 // @option maxWidth: Number = 100
 
5495                 // Maximum width of the control in pixels. The width is set dynamically to show round values (e.g. 100, 200, 500).
 
5498                 // @option metric: Boolean = True
 
5499                 // Whether to show the metric scale line (m/km).
 
5502                 // @option imperial: Boolean = True
 
5503                 // Whether to show the imperial scale line (mi/ft).
 
5506                 // @option updateWhenIdle: Boolean = false
 
5507                 // If `true`, the control is updated on [`moveend`](#map-moveend), otherwise it's always up-to-date (updated on [`move`](#map-move)).
 
5510         onAdd: function (map) {
 
5511                 var className = 'leaflet-control-scale',
 
5512                     container = create$1('div', className),
 
5513                     options = this.options;
 
5515                 this._addScales(options, className + '-line', container);
 
5517                 map.on(options.updateWhenIdle ? 'moveend' : 'move', this._update, this);
 
5518                 map.whenReady(this._update, this);
 
5523         onRemove: function (map) {
 
5524                 map.off(this.options.updateWhenIdle ? 'moveend' : 'move', this._update, this);
 
5527         _addScales: function (options, className, container) {
 
5528                 if (options.metric) {
 
5529                         this._mScale = create$1('div', className, container);
 
5531                 if (options.imperial) {
 
5532                         this._iScale = create$1('div', className, container);
 
5536         _update: function () {
 
5537                 var map = this._map,
 
5538                     y = map.getSize().y / 2;
 
5540                 var maxMeters = map.distance(
 
5541                         map.containerPointToLatLng([0, y]),
 
5542                         map.containerPointToLatLng([this.options.maxWidth, y]));
 
5544                 this._updateScales(maxMeters);
 
5547         _updateScales: function (maxMeters) {
 
5548                 if (this.options.metric && maxMeters) {
 
5549                         this._updateMetric(maxMeters);
 
5551                 if (this.options.imperial && maxMeters) {
 
5552                         this._updateImperial(maxMeters);
 
5556         _updateMetric: function (maxMeters) {
 
5557                 var meters = this._getRoundNum(maxMeters),
 
5558                     label = meters < 1000 ? meters + ' m' : (meters / 1000) + ' km';
 
5560                 this._updateScale(this._mScale, label, meters / maxMeters);
 
5563         _updateImperial: function (maxMeters) {
 
5564                 var maxFeet = maxMeters * 3.2808399,
 
5565                     maxMiles, miles, feet;
 
5567                 if (maxFeet > 5280) {
 
5568                         maxMiles = maxFeet / 5280;
 
5569                         miles = this._getRoundNum(maxMiles);
 
5570                         this._updateScale(this._iScale, miles + ' mi', miles / maxMiles);
 
5573                         feet = this._getRoundNum(maxFeet);
 
5574                         this._updateScale(this._iScale, feet + ' ft', feet / maxFeet);
 
5578         _updateScale: function (scale, text, ratio) {
 
5579                 scale.style.width = Math.round(this.options.maxWidth * ratio) + 'px';
 
5580                 scale.innerHTML = text;
 
5583         _getRoundNum: function (num) {
 
5584                 var pow10 = Math.pow(10, (Math.floor(num) + '').length - 1),
 
5597 // @factory L.control.scale(options?: Control.Scale options)
 
5598 // Creates an scale control with the given options.
 
5599 var scale = function (options) {
 
5600         return new Scale(options);
 
5604  * @class Control.Attribution
 
5605  * @aka L.Control.Attribution
 
5608  * The attribution control allows you to display attribution data in a small text box on a map. It is put on the map by default unless you set its [`attributionControl` option](#map-attributioncontrol) to `false`, and it fetches attribution texts from layers with the [`getAttribution` method](#layer-getattribution) automatically. Extends Control.
 
5611 var Attribution = Control.extend({
 
5613         // @aka Control.Attribution options
 
5615                 position: 'bottomright',
 
5617                 // @option prefix: String = 'Leaflet'
 
5618                 // The HTML text shown before the attributions. Pass `false` to disable.
 
5619                 prefix: '<a href="https://leafletjs.com" title="A JS library for interactive maps">Leaflet</a>'
 
5622         initialize: function (options) {
 
5623                 setOptions(this, options);
 
5625                 this._attributions = {};
 
5628         onAdd: function (map) {
 
5629                 map.attributionControl = this;
 
5630                 this._container = create$1('div', 'leaflet-control-attribution');
 
5631                 disableClickPropagation(this._container);
 
5633                 // TODO ugly, refactor
 
5634                 for (var i in map._layers) {
 
5635                         if (map._layers[i].getAttribution) {
 
5636                                 this.addAttribution(map._layers[i].getAttribution());
 
5642                 return this._container;
 
5645         // @method setPrefix(prefix: String): this
 
5646         // Sets the text before the attributions.
 
5647         setPrefix: function (prefix) {
 
5648                 this.options.prefix = prefix;
 
5653         // @method addAttribution(text: String): this
 
5654         // Adds an attribution text (e.g. `'Vector data © Mapbox'`).
 
5655         addAttribution: function (text) {
 
5656                 if (!text) { return this; }
 
5658                 if (!this._attributions[text]) {
 
5659                         this._attributions[text] = 0;
 
5661                 this._attributions[text]++;
 
5668         // @method removeAttribution(text: String): this
 
5669         // Removes an attribution text.
 
5670         removeAttribution: function (text) {
 
5671                 if (!text) { return this; }
 
5673                 if (this._attributions[text]) {
 
5674                         this._attributions[text]--;
 
5681         _update: function () {
 
5682                 if (!this._map) { return; }
 
5686                 for (var i in this._attributions) {
 
5687                         if (this._attributions[i]) {
 
5692                 var prefixAndAttribs = [];
 
5694                 if (this.options.prefix) {
 
5695                         prefixAndAttribs.push(this.options.prefix);
 
5697                 if (attribs.length) {
 
5698                         prefixAndAttribs.push(attribs.join(', '));
 
5701                 this._container.innerHTML = prefixAndAttribs.join(' | ');
 
5706 // @section Control options
 
5707 // @option attributionControl: Boolean = true
 
5708 // Whether a [attribution control](#control-attribution) is added to the map by default.
 
5710         attributionControl: true
 
5713 Map.addInitHook(function () {
 
5714         if (this.options.attributionControl) {
 
5715                 new Attribution().addTo(this);
 
5719 // @namespace Control.Attribution
 
5720 // @factory L.control.attribution(options: Control.Attribution options)
 
5721 // Creates an attribution control.
 
5722 var attribution = function (options) {
 
5723         return new Attribution(options);
 
5726 Control.Layers = Layers;
 
5727 Control.Zoom = Zoom;
 
5728 Control.Scale = Scale;
 
5729 Control.Attribution = Attribution;
 
5731 control.layers = layers;
 
5732 control.zoom = zoom;
 
5733 control.scale = scale;
 
5734 control.attribution = attribution;
 
5737         L.Handler is a base class for handler classes that are used internally to inject
 
5738         interaction features like dragging to classes like Map and Marker.
 
5743 // Abstract class for map interaction handlers
 
5745 var Handler = Class.extend({
 
5746         initialize: function (map) {
 
5750         // @method enable(): this
 
5751         // Enables the handler
 
5752         enable: function () {
 
5753                 if (this._enabled) { return this; }
 
5755                 this._enabled = true;
 
5760         // @method disable(): this
 
5761         // Disables the handler
 
5762         disable: function () {
 
5763                 if (!this._enabled) { return this; }
 
5765                 this._enabled = false;
 
5770         // @method enabled(): Boolean
 
5771         // Returns `true` if the handler is enabled
 
5772         enabled: function () {
 
5773                 return !!this._enabled;
 
5776         // @section Extension methods
 
5777         // Classes inheriting from `Handler` must implement the two following methods:
 
5778         // @method addHooks()
 
5779         // Called when the handler is enabled, should add event hooks.
 
5780         // @method removeHooks()
 
5781         // Called when the handler is disabled, should remove the event hooks added previously.
 
5784 // @section There is static function which can be called without instantiating L.Handler:
 
5785 // @function addTo(map: Map, name: String): this
 
5786 // Adds a new Handler to the given map with the given name.
 
5787 Handler.addTo = function (map, name) {
 
5788         map.addHandler(name, this);
 
5792 var Mixin = {Events: Events};
 
5799  * A class for making DOM elements draggable (including touch support).
 
5800  * Used internally for map and marker dragging. Only works for elements
 
5801  * that were positioned with [`L.DomUtil.setPosition`](#domutil-setposition).
 
5805  * var draggable = new L.Draggable(elementToDrag);
 
5806  * draggable.enable();
 
5810 var START = touch ? 'touchstart mousedown' : 'mousedown';
 
5812         mousedown: 'mouseup',
 
5813         touchstart: 'touchend',
 
5814         pointerdown: 'touchend',
 
5815         MSPointerDown: 'touchend'
 
5818         mousedown: 'mousemove',
 
5819         touchstart: 'touchmove',
 
5820         pointerdown: 'touchmove',
 
5821         MSPointerDown: 'touchmove'
 
5825 var Draggable = Evented.extend({
 
5829                 // @aka Draggable options
 
5830                 // @option clickTolerance: Number = 3
 
5831                 // The max number of pixels a user can shift the mouse pointer during a click
 
5832                 // for it to be considered a valid click (as opposed to a mouse drag).
 
5836         // @constructor L.Draggable(el: HTMLElement, dragHandle?: HTMLElement, preventOutline?: Boolean, options?: Draggable options)
 
5837         // Creates a `Draggable` object for moving `el` when you start dragging the `dragHandle` element (equals `el` itself by default).
 
5838         initialize: function (element, dragStartTarget, preventOutline$$1, options) {
 
5839                 setOptions(this, options);
 
5841                 this._element = element;
 
5842                 this._dragStartTarget = dragStartTarget || element;
 
5843                 this._preventOutline = preventOutline$$1;
 
5847         // Enables the dragging ability
 
5848         enable: function () {
 
5849                 if (this._enabled) { return; }
 
5851                 on(this._dragStartTarget, START, this._onDown, this);
 
5853                 this._enabled = true;
 
5856         // @method disable()
 
5857         // Disables the dragging ability
 
5858         disable: function () {
 
5859                 if (!this._enabled) { return; }
 
5861                 // If we're currently dragging this draggable,
 
5862                 // disabling it counts as first ending the drag.
 
5863                 if (Draggable._dragging === this) {
 
5867                 off(this._dragStartTarget, START, this._onDown, this);
 
5869                 this._enabled = false;
 
5870                 this._moved = false;
 
5873         _onDown: function (e) {
 
5874                 // Ignore simulated events, since we handle both touch and
 
5875                 // mouse explicitly; otherwise we risk getting duplicates of
 
5876                 // touch events, see #4315.
 
5877                 // Also ignore the event if disabled; this happens in IE11
 
5878                 // under some circumstances, see #3666.
 
5879                 if (e._simulated || !this._enabled) { return; }
 
5881                 this._moved = false;
 
5883                 if (hasClass(this._element, 'leaflet-zoom-anim')) { return; }
 
5885                 if (Draggable._dragging || e.shiftKey || ((e.which !== 1) && (e.button !== 1) && !e.touches)) { return; }
 
5886                 Draggable._dragging = this;  // Prevent dragging multiple objects at once.
 
5888                 if (this._preventOutline) {
 
5889                         preventOutline(this._element);
 
5893                 disableTextSelection();
 
5895                 if (this._moving) { return; }
 
5897                 // @event down: Event
 
5898                 // Fired when a drag is about to start.
 
5901                 var first = e.touches ? e.touches[0] : e,
 
5902                     sizedParent = getSizedParentNode(this._element);
 
5904                 this._startPoint = new Point(first.clientX, first.clientY);
 
5906                 // Cache the scale, so that we can continuously compensate for it during drag (_onMove).
 
5907                 this._parentScale = getScale(sizedParent);
 
5909                 on(document, MOVE[e.type], this._onMove, this);
 
5910                 on(document, END[e.type], this._onUp, this);
 
5913         _onMove: function (e) {
 
5914                 // Ignore simulated events, since we handle both touch and
 
5915                 // mouse explicitly; otherwise we risk getting duplicates of
 
5916                 // touch events, see #4315.
 
5917                 // Also ignore the event if disabled; this happens in IE11
 
5918                 // under some circumstances, see #3666.
 
5919                 if (e._simulated || !this._enabled) { return; }
 
5921                 if (e.touches && e.touches.length > 1) {
 
5926                 var first = (e.touches && e.touches.length === 1 ? e.touches[0] : e),
 
5927                     offset = new Point(first.clientX, first.clientY)._subtract(this._startPoint);
 
5929                 if (!offset.x && !offset.y) { return; }
 
5930                 if (Math.abs(offset.x) + Math.abs(offset.y) < this.options.clickTolerance) { return; }
 
5932                 // We assume that the parent container's position, border and scale do not change for the duration of the drag.
 
5933                 // Therefore there is no need to account for the position and border (they are eliminated by the subtraction)
 
5934                 // and we can use the cached value for the scale.
 
5935                 offset.x /= this._parentScale.x;
 
5936                 offset.y /= this._parentScale.y;
 
5941                         // @event dragstart: Event
 
5942                         // Fired when a drag starts
 
5943                         this.fire('dragstart');
 
5946                         this._startPos = getPosition(this._element).subtract(offset);
 
5948                         addClass(document.body, 'leaflet-dragging');
 
5950                         this._lastTarget = e.target || e.srcElement;
 
5951                         // IE and Edge do not give the <use> element, so fetch it
 
5953                         if ((window.SVGElementInstance) && (this._lastTarget instanceof SVGElementInstance)) {
 
5954                                 this._lastTarget = this._lastTarget.correspondingUseElement;
 
5956                         addClass(this._lastTarget, 'leaflet-drag-target');
 
5959                 this._newPos = this._startPos.add(offset);
 
5960                 this._moving = true;
 
5962                 cancelAnimFrame(this._animRequest);
 
5963                 this._lastEvent = e;
 
5964                 this._animRequest = requestAnimFrame(this._updatePosition, this, true);
 
5967         _updatePosition: function () {
 
5968                 var e = {originalEvent: this._lastEvent};
 
5970                 // @event predrag: Event
 
5971                 // Fired continuously during dragging *before* each corresponding
 
5972                 // update of the element's position.
 
5973                 this.fire('predrag', e);
 
5974                 setPosition(this._element, this._newPos);
 
5976                 // @event drag: Event
 
5977                 // Fired continuously during dragging.
 
5978                 this.fire('drag', e);
 
5981         _onUp: function (e) {
 
5982                 // Ignore simulated events, since we handle both touch and
 
5983                 // mouse explicitly; otherwise we risk getting duplicates of
 
5984                 // touch events, see #4315.
 
5985                 // Also ignore the event if disabled; this happens in IE11
 
5986                 // under some circumstances, see #3666.
 
5987                 if (e._simulated || !this._enabled) { return; }
 
5991         finishDrag: function () {
 
5992                 removeClass(document.body, 'leaflet-dragging');
 
5994                 if (this._lastTarget) {
 
5995                         removeClass(this._lastTarget, 'leaflet-drag-target');
 
5996                         this._lastTarget = null;
 
5999                 for (var i in MOVE) {
 
6000                         off(document, MOVE[i], this._onMove, this);
 
6001                         off(document, END[i], this._onUp, this);
 
6005                 enableTextSelection();
 
6007                 if (this._moved && this._moving) {
 
6008                         // ensure drag is not fired after dragend
 
6009                         cancelAnimFrame(this._animRequest);
 
6011                         // @event dragend: DragEndEvent
 
6012                         // Fired when the drag ends.
 
6013                         this.fire('dragend', {
 
6014                                 distance: this._newPos.distanceTo(this._startPos)
 
6018                 this._moving = false;
 
6019                 Draggable._dragging = false;
 
6025  * @namespace LineUtil
 
6027  * Various utility functions for polyline points processing, used by Leaflet internally to make polylines lightning-fast.
 
6030 // Simplify polyline with vertex reduction and Douglas-Peucker simplification.
 
6031 // Improves rendering performance dramatically by lessening the number of points to draw.
 
6033 // @function simplify(points: Point[], tolerance: Number): Point[]
 
6034 // Dramatically reduces the number of points in a polyline while retaining
 
6035 // its shape and returns a new array of simplified points, using the
 
6036 // [Douglas-Peucker algorithm](http://en.wikipedia.org/wiki/Douglas-Peucker_algorithm).
 
6037 // Used for a huge performance boost when processing/displaying Leaflet polylines for
 
6038 // each zoom level and also reducing visual noise. tolerance affects the amount of
 
6039 // simplification (lesser value means higher quality but slower and with more points).
 
6040 // Also released as a separated micro-library [Simplify.js](http://mourner.github.com/simplify-js/).
 
6041 function simplify(points, tolerance) {
 
6042         if (!tolerance || !points.length) {
 
6043                 return points.slice();
 
6046         var sqTolerance = tolerance * tolerance;
 
6048             // stage 1: vertex reduction
 
6049             points = _reducePoints(points, sqTolerance);
 
6051             // stage 2: Douglas-Peucker simplification
 
6052             points = _simplifyDP(points, sqTolerance);
 
6057 // @function pointToSegmentDistance(p: Point, p1: Point, p2: Point): Number
 
6058 // Returns the distance between point `p` and segment `p1` to `p2`.
 
6059 function pointToSegmentDistance(p, p1, p2) {
 
6060         return Math.sqrt(_sqClosestPointOnSegment(p, p1, p2, true));
 
6063 // @function closestPointOnSegment(p: Point, p1: Point, p2: Point): Number
 
6064 // Returns the closest point from a point `p` on a segment `p1` to `p2`.
 
6065 function closestPointOnSegment(p, p1, p2) {
 
6066         return _sqClosestPointOnSegment(p, p1, p2);
 
6069 // Douglas-Peucker simplification, see http://en.wikipedia.org/wiki/Douglas-Peucker_algorithm
 
6070 function _simplifyDP(points, sqTolerance) {
 
6072         var len = points.length,
 
6073             ArrayConstructor = typeof Uint8Array !== undefined + '' ? Uint8Array : Array,
 
6074             markers = new ArrayConstructor(len);
 
6076             markers[0] = markers[len - 1] = 1;
 
6078         _simplifyDPStep(points, markers, sqTolerance, 0, len - 1);
 
6083         for (i = 0; i < len; i++) {
 
6085                         newPoints.push(points[i]);
 
6092 function _simplifyDPStep(points, markers, sqTolerance, first, last) {
 
6097         for (i = first + 1; i <= last - 1; i++) {
 
6098                 sqDist = _sqClosestPointOnSegment(points[i], points[first], points[last], true);
 
6100                 if (sqDist > maxSqDist) {
 
6106         if (maxSqDist > sqTolerance) {
 
6109                 _simplifyDPStep(points, markers, sqTolerance, first, index);
 
6110                 _simplifyDPStep(points, markers, sqTolerance, index, last);
 
6114 // reduce points that are too close to each other to a single point
 
6115 function _reducePoints(points, sqTolerance) {
 
6116         var reducedPoints = [points[0]];
 
6118         for (var i = 1, prev = 0, len = points.length; i < len; i++) {
 
6119                 if (_sqDist(points[i], points[prev]) > sqTolerance) {
 
6120                         reducedPoints.push(points[i]);
 
6124         if (prev < len - 1) {
 
6125                 reducedPoints.push(points[len - 1]);
 
6127         return reducedPoints;
 
6132 // @function clipSegment(a: Point, b: Point, bounds: Bounds, useLastCode?: Boolean, round?: Boolean): Point[]|Boolean
 
6133 // Clips the segment a to b by rectangular bounds with the
 
6134 // [Cohen-Sutherland algorithm](https://en.wikipedia.org/wiki/Cohen%E2%80%93Sutherland_algorithm)
 
6135 // (modifying the segment points directly!). Used by Leaflet to only show polyline
 
6136 // points that are on the screen or near, increasing performance.
 
6137 function clipSegment(a, b, bounds, useLastCode, round) {
 
6138         var codeA = useLastCode ? _lastCode : _getBitCode(a, bounds),
 
6139             codeB = _getBitCode(b, bounds),
 
6141             codeOut, p, newCode;
 
6143             // save 2nd code to avoid calculating it on the next segment
 
6147                 // if a,b is inside the clip window (trivial accept)
 
6148                 if (!(codeA | codeB)) {
 
6152                 // if a,b is outside the clip window (trivial reject)
 
6153                 if (codeA & codeB) {
 
6158                 codeOut = codeA || codeB;
 
6159                 p = _getEdgeIntersection(a, b, codeOut, bounds, round);
 
6160                 newCode = _getBitCode(p, bounds);
 
6162                 if (codeOut === codeA) {
 
6172 function _getEdgeIntersection(a, b, code, bounds, round) {
 
6179         if (code & 8) { // top
 
6180                 x = a.x + dx * (max.y - a.y) / dy;
 
6183         } else if (code & 4) { // bottom
 
6184                 x = a.x + dx * (min.y - a.y) / dy;
 
6187         } else if (code & 2) { // right
 
6189                 y = a.y + dy * (max.x - a.x) / dx;
 
6191         } else if (code & 1) { // left
 
6193                 y = a.y + dy * (min.x - a.x) / dx;
 
6196         return new Point(x, y, round);
 
6199 function _getBitCode(p, bounds) {
 
6202         if (p.x < bounds.min.x) { // left
 
6204         } else if (p.x > bounds.max.x) { // right
 
6208         if (p.y < bounds.min.y) { // bottom
 
6210         } else if (p.y > bounds.max.y) { // top
 
6217 // square distance (to avoid unnecessary Math.sqrt calls)
 
6218 function _sqDist(p1, p2) {
 
6219         var dx = p2.x - p1.x,
 
6221         return dx * dx + dy * dy;
 
6224 // return closest point on segment or distance to that point
 
6225 function _sqClosestPointOnSegment(p, p1, p2, sqDist) {
 
6230             dot = dx * dx + dy * dy,
 
6234                 t = ((p.x - x) * dx + (p.y - y) * dy) / dot;
 
6248         return sqDist ? dx * dx + dy * dy : new Point(x, y);
 
6252 // @function isFlat(latlngs: LatLng[]): Boolean
 
6253 // Returns true if `latlngs` is a flat array, false is nested.
 
6254 function isFlat(latlngs) {
 
6255         return !isArray(latlngs[0]) || (typeof latlngs[0][0] !== 'object' && typeof latlngs[0][0] !== 'undefined');
 
6258 function _flat(latlngs) {
 
6259         console.warn('Deprecated use of _flat, please use L.LineUtil.isFlat instead.');
 
6260         return isFlat(latlngs);
 
6264 var LineUtil = (Object.freeze || Object)({
 
6266         pointToSegmentDistance: pointToSegmentDistance,
 
6267         closestPointOnSegment: closestPointOnSegment,
 
6268         clipSegment: clipSegment,
 
6269         _getEdgeIntersection: _getEdgeIntersection,
 
6270         _getBitCode: _getBitCode,
 
6271         _sqClosestPointOnSegment: _sqClosestPointOnSegment,
 
6277  * @namespace PolyUtil
 
6278  * Various utility functions for polygon geometries.
 
6281 /* @function clipPolygon(points: Point[], bounds: Bounds, round?: Boolean): Point[]
 
6282  * Clips the polygon geometry defined by the given `points` by the given bounds (using the [Sutherland-Hodgman algorithm](https://en.wikipedia.org/wiki/Sutherland%E2%80%93Hodgman_algorithm)).
 
6283  * Used by Leaflet to only show polygon points that are on the screen or near, increasing
 
6284  * performance. Note that polygon points needs different algorithm for clipping
 
6285  * than polyline, so there's a separate method for it.
 
6287 function clipPolygon(points, bounds, round) {
 
6289             edges = [1, 4, 2, 8],
 
6294         for (i = 0, len = points.length; i < len; i++) {
 
6295                 points[i]._code = _getBitCode(points[i], bounds);
 
6298         // for each edge (left, bottom, right, top)
 
6299         for (k = 0; k < 4; k++) {
 
6303                 for (i = 0, len = points.length, j = len - 1; i < len; j = i++) {
 
6307                         // if a is inside the clip window
 
6308                         if (!(a._code & edge)) {
 
6309                                 // if b is outside the clip window (a->b goes out of screen)
 
6310                                 if (b._code & edge) {
 
6311                                         p = _getEdgeIntersection(b, a, edge, bounds, round);
 
6312                                         p._code = _getBitCode(p, bounds);
 
6313                                         clippedPoints.push(p);
 
6315                                 clippedPoints.push(a);
 
6317                         // else if b is inside the clip window (a->b enters the screen)
 
6318                         } else if (!(b._code & edge)) {
 
6319                                 p = _getEdgeIntersection(b, a, edge, bounds, round);
 
6320                                 p._code = _getBitCode(p, bounds);
 
6321                                 clippedPoints.push(p);
 
6324                 points = clippedPoints;
 
6331 var PolyUtil = (Object.freeze || Object)({
 
6332         clipPolygon: clipPolygon
 
6336  * @namespace Projection
 
6338  * Leaflet comes with a set of already defined Projections out of the box:
 
6340  * @projection L.Projection.LonLat
 
6342  * Equirectangular, or Plate Carree projection — the most simple projection,
 
6343  * mostly used by GIS enthusiasts. Directly maps `x` as longitude, and `y` as
 
6344  * latitude. Also suitable for flat worlds, e.g. game maps. Used by the
 
6345  * `EPSG:4326` and `Simple` CRS.
 
6349         project: function (latlng) {
 
6350                 return new Point(latlng.lng, latlng.lat);
 
6353         unproject: function (point) {
 
6354                 return new LatLng(point.y, point.x);
 
6357         bounds: new Bounds([-180, -90], [180, 90])
 
6361  * @namespace Projection
 
6362  * @projection L.Projection.Mercator
 
6364  * Elliptical Mercator projection — more complex than Spherical Mercator. Assumes that Earth is an ellipsoid. Used by the EPSG:3395 CRS.
 
6369         R_MINOR: 6356752.314245179,
 
6371         bounds: new Bounds([-20037508.34279, -15496570.73972], [20037508.34279, 18764656.23138]),
 
6373         project: function (latlng) {
 
6374                 var d = Math.PI / 180,
 
6377                     tmp = this.R_MINOR / r,
 
6378                     e = Math.sqrt(1 - tmp * tmp),
 
6379                     con = e * Math.sin(y);
 
6381                 var ts = Math.tan(Math.PI / 4 - y / 2) / Math.pow((1 - con) / (1 + con), e / 2);
 
6382                 y = -r * Math.log(Math.max(ts, 1E-10));
 
6384                 return new Point(latlng.lng * d * r, y);
 
6387         unproject: function (point) {
 
6388                 var d = 180 / Math.PI,
 
6390                     tmp = this.R_MINOR / r,
 
6391                     e = Math.sqrt(1 - tmp * tmp),
 
6392                     ts = Math.exp(-point.y / r),
 
6393                     phi = Math.PI / 2 - 2 * Math.atan(ts);
 
6395                 for (var i = 0, dphi = 0.1, con; i < 15 && Math.abs(dphi) > 1e-7; i++) {
 
6396                         con = e * Math.sin(phi);
 
6397                         con = Math.pow((1 - con) / (1 + con), e / 2);
 
6398                         dphi = Math.PI / 2 - 2 * Math.atan(ts * con) - phi;
 
6402                 return new LatLng(phi * d, point.x * d / r);
 
6409  * An object with methods for projecting geographical coordinates of the world onto
 
6410  * a flat surface (and back). See [Map projection](http://en.wikipedia.org/wiki/Map_projection).
 
6412  * @property bounds: Bounds
 
6413  * The bounds (specified in CRS units) where the projection is valid
 
6415  * @method project(latlng: LatLng): Point
 
6416  * Projects geographical coordinates into a 2D point.
 
6417  * Only accepts actual `L.LatLng` instances, not arrays.
 
6419  * @method unproject(point: Point): LatLng
 
6420  * The inverse of `project`. Projects a 2D point into a geographical location.
 
6421  * Only accepts actual `L.Point` instances, not arrays.
 
6423  * Note that the projection instances do not inherit from Leafet's `Class` object,
 
6424  * and can't be instantiated. Also, new classes can't inherit from them,
 
6425  * and methods can't be added to them with the `include` function.
 
6432 var index = (Object.freeze || Object)({
 
6435         SphericalMercator: SphericalMercator
 
6440  * @crs L.CRS.EPSG3395
 
6442  * Rarely used by some commercial tile providers. Uses Elliptical Mercator projection.
 
6444 var EPSG3395 = extend({}, Earth, {
 
6446         projection: Mercator,
 
6448         transformation: (function () {
 
6449                 var scale = 0.5 / (Math.PI * Mercator.R);
 
6450                 return toTransformation(scale, 0.5, -scale, 0.5);
 
6456  * @crs L.CRS.EPSG4326
 
6458  * A common CRS among GIS enthusiasts. Uses simple Equirectangular projection.
 
6460  * Leaflet 1.0.x complies with the [TMS coordinate scheme for EPSG:4326](https://wiki.osgeo.org/wiki/Tile_Map_Service_Specification#global-geodetic),
 
6461  * which is a breaking change from 0.7.x behaviour.  If you are using a `TileLayer`
 
6462  * with this CRS, ensure that there are two 256x256 pixel tiles covering the
 
6463  * whole earth at zoom level zero, and that the tile coordinate origin is (-180,+90),
 
6464  * or (-180,-90) for `TileLayer`s with [the `tms` option](#tilelayer-tms) set.
 
6467 var EPSG4326 = extend({}, Earth, {
 
6470         transformation: toTransformation(1 / 180, 1, -1 / 180, 0.5)
 
6477  * A simple CRS that maps longitude and latitude into `x` and `y` directly.
 
6478  * May be used for maps of flat surfaces (e.g. game maps). Note that the `y`
 
6479  * axis should still be inverted (going from bottom to top). `distance()` returns
 
6480  * simple euclidean distance.
 
6483 var Simple = extend({}, CRS, {
 
6485         transformation: toTransformation(1, 0, -1, 0),
 
6487         scale: function (zoom) {
 
6488                 return Math.pow(2, zoom);
 
6491         zoom: function (scale) {
 
6492                 return Math.log(scale) / Math.LN2;
 
6495         distance: function (latlng1, latlng2) {
 
6496                 var dx = latlng2.lng - latlng1.lng,
 
6497                     dy = latlng2.lat - latlng1.lat;
 
6499                 return Math.sqrt(dx * dx + dy * dy);
 
6506 CRS.EPSG3395 = EPSG3395;
 
6507 CRS.EPSG3857 = EPSG3857;
 
6508 CRS.EPSG900913 = EPSG900913;
 
6509 CRS.EPSG4326 = EPSG4326;
 
6510 CRS.Simple = Simple;
 
6518  * A set of methods from the Layer base class that all Leaflet layers use.
 
6519  * Inherits all methods, options and events from `L.Evented`.
 
6524  * var layer = L.marker(latlng).addTo(map);
 
6530  * Fired after the layer is added to a map
 
6532  * @event remove: Event
 
6533  * Fired after the layer is removed from a map
 
6537 var Layer = Evented.extend({
 
6539         // Classes extending `L.Layer` will inherit the following options:
 
6541                 // @option pane: String = 'overlayPane'
 
6542                 // By default the layer will be added to the map's [overlay pane](#map-overlaypane). Overriding this option will cause the layer to be placed on another pane by default.
 
6543                 pane: 'overlayPane',
 
6545                 // @option attribution: String = null
 
6546                 // String to be shown in the attribution control, e.g. "© OpenStreetMap contributors". It describes the layer data and is often a legal obligation towards copyright holders and tile providers.
 
6549                 bubblingMouseEvents: true
 
6553          * Classes extending `L.Layer` will inherit the following methods:
 
6555          * @method addTo(map: Map|LayerGroup): this
 
6556          * Adds the layer to the given map or layer group.
 
6558         addTo: function (map) {
 
6563         // @method remove: this
 
6564         // Removes the layer from the map it is currently active on.
 
6565         remove: function () {
 
6566                 return this.removeFrom(this._map || this._mapToAdd);
 
6569         // @method removeFrom(map: Map): this
 
6570         // Removes the layer from the given map
 
6571         removeFrom: function (obj) {
 
6573                         obj.removeLayer(this);
 
6578         // @method getPane(name? : String): HTMLElement
 
6579         // Returns the `HTMLElement` representing the named pane on the map. If `name` is omitted, returns the pane for this layer.
 
6580         getPane: function (name) {
 
6581                 return this._map.getPane(name ? (this.options[name] || name) : this.options.pane);
 
6584         addInteractiveTarget: function (targetEl) {
 
6585                 this._map._targets[stamp(targetEl)] = this;
 
6589         removeInteractiveTarget: function (targetEl) {
 
6590                 delete this._map._targets[stamp(targetEl)];
 
6594         // @method getAttribution: String
 
6595         // Used by the `attribution control`, returns the [attribution option](#gridlayer-attribution).
 
6596         getAttribution: function () {
 
6597                 return this.options.attribution;
 
6600         _layerAdd: function (e) {
 
6603                 // check in case layer gets added and then removed before the map is ready
 
6604                 if (!map.hasLayer(this)) { return; }
 
6607                 this._zoomAnimated = map._zoomAnimated;
 
6609                 if (this.getEvents) {
 
6610                         var events = this.getEvents();
 
6611                         map.on(events, this);
 
6612                         this.once('remove', function () {
 
6613                                 map.off(events, this);
 
6619                 if (this.getAttribution && map.attributionControl) {
 
6620                         map.attributionControl.addAttribution(this.getAttribution());
 
6624                 map.fire('layeradd', {layer: this});
 
6628 /* @section Extension methods
 
6631  * Every layer should extend from `L.Layer` and (re-)implement the following methods.
 
6633  * @method onAdd(map: Map): this
 
6634  * Should contain code that creates DOM elements for the layer, adds them to `map panes` where they should belong and puts listeners on relevant map events. Called on [`map.addLayer(layer)`](#map-addlayer).
 
6636  * @method onRemove(map: Map): this
 
6637  * Should contain all clean up code that removes the layer's elements from the DOM and removes listeners previously added in [`onAdd`](#layer-onadd). Called on [`map.removeLayer(layer)`](#map-removelayer).
 
6639  * @method getEvents(): Object
 
6640  * This optional method should return an object like `{ viewreset: this._reset }` for [`addEventListener`](#evented-addeventlistener). The event handlers in this object will be automatically added and removed from the map with your layer.
 
6642  * @method getAttribution(): String
 
6643  * This optional method should return a string containing HTML to be shown on the `Attribution control` whenever the layer is visible.
 
6645  * @method beforeAdd(map: Map): this
 
6646  * Optional method. Called on [`map.addLayer(layer)`](#map-addlayer), before the layer is added to the map, before events are initialized, without waiting until the map is in a usable state. Use for early initialization only.
 
6651  * @section Layer events
 
6653  * @event layeradd: LayerEvent
 
6654  * Fired when a new layer is added to the map.
 
6656  * @event layerremove: LayerEvent
 
6657  * Fired when some layer is removed from the map
 
6659  * @section Methods for Layers and Controls
 
6662         // @method addLayer(layer: Layer): this
 
6663         // Adds the given layer to the map
 
6664         addLayer: function (layer) {
 
6665                 if (!layer._layerAdd) {
 
6666                         throw new Error('The provided object is not a Layer.');
 
6669                 var id = stamp(layer);
 
6670                 if (this._layers[id]) { return this; }
 
6671                 this._layers[id] = layer;
 
6673                 layer._mapToAdd = this;
 
6675                 if (layer.beforeAdd) {
 
6676                         layer.beforeAdd(this);
 
6679                 this.whenReady(layer._layerAdd, layer);
 
6684         // @method removeLayer(layer: Layer): this
 
6685         // Removes the given layer from the map.
 
6686         removeLayer: function (layer) {
 
6687                 var id = stamp(layer);
 
6689                 if (!this._layers[id]) { return this; }
 
6692                         layer.onRemove(this);
 
6695                 if (layer.getAttribution && this.attributionControl) {
 
6696                         this.attributionControl.removeAttribution(layer.getAttribution());
 
6699                 delete this._layers[id];
 
6702                         this.fire('layerremove', {layer: layer});
 
6703                         layer.fire('remove');
 
6706                 layer._map = layer._mapToAdd = null;
 
6711         // @method hasLayer(layer: Layer): Boolean
 
6712         // Returns `true` if the given layer is currently added to the map
 
6713         hasLayer: function (layer) {
 
6714                 return !!layer && (stamp(layer) in this._layers);
 
6717         /* @method eachLayer(fn: Function, context?: Object): this
 
6718          * Iterates over the layers of the map, optionally specifying context of the iterator function.
 
6720          * map.eachLayer(function(layer){
 
6721          *     layer.bindPopup('Hello');
 
6725         eachLayer: function (method, context) {
 
6726                 for (var i in this._layers) {
 
6727                         method.call(context, this._layers[i]);
 
6732         _addLayers: function (layers) {
 
6733                 layers = layers ? (isArray(layers) ? layers : [layers]) : [];
 
6735                 for (var i = 0, len = layers.length; i < len; i++) {
 
6736                         this.addLayer(layers[i]);
 
6740         _addZoomLimit: function (layer) {
 
6741                 if (isNaN(layer.options.maxZoom) || !isNaN(layer.options.minZoom)) {
 
6742                         this._zoomBoundLayers[stamp(layer)] = layer;
 
6743                         this._updateZoomLevels();
 
6747         _removeZoomLimit: function (layer) {
 
6748                 var id = stamp(layer);
 
6750                 if (this._zoomBoundLayers[id]) {
 
6751                         delete this._zoomBoundLayers[id];
 
6752                         this._updateZoomLevels();
 
6756         _updateZoomLevels: function () {
 
6757                 var minZoom = Infinity,
 
6758                     maxZoom = -Infinity,
 
6759                     oldZoomSpan = this._getZoomSpan();
 
6761                 for (var i in this._zoomBoundLayers) {
 
6762                         var options = this._zoomBoundLayers[i].options;
 
6764                         minZoom = options.minZoom === undefined ? minZoom : Math.min(minZoom, options.minZoom);
 
6765                         maxZoom = options.maxZoom === undefined ? maxZoom : Math.max(maxZoom, options.maxZoom);
 
6768                 this._layersMaxZoom = maxZoom === -Infinity ? undefined : maxZoom;
 
6769                 this._layersMinZoom = minZoom === Infinity ? undefined : minZoom;
 
6771                 // @section Map state change events
 
6772                 // @event zoomlevelschange: Event
 
6773                 // Fired when the number of zoomlevels on the map is changed due
 
6774                 // to adding or removing a layer.
 
6775                 if (oldZoomSpan !== this._getZoomSpan()) {
 
6776                         this.fire('zoomlevelschange');
 
6779                 if (this.options.maxZoom === undefined && this._layersMaxZoom && this.getZoom() > this._layersMaxZoom) {
 
6780                         this.setZoom(this._layersMaxZoom);
 
6782                 if (this.options.minZoom === undefined && this._layersMinZoom && this.getZoom() < this._layersMinZoom) {
 
6783                         this.setZoom(this._layersMinZoom);
 
6793  * Used to group several layers and handle them as one. If you add it to the map,
 
6794  * any layers added or removed from the group will be added/removed on the map as
 
6795  * well. Extends `Layer`.
 
6800  * L.layerGroup([marker1, marker2])
 
6801  *      .addLayer(polyline)
 
6806 var LayerGroup = Layer.extend({
 
6808         initialize: function (layers, options) {
 
6809                 setOptions(this, options);
 
6816                         for (i = 0, len = layers.length; i < len; i++) {
 
6817                                 this.addLayer(layers[i]);
 
6822         // @method addLayer(layer: Layer): this
 
6823         // Adds the given layer to the group.
 
6824         addLayer: function (layer) {
 
6825                 var id = this.getLayerId(layer);
 
6827                 this._layers[id] = layer;
 
6830                         this._map.addLayer(layer);
 
6836         // @method removeLayer(layer: Layer): this
 
6837         // Removes the given layer from the group.
 
6839         // @method removeLayer(id: Number): this
 
6840         // Removes the layer with the given internal ID from the group.
 
6841         removeLayer: function (layer) {
 
6842                 var id = layer in this._layers ? layer : this.getLayerId(layer);
 
6844                 if (this._map && this._layers[id]) {
 
6845                         this._map.removeLayer(this._layers[id]);
 
6848                 delete this._layers[id];
 
6853         // @method hasLayer(layer: Layer): Boolean
 
6854         // Returns `true` if the given layer is currently added to the group.
 
6856         // @method hasLayer(id: Number): Boolean
 
6857         // Returns `true` if the given internal ID is currently added to the group.
 
6858         hasLayer: function (layer) {
 
6859                 return !!layer && (layer in this._layers || this.getLayerId(layer) in this._layers);
 
6862         // @method clearLayers(): this
 
6863         // Removes all the layers from the group.
 
6864         clearLayers: function () {
 
6865                 return this.eachLayer(this.removeLayer, this);
 
6868         // @method invoke(methodName: String, …): this
 
6869         // Calls `methodName` on every layer contained in this group, passing any
 
6870         // additional parameters. Has no effect if the layers contained do not
 
6871         // implement `methodName`.
 
6872         invoke: function (methodName) {
 
6873                 var args = Array.prototype.slice.call(arguments, 1),
 
6876                 for (i in this._layers) {
 
6877                         layer = this._layers[i];
 
6879                         if (layer[methodName]) {
 
6880                                 layer[methodName].apply(layer, args);
 
6887         onAdd: function (map) {
 
6888                 this.eachLayer(map.addLayer, map);
 
6891         onRemove: function (map) {
 
6892                 this.eachLayer(map.removeLayer, map);
 
6895         // @method eachLayer(fn: Function, context?: Object): this
 
6896         // Iterates over the layers of the group, optionally specifying context of the iterator function.
 
6898         // group.eachLayer(function (layer) {
 
6899         //      layer.bindPopup('Hello');
 
6902         eachLayer: function (method, context) {
 
6903                 for (var i in this._layers) {
 
6904                         method.call(context, this._layers[i]);
 
6909         // @method getLayer(id: Number): Layer
 
6910         // Returns the layer with the given internal ID.
 
6911         getLayer: function (id) {
 
6912                 return this._layers[id];
 
6915         // @method getLayers(): Layer[]
 
6916         // Returns an array of all the layers added to the group.
 
6917         getLayers: function () {
 
6919                 this.eachLayer(layers.push, layers);
 
6923         // @method setZIndex(zIndex: Number): this
 
6924         // Calls `setZIndex` on every layer contained in this group, passing the z-index.
 
6925         setZIndex: function (zIndex) {
 
6926                 return this.invoke('setZIndex', zIndex);
 
6929         // @method getLayerId(layer: Layer): Number
 
6930         // Returns the internal ID for a layer
 
6931         getLayerId: function (layer) {
 
6932                 return stamp(layer);
 
6937 // @factory L.layerGroup(layers?: Layer[], options?: Object)
 
6938 // Create a layer group, optionally given an initial set of layers and an `options` object.
 
6939 var layerGroup = function (layers, options) {
 
6940         return new LayerGroup(layers, options);
 
6944  * @class FeatureGroup
 
6945  * @aka L.FeatureGroup
 
6946  * @inherits LayerGroup
 
6948  * Extended `LayerGroup` that makes it easier to do the same thing to all its member layers:
 
6949  *  * [`bindPopup`](#layer-bindpopup) binds a popup to all of the layers at once (likewise with [`bindTooltip`](#layer-bindtooltip))
 
6950  *  * Events are propagated to the `FeatureGroup`, so if the group has an event
 
6951  * handler, it will handle events from any of the layers. This includes mouse events
 
6952  * and custom events.
 
6953  *  * Has `layeradd` and `layerremove` events
 
6958  * L.featureGroup([marker1, marker2, polyline])
 
6959  *      .bindPopup('Hello world!')
 
6960  *      .on('click', function() { alert('Clicked on a member of the group!'); })
 
6965 var FeatureGroup = LayerGroup.extend({
 
6967         addLayer: function (layer) {
 
6968                 if (this.hasLayer(layer)) {
 
6972                 layer.addEventParent(this);
 
6974                 LayerGroup.prototype.addLayer.call(this, layer);
 
6976                 // @event layeradd: LayerEvent
 
6977                 // Fired when a layer is added to this `FeatureGroup`
 
6978                 return this.fire('layeradd', {layer: layer});
 
6981         removeLayer: function (layer) {
 
6982                 if (!this.hasLayer(layer)) {
 
6985                 if (layer in this._layers) {
 
6986                         layer = this._layers[layer];
 
6989                 layer.removeEventParent(this);
 
6991                 LayerGroup.prototype.removeLayer.call(this, layer);
 
6993                 // @event layerremove: LayerEvent
 
6994                 // Fired when a layer is removed from this `FeatureGroup`
 
6995                 return this.fire('layerremove', {layer: layer});
 
6998         // @method setStyle(style: Path options): this
 
6999         // Sets the given path options to each layer of the group that has a `setStyle` method.
 
7000         setStyle: function (style) {
 
7001                 return this.invoke('setStyle', style);
 
7004         // @method bringToFront(): this
 
7005         // Brings the layer group to the top of all other layers
 
7006         bringToFront: function () {
 
7007                 return this.invoke('bringToFront');
 
7010         // @method bringToBack(): this
 
7011         // Brings the layer group to the back of all other layers
 
7012         bringToBack: function () {
 
7013                 return this.invoke('bringToBack');
 
7016         // @method getBounds(): LatLngBounds
 
7017         // Returns the LatLngBounds of the Feature Group (created from bounds and coordinates of its children).
 
7018         getBounds: function () {
 
7019                 var bounds = new LatLngBounds();
 
7021                 for (var id in this._layers) {
 
7022                         var layer = this._layers[id];
 
7023                         bounds.extend(layer.getBounds ? layer.getBounds() : layer.getLatLng());
 
7029 // @factory L.featureGroup(layers: Layer[])
 
7030 // Create a feature group, optionally given an initial set of layers.
 
7031 var featureGroup = function (layers) {
 
7032         return new FeatureGroup(layers);
 
7039  * Represents an icon to provide when creating a marker.
 
7044  * var myIcon = L.icon({
 
7045  *     iconUrl: 'my-icon.png',
 
7046  *     iconRetinaUrl: 'my-icon@2x.png',
 
7047  *     iconSize: [38, 95],
 
7048  *     iconAnchor: [22, 94],
 
7049  *     popupAnchor: [-3, -76],
 
7050  *     shadowUrl: 'my-icon-shadow.png',
 
7051  *     shadowRetinaUrl: 'my-icon-shadow@2x.png',
 
7052  *     shadowSize: [68, 95],
 
7053  *     shadowAnchor: [22, 94]
 
7056  * L.marker([50.505, 30.57], {icon: myIcon}).addTo(map);
 
7059  * `L.Icon.Default` extends `L.Icon` and is the blue icon Leaflet uses for markers by default.
 
7063 var Icon = Class.extend({
 
7068          * @option iconUrl: String = null
 
7069          * **(required)** The URL to the icon image (absolute or relative to your script path).
 
7071          * @option iconRetinaUrl: String = null
 
7072          * The URL to a retina sized version of the icon image (absolute or relative to your
 
7073          * script path). Used for Retina screen devices.
 
7075          * @option iconSize: Point = null
 
7076          * Size of the icon image in pixels.
 
7078          * @option iconAnchor: Point = null
 
7079          * The coordinates of the "tip" of the icon (relative to its top left corner). The icon
 
7080          * will be aligned so that this point is at the marker's geographical location. Centered
 
7081          * by default if size is specified, also can be set in CSS with negative margins.
 
7083          * @option popupAnchor: Point = [0, 0]
 
7084          * The coordinates of the point from which popups will "open", relative to the icon anchor.
 
7086          * @option tooltipAnchor: Point = [0, 0]
 
7087          * The coordinates of the point from which tooltips will "open", relative to the icon anchor.
 
7089          * @option shadowUrl: String = null
 
7090          * The URL to the icon shadow image. If not specified, no shadow image will be created.
 
7092          * @option shadowRetinaUrl: String = null
 
7094          * @option shadowSize: Point = null
 
7095          * Size of the shadow image in pixels.
 
7097          * @option shadowAnchor: Point = null
 
7098          * The coordinates of the "tip" of the shadow (relative to its top left corner) (the same
 
7099          * as iconAnchor if not specified).
 
7101          * @option className: String = ''
 
7102          * A custom class name to assign to both icon and shadow images. Empty by default.
 
7106                 popupAnchor: [0, 0],
 
7107                 tooltipAnchor: [0, 0]
 
7110         initialize: function (options) {
 
7111                 setOptions(this, options);
 
7114         // @method createIcon(oldIcon?: HTMLElement): HTMLElement
 
7115         // Called internally when the icon has to be shown, returns a `<img>` HTML element
 
7116         // styled according to the options.
 
7117         createIcon: function (oldIcon) {
 
7118                 return this._createIcon('icon', oldIcon);
 
7121         // @method createShadow(oldIcon?: HTMLElement): HTMLElement
 
7122         // As `createIcon`, but for the shadow beneath it.
 
7123         createShadow: function (oldIcon) {
 
7124                 return this._createIcon('shadow', oldIcon);
 
7127         _createIcon: function (name, oldIcon) {
 
7128                 var src = this._getIconUrl(name);
 
7131                         if (name === 'icon') {
 
7132                                 throw new Error('iconUrl not set in Icon options (see the docs).');
 
7137                 var img = this._createImg(src, oldIcon && oldIcon.tagName === 'IMG' ? oldIcon : null);
 
7138                 this._setIconStyles(img, name);
 
7143         _setIconStyles: function (img, name) {
 
7144                 var options = this.options;
 
7145                 var sizeOption = options[name + 'Size'];
 
7147                 if (typeof sizeOption === 'number') {
 
7148                         sizeOption = [sizeOption, sizeOption];
 
7151                 var size = toPoint(sizeOption),
 
7152                     anchor = toPoint(name === 'shadow' && options.shadowAnchor || options.iconAnchor ||
 
7153                             size && size.divideBy(2, true));
 
7155                 img.className = 'leaflet-marker-' + name + ' ' + (options.className || '');
 
7158                         img.style.marginLeft = (-anchor.x) + 'px';
 
7159                         img.style.marginTop  = (-anchor.y) + 'px';
 
7163                         img.style.width  = size.x + 'px';
 
7164                         img.style.height = size.y + 'px';
 
7168         _createImg: function (src, el) {
 
7169                 el = el || document.createElement('img');
 
7174         _getIconUrl: function (name) {
 
7175                 return retina && this.options[name + 'RetinaUrl'] || this.options[name + 'Url'];
 
7180 // @factory L.icon(options: Icon options)
 
7181 // Creates an icon instance with the given options.
 
7182 function icon(options) {
 
7183         return new Icon(options);
 
7187  * @miniclass Icon.Default (Icon)
 
7188  * @aka L.Icon.Default
 
7191  * A trivial subclass of `Icon`, represents the icon to use in `Marker`s when
 
7192  * no icon is specified. Points to the blue marker image distributed with Leaflet
 
7195  * In order to customize the default icon, just change the properties of `L.Icon.Default.prototype.options`
 
7196  * (which is a set of `Icon options`).
 
7198  * If you want to _completely_ replace the default icon, override the
 
7199  * `L.Marker.prototype.options.icon` with your own icon instead.
 
7202 var IconDefault = Icon.extend({
 
7205                 iconUrl:       'marker-icon.png',
 
7206                 iconRetinaUrl: 'marker-icon-2x.png',
 
7207                 shadowUrl:     'marker-shadow.png',
 
7209                 iconAnchor:  [12, 41],
 
7210                 popupAnchor: [1, -34],
 
7211                 tooltipAnchor: [16, -28],
 
7212                 shadowSize:  [41, 41]
 
7215         _getIconUrl: function (name) {
 
7216                 if (!IconDefault.imagePath) {   // Deprecated, backwards-compatibility only
 
7217                         IconDefault.imagePath = this._detectIconPath();
 
7220                 // @option imagePath: String
 
7221                 // `Icon.Default` will try to auto-detect the location of the
 
7222                 // blue icon images. If you are placing these images in a non-standard
 
7223                 // way, set this option to point to the right path.
 
7224                 return (this.options.imagePath || IconDefault.imagePath) + Icon.prototype._getIconUrl.call(this, name);
 
7227         _detectIconPath: function () {
 
7228                 var el = create$1('div',  'leaflet-default-icon-path', document.body);
 
7229                 var path = getStyle(el, 'background-image') ||
 
7230                            getStyle(el, 'backgroundImage');     // IE8
 
7232                 document.body.removeChild(el);
 
7234                 if (path === null || path.indexOf('url') !== 0) {
 
7237                         path = path.replace(/^url\(["']?/, '').replace(/marker-icon\.png["']?\)$/, '');
 
7245  * L.Handler.MarkerDrag is used internally by L.Marker to make the markers draggable.
 
7249 /* @namespace Marker
 
7250  * @section Interaction handlers
 
7252  * Interaction handlers are properties of a marker instance that allow you to control interaction behavior in runtime, enabling or disabling certain features such as dragging (see `Handler` methods). Example:
 
7255  * marker.dragging.disable();
 
7258  * @property dragging: Handler
 
7259  * Marker dragging handler (by both mouse and touch). Only valid when the marker is on the map (Otherwise set [`marker.options.draggable`](#marker-draggable)).
 
7262 var MarkerDrag = Handler.extend({
 
7263         initialize: function (marker) {
 
7264                 this._marker = marker;
 
7267         addHooks: function () {
 
7268                 var icon = this._marker._icon;
 
7270                 if (!this._draggable) {
 
7271                         this._draggable = new Draggable(icon, icon, true);
 
7274                 this._draggable.on({
 
7275                         dragstart: this._onDragStart,
 
7276                         predrag: this._onPreDrag,
 
7278                         dragend: this._onDragEnd
 
7281                 addClass(icon, 'leaflet-marker-draggable');
 
7284         removeHooks: function () {
 
7285                 this._draggable.off({
 
7286                         dragstart: this._onDragStart,
 
7287                         predrag: this._onPreDrag,
 
7289                         dragend: this._onDragEnd
 
7292                 if (this._marker._icon) {
 
7293                         removeClass(this._marker._icon, 'leaflet-marker-draggable');
 
7297         moved: function () {
 
7298                 return this._draggable && this._draggable._moved;
 
7301         _adjustPan: function (e) {
 
7302                 var marker = this._marker,
 
7304                     speed = this._marker.options.autoPanSpeed,
 
7305                     padding = this._marker.options.autoPanPadding,
 
7306                     iconPos = getPosition(marker._icon),
 
7307                     bounds = map.getPixelBounds(),
 
7308                     origin = map.getPixelOrigin();
 
7310                 var panBounds = toBounds(
 
7311                         bounds.min._subtract(origin).add(padding),
 
7312                         bounds.max._subtract(origin).subtract(padding)
 
7315                 if (!panBounds.contains(iconPos)) {
 
7316                         // Compute incremental movement
 
7317                         var movement = toPoint(
 
7318                                 (Math.max(panBounds.max.x, iconPos.x) - panBounds.max.x) / (bounds.max.x - panBounds.max.x) -
 
7319                                 (Math.min(panBounds.min.x, iconPos.x) - panBounds.min.x) / (bounds.min.x - panBounds.min.x),
 
7321                                 (Math.max(panBounds.max.y, iconPos.y) - panBounds.max.y) / (bounds.max.y - panBounds.max.y) -
 
7322                                 (Math.min(panBounds.min.y, iconPos.y) - panBounds.min.y) / (bounds.min.y - panBounds.min.y)
 
7323                         ).multiplyBy(speed);
 
7325                         map.panBy(movement, {animate: false});
 
7327                         this._draggable._newPos._add(movement);
 
7328                         this._draggable._startPos._add(movement);
 
7330                         setPosition(marker._icon, this._draggable._newPos);
 
7333                         this._panRequest = requestAnimFrame(this._adjustPan.bind(this, e));
 
7337         _onDragStart: function () {
 
7338                 // @section Dragging events
 
7339                 // @event dragstart: Event
 
7340                 // Fired when the user starts dragging the marker.
 
7342                 // @event movestart: Event
 
7343                 // Fired when the marker starts moving (because of dragging).
 
7345                 this._oldLatLng = this._marker.getLatLng();
 
7352         _onPreDrag: function (e) {
 
7353                 if (this._marker.options.autoPan) {
 
7354                         cancelAnimFrame(this._panRequest);
 
7355                         this._panRequest = requestAnimFrame(this._adjustPan.bind(this, e));
 
7359         _onDrag: function (e) {
 
7360                 var marker = this._marker,
 
7361                     shadow = marker._shadow,
 
7362                     iconPos = getPosition(marker._icon),
 
7363                     latlng = marker._map.layerPointToLatLng(iconPos);
 
7365                 // update shadow position
 
7367                         setPosition(shadow, iconPos);
 
7370                 marker._latlng = latlng;
 
7372                 e.oldLatLng = this._oldLatLng;
 
7374                 // @event drag: Event
 
7375                 // Fired repeatedly while the user drags the marker.
 
7381         _onDragEnd: function (e) {
 
7382                 // @event dragend: DragEndEvent
 
7383                 // Fired when the user stops dragging the marker.
 
7385                  cancelAnimFrame(this._panRequest);
 
7387                 // @event moveend: Event
 
7388                 // Fired when the marker stops moving (because of dragging).
 
7389                 delete this._oldLatLng;
 
7392                     .fire('dragend', e);
 
7398  * @inherits Interactive layer
 
7400  * L.Marker is used to display clickable/draggable icons on the map. Extends `Layer`.
 
7405  * L.marker([50.5, 30.5]).addTo(map);
 
7409 var Marker = Layer.extend({
 
7412         // @aka Marker options
 
7414                 // @option icon: Icon = *
 
7415                 // Icon instance to use for rendering the marker.
 
7416                 // See [Icon documentation](#L.Icon) for details on how to customize the marker icon.
 
7417                 // If not specified, a common instance of `L.Icon.Default` is used.
 
7418                 icon: new IconDefault(),
 
7420                 // Option inherited from "Interactive layer" abstract class
 
7423                 // @option keyboard: Boolean = true
 
7424                 // Whether the marker can be tabbed to with a keyboard and clicked by pressing enter.
 
7427                 // @option title: String = ''
 
7428                 // Text for the browser tooltip that appear on marker hover (no tooltip by default).
 
7431                 // @option alt: String = ''
 
7432                 // Text for the `alt` attribute of the icon image (useful for accessibility).
 
7435                 // @option zIndexOffset: Number = 0
 
7436                 // By default, marker images zIndex is set automatically based on its latitude. Use this option if you want to put the marker on top of all others (or below), specifying a high value like `1000` (or high negative value, respectively).
 
7439                 // @option opacity: Number = 1.0
 
7440                 // The opacity of the marker.
 
7443                 // @option riseOnHover: Boolean = false
 
7444                 // If `true`, the marker will get on top of others when you hover the mouse over it.
 
7447                 // @option riseOffset: Number = 250
 
7448                 // The z-index offset used for the `riseOnHover` feature.
 
7451                 // @option pane: String = 'markerPane'
 
7452                 // `Map pane` where the markers icon will be added.
 
7455                 // @option pane: String = 'shadowPane'
 
7456                 // `Map pane` where the markers shadow will be added.
 
7457                 shadowPane: 'shadowPane',
 
7459                 // @option bubblingMouseEvents: Boolean = false
 
7460                 // When `true`, a mouse event on this marker will trigger the same event on the map
 
7461                 // (unless [`L.DomEvent.stopPropagation`](#domevent-stoppropagation) is used).
 
7462                 bubblingMouseEvents: false,
 
7464                 // @section Draggable marker options
 
7465                 // @option draggable: Boolean = false
 
7466                 // Whether the marker is draggable with mouse/touch or not.
 
7469                 // @option autoPan: Boolean = false
 
7470                 // Whether to pan the map when dragging this marker near its edge or not.
 
7473                 // @option autoPanPadding: Point = Point(50, 50)
 
7474                 // Distance (in pixels to the left/right and to the top/bottom) of the
 
7475                 // map edge to start panning the map.
 
7476                 autoPanPadding: [50, 50],
 
7478                 // @option autoPanSpeed: Number = 10
 
7479                 // Number of pixels the map should pan by.
 
7485          * In addition to [shared layer methods](#Layer) like `addTo()` and `remove()` and [popup methods](#Popup) like bindPopup() you can also use the following methods:
 
7488         initialize: function (latlng, options) {
 
7489                 setOptions(this, options);
 
7490                 this._latlng = toLatLng(latlng);
 
7493         onAdd: function (map) {
 
7494                 this._zoomAnimated = this._zoomAnimated && map.options.markerZoomAnimation;
 
7496                 if (this._zoomAnimated) {
 
7497                         map.on('zoomanim', this._animateZoom, this);
 
7504         onRemove: function (map) {
 
7505                 if (this.dragging && this.dragging.enabled()) {
 
7506                         this.options.draggable = true;
 
7507                         this.dragging.removeHooks();
 
7509                 delete this.dragging;
 
7511                 if (this._zoomAnimated) {
 
7512                         map.off('zoomanim', this._animateZoom, this);
 
7516                 this._removeShadow();
 
7519         getEvents: function () {
 
7522                         viewreset: this.update
 
7526         // @method getLatLng: LatLng
 
7527         // Returns the current geographical position of the marker.
 
7528         getLatLng: function () {
 
7529                 return this._latlng;
 
7532         // @method setLatLng(latlng: LatLng): this
 
7533         // Changes the marker position to the given point.
 
7534         setLatLng: function (latlng) {
 
7535                 var oldLatLng = this._latlng;
 
7536                 this._latlng = toLatLng(latlng);
 
7539                 // @event move: Event
 
7540                 // Fired when the marker is moved via [`setLatLng`](#marker-setlatlng) or by [dragging](#marker-dragging). Old and new coordinates are included in event arguments as `oldLatLng`, `latlng`.
 
7541                 return this.fire('move', {oldLatLng: oldLatLng, latlng: this._latlng});
 
7544         // @method setZIndexOffset(offset: Number): this
 
7545         // Changes the [zIndex offset](#marker-zindexoffset) of the marker.
 
7546         setZIndexOffset: function (offset) {
 
7547                 this.options.zIndexOffset = offset;
 
7548                 return this.update();
 
7551         // @method getIcon: Icon
 
7552         // Returns the current icon used by the marker
 
7553         getIcon: function () {
 
7554                 return this.options.icon;
 
7557         // @method setIcon(icon: Icon): this
 
7558         // Changes the marker icon.
 
7559         setIcon: function (icon) {
 
7561                 this.options.icon = icon;
 
7569                         this.bindPopup(this._popup, this._popup.options);
 
7575         getElement: function () {
 
7579         update: function () {
 
7581                 if (this._icon && this._map) {
 
7582                         var pos = this._map.latLngToLayerPoint(this._latlng).round();
 
7589         _initIcon: function () {
 
7590                 var options = this.options,
 
7591                     classToAdd = 'leaflet-zoom-' + (this._zoomAnimated ? 'animated' : 'hide');
 
7593                 var icon = options.icon.createIcon(this._icon),
 
7596                 // if we're not reusing the icon, remove the old one and init new one
 
7597                 if (icon !== this._icon) {
 
7603                         if (options.title) {
 
7604                                 icon.title = options.title;
 
7607                         if (icon.tagName === 'IMG') {
 
7608                                 icon.alt = options.alt || '';
 
7612                 addClass(icon, classToAdd);
 
7614                 if (options.keyboard) {
 
7615                         icon.tabIndex = '0';
 
7620                 if (options.riseOnHover) {
 
7622                                 mouseover: this._bringToFront,
 
7623                                 mouseout: this._resetZIndex
 
7627                 var newShadow = options.icon.createShadow(this._shadow),
 
7630                 if (newShadow !== this._shadow) {
 
7631                         this._removeShadow();
 
7636                         addClass(newShadow, classToAdd);
 
7639                 this._shadow = newShadow;
 
7642                 if (options.opacity < 1) {
 
7643                         this._updateOpacity();
 
7648                         this.getPane().appendChild(this._icon);
 
7650                 this._initInteraction();
 
7651                 if (newShadow && addShadow) {
 
7652                         this.getPane(options.shadowPane).appendChild(this._shadow);
 
7656         _removeIcon: function () {
 
7657                 if (this.options.riseOnHover) {
 
7659                                 mouseover: this._bringToFront,
 
7660                                 mouseout: this._resetZIndex
 
7665                 this.removeInteractiveTarget(this._icon);
 
7670         _removeShadow: function () {
 
7672                         remove(this._shadow);
 
7674                 this._shadow = null;
 
7677         _setPos: function (pos) {
 
7680                         setPosition(this._icon, pos);
 
7684                         setPosition(this._shadow, pos);
 
7687                 this._zIndex = pos.y + this.options.zIndexOffset;
 
7689                 this._resetZIndex();
 
7692         _updateZIndex: function (offset) {
 
7694                         this._icon.style.zIndex = this._zIndex + offset;
 
7698         _animateZoom: function (opt) {
 
7699                 var pos = this._map._latLngToNewLayerPoint(this._latlng, opt.zoom, opt.center).round();
 
7704         _initInteraction: function () {
 
7706                 if (!this.options.interactive) { return; }
 
7708                 addClass(this._icon, 'leaflet-interactive');
 
7710                 this.addInteractiveTarget(this._icon);
 
7713                         var draggable = this.options.draggable;
 
7714                         if (this.dragging) {
 
7715                                 draggable = this.dragging.enabled();
 
7716                                 this.dragging.disable();
 
7719                         this.dragging = new MarkerDrag(this);
 
7722                                 this.dragging.enable();
 
7727         // @method setOpacity(opacity: Number): this
 
7728         // Changes the opacity of the marker.
 
7729         setOpacity: function (opacity) {
 
7730                 this.options.opacity = opacity;
 
7732                         this._updateOpacity();
 
7738         _updateOpacity: function () {
 
7739                 var opacity = this.options.opacity;
 
7742                         setOpacity(this._icon, opacity);
 
7746                         setOpacity(this._shadow, opacity);
 
7750         _bringToFront: function () {
 
7751                 this._updateZIndex(this.options.riseOffset);
 
7754         _resetZIndex: function () {
 
7755                 this._updateZIndex(0);
 
7758         _getPopupAnchor: function () {
 
7759                 return this.options.icon.options.popupAnchor;
 
7762         _getTooltipAnchor: function () {
 
7763                 return this.options.icon.options.tooltipAnchor;
 
7768 // factory L.marker(latlng: LatLng, options? : Marker options)
 
7770 // @factory L.marker(latlng: LatLng, options? : Marker options)
 
7771 // Instantiates a Marker object given a geographical point and optionally an options object.
 
7772 function marker(latlng, options) {
 
7773         return new Marker(latlng, options);
 
7779  * @inherits Interactive layer
 
7781  * An abstract class that contains options and constants shared between vector
 
7782  * overlays (Polygon, Polyline, Circle). Do not use it directly. Extends `Layer`.
 
7785 var Path = Layer.extend({
 
7788         // @aka Path options
 
7790                 // @option stroke: Boolean = true
 
7791                 // Whether to draw stroke along the path. Set it to `false` to disable borders on polygons or circles.
 
7794                 // @option color: String = '#3388ff'
 
7798                 // @option weight: Number = 3
 
7799                 // Stroke width in pixels
 
7802                 // @option opacity: Number = 1.0
 
7806                 // @option lineCap: String= 'round'
 
7807                 // A string that defines [shape to be used at the end](https://developer.mozilla.org/docs/Web/SVG/Attribute/stroke-linecap) of the stroke.
 
7810                 // @option lineJoin: String = 'round'
 
7811                 // A string that defines [shape to be used at the corners](https://developer.mozilla.org/docs/Web/SVG/Attribute/stroke-linejoin) of the stroke.
 
7814                 // @option dashArray: String = null
 
7815                 // A string that defines the stroke [dash pattern](https://developer.mozilla.org/docs/Web/SVG/Attribute/stroke-dasharray). Doesn't work on `Canvas`-powered layers in [some old browsers](https://developer.mozilla.org/docs/Web/API/CanvasRenderingContext2D/setLineDash#Browser_compatibility).
 
7818                 // @option dashOffset: String = null
 
7819                 // A string that defines the [distance into the dash pattern to start the dash](https://developer.mozilla.org/docs/Web/SVG/Attribute/stroke-dashoffset). Doesn't work on `Canvas`-powered layers in [some old browsers](https://developer.mozilla.org/docs/Web/API/CanvasRenderingContext2D/setLineDash#Browser_compatibility).
 
7822                 // @option fill: Boolean = depends
 
7823                 // Whether to fill the path with color. Set it to `false` to disable filling on polygons or circles.
 
7826                 // @option fillColor: String = *
 
7827                 // Fill color. Defaults to the value of the [`color`](#path-color) option
 
7830                 // @option fillOpacity: Number = 0.2
 
7834                 // @option fillRule: String = 'evenodd'
 
7835                 // A string that defines [how the inside of a shape](https://developer.mozilla.org/docs/Web/SVG/Attribute/fill-rule) is determined.
 
7836                 fillRule: 'evenodd',
 
7840                 // Option inherited from "Interactive layer" abstract class
 
7843                 // @option bubblingMouseEvents: Boolean = true
 
7844                 // When `true`, a mouse event on this path will trigger the same event on the map
 
7845                 // (unless [`L.DomEvent.stopPropagation`](#domevent-stoppropagation) is used).
 
7846                 bubblingMouseEvents: true
 
7849         beforeAdd: function (map) {
 
7850                 // Renderer is set here because we need to call renderer.getEvents
 
7851                 // before this.getEvents.
 
7852                 this._renderer = map.getRenderer(this);
 
7855         onAdd: function () {
 
7856                 this._renderer._initPath(this);
 
7858                 this._renderer._addPath(this);
 
7861         onRemove: function () {
 
7862                 this._renderer._removePath(this);
 
7865         // @method redraw(): this
 
7866         // Redraws the layer. Sometimes useful after you changed the coordinates that the path uses.
 
7867         redraw: function () {
 
7869                         this._renderer._updatePath(this);
 
7874         // @method setStyle(style: Path options): this
 
7875         // Changes the appearance of a Path based on the options in the `Path options` object.
 
7876         setStyle: function (style) {
 
7877                 setOptions(this, style);
 
7878                 if (this._renderer) {
 
7879                         this._renderer._updateStyle(this);
 
7880                         if (this.options.stroke && style && style.hasOwnProperty('weight')) {
 
7881                                 this._updateBounds();
 
7887         // @method bringToFront(): this
 
7888         // Brings the layer to the top of all path layers.
 
7889         bringToFront: function () {
 
7890                 if (this._renderer) {
 
7891                         this._renderer._bringToFront(this);
 
7896         // @method bringToBack(): this
 
7897         // Brings the layer to the bottom of all path layers.
 
7898         bringToBack: function () {
 
7899                 if (this._renderer) {
 
7900                         this._renderer._bringToBack(this);
 
7905         getElement: function () {
 
7909         _reset: function () {
 
7910                 // defined in child classes
 
7915         _clickTolerance: function () {
 
7916                 // used when doing hit detection for Canvas layers
 
7917                 return (this.options.stroke ? this.options.weight / 2 : 0) + this._renderer.options.tolerance;
 
7922  * @class CircleMarker
 
7923  * @aka L.CircleMarker
 
7926  * A circle of a fixed size with radius specified in pixels. Extends `Path`.
 
7929 var CircleMarker = Path.extend({
 
7932         // @aka CircleMarker options
 
7936                 // @option radius: Number = 10
 
7937                 // Radius of the circle marker, in pixels
 
7941         initialize: function (latlng, options) {
 
7942                 setOptions(this, options);
 
7943                 this._latlng = toLatLng(latlng);
 
7944                 this._radius = this.options.radius;
 
7947         // @method setLatLng(latLng: LatLng): this
 
7948         // Sets the position of a circle marker to a new location.
 
7949         setLatLng: function (latlng) {
 
7950                 var oldLatLng = this._latlng;
 
7951                 this._latlng = toLatLng(latlng);
 
7954                 // @event move: Event
 
7955                 // Fired when the marker is moved via [`setLatLng`](#circlemarker-setlatlng). Old and new coordinates are included in event arguments as `oldLatLng`, `latlng`.
 
7956                 return this.fire('move', {oldLatLng: oldLatLng, latlng: this._latlng});
 
7959         // @method getLatLng(): LatLng
 
7960         // Returns the current geographical position of the circle marker
 
7961         getLatLng: function () {
 
7962                 return this._latlng;
 
7965         // @method setRadius(radius: Number): this
 
7966         // Sets the radius of a circle marker. Units are in pixels.
 
7967         setRadius: function (radius) {
 
7968                 this.options.radius = this._radius = radius;
 
7969                 return this.redraw();
 
7972         // @method getRadius(): Number
 
7973         // Returns the current radius of the circle
 
7974         getRadius: function () {
 
7975                 return this._radius;
 
7978         setStyle : function (options) {
 
7979                 var radius = options && options.radius || this._radius;
 
7980                 Path.prototype.setStyle.call(this, options);
 
7981                 this.setRadius(radius);
 
7985         _project: function () {
 
7986                 this._point = this._map.latLngToLayerPoint(this._latlng);
 
7987                 this._updateBounds();
 
7990         _updateBounds: function () {
 
7991                 var r = this._radius,
 
7992                     r2 = this._radiusY || r,
 
7993                     w = this._clickTolerance(),
 
7994                     p = [r + w, r2 + w];
 
7995                 this._pxBounds = new Bounds(this._point.subtract(p), this._point.add(p));
 
7998         _update: function () {
 
8004         _updatePath: function () {
 
8005                 this._renderer._updateCircle(this);
 
8008         _empty: function () {
 
8009                 return this._radius && !this._renderer._bounds.intersects(this._pxBounds);
 
8012         // Needed by the `Canvas` renderer for interactivity
 
8013         _containsPoint: function (p) {
 
8014                 return p.distanceTo(this._point) <= this._radius + this._clickTolerance();
 
8019 // @factory L.circleMarker(latlng: LatLng, options?: CircleMarker options)
 
8020 // Instantiates a circle marker object given a geographical point, and an optional options object.
 
8021 function circleMarker(latlng, options) {
 
8022         return new CircleMarker(latlng, options);
 
8028  * @inherits CircleMarker
 
8030  * A class for drawing circle overlays on a map. Extends `CircleMarker`.
 
8032  * It's an approximation and starts to diverge from a real circle closer to poles (due to projection distortion).
 
8037  * L.circle([50.5, 30.5], {radius: 200}).addTo(map);
 
8041 var Circle = CircleMarker.extend({
 
8043         initialize: function (latlng, options, legacyOptions) {
 
8044                 if (typeof options === 'number') {
 
8045                         // Backwards compatibility with 0.7.x factory (latlng, radius, options?)
 
8046                         options = extend({}, legacyOptions, {radius: options});
 
8048                 setOptions(this, options);
 
8049                 this._latlng = toLatLng(latlng);
 
8051                 if (isNaN(this.options.radius)) { throw new Error('Circle radius cannot be NaN'); }
 
8054                 // @aka Circle options
 
8055                 // @option radius: Number; Radius of the circle, in meters.
 
8056                 this._mRadius = this.options.radius;
 
8059         // @method setRadius(radius: Number): this
 
8060         // Sets the radius of a circle. Units are in meters.
 
8061         setRadius: function (radius) {
 
8062                 this._mRadius = radius;
 
8063                 return this.redraw();
 
8066         // @method getRadius(): Number
 
8067         // Returns the current radius of a circle. Units are in meters.
 
8068         getRadius: function () {
 
8069                 return this._mRadius;
 
8072         // @method getBounds(): LatLngBounds
 
8073         // Returns the `LatLngBounds` of the path.
 
8074         getBounds: function () {
 
8075                 var half = [this._radius, this._radiusY || this._radius];
 
8077                 return new LatLngBounds(
 
8078                         this._map.layerPointToLatLng(this._point.subtract(half)),
 
8079                         this._map.layerPointToLatLng(this._point.add(half)));
 
8082         setStyle: Path.prototype.setStyle,
 
8084         _project: function () {
 
8086                 var lng = this._latlng.lng,
 
8087                     lat = this._latlng.lat,
 
8089                     crs = map.options.crs;
 
8091                 if (crs.distance === Earth.distance) {
 
8092                         var d = Math.PI / 180,
 
8093                             latR = (this._mRadius / Earth.R) / d,
 
8094                             top = map.project([lat + latR, lng]),
 
8095                             bottom = map.project([lat - latR, lng]),
 
8096                             p = top.add(bottom).divideBy(2),
 
8097                             lat2 = map.unproject(p).lat,
 
8098                             lngR = Math.acos((Math.cos(latR * d) - Math.sin(lat * d) * Math.sin(lat2 * d)) /
 
8099                                     (Math.cos(lat * d) * Math.cos(lat2 * d))) / d;
 
8101                         if (isNaN(lngR) || lngR === 0) {
 
8102                                 lngR = latR / Math.cos(Math.PI / 180 * lat); // Fallback for edge case, #2425
 
8105                         this._point = p.subtract(map.getPixelOrigin());
 
8106                         this._radius = isNaN(lngR) ? 0 : p.x - map.project([lat2, lng - lngR]).x;
 
8107                         this._radiusY = p.y - top.y;
 
8110                         var latlng2 = crs.unproject(crs.project(this._latlng).subtract([this._mRadius, 0]));
 
8112                         this._point = map.latLngToLayerPoint(this._latlng);
 
8113                         this._radius = this._point.x - map.latLngToLayerPoint(latlng2).x;
 
8116                 this._updateBounds();
 
8120 // @factory L.circle(latlng: LatLng, options?: Circle options)
 
8121 // Instantiates a circle object given a geographical point, and an options object
 
8122 // which contains the circle radius.
 
8124 // @factory L.circle(latlng: LatLng, radius: Number, options?: Circle options)
 
8125 // Obsolete way of instantiating a circle, for compatibility with 0.7.x code.
 
8126 // Do not use in new applications or plugins.
 
8127 function circle(latlng, options, legacyOptions) {
 
8128         return new Circle(latlng, options, legacyOptions);
 
8136  * A class for drawing polyline overlays on a map. Extends `Path`.
 
8141  * // create a red polyline from an array of LatLng points
 
8148  * var polyline = L.polyline(latlngs, {color: 'red'}).addTo(map);
 
8150  * // zoom the map to the polyline
 
8151  * map.fitBounds(polyline.getBounds());
 
8154  * You can also pass a multi-dimensional array to represent a `MultiPolyline` shape:
 
8157  * // create a red polyline from an array of arrays of LatLng points
 
8159  *      [[45.51, -122.68],
 
8170 var Polyline = Path.extend({
 
8173         // @aka Polyline options
 
8175                 // @option smoothFactor: Number = 1.0
 
8176                 // How much to simplify the polyline on each zoom level. More means
 
8177                 // better performance and smoother look, and less means more accurate representation.
 
8180                 // @option noClip: Boolean = false
 
8181                 // Disable polyline clipping.
 
8185         initialize: function (latlngs, options) {
 
8186                 setOptions(this, options);
 
8187                 this._setLatLngs(latlngs);
 
8190         // @method getLatLngs(): LatLng[]
 
8191         // Returns an array of the points in the path, or nested arrays of points in case of multi-polyline.
 
8192         getLatLngs: function () {
 
8193                 return this._latlngs;
 
8196         // @method setLatLngs(latlngs: LatLng[]): this
 
8197         // Replaces all the points in the polyline with the given array of geographical points.
 
8198         setLatLngs: function (latlngs) {
 
8199                 this._setLatLngs(latlngs);
 
8200                 return this.redraw();
 
8203         // @method isEmpty(): Boolean
 
8204         // Returns `true` if the Polyline has no LatLngs.
 
8205         isEmpty: function () {
 
8206                 return !this._latlngs.length;
 
8209         // @method closestLayerPoint(p: Point): Point
 
8210         // Returns the point closest to `p` on the Polyline.
 
8211         closestLayerPoint: function (p) {
 
8212                 var minDistance = Infinity,
 
8214                     closest = _sqClosestPointOnSegment,
 
8217                 for (var j = 0, jLen = this._parts.length; j < jLen; j++) {
 
8218                         var points = this._parts[j];
 
8220                         for (var i = 1, len = points.length; i < len; i++) {
 
8224                                 var sqDist = closest(p, p1, p2, true);
 
8226                                 if (sqDist < minDistance) {
 
8227                                         minDistance = sqDist;
 
8228                                         minPoint = closest(p, p1, p2);
 
8233                         minPoint.distance = Math.sqrt(minDistance);
 
8238         // @method getCenter(): LatLng
 
8239         // Returns the center ([centroid](http://en.wikipedia.org/wiki/Centroid)) of the polyline.
 
8240         getCenter: function () {
 
8241                 // throws error when not yet added to map as this center calculation requires projected coordinates
 
8243                         throw new Error('Must add layer to map before using getCenter()');
 
8246                 var i, halfDist, segDist, dist, p1, p2, ratio,
 
8247                     points = this._rings[0],
 
8248                     len = points.length;
 
8250                 if (!len) { return null; }
 
8252                 // polyline centroid algorithm; only uses the first ring if there are multiple
 
8254                 for (i = 0, halfDist = 0; i < len - 1; i++) {
 
8255                         halfDist += points[i].distanceTo(points[i + 1]) / 2;
 
8258                 // The line is so small in the current view that all points are on the same pixel.
 
8259                 if (halfDist === 0) {
 
8260                         return this._map.layerPointToLatLng(points[0]);
 
8263                 for (i = 0, dist = 0; i < len - 1; i++) {
 
8266                         segDist = p1.distanceTo(p2);
 
8269                         if (dist > halfDist) {
 
8270                                 ratio = (dist - halfDist) / segDist;
 
8271                                 return this._map.layerPointToLatLng([
 
8272                                         p2.x - ratio * (p2.x - p1.x),
 
8273                                         p2.y - ratio * (p2.y - p1.y)
 
8279         // @method getBounds(): LatLngBounds
 
8280         // Returns the `LatLngBounds` of the path.
 
8281         getBounds: function () {
 
8282                 return this._bounds;
 
8285         // @method addLatLng(latlng: LatLng, latlngs? LatLng[]): this
 
8286         // Adds a given point to the polyline. By default, adds to the first ring of
 
8287         // the polyline in case of a multi-polyline, but can be overridden by passing
 
8288         // a specific ring as a LatLng array (that you can earlier access with [`getLatLngs`](#polyline-getlatlngs)).
 
8289         addLatLng: function (latlng, latlngs) {
 
8290                 latlngs = latlngs || this._defaultShape();
 
8291                 latlng = toLatLng(latlng);
 
8292                 latlngs.push(latlng);
 
8293                 this._bounds.extend(latlng);
 
8294                 return this.redraw();
 
8297         _setLatLngs: function (latlngs) {
 
8298                 this._bounds = new LatLngBounds();
 
8299                 this._latlngs = this._convertLatLngs(latlngs);
 
8302         _defaultShape: function () {
 
8303                 return isFlat(this._latlngs) ? this._latlngs : this._latlngs[0];
 
8306         // recursively convert latlngs input into actual LatLng instances; calculate bounds along the way
 
8307         _convertLatLngs: function (latlngs) {
 
8309                     flat = isFlat(latlngs);
 
8311                 for (var i = 0, len = latlngs.length; i < len; i++) {
 
8313                                 result[i] = toLatLng(latlngs[i]);
 
8314                                 this._bounds.extend(result[i]);
 
8316                                 result[i] = this._convertLatLngs(latlngs[i]);
 
8323         _project: function () {
 
8324                 var pxBounds = new Bounds();
 
8326                 this._projectLatlngs(this._latlngs, this._rings, pxBounds);
 
8328                 if (this._bounds.isValid() && pxBounds.isValid()) {
 
8329                         this._rawPxBounds = pxBounds;
 
8330                         this._updateBounds();
 
8334         _updateBounds: function () {
 
8335                 var w = this._clickTolerance(),
 
8336                     p = new Point(w, w);
 
8337                 this._pxBounds = new Bounds([
 
8338                         this._rawPxBounds.min.subtract(p),
 
8339                         this._rawPxBounds.max.add(p)
 
8343         // recursively turns latlngs into a set of rings with projected coordinates
 
8344         _projectLatlngs: function (latlngs, result, projectedBounds) {
 
8345                 var flat = latlngs[0] instanceof LatLng,
 
8346                     len = latlngs.length,
 
8351                         for (i = 0; i < len; i++) {
 
8352                                 ring[i] = this._map.latLngToLayerPoint(latlngs[i]);
 
8353                                 projectedBounds.extend(ring[i]);
 
8357                         for (i = 0; i < len; i++) {
 
8358                                 this._projectLatlngs(latlngs[i], result, projectedBounds);
 
8363         // clip polyline by renderer bounds so that we have less to render for performance
 
8364         _clipPoints: function () {
 
8365                 var bounds = this._renderer._bounds;
 
8368                 if (!this._pxBounds || !this._pxBounds.intersects(bounds)) {
 
8372                 if (this.options.noClip) {
 
8373                         this._parts = this._rings;
 
8377                 var parts = this._parts,
 
8378                     i, j, k, len, len2, segment, points;
 
8380                 for (i = 0, k = 0, len = this._rings.length; i < len; i++) {
 
8381                         points = this._rings[i];
 
8383                         for (j = 0, len2 = points.length; j < len2 - 1; j++) {
 
8384                                 segment = clipSegment(points[j], points[j + 1], bounds, j, true);
 
8386                                 if (!segment) { continue; }
 
8388                                 parts[k] = parts[k] || [];
 
8389                                 parts[k].push(segment[0]);
 
8391                                 // if segment goes out of screen, or it's the last one, it's the end of the line part
 
8392                                 if ((segment[1] !== points[j + 1]) || (j === len2 - 2)) {
 
8393                                         parts[k].push(segment[1]);
 
8400         // simplify each clipped part of the polyline for performance
 
8401         _simplifyPoints: function () {
 
8402                 var parts = this._parts,
 
8403                     tolerance = this.options.smoothFactor;
 
8405                 for (var i = 0, len = parts.length; i < len; i++) {
 
8406                         parts[i] = simplify(parts[i], tolerance);
 
8410         _update: function () {
 
8411                 if (!this._map) { return; }
 
8414                 this._simplifyPoints();
 
8418         _updatePath: function () {
 
8419                 this._renderer._updatePoly(this);
 
8422         // Needed by the `Canvas` renderer for interactivity
 
8423         _containsPoint: function (p, closed) {
 
8424                 var i, j, k, len, len2, part,
 
8425                     w = this._clickTolerance();
 
8427                 if (!this._pxBounds || !this._pxBounds.contains(p)) { return false; }
 
8429                 // hit detection for polylines
 
8430                 for (i = 0, len = this._parts.length; i < len; i++) {
 
8431                         part = this._parts[i];
 
8433                         for (j = 0, len2 = part.length, k = len2 - 1; j < len2; k = j++) {
 
8434                                 if (!closed && (j === 0)) { continue; }
 
8436                                 if (pointToSegmentDistance(p, part[k], part[j]) <= w) {
 
8445 // @factory L.polyline(latlngs: LatLng[], options?: Polyline options)
 
8446 // Instantiates a polyline object given an array of geographical points and
 
8447 // optionally an options object. You can create a `Polyline` object with
 
8448 // multiple separate lines (`MultiPolyline`) by passing an array of arrays
 
8449 // of geographic points.
 
8450 function polyline(latlngs, options) {
 
8451         return new Polyline(latlngs, options);
 
8454 // Retrocompat. Allow plugins to support Leaflet versions before and after 1.1.
 
8455 Polyline._flat = _flat;
 
8460  * @inherits Polyline
 
8462  * A class for drawing polygon overlays on a map. Extends `Polyline`.
 
8464  * Note that points you pass when creating a polygon shouldn't have an additional last point equal to the first one — it's better to filter out such points.
 
8470  * // create a red polygon from an array of LatLng points
 
8471  * var latlngs = [[37, -109.05],[41, -109.03],[41, -102.05],[37, -102.04]];
 
8473  * var polygon = L.polygon(latlngs, {color: 'red'}).addTo(map);
 
8475  * // zoom the map to the polygon
 
8476  * map.fitBounds(polygon.getBounds());
 
8479  * You can also pass an array of arrays of latlngs, with the first array representing the outer shape and the other arrays representing holes in the outer shape:
 
8483  *   [[37, -109.05],[41, -109.03],[41, -102.05],[37, -102.04]], // outer ring
 
8484  *   [[37.29, -108.58],[40.71, -108.58],[40.71, -102.50],[37.29, -102.50]] // hole
 
8488  * Additionally, you can pass a multi-dimensional array to represent a MultiPolygon shape.
 
8492  *   [ // first polygon
 
8493  *     [[37, -109.05],[41, -109.03],[41, -102.05],[37, -102.04]], // outer ring
 
8494  *     [[37.29, -108.58],[40.71, -108.58],[40.71, -102.50],[37.29, -102.50]] // hole
 
8496  *   [ // second polygon
 
8497  *     [[41, -111.03],[45, -111.04],[45, -104.05],[41, -104.05]]
 
8503 var Polygon = Polyline.extend({
 
8509         isEmpty: function () {
 
8510                 return !this._latlngs.length || !this._latlngs[0].length;
 
8513         getCenter: function () {
 
8514                 // throws error when not yet added to map as this center calculation requires projected coordinates
 
8516                         throw new Error('Must add layer to map before using getCenter()');
 
8519                 var i, j, p1, p2, f, area, x, y, center,
 
8520                     points = this._rings[0],
 
8521                     len = points.length;
 
8523                 if (!len) { return null; }
 
8525                 // polygon centroid algorithm; only uses the first ring if there are multiple
 
8529                 for (i = 0, j = len - 1; i < len; j = i++) {
 
8533                         f = p1.y * p2.x - p2.y * p1.x;
 
8534                         x += (p1.x + p2.x) * f;
 
8535                         y += (p1.y + p2.y) * f;
 
8540                         // Polygon is so small that all points are on same pixel.
 
8543                         center = [x / area, y / area];
 
8545                 return this._map.layerPointToLatLng(center);
 
8548         _convertLatLngs: function (latlngs) {
 
8549                 var result = Polyline.prototype._convertLatLngs.call(this, latlngs),
 
8550                     len = result.length;
 
8552                 // remove last point if it equals first one
 
8553                 if (len >= 2 && result[0] instanceof LatLng && result[0].equals(result[len - 1])) {
 
8559         _setLatLngs: function (latlngs) {
 
8560                 Polyline.prototype._setLatLngs.call(this, latlngs);
 
8561                 if (isFlat(this._latlngs)) {
 
8562                         this._latlngs = [this._latlngs];
 
8566         _defaultShape: function () {
 
8567                 return isFlat(this._latlngs[0]) ? this._latlngs[0] : this._latlngs[0][0];
 
8570         _clipPoints: function () {
 
8571                 // polygons need a different clipping algorithm so we redefine that
 
8573                 var bounds = this._renderer._bounds,
 
8574                     w = this.options.weight,
 
8575                     p = new Point(w, w);
 
8577                 // increase clip padding by stroke width to avoid stroke on clip edges
 
8578                 bounds = new Bounds(bounds.min.subtract(p), bounds.max.add(p));
 
8581                 if (!this._pxBounds || !this._pxBounds.intersects(bounds)) {
 
8585                 if (this.options.noClip) {
 
8586                         this._parts = this._rings;
 
8590                 for (var i = 0, len = this._rings.length, clipped; i < len; i++) {
 
8591                         clipped = clipPolygon(this._rings[i], bounds, true);
 
8592                         if (clipped.length) {
 
8593                                 this._parts.push(clipped);
 
8598         _updatePath: function () {
 
8599                 this._renderer._updatePoly(this, true);
 
8602         // Needed by the `Canvas` renderer for interactivity
 
8603         _containsPoint: function (p) {
 
8605                     part, p1, p2, i, j, k, len, len2;
 
8607                 if (!this._pxBounds || !this._pxBounds.contains(p)) { return false; }
 
8609                 // ray casting algorithm for detecting if point is in polygon
 
8610                 for (i = 0, len = this._parts.length; i < len; i++) {
 
8611                         part = this._parts[i];
 
8613                         for (j = 0, len2 = part.length, k = len2 - 1; j < len2; k = j++) {
 
8617                                 if (((p1.y > p.y) !== (p2.y > p.y)) && (p.x < (p2.x - p1.x) * (p.y - p1.y) / (p2.y - p1.y) + p1.x)) {
 
8623                 // also check if it's on polygon stroke
 
8624                 return inside || Polyline.prototype._containsPoint.call(this, p, true);
 
8630 // @factory L.polygon(latlngs: LatLng[], options?: Polyline options)
 
8631 function polygon(latlngs, options) {
 
8632         return new Polygon(latlngs, options);
 
8638  * @inherits FeatureGroup
 
8640  * Represents a GeoJSON object or an array of GeoJSON objects. Allows you to parse
 
8641  * GeoJSON data and display it on the map. Extends `FeatureGroup`.
 
8647  *      style: function (feature) {
 
8648  *              return {color: feature.properties.color};
 
8650  * }).bindPopup(function (layer) {
 
8651  *      return layer.feature.properties.description;
 
8656 var GeoJSON = FeatureGroup.extend({
 
8659          * @aka GeoJSON options
 
8661          * @option pointToLayer: Function = *
 
8662          * A `Function` defining how GeoJSON points spawn Leaflet layers. It is internally
 
8663          * called when data is added, passing the GeoJSON point feature and its `LatLng`.
 
8664          * The default is to spawn a default `Marker`:
 
8666          * function(geoJsonPoint, latlng) {
 
8667          *      return L.marker(latlng);
 
8671          * @option style: Function = *
 
8672          * A `Function` defining the `Path options` for styling GeoJSON lines and polygons,
 
8673          * called internally when data is added.
 
8674          * The default value is to not override any defaults:
 
8676          * function (geoJsonFeature) {
 
8681          * @option onEachFeature: Function = *
 
8682          * A `Function` that will be called once for each created `Feature`, after it has
 
8683          * been created and styled. Useful for attaching events and popups to features.
 
8684          * The default is to do nothing with the newly created layers:
 
8686          * function (feature, layer) {}
 
8689          * @option filter: Function = *
 
8690          * A `Function` that will be used to decide whether to include a feature or not.
 
8691          * The default is to include all features:
 
8693          * function (geoJsonFeature) {
 
8697          * Note: dynamically changing the `filter` option will have effect only on newly
 
8698          * added data. It will _not_ re-evaluate already included features.
 
8700          * @option coordsToLatLng: Function = *
 
8701          * A `Function` that will be used for converting GeoJSON coordinates to `LatLng`s.
 
8702          * The default is the `coordsToLatLng` static method.
 
8704          * @option markersInheritOptions: Boolean = false
 
8705          * Whether default Markers for "Point" type Features inherit from group options.
 
8708         initialize: function (geojson, options) {
 
8709                 setOptions(this, options);
 
8714                         this.addData(geojson);
 
8718         // @method addData( <GeoJSON> data ): this
 
8719         // Adds a GeoJSON object to the layer.
 
8720         addData: function (geojson) {
 
8721                 var features = isArray(geojson) ? geojson : geojson.features,
 
8725                         for (i = 0, len = features.length; i < len; i++) {
 
8726                                 // only add this if geometry or geometries are set and not null
 
8727                                 feature = features[i];
 
8728                                 if (feature.geometries || feature.geometry || feature.features || feature.coordinates) {
 
8729                                         this.addData(feature);
 
8735                 var options = this.options;
 
8737                 if (options.filter && !options.filter(geojson)) { return this; }
 
8739                 var layer = geometryToLayer(geojson, options);
 
8743                 layer.feature = asFeature(geojson);
 
8745                 layer.defaultOptions = layer.options;
 
8746                 this.resetStyle(layer);
 
8748                 if (options.onEachFeature) {
 
8749                         options.onEachFeature(geojson, layer);
 
8752                 return this.addLayer(layer);
 
8755         // @method resetStyle( <Path> layer? ): this
 
8756         // Resets the given vector layer's style to the original GeoJSON style, useful for resetting style after hover events.
 
8757         // If `layer` is omitted, the style of all features in the current layer is reset.
 
8758         resetStyle: function (layer) {
 
8759                 if (layer === undefined) {
 
8760                         return this.eachLayer(this.resetStyle, this);
 
8762                 // reset any custom styles
 
8763                 layer.options = extend({}, layer.defaultOptions);
 
8764                 this._setLayerStyle(layer, this.options.style);
 
8768         // @method setStyle( <Function> style ): this
 
8769         // Changes styles of GeoJSON vector layers with the given style function.
 
8770         setStyle: function (style) {
 
8771                 return this.eachLayer(function (layer) {
 
8772                         this._setLayerStyle(layer, style);
 
8776         _setLayerStyle: function (layer, style) {
 
8777                 if (layer.setStyle) {
 
8778                         if (typeof style === 'function') {
 
8779                                 style = style(layer.feature);
 
8781                         layer.setStyle(style);
 
8787 // There are several static functions which can be called without instantiating L.GeoJSON:
 
8789 // @function geometryToLayer(featureData: Object, options?: GeoJSON options): Layer
 
8790 // Creates a `Layer` from a given GeoJSON feature. Can use a custom
 
8791 // [`pointToLayer`](#geojson-pointtolayer) and/or [`coordsToLatLng`](#geojson-coordstolatlng)
 
8792 // functions if provided as options.
 
8793 function geometryToLayer(geojson, options) {
 
8795         var geometry = geojson.type === 'Feature' ? geojson.geometry : geojson,
 
8796             coords = geometry ? geometry.coordinates : null,
 
8798             pointToLayer = options && options.pointToLayer,
 
8799             _coordsToLatLng = options && options.coordsToLatLng || coordsToLatLng,
 
8800             latlng, latlngs, i, len;
 
8802         if (!coords && !geometry) {
 
8806         switch (geometry.type) {
 
8808                 latlng = _coordsToLatLng(coords);
 
8809                 return _pointToLayer(pointToLayer, geojson, latlng, options);
 
8812                 for (i = 0, len = coords.length; i < len; i++) {
 
8813                         latlng = _coordsToLatLng(coords[i]);
 
8814                         layers.push(_pointToLayer(pointToLayer, geojson, latlng, options));
 
8816                 return new FeatureGroup(layers);
 
8819         case 'MultiLineString':
 
8820                 latlngs = coordsToLatLngs(coords, geometry.type === 'LineString' ? 0 : 1, _coordsToLatLng);
 
8821                 return new Polyline(latlngs, options);
 
8824         case 'MultiPolygon':
 
8825                 latlngs = coordsToLatLngs(coords, geometry.type === 'Polygon' ? 1 : 2, _coordsToLatLng);
 
8826                 return new Polygon(latlngs, options);
 
8828         case 'GeometryCollection':
 
8829                 for (i = 0, len = geometry.geometries.length; i < len; i++) {
 
8830                         var layer = geometryToLayer({
 
8831                                 geometry: geometry.geometries[i],
 
8833                                 properties: geojson.properties
 
8840                 return new FeatureGroup(layers);
 
8843                 throw new Error('Invalid GeoJSON object.');
 
8847 function _pointToLayer(pointToLayerFn, geojson, latlng, options) {
 
8848         return pointToLayerFn ?
 
8849                 pointToLayerFn(geojson, latlng) :
 
8850                 new Marker(latlng, options && options.markersInheritOptions && options);
 
8853 // @function coordsToLatLng(coords: Array): LatLng
 
8854 // Creates a `LatLng` object from an array of 2 numbers (longitude, latitude)
 
8855 // or 3 numbers (longitude, latitude, altitude) used in GeoJSON for points.
 
8856 function coordsToLatLng(coords) {
 
8857         return new LatLng(coords[1], coords[0], coords[2]);
 
8860 // @function coordsToLatLngs(coords: Array, levelsDeep?: Number, coordsToLatLng?: Function): Array
 
8861 // Creates a multidimensional array of `LatLng`s from a GeoJSON coordinates array.
 
8862 // `levelsDeep` specifies the nesting level (0 is for an array of points, 1 for an array of arrays of points, etc., 0 by default).
 
8863 // Can use a custom [`coordsToLatLng`](#geojson-coordstolatlng) function.
 
8864 function coordsToLatLngs(coords, levelsDeep, _coordsToLatLng) {
 
8867         for (var i = 0, len = coords.length, latlng; i < len; i++) {
 
8868                 latlng = levelsDeep ?
 
8869                         coordsToLatLngs(coords[i], levelsDeep - 1, _coordsToLatLng) :
 
8870                         (_coordsToLatLng || coordsToLatLng)(coords[i]);
 
8872                 latlngs.push(latlng);
 
8878 // @function latLngToCoords(latlng: LatLng, precision?: Number): Array
 
8879 // Reverse of [`coordsToLatLng`](#geojson-coordstolatlng)
 
8880 function latLngToCoords(latlng, precision) {
 
8881         precision = typeof precision === 'number' ? precision : 6;
 
8882         return latlng.alt !== undefined ?
 
8883                 [formatNum(latlng.lng, precision), formatNum(latlng.lat, precision), formatNum(latlng.alt, precision)] :
 
8884                 [formatNum(latlng.lng, precision), formatNum(latlng.lat, precision)];
 
8887 // @function latLngsToCoords(latlngs: Array, levelsDeep?: Number, closed?: Boolean): Array
 
8888 // Reverse of [`coordsToLatLngs`](#geojson-coordstolatlngs)
 
8889 // `closed` determines whether the first point should be appended to the end of the array to close the feature, only used when `levelsDeep` is 0. False by default.
 
8890 function latLngsToCoords(latlngs, levelsDeep, closed, precision) {
 
8893         for (var i = 0, len = latlngs.length; i < len; i++) {
 
8894                 coords.push(levelsDeep ?
 
8895                         latLngsToCoords(latlngs[i], levelsDeep - 1, closed, precision) :
 
8896                         latLngToCoords(latlngs[i], precision));
 
8899         if (!levelsDeep && closed) {
 
8900                 coords.push(coords[0]);
 
8906 function getFeature(layer, newGeometry) {
 
8907         return layer.feature ?
 
8908                 extend({}, layer.feature, {geometry: newGeometry}) :
 
8909                 asFeature(newGeometry);
 
8912 // @function asFeature(geojson: Object): Object
 
8913 // Normalize GeoJSON geometries/features into GeoJSON features.
 
8914 function asFeature(geojson) {
 
8915         if (geojson.type === 'Feature' || geojson.type === 'FeatureCollection') {
 
8926 var PointToGeoJSON = {
 
8927         toGeoJSON: function (precision) {
 
8928                 return getFeature(this, {
 
8930                         coordinates: latLngToCoords(this.getLatLng(), precision)
 
8935 // @namespace Marker
 
8936 // @section Other methods
 
8937 // @method toGeoJSON(precision?: Number): Object
 
8938 // `precision` is the number of decimal places for coordinates.
 
8939 // The default value is 6 places.
 
8940 // Returns a [`GeoJSON`](http://en.wikipedia.org/wiki/GeoJSON) representation of the marker (as a GeoJSON `Point` Feature).
 
8941 Marker.include(PointToGeoJSON);
 
8943 // @namespace CircleMarker
 
8944 // @method toGeoJSON(precision?: Number): Object
 
8945 // `precision` is the number of decimal places for coordinates.
 
8946 // The default value is 6 places.
 
8947 // Returns a [`GeoJSON`](http://en.wikipedia.org/wiki/GeoJSON) representation of the circle marker (as a GeoJSON `Point` Feature).
 
8948 Circle.include(PointToGeoJSON);
 
8949 CircleMarker.include(PointToGeoJSON);
 
8952 // @namespace Polyline
 
8953 // @method toGeoJSON(precision?: Number): Object
 
8954 // `precision` is the number of decimal places for coordinates.
 
8955 // The default value is 6 places.
 
8956 // Returns a [`GeoJSON`](http://en.wikipedia.org/wiki/GeoJSON) representation of the polyline (as a GeoJSON `LineString` or `MultiLineString` Feature).
 
8958         toGeoJSON: function (precision) {
 
8959                 var multi = !isFlat(this._latlngs);
 
8961                 var coords = latLngsToCoords(this._latlngs, multi ? 1 : 0, false, precision);
 
8963                 return getFeature(this, {
 
8964                         type: (multi ? 'Multi' : '') + 'LineString',
 
8970 // @namespace Polygon
 
8971 // @method toGeoJSON(precision?: Number): Object
 
8972 // `precision` is the number of decimal places for coordinates.
 
8973 // The default value is 6 places.
 
8974 // Returns a [`GeoJSON`](http://en.wikipedia.org/wiki/GeoJSON) representation of the polygon (as a GeoJSON `Polygon` or `MultiPolygon` Feature).
 
8976         toGeoJSON: function (precision) {
 
8977                 var holes = !isFlat(this._latlngs),
 
8978                     multi = holes && !isFlat(this._latlngs[0]);
 
8980                 var coords = latLngsToCoords(this._latlngs, multi ? 2 : holes ? 1 : 0, true, precision);
 
8986                 return getFeature(this, {
 
8987                         type: (multi ? 'Multi' : '') + 'Polygon',
 
8994 // @namespace LayerGroup
 
8995 LayerGroup.include({
 
8996         toMultiPoint: function (precision) {
 
8999                 this.eachLayer(function (layer) {
 
9000                         coords.push(layer.toGeoJSON(precision).geometry.coordinates);
 
9003                 return getFeature(this, {
 
9009         // @method toGeoJSON(precision?: Number): Object
 
9010         // `precision` is the number of decimal places for coordinates.
 
9011         // The default value is 6 places.
 
9012         // Returns a [`GeoJSON`](http://en.wikipedia.org/wiki/GeoJSON) representation of the layer group (as a GeoJSON `FeatureCollection`, `GeometryCollection`, or `MultiPoint`).
 
9013         toGeoJSON: function (precision) {
 
9015                 var type = this.feature && this.feature.geometry && this.feature.geometry.type;
 
9017                 if (type === 'MultiPoint') {
 
9018                         return this.toMultiPoint(precision);
 
9021                 var isGeometryCollection = type === 'GeometryCollection',
 
9024                 this.eachLayer(function (layer) {
 
9025                         if (layer.toGeoJSON) {
 
9026                                 var json = layer.toGeoJSON(precision);
 
9027                                 if (isGeometryCollection) {
 
9028                                         jsons.push(json.geometry);
 
9030                                         var feature = asFeature(json);
 
9031                                         // Squash nested feature collections
 
9032                                         if (feature.type === 'FeatureCollection') {
 
9033                                                 jsons.push.apply(jsons, feature.features);
 
9035                                                 jsons.push(feature);
 
9041                 if (isGeometryCollection) {
 
9042                         return getFeature(this, {
 
9044                                 type: 'GeometryCollection'
 
9049                         type: 'FeatureCollection',
 
9055 // @namespace GeoJSON
 
9056 // @factory L.geoJSON(geojson?: Object, options?: GeoJSON options)
 
9057 // Creates a GeoJSON layer. Optionally accepts an object in
 
9058 // [GeoJSON format](https://tools.ietf.org/html/rfc7946) to display on the map
 
9059 // (you can alternatively add it later with `addData` method) and an `options` object.
 
9060 function geoJSON(geojson, options) {
 
9061         return new GeoJSON(geojson, options);
 
9064 // Backward compatibility.
 
9065 var geoJson = geoJSON;
 
9068  * @class ImageOverlay
 
9069  * @aka L.ImageOverlay
 
9070  * @inherits Interactive layer
 
9072  * Used to load and display a single image over specific bounds of the map. Extends `Layer`.
 
9077  * var imageUrl = 'http://www.lib.utexas.edu/maps/historical/newark_nj_1922.jpg',
 
9078  *      imageBounds = [[40.712216, -74.22655], [40.773941, -74.12544]];
 
9079  * L.imageOverlay(imageUrl, imageBounds).addTo(map);
 
9083 var ImageOverlay = Layer.extend({
 
9086         // @aka ImageOverlay options
 
9088                 // @option opacity: Number = 1.0
 
9089                 // The opacity of the image overlay.
 
9092                 // @option alt: String = ''
 
9093                 // Text for the `alt` attribute of the image (useful for accessibility).
 
9096                 // @option interactive: Boolean = false
 
9097                 // If `true`, the image overlay will emit [mouse events](#interactive-layer) when clicked or hovered.
 
9100                 // @option crossOrigin: Boolean|String = false
 
9101                 // Whether the crossOrigin attribute will be added to the image.
 
9102                 // If a String is provided, the image will have its crossOrigin attribute set to the String provided. This is needed if you want to access image pixel data.
 
9103                 // Refer to [CORS Settings](https://developer.mozilla.org/en-US/docs/Web/HTML/CORS_settings_attributes) for valid String values.
 
9106                 // @option errorOverlayUrl: String = ''
 
9107                 // URL to the overlay image to show in place of the overlay that failed to load.
 
9108                 errorOverlayUrl: '',
 
9110                 // @option zIndex: Number = 1
 
9111                 // The explicit [zIndex](https://developer.mozilla.org/docs/Web/CSS/CSS_Positioning/Understanding_z_index) of the overlay layer.
 
9114                 // @option className: String = ''
 
9115                 // A custom class name to assign to the image. Empty by default.
 
9119         initialize: function (url, bounds, options) { // (String, LatLngBounds, Object)
 
9121                 this._bounds = toLatLngBounds(bounds);
 
9123                 setOptions(this, options);
 
9126         onAdd: function () {
 
9130                         if (this.options.opacity < 1) {
 
9131                                 this._updateOpacity();
 
9135                 if (this.options.interactive) {
 
9136                         addClass(this._image, 'leaflet-interactive');
 
9137                         this.addInteractiveTarget(this._image);
 
9140                 this.getPane().appendChild(this._image);
 
9144         onRemove: function () {
 
9145                 remove(this._image);
 
9146                 if (this.options.interactive) {
 
9147                         this.removeInteractiveTarget(this._image);
 
9151         // @method setOpacity(opacity: Number): this
 
9152         // Sets the opacity of the overlay.
 
9153         setOpacity: function (opacity) {
 
9154                 this.options.opacity = opacity;
 
9157                         this._updateOpacity();
 
9162         setStyle: function (styleOpts) {
 
9163                 if (styleOpts.opacity) {
 
9164                         this.setOpacity(styleOpts.opacity);
 
9169         // @method bringToFront(): this
 
9170         // Brings the layer to the top of all overlays.
 
9171         bringToFront: function () {
 
9173                         toFront(this._image);
 
9178         // @method bringToBack(): this
 
9179         // Brings the layer to the bottom of all overlays.
 
9180         bringToBack: function () {
 
9182                         toBack(this._image);
 
9187         // @method setUrl(url: String): this
 
9188         // Changes the URL of the image.
 
9189         setUrl: function (url) {
 
9193                         this._image.src = url;
 
9198         // @method setBounds(bounds: LatLngBounds): this
 
9199         // Update the bounds that this ImageOverlay covers
 
9200         setBounds: function (bounds) {
 
9201                 this._bounds = toLatLngBounds(bounds);
 
9209         getEvents: function () {
 
9212                         viewreset: this._reset
 
9215                 if (this._zoomAnimated) {
 
9216                         events.zoomanim = this._animateZoom;
 
9222         // @method setZIndex(value: Number): this
 
9223         // Changes the [zIndex](#imageoverlay-zindex) of the image overlay.
 
9224         setZIndex: function (value) {
 
9225                 this.options.zIndex = value;
 
9226                 this._updateZIndex();
 
9230         // @method getBounds(): LatLngBounds
 
9231         // Get the bounds that this ImageOverlay covers
 
9232         getBounds: function () {
 
9233                 return this._bounds;
 
9236         // @method getElement(): HTMLElement
 
9237         // Returns the instance of [`HTMLImageElement`](https://developer.mozilla.org/docs/Web/API/HTMLImageElement)
 
9238         // used by this overlay.
 
9239         getElement: function () {
 
9243         _initImage: function () {
 
9244                 var wasElementSupplied = this._url.tagName === 'IMG';
 
9245                 var img = this._image = wasElementSupplied ? this._url : create$1('img');
 
9247                 addClass(img, 'leaflet-image-layer');
 
9248                 if (this._zoomAnimated) { addClass(img, 'leaflet-zoom-animated'); }
 
9249                 if (this.options.className) { addClass(img, this.options.className); }
 
9251                 img.onselectstart = falseFn;
 
9252                 img.onmousemove = falseFn;
 
9254                 // @event load: Event
 
9255                 // Fired when the ImageOverlay layer has loaded its image
 
9256                 img.onload = bind(this.fire, this, 'load');
 
9257                 img.onerror = bind(this._overlayOnError, this, 'error');
 
9259                 if (this.options.crossOrigin || this.options.crossOrigin === '') {
 
9260                         img.crossOrigin = this.options.crossOrigin === true ? '' : this.options.crossOrigin;
 
9263                 if (this.options.zIndex) {
 
9264                         this._updateZIndex();
 
9267                 if (wasElementSupplied) {
 
9268                         this._url = img.src;
 
9272                 img.src = this._url;
 
9273                 img.alt = this.options.alt;
 
9276         _animateZoom: function (e) {
 
9277                 var scale = this._map.getZoomScale(e.zoom),
 
9278                     offset = this._map._latLngBoundsToNewLayerBounds(this._bounds, e.zoom, e.center).min;
 
9280                 setTransform(this._image, offset, scale);
 
9283         _reset: function () {
 
9284                 var image = this._image,
 
9285                     bounds = new Bounds(
 
9286                         this._map.latLngToLayerPoint(this._bounds.getNorthWest()),
 
9287                         this._map.latLngToLayerPoint(this._bounds.getSouthEast())),
 
9288                     size = bounds.getSize();
 
9290                 setPosition(image, bounds.min);
 
9292                 image.style.width  = size.x + 'px';
 
9293                 image.style.height = size.y + 'px';
 
9296         _updateOpacity: function () {
 
9297                 setOpacity(this._image, this.options.opacity);
 
9300         _updateZIndex: function () {
 
9301                 if (this._image && this.options.zIndex !== undefined && this.options.zIndex !== null) {
 
9302                         this._image.style.zIndex = this.options.zIndex;
 
9306         _overlayOnError: function () {
 
9307                 // @event error: Event
 
9308                 // Fired when the ImageOverlay layer fails to load its image
 
9311                 var errorUrl = this.options.errorOverlayUrl;
 
9312                 if (errorUrl && this._url !== errorUrl) {
 
9313                         this._url = errorUrl;
 
9314                         this._image.src = errorUrl;
 
9319 // @factory L.imageOverlay(imageUrl: String, bounds: LatLngBounds, options?: ImageOverlay options)
 
9320 // Instantiates an image overlay object given the URL of the image and the
 
9321 // geographical bounds it is tied to.
 
9322 var imageOverlay = function (url, bounds, options) {
 
9323         return new ImageOverlay(url, bounds, options);
 
9327  * @class VideoOverlay
 
9328  * @aka L.VideoOverlay
 
9329  * @inherits ImageOverlay
 
9331  * Used to load and display a video player over specific bounds of the map. Extends `ImageOverlay`.
 
9333  * A video overlay uses the [`<video>`](https://developer.mozilla.org/docs/Web/HTML/Element/video)
 
9339  * var videoUrl = 'https://www.mapbox.com/bites/00188/patricia_nasa.webm',
 
9340  *      videoBounds = [[ 32, -130], [ 13, -100]];
 
9341  * L.videoOverlay(videoUrl, videoBounds ).addTo(map);
 
9345 var VideoOverlay = ImageOverlay.extend({
 
9348         // @aka VideoOverlay options
 
9350                 // @option autoplay: Boolean = true
 
9351                 // Whether the video starts playing automatically when loaded.
 
9354                 // @option loop: Boolean = true
 
9355                 // Whether the video will loop back to the beginning when played.
 
9358                 // @option keepAspectRatio: Boolean = true
 
9359                 // Whether the video will save aspect ratio after the projection.
 
9360                 // Relevant for supported browsers. Browser compatibility- https://developer.mozilla.org/en-US/docs/Web/CSS/object-fit
 
9361                 keepAspectRatio: true
 
9364         _initImage: function () {
 
9365                 var wasElementSupplied = this._url.tagName === 'VIDEO';
 
9366                 var vid = this._image = wasElementSupplied ? this._url : create$1('video');
 
9368                 addClass(vid, 'leaflet-image-layer');
 
9369                 if (this._zoomAnimated) { addClass(vid, 'leaflet-zoom-animated'); }
 
9370                 if (this.options.className) { addClass(vid, this.options.className); }
 
9372                 vid.onselectstart = falseFn;
 
9373                 vid.onmousemove = falseFn;
 
9375                 // @event load: Event
 
9376                 // Fired when the video has finished loading the first frame
 
9377                 vid.onloadeddata = bind(this.fire, this, 'load');
 
9379                 if (wasElementSupplied) {
 
9380                         var sourceElements = vid.getElementsByTagName('source');
 
9382                         for (var j = 0; j < sourceElements.length; j++) {
 
9383                                 sources.push(sourceElements[j].src);
 
9386                         this._url = (sourceElements.length > 0) ? sources : [vid.src];
 
9390                 if (!isArray(this._url)) { this._url = [this._url]; }
 
9392                 if (!this.options.keepAspectRatio && vid.style.hasOwnProperty('objectFit')) { vid.style['objectFit'] = 'fill'; }
 
9393                 vid.autoplay = !!this.options.autoplay;
 
9394                 vid.loop = !!this.options.loop;
 
9395                 for (var i = 0; i < this._url.length; i++) {
 
9396                         var source = create$1('source');
 
9397                         source.src = this._url[i];
 
9398                         vid.appendChild(source);
 
9402         // @method getElement(): HTMLVideoElement
 
9403         // Returns the instance of [`HTMLVideoElement`](https://developer.mozilla.org/docs/Web/API/HTMLVideoElement)
 
9404         // used by this overlay.
 
9408 // @factory L.videoOverlay(video: String|Array|HTMLVideoElement, bounds: LatLngBounds, options?: VideoOverlay options)
 
9409 // Instantiates an image overlay object given the URL of the video (or array of URLs, or even a video element) and the
 
9410 // geographical bounds it is tied to.
 
9412 function videoOverlay(video, bounds, options) {
 
9413         return new VideoOverlay(video, bounds, options);
 
9419  * @inherits ImageOverlay
 
9421  * Used to load, display and provide DOM access to an SVG file over specific bounds of the map. Extends `ImageOverlay`.
 
9423  * An SVG overlay uses the [`<svg>`](https://developer.mozilla.org/docs/Web/SVG/Element/svg) element.
 
9428  * var svgElement = document.createElementNS("http://www.w3.org/2000/svg", "svg");
 
9429  * svgElement.setAttribute('xmlns', "http://www.w3.org/2000/svg");
 
9430  * svgElement.setAttribute('viewBox', "0 0 200 200");
 
9431  * svgElement.innerHTML = '<rect width="200" height="200"/><rect x="75" y="23" width="50" height="50" style="fill:red"/><rect x="75" y="123" width="50" height="50" style="fill:#0013ff"/>';
 
9432  * var svgElementBounds = [ [ 32, -130 ], [ 13, -100 ] ];
 
9433  * L.svgOverlay(svgElement, svgElementBounds).addTo(map);
 
9437 var SVGOverlay = ImageOverlay.extend({
 
9438         _initImage: function () {
 
9439                 var el = this._image = this._url;
 
9441                 addClass(el, 'leaflet-image-layer');
 
9442                 if (this._zoomAnimated) { addClass(el, 'leaflet-zoom-animated'); }
 
9443                 if (this.options.className) { addClass(el, this.options.className); }
 
9445                 el.onselectstart = falseFn;
 
9446                 el.onmousemove = falseFn;
 
9449         // @method getElement(): SVGElement
 
9450         // Returns the instance of [`SVGElement`](https://developer.mozilla.org/docs/Web/API/SVGElement)
 
9451         // used by this overlay.
 
9455 // @factory L.svgOverlay(svg: String|SVGElement, bounds: LatLngBounds, options?: SVGOverlay options)
 
9456 // Instantiates an image overlay object given an SVG element and the geographical bounds it is tied to.
 
9457 // A viewBox attribute is required on the SVG element to zoom in and out properly.
 
9459 function svgOverlay(el, bounds, options) {
 
9460         return new SVGOverlay(el, bounds, options);
 
9467  * Base model for L.Popup and L.Tooltip. Inherit from it for custom popup like plugins.
 
9470 // @namespace DivOverlay
 
9471 var DivOverlay = Layer.extend({
 
9474         // @aka DivOverlay options
 
9476                 // @option offset: Point = Point(0, 7)
 
9477                 // The offset of the popup position. Useful to control the anchor
 
9478                 // of the popup when opening it on some overlays.
 
9481                 // @option className: String = ''
 
9482                 // A custom CSS class name to assign to the popup.
 
9485                 // @option pane: String = 'popupPane'
 
9486                 // `Map pane` where the popup will be added.
 
9490         initialize: function (options, source) {
 
9491                 setOptions(this, options);
 
9493                 this._source = source;
 
9496         onAdd: function (map) {
 
9497                 this._zoomAnimated = map._zoomAnimated;
 
9499                 if (!this._container) {
 
9503                 if (map._fadeAnimated) {
 
9504                         setOpacity(this._container, 0);
 
9507                 clearTimeout(this._removeTimeout);
 
9508                 this.getPane().appendChild(this._container);
 
9511                 if (map._fadeAnimated) {
 
9512                         setOpacity(this._container, 1);
 
9515                 this.bringToFront();
 
9518         onRemove: function (map) {
 
9519                 if (map._fadeAnimated) {
 
9520                         setOpacity(this._container, 0);
 
9521                         this._removeTimeout = setTimeout(bind(remove, undefined, this._container), 200);
 
9523                         remove(this._container);
 
9528         // @method getLatLng: LatLng
 
9529         // Returns the geographical point of popup.
 
9530         getLatLng: function () {
 
9531                 return this._latlng;
 
9534         // @method setLatLng(latlng: LatLng): this
 
9535         // Sets the geographical point where the popup will open.
 
9536         setLatLng: function (latlng) {
 
9537                 this._latlng = toLatLng(latlng);
 
9539                         this._updatePosition();
 
9545         // @method getContent: String|HTMLElement
 
9546         // Returns the content of the popup.
 
9547         getContent: function () {
 
9548                 return this._content;
 
9551         // @method setContent(htmlContent: String|HTMLElement|Function): this
 
9552         // 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.
 
9553         setContent: function (content) {
 
9554                 this._content = content;
 
9559         // @method getElement: String|HTMLElement
 
9560         // Alias for [getContent()](#popup-getcontent)
 
9561         getElement: function () {
 
9562                 return this._container;
 
9565         // @method update: null
 
9566         // Updates the popup content, layout and position. Useful for updating the popup after something inside changed, e.g. image loaded.
 
9567         update: function () {
 
9568                 if (!this._map) { return; }
 
9570                 this._container.style.visibility = 'hidden';
 
9572                 this._updateContent();
 
9573                 this._updateLayout();
 
9574                 this._updatePosition();
 
9576                 this._container.style.visibility = '';
 
9581         getEvents: function () {
 
9583                         zoom: this._updatePosition,
 
9584                         viewreset: this._updatePosition
 
9587                 if (this._zoomAnimated) {
 
9588                         events.zoomanim = this._animateZoom;
 
9593         // @method isOpen: Boolean
 
9594         // Returns `true` when the popup is visible on the map.
 
9595         isOpen: function () {
 
9596                 return !!this._map && this._map.hasLayer(this);
 
9599         // @method bringToFront: this
 
9600         // Brings this popup in front of other popups (in the same map pane).
 
9601         bringToFront: function () {
 
9603                         toFront(this._container);
 
9608         // @method bringToBack: this
 
9609         // Brings this popup to the back of other popups (in the same map pane).
 
9610         bringToBack: function () {
 
9612                         toBack(this._container);
 
9617         _prepareOpen: function (parent, layer, latlng) {
 
9618                 if (!(layer instanceof Layer)) {
 
9623                 if (layer instanceof FeatureGroup) {
 
9624                         for (var id in parent._layers) {
 
9625                                 layer = parent._layers[id];
 
9631                         if (layer.getCenter) {
 
9632                                 latlng = layer.getCenter();
 
9633                         } else if (layer.getLatLng) {
 
9634                                 latlng = layer.getLatLng();
 
9636                                 throw new Error('Unable to get source layer LatLng.');
 
9640                 // set overlay source to this layer
 
9641                 this._source = layer;
 
9643                 // update the overlay (content, layout, ect...)
 
9649         _updateContent: function () {
 
9650                 if (!this._content) { return; }
 
9652                 var node = this._contentNode;
 
9653                 var content = (typeof this._content === 'function') ? this._content(this._source || this) : this._content;
 
9655                 if (typeof content === 'string') {
 
9656                         node.innerHTML = content;
 
9658                         while (node.hasChildNodes()) {
 
9659                                 node.removeChild(node.firstChild);
 
9661                         node.appendChild(content);
 
9663                 this.fire('contentupdate');
 
9666         _updatePosition: function () {
 
9667                 if (!this._map) { return; }
 
9669                 var pos = this._map.latLngToLayerPoint(this._latlng),
 
9670                     offset = toPoint(this.options.offset),
 
9671                     anchor = this._getAnchor();
 
9673                 if (this._zoomAnimated) {
 
9674                         setPosition(this._container, pos.add(anchor));
 
9676                         offset = offset.add(pos).add(anchor);
 
9679                 var bottom = this._containerBottom = -offset.y,
 
9680                     left = this._containerLeft = -Math.round(this._containerWidth / 2) + offset.x;
 
9682                 // bottom position the popup in case the height of the popup changes (images loading etc)
 
9683                 this._container.style.bottom = bottom + 'px';
 
9684                 this._container.style.left = left + 'px';
 
9687         _getAnchor: function () {
 
9695  * @inherits DivOverlay
 
9697  * Used to open popups in certain places of the map. Use [Map.openPopup](#map-openpopup) to
 
9698  * open popups while making sure that only one popup is open at one time
 
9699  * (recommended for usability), or use [Map.addLayer](#map-addlayer) to open as many as you want.
 
9703  * If you want to just bind a popup to marker click and then open it, it's really easy:
 
9706  * marker.bindPopup(popupContent).openPopup();
 
9708  * Path overlays like polylines also have a `bindPopup` method.
 
9709  * Here's a more complicated way to open a popup on a map:
 
9712  * var popup = L.popup()
 
9713  *      .setLatLng(latlng)
 
9714  *      .setContent('<p>Hello world!<br />This is a nice popup.</p>')
 
9721 var Popup = DivOverlay.extend({
 
9724         // @aka Popup options
 
9726                 // @option maxWidth: Number = 300
 
9727                 // Max width of the popup, in pixels.
 
9730                 // @option minWidth: Number = 50
 
9731                 // Min width of the popup, in pixels.
 
9734                 // @option maxHeight: Number = null
 
9735                 // If set, creates a scrollable container of the given height
 
9736                 // inside a popup if its content exceeds it.
 
9739                 // @option autoPan: Boolean = true
 
9740                 // Set it to `false` if you don't want the map to do panning animation
 
9741                 // to fit the opened popup.
 
9744                 // @option autoPanPaddingTopLeft: Point = null
 
9745                 // The margin between the popup and the top left corner of the map
 
9746                 // view after autopanning was performed.
 
9747                 autoPanPaddingTopLeft: null,
 
9749                 // @option autoPanPaddingBottomRight: Point = null
 
9750                 // The margin between the popup and the bottom right corner of the map
 
9751                 // view after autopanning was performed.
 
9752                 autoPanPaddingBottomRight: null,
 
9754                 // @option autoPanPadding: Point = Point(5, 5)
 
9755                 // Equivalent of setting both top left and bottom right autopan padding to the same value.
 
9756                 autoPanPadding: [5, 5],
 
9758                 // @option keepInView: Boolean = false
 
9759                 // Set it to `true` if you want to prevent users from panning the popup
 
9760                 // off of the screen while it is open.
 
9763                 // @option closeButton: Boolean = true
 
9764                 // Controls the presence of a close button in the popup.
 
9767                 // @option autoClose: Boolean = true
 
9768                 // Set it to `false` if you want to override the default behavior of
 
9769                 // the popup closing when another popup is opened.
 
9772                 // @option closeOnEscapeKey: Boolean = true
 
9773                 // Set it to `false` if you want to override the default behavior of
 
9774                 // the ESC key for closing of the popup.
 
9775                 closeOnEscapeKey: true,
 
9777                 // @option closeOnClick: Boolean = *
 
9778                 // Set it if you want to override the default behavior of the popup closing when user clicks
 
9779                 // on the map. Defaults to the map's [`closePopupOnClick`](#map-closepopuponclick) option.
 
9781                 // @option className: String = ''
 
9782                 // A custom CSS class name to assign to the popup.
 
9787         // @method openOn(map: Map): this
 
9788         // Adds the popup to the map and closes the previous one. The same as `map.openPopup(popup)`.
 
9789         openOn: function (map) {
 
9790                 map.openPopup(this);
 
9794         onAdd: function (map) {
 
9795                 DivOverlay.prototype.onAdd.call(this, map);
 
9798                 // @section Popup events
 
9799                 // @event popupopen: PopupEvent
 
9800                 // Fired when a popup is opened in the map
 
9801                 map.fire('popupopen', {popup: this});
 
9805                         // @section Popup events
 
9806                         // @event popupopen: PopupEvent
 
9807                         // Fired when a popup bound to this layer is opened
 
9808                         this._source.fire('popupopen', {popup: this}, true);
 
9809                         // For non-path layers, we toggle the popup when clicking
 
9810                         // again the layer, so prevent the map to reopen it.
 
9811                         if (!(this._source instanceof Path)) {
 
9812                                 this._source.on('preclick', stopPropagation);
 
9817         onRemove: function (map) {
 
9818                 DivOverlay.prototype.onRemove.call(this, map);
 
9821                 // @section Popup events
 
9822                 // @event popupclose: PopupEvent
 
9823                 // Fired when a popup in the map is closed
 
9824                 map.fire('popupclose', {popup: this});
 
9828                         // @section Popup events
 
9829                         // @event popupclose: PopupEvent
 
9830                         // Fired when a popup bound to this layer is closed
 
9831                         this._source.fire('popupclose', {popup: this}, true);
 
9832                         if (!(this._source instanceof Path)) {
 
9833                                 this._source.off('preclick', stopPropagation);
 
9838         getEvents: function () {
 
9839                 var events = DivOverlay.prototype.getEvents.call(this);
 
9841                 if (this.options.closeOnClick !== undefined ? this.options.closeOnClick : this._map.options.closePopupOnClick) {
 
9842                         events.preclick = this._close;
 
9845                 if (this.options.keepInView) {
 
9846                         events.moveend = this._adjustPan;
 
9852         _close: function () {
 
9854                         this._map.closePopup(this);
 
9858         _initLayout: function () {
 
9859                 var prefix = 'leaflet-popup',
 
9860                     container = this._container = create$1('div',
 
9861                         prefix + ' ' + (this.options.className || '') +
 
9862                         ' leaflet-zoom-animated');
 
9864                 var wrapper = this._wrapper = create$1('div', prefix + '-content-wrapper', container);
 
9865                 this._contentNode = create$1('div', prefix + '-content', wrapper);
 
9867                 disableClickPropagation(wrapper);
 
9868                 disableScrollPropagation(this._contentNode);
 
9869                 on(wrapper, 'contextmenu', stopPropagation);
 
9871                 this._tipContainer = create$1('div', prefix + '-tip-container', container);
 
9872                 this._tip = create$1('div', prefix + '-tip', this._tipContainer);
 
9874                 if (this.options.closeButton) {
 
9875                         var closeButton = this._closeButton = create$1('a', prefix + '-close-button', container);
 
9876                         closeButton.href = '#close';
 
9877                         closeButton.innerHTML = '×';
 
9879                         on(closeButton, 'click', this._onCloseButtonClick, this);
 
9883         _updateLayout: function () {
 
9884                 var container = this._contentNode,
 
9885                     style = container.style;
 
9888                 style.whiteSpace = 'nowrap';
 
9890                 var width = container.offsetWidth;
 
9891                 width = Math.min(width, this.options.maxWidth);
 
9892                 width = Math.max(width, this.options.minWidth);
 
9894                 style.width = (width + 1) + 'px';
 
9895                 style.whiteSpace = '';
 
9899                 var height = container.offsetHeight,
 
9900                     maxHeight = this.options.maxHeight,
 
9901                     scrolledClass = 'leaflet-popup-scrolled';
 
9903                 if (maxHeight && height > maxHeight) {
 
9904                         style.height = maxHeight + 'px';
 
9905                         addClass(container, scrolledClass);
 
9907                         removeClass(container, scrolledClass);
 
9910                 this._containerWidth = this._container.offsetWidth;
 
9913         _animateZoom: function (e) {
 
9914                 var pos = this._map._latLngToNewLayerPoint(this._latlng, e.zoom, e.center),
 
9915                     anchor = this._getAnchor();
 
9916                 setPosition(this._container, pos.add(anchor));
 
9919         _adjustPan: function () {
 
9920                 if (!this.options.autoPan) { return; }
 
9921                 if (this._map._panAnim) { this._map._panAnim.stop(); }
 
9923                 var map = this._map,
 
9924                     marginBottom = parseInt(getStyle(this._container, 'marginBottom'), 10) || 0,
 
9925                     containerHeight = this._container.offsetHeight + marginBottom,
 
9926                     containerWidth = this._containerWidth,
 
9927                     layerPos = new Point(this._containerLeft, -containerHeight - this._containerBottom);
 
9929                 layerPos._add(getPosition(this._container));
 
9931                 var containerPos = map.layerPointToContainerPoint(layerPos),
 
9932                     padding = toPoint(this.options.autoPanPadding),
 
9933                     paddingTL = toPoint(this.options.autoPanPaddingTopLeft || padding),
 
9934                     paddingBR = toPoint(this.options.autoPanPaddingBottomRight || padding),
 
9935                     size = map.getSize(),
 
9939                 if (containerPos.x + containerWidth + paddingBR.x > size.x) { // right
 
9940                         dx = containerPos.x + containerWidth - size.x + paddingBR.x;
 
9942                 if (containerPos.x - dx - paddingTL.x < 0) { // left
 
9943                         dx = containerPos.x - paddingTL.x;
 
9945                 if (containerPos.y + containerHeight + paddingBR.y > size.y) { // bottom
 
9946                         dy = containerPos.y + containerHeight - size.y + paddingBR.y;
 
9948                 if (containerPos.y - dy - paddingTL.y < 0) { // top
 
9949                         dy = containerPos.y - paddingTL.y;
 
9953                 // @section Popup events
 
9954                 // @event autopanstart: Event
 
9955                 // Fired when the map starts autopanning when opening a popup.
 
9958                             .fire('autopanstart')
 
9963         _onCloseButtonClick: function (e) {
 
9968         _getAnchor: function () {
 
9969                 // Where should we anchor the popup on the source layer?
 
9970                 return toPoint(this._source && this._source._getPopupAnchor ? this._source._getPopupAnchor() : [0, 0]);
 
9976 // @factory L.popup(options?: Popup options, source?: Layer)
 
9977 // 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.
 
9978 var popup = function (options, source) {
 
9979         return new Popup(options, source);
 
9984  * @section Interaction Options
 
9985  * @option closePopupOnClick: Boolean = true
 
9986  * Set it to `false` if you don't want popups to close when user clicks the map.
 
9989         closePopupOnClick: true
 
9994 // @section Methods for Layers and Controls
 
9996         // @method openPopup(popup: Popup): this
 
9997         // Opens the specified popup while closing the previously opened (to make sure only one is opened at one time for usability).
 
9999         // @method openPopup(content: String|HTMLElement, latlng: LatLng, options?: Popup options): this
 
10000         // Creates a popup with the specified content and options and opens it in the given point on a map.
 
10001         openPopup: function (popup, latlng, options) {
 
10002                 if (!(popup instanceof Popup)) {
 
10003                         popup = new Popup(options).setContent(popup);
 
10007                         popup.setLatLng(latlng);
 
10010                 if (this.hasLayer(popup)) {
 
10014                 if (this._popup && this._popup.options.autoClose) {
 
10018                 this._popup = popup;
 
10019                 return this.addLayer(popup);
 
10022         // @method closePopup(popup?: Popup): this
 
10023         // Closes the popup previously opened with [openPopup](#map-openpopup) (or the given one).
 
10024         closePopup: function (popup) {
 
10025                 if (!popup || popup === this._popup) {
 
10026                         popup = this._popup;
 
10027                         this._popup = null;
 
10030                         this.removeLayer(popup);
 
10038  * @section Popup methods example
 
10040  * All layers share a set of methods convenient for binding popups to it.
 
10043  * var layer = L.Polygon(latlngs).bindPopup('Hi There!').addTo(map);
 
10044  * layer.openPopup();
 
10045  * layer.closePopup();
 
10048  * 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.
 
10051 // @section Popup methods
 
10054         // @method bindPopup(content: String|HTMLElement|Function|Popup, options?: Popup options): this
 
10055         // Binds a popup to the layer with the passed `content` and sets up the
 
10056         // necessary event listeners. If a `Function` is passed it will receive
 
10057         // the layer as the first argument and should return a `String` or `HTMLElement`.
 
10058         bindPopup: function (content, options) {
 
10060                 if (content instanceof Popup) {
 
10061                         setOptions(content, options);
 
10062                         this._popup = content;
 
10063                         content._source = this;
 
10065                         if (!this._popup || options) {
 
10066                                 this._popup = new Popup(options, this);
 
10068                         this._popup.setContent(content);
 
10071                 if (!this._popupHandlersAdded) {
 
10073                                 click: this._openPopup,
 
10074                                 keypress: this._onKeyPress,
 
10075                                 remove: this.closePopup,
 
10076                                 move: this._movePopup
 
10078                         this._popupHandlersAdded = true;
 
10084         // @method unbindPopup(): this
 
10085         // Removes the popup previously bound with `bindPopup`.
 
10086         unbindPopup: function () {
 
10089                                 click: this._openPopup,
 
10090                                 keypress: this._onKeyPress,
 
10091                                 remove: this.closePopup,
 
10092                                 move: this._movePopup
 
10094                         this._popupHandlersAdded = false;
 
10095                         this._popup = null;
 
10100         // @method openPopup(latlng?: LatLng): this
 
10101         // Opens the bound popup at the specified `latlng` or at the default popup anchor if no `latlng` is passed.
 
10102         openPopup: function (layer, latlng) {
 
10103                 if (this._popup && this._map) {
 
10104                         latlng = this._popup._prepareOpen(this, layer, latlng);
 
10106                         // open the popup on the map
 
10107                         this._map.openPopup(this._popup, latlng);
 
10113         // @method closePopup(): this
 
10114         // Closes the popup bound to this layer if it is open.
 
10115         closePopup: function () {
 
10117                         this._popup._close();
 
10122         // @method togglePopup(): this
 
10123         // Opens or closes the popup bound to this layer depending on its current state.
 
10124         togglePopup: function (target) {
 
10126                         if (this._popup._map) {
 
10129                                 this.openPopup(target);
 
10135         // @method isPopupOpen(): boolean
 
10136         // Returns `true` if the popup bound to this layer is currently open.
 
10137         isPopupOpen: function () {
 
10138                 return (this._popup ? this._popup.isOpen() : false);
 
10141         // @method setPopupContent(content: String|HTMLElement|Popup): this
 
10142         // Sets the content of the popup bound to this layer.
 
10143         setPopupContent: function (content) {
 
10145                         this._popup.setContent(content);
 
10150         // @method getPopup(): Popup
 
10151         // Returns the popup bound to this layer.
 
10152         getPopup: function () {
 
10153                 return this._popup;
 
10156         _openPopup: function (e) {
 
10157                 var layer = e.layer || e.target;
 
10159                 if (!this._popup) {
 
10167                 // prevent map click
 
10170                 // if this inherits from Path its a vector and we can just
 
10171                 // open the popup at the new location
 
10172                 if (layer instanceof Path) {
 
10173                         this.openPopup(e.layer || e.target, e.latlng);
 
10177                 // otherwise treat it like a marker and figure out
 
10178                 // if we should toggle it open/closed
 
10179                 if (this._map.hasLayer(this._popup) && this._popup._source === layer) {
 
10182                         this.openPopup(layer, e.latlng);
 
10186         _movePopup: function (e) {
 
10187                 this._popup.setLatLng(e.latlng);
 
10190         _onKeyPress: function (e) {
 
10191                 if (e.originalEvent.keyCode === 13) {
 
10192                         this._openPopup(e);
 
10199  * @inherits DivOverlay
 
10201  * Used to display small texts on top of map layers.
 
10206  * marker.bindTooltip("my tooltip text").openTooltip();
 
10208  * Note about tooltip offset. Leaflet takes two options in consideration
 
10209  * for computing tooltip offsetting:
 
10210  * - the `offset` Tooltip option: it defaults to [0, 0], and it's specific to one tooltip.
 
10211  *   Add a positive x offset to move the tooltip to the right, and a positive y offset to
 
10212  *   move it to the bottom. Negatives will move to the left and top.
 
10213  * - the `tooltipAnchor` Icon option: this will only be considered for Marker. You
 
10214  *   should adapt this value if you use a custom icon.
 
10218 // @namespace Tooltip
 
10219 var Tooltip = DivOverlay.extend({
 
10222         // @aka Tooltip options
 
10224                 // @option pane: String = 'tooltipPane'
 
10225                 // `Map pane` where the tooltip will be added.
 
10226                 pane: 'tooltipPane',
 
10228                 // @option offset: Point = Point(0, 0)
 
10229                 // Optional offset of the tooltip position.
 
10232                 // @option direction: String = 'auto'
 
10233                 // Direction where to open the tooltip. Possible values are: `right`, `left`,
 
10234                 // `top`, `bottom`, `center`, `auto`.
 
10235                 // `auto` will dynamically switch between `right` and `left` according to the tooltip
 
10236                 // position on the map.
 
10239                 // @option permanent: Boolean = false
 
10240                 // Whether to open the tooltip permanently or only on mouseover.
 
10243                 // @option sticky: Boolean = false
 
10244                 // If true, the tooltip will follow the mouse instead of being fixed at the feature center.
 
10247                 // @option interactive: Boolean = false
 
10248                 // If true, the tooltip will listen to the feature events.
 
10249                 interactive: false,
 
10251                 // @option opacity: Number = 0.9
 
10252                 // Tooltip container opacity.
 
10256         onAdd: function (map) {
 
10257                 DivOverlay.prototype.onAdd.call(this, map);
 
10258                 this.setOpacity(this.options.opacity);
 
10261                 // @section Tooltip events
 
10262                 // @event tooltipopen: TooltipEvent
 
10263                 // Fired when a tooltip is opened in the map.
 
10264                 map.fire('tooltipopen', {tooltip: this});
 
10266                 if (this._source) {
 
10267                         // @namespace Layer
 
10268                         // @section Tooltip events
 
10269                         // @event tooltipopen: TooltipEvent
 
10270                         // Fired when a tooltip bound to this layer is opened.
 
10271                         this._source.fire('tooltipopen', {tooltip: this}, true);
 
10275         onRemove: function (map) {
 
10276                 DivOverlay.prototype.onRemove.call(this, map);
 
10279                 // @section Tooltip events
 
10280                 // @event tooltipclose: TooltipEvent
 
10281                 // Fired when a tooltip in the map is closed.
 
10282                 map.fire('tooltipclose', {tooltip: this});
 
10284                 if (this._source) {
 
10285                         // @namespace Layer
 
10286                         // @section Tooltip events
 
10287                         // @event tooltipclose: TooltipEvent
 
10288                         // Fired when a tooltip bound to this layer is closed.
 
10289                         this._source.fire('tooltipclose', {tooltip: this}, true);
 
10293         getEvents: function () {
 
10294                 var events = DivOverlay.prototype.getEvents.call(this);
 
10296                 if (touch && !this.options.permanent) {
 
10297                         events.preclick = this._close;
 
10303         _close: function () {
 
10305                         this._map.closeTooltip(this);
 
10309         _initLayout: function () {
 
10310                 var prefix = 'leaflet-tooltip',
 
10311                     className = prefix + ' ' + (this.options.className || '') + ' leaflet-zoom-' + (this._zoomAnimated ? 'animated' : 'hide');
 
10313                 this._contentNode = this._container = create$1('div', className);
 
10316         _updateLayout: function () {},
 
10318         _adjustPan: function () {},
 
10320         _setPosition: function (pos) {
 
10321                 var map = this._map,
 
10322                     container = this._container,
 
10323                     centerPoint = map.latLngToContainerPoint(map.getCenter()),
 
10324                     tooltipPoint = map.layerPointToContainerPoint(pos),
 
10325                     direction = this.options.direction,
 
10326                     tooltipWidth = container.offsetWidth,
 
10327                     tooltipHeight = container.offsetHeight,
 
10328                     offset = toPoint(this.options.offset),
 
10329                     anchor = this._getAnchor();
 
10331                 if (direction === 'top') {
 
10332                         pos = pos.add(toPoint(-tooltipWidth / 2 + offset.x, -tooltipHeight + offset.y + anchor.y, true));
 
10333                 } else if (direction === 'bottom') {
 
10334                         pos = pos.subtract(toPoint(tooltipWidth / 2 - offset.x, -offset.y, true));
 
10335                 } else if (direction === 'center') {
 
10336                         pos = pos.subtract(toPoint(tooltipWidth / 2 + offset.x, tooltipHeight / 2 - anchor.y + offset.y, true));
 
10337                 } else if (direction === 'right' || direction === 'auto' && tooltipPoint.x < centerPoint.x) {
 
10338                         direction = 'right';
 
10339                         pos = pos.add(toPoint(offset.x + anchor.x, anchor.y - tooltipHeight / 2 + offset.y, true));
 
10341                         direction = 'left';
 
10342                         pos = pos.subtract(toPoint(tooltipWidth + anchor.x - offset.x, tooltipHeight / 2 - anchor.y - offset.y, true));
 
10345                 removeClass(container, 'leaflet-tooltip-right');
 
10346                 removeClass(container, 'leaflet-tooltip-left');
 
10347                 removeClass(container, 'leaflet-tooltip-top');
 
10348                 removeClass(container, 'leaflet-tooltip-bottom');
 
10349                 addClass(container, 'leaflet-tooltip-' + direction);
 
10350                 setPosition(container, pos);
 
10353         _updatePosition: function () {
 
10354                 var pos = this._map.latLngToLayerPoint(this._latlng);
 
10355                 this._setPosition(pos);
 
10358         setOpacity: function (opacity) {
 
10359                 this.options.opacity = opacity;
 
10361                 if (this._container) {
 
10362                         setOpacity(this._container, opacity);
 
10366         _animateZoom: function (e) {
 
10367                 var pos = this._map._latLngToNewLayerPoint(this._latlng, e.zoom, e.center);
 
10368                 this._setPosition(pos);
 
10371         _getAnchor: function () {
 
10372                 // Where should we anchor the tooltip on the source layer?
 
10373                 return toPoint(this._source && this._source._getTooltipAnchor && !this.options.sticky ? this._source._getTooltipAnchor() : [0, 0]);
 
10378 // @namespace Tooltip
 
10379 // @factory L.tooltip(options?: Tooltip options, source?: Layer)
 
10380 // 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.
 
10381 var tooltip = function (options, source) {
 
10382         return new Tooltip(options, source);
 
10386 // @section Methods for Layers and Controls
 
10389         // @method openTooltip(tooltip: Tooltip): this
 
10390         // Opens the specified tooltip.
 
10392         // @method openTooltip(content: String|HTMLElement, latlng: LatLng, options?: Tooltip options): this
 
10393         // Creates a tooltip with the specified content and options and open it.
 
10394         openTooltip: function (tooltip, latlng, options) {
 
10395                 if (!(tooltip instanceof Tooltip)) {
 
10396                         tooltip = new Tooltip(options).setContent(tooltip);
 
10400                         tooltip.setLatLng(latlng);
 
10403                 if (this.hasLayer(tooltip)) {
 
10407                 return this.addLayer(tooltip);
 
10410         // @method closeTooltip(tooltip?: Tooltip): this
 
10411         // Closes the tooltip given as parameter.
 
10412         closeTooltip: function (tooltip) {
 
10414                         this.removeLayer(tooltip);
 
10423  * @section Tooltip methods example
 
10425  * All layers share a set of methods convenient for binding tooltips to it.
 
10428  * var layer = L.Polygon(latlngs).bindTooltip('Hi There!').addTo(map);
 
10429  * layer.openTooltip();
 
10430  * layer.closeTooltip();
 
10434 // @section Tooltip methods
 
10437         // @method bindTooltip(content: String|HTMLElement|Function|Tooltip, options?: Tooltip options): this
 
10438         // Binds a tooltip to the layer with the passed `content` and sets up the
 
10439         // necessary event listeners. If a `Function` is passed it will receive
 
10440         // the layer as the first argument and should return a `String` or `HTMLElement`.
 
10441         bindTooltip: function (content, options) {
 
10443                 if (content instanceof Tooltip) {
 
10444                         setOptions(content, options);
 
10445                         this._tooltip = content;
 
10446                         content._source = this;
 
10448                         if (!this._tooltip || options) {
 
10449                                 this._tooltip = new Tooltip(options, this);
 
10451                         this._tooltip.setContent(content);
 
10455                 this._initTooltipInteractions();
 
10457                 if (this._tooltip.options.permanent && this._map && this._map.hasLayer(this)) {
 
10458                         this.openTooltip();
 
10464         // @method unbindTooltip(): this
 
10465         // Removes the tooltip previously bound with `bindTooltip`.
 
10466         unbindTooltip: function () {
 
10467                 if (this._tooltip) {
 
10468                         this._initTooltipInteractions(true);
 
10469                         this.closeTooltip();
 
10470                         this._tooltip = null;
 
10475         _initTooltipInteractions: function (remove$$1) {
 
10476                 if (!remove$$1 && this._tooltipHandlersAdded) { return; }
 
10477                 var onOff = remove$$1 ? 'off' : 'on',
 
10479                         remove: this.closeTooltip,
 
10480                         move: this._moveTooltip
 
10482                 if (!this._tooltip.options.permanent) {
 
10483                         events.mouseover = this._openTooltip;
 
10484                         events.mouseout = this.closeTooltip;
 
10485                         if (this._tooltip.options.sticky) {
 
10486                                 events.mousemove = this._moveTooltip;
 
10489                                 events.click = this._openTooltip;
 
10492                         events.add = this._openTooltip;
 
10494                 this[onOff](events);
 
10495                 this._tooltipHandlersAdded = !remove$$1;
 
10498         // @method openTooltip(latlng?: LatLng): this
 
10499         // Opens the bound tooltip at the specified `latlng` or at the default tooltip anchor if no `latlng` is passed.
 
10500         openTooltip: function (layer, latlng) {
 
10501                 if (this._tooltip && this._map) {
 
10502                         latlng = this._tooltip._prepareOpen(this, layer, latlng);
 
10504                         // open the tooltip on the map
 
10505                         this._map.openTooltip(this._tooltip, latlng);
 
10507                         // Tooltip container may not be defined if not permanent and never
 
10509                         if (this._tooltip.options.interactive && this._tooltip._container) {
 
10510                                 addClass(this._tooltip._container, 'leaflet-clickable');
 
10511                                 this.addInteractiveTarget(this._tooltip._container);
 
10518         // @method closeTooltip(): this
 
10519         // Closes the tooltip bound to this layer if it is open.
 
10520         closeTooltip: function () {
 
10521                 if (this._tooltip) {
 
10522                         this._tooltip._close();
 
10523                         if (this._tooltip.options.interactive && this._tooltip._container) {
 
10524                                 removeClass(this._tooltip._container, 'leaflet-clickable');
 
10525                                 this.removeInteractiveTarget(this._tooltip._container);
 
10531         // @method toggleTooltip(): this
 
10532         // Opens or closes the tooltip bound to this layer depending on its current state.
 
10533         toggleTooltip: function (target) {
 
10534                 if (this._tooltip) {
 
10535                         if (this._tooltip._map) {
 
10536                                 this.closeTooltip();
 
10538                                 this.openTooltip(target);
 
10544         // @method isTooltipOpen(): boolean
 
10545         // Returns `true` if the tooltip bound to this layer is currently open.
 
10546         isTooltipOpen: function () {
 
10547                 return this._tooltip.isOpen();
 
10550         // @method setTooltipContent(content: String|HTMLElement|Tooltip): this
 
10551         // Sets the content of the tooltip bound to this layer.
 
10552         setTooltipContent: function (content) {
 
10553                 if (this._tooltip) {
 
10554                         this._tooltip.setContent(content);
 
10559         // @method getTooltip(): Tooltip
 
10560         // Returns the tooltip bound to this layer.
 
10561         getTooltip: function () {
 
10562                 return this._tooltip;
 
10565         _openTooltip: function (e) {
 
10566                 var layer = e.layer || e.target;
 
10568                 if (!this._tooltip || !this._map) {
 
10571                 this.openTooltip(layer, this._tooltip.options.sticky ? e.latlng : undefined);
 
10574         _moveTooltip: function (e) {
 
10575                 var latlng = e.latlng, containerPoint, layerPoint;
 
10576                 if (this._tooltip.options.sticky && e.originalEvent) {
 
10577                         containerPoint = this._map.mouseEventToContainerPoint(e.originalEvent);
 
10578                         layerPoint = this._map.containerPointToLayerPoint(containerPoint);
 
10579                         latlng = this._map.layerPointToLatLng(layerPoint);
 
10581                 this._tooltip.setLatLng(latlng);
 
10590  * Represents a lightweight icon for markers that uses a simple `<div>`
 
10591  * element instead of an image. Inherits from `Icon` but ignores the `iconUrl` and shadow options.
 
10595  * var myIcon = L.divIcon({className: 'my-div-icon'});
 
10596  * // you can set .my-div-icon styles in CSS
 
10598  * L.marker([50.505, 30.57], {icon: myIcon}).addTo(map);
 
10601  * By default, it has a 'leaflet-div-icon' CSS class and is styled as a little white square with a shadow.
 
10604 var DivIcon = Icon.extend({
 
10607                 // @aka DivIcon options
 
10608                 iconSize: [12, 12], // also can be set through CSS
 
10610                 // iconAnchor: (Point),
 
10611                 // popupAnchor: (Point),
 
10613                 // @option html: String|HTMLElement = ''
 
10614                 // Custom HTML code to put inside the div element, empty by default. Alternatively,
 
10615                 // an instance of `HTMLElement`.
 
10618                 // @option bgPos: Point = [0, 0]
 
10619                 // Optional relative position of the background, in pixels
 
10622                 className: 'leaflet-div-icon'
 
10625         createIcon: function (oldIcon) {
 
10626                 var div = (oldIcon && oldIcon.tagName === 'DIV') ? oldIcon : document.createElement('div'),
 
10627                     options = this.options;
 
10629                 if (options.html instanceof Element) {
 
10631                         div.appendChild(options.html);
 
10633                         div.innerHTML = options.html !== false ? options.html : '';
 
10636                 if (options.bgPos) {
 
10637                         var bgPos = toPoint(options.bgPos);
 
10638                         div.style.backgroundPosition = (-bgPos.x) + 'px ' + (-bgPos.y) + 'px';
 
10640                 this._setIconStyles(div, 'icon');
 
10645         createShadow: function () {
 
10650 // @factory L.divIcon(options: DivIcon options)
 
10651 // Creates a `DivIcon` instance with the given options.
 
10652 function divIcon(options) {
 
10653         return new DivIcon(options);
 
10656 Icon.Default = IconDefault;
 
10663  * Generic class for handling a tiled grid of HTML elements. This is the base class for all tile layers and replaces `TileLayer.Canvas`.
 
10664  * 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.
 
10667  * @section Synchronous usage
 
10670  * 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.
 
10673  * var CanvasLayer = L.GridLayer.extend({
 
10674  *     createTile: function(coords){
 
10675  *         // create a <canvas> element for drawing
 
10676  *         var tile = L.DomUtil.create('canvas', 'leaflet-tile');
 
10678  *         // setup tile width and height according to the options
 
10679  *         var size = this.getTileSize();
 
10680  *         tile.width = size.x;
 
10681  *         tile.height = size.y;
 
10683  *         // get a canvas context and draw something on it using coords.x, coords.y and coords.z
 
10684  *         var ctx = tile.getContext('2d');
 
10686  *         // return the tile so it can be rendered on screen
 
10692  * @section Asynchronous usage
 
10695  * 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.
 
10698  * var CanvasLayer = L.GridLayer.extend({
 
10699  *     createTile: function(coords, done){
 
10702  *         // create a <canvas> element for drawing
 
10703  *         var tile = L.DomUtil.create('canvas', 'leaflet-tile');
 
10705  *         // setup tile width and height according to the options
 
10706  *         var size = this.getTileSize();
 
10707  *         tile.width = size.x;
 
10708  *         tile.height = size.y;
 
10710  *         // draw something asynchronously and pass the tile to the done() callback
 
10711  *         setTimeout(function() {
 
10712  *             done(error, tile);
 
10724 var GridLayer = Layer.extend({
 
10727         // @aka GridLayer options
 
10729                 // @option tileSize: Number|Point = 256
 
10730                 // Width and height of tiles in the grid. Use a number if width and height are equal, or `L.point(width, height)` otherwise.
 
10733                 // @option opacity: Number = 1.0
 
10734                 // Opacity of the tiles. Can be used in the `createTile()` function.
 
10737                 // @option updateWhenIdle: Boolean = (depends)
 
10738                 // Load new tiles only when panning ends.
 
10739                 // `true` by default on mobile browsers, in order to avoid too many requests and keep smooth navigation.
 
10740                 // `false` otherwise in order to display new tiles _during_ panning, since it is easy to pan outside the
 
10741                 // [`keepBuffer`](#gridlayer-keepbuffer) option in desktop browsers.
 
10742                 updateWhenIdle: mobile,
 
10744                 // @option updateWhenZooming: Boolean = true
 
10745                 // 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.
 
10746                 updateWhenZooming: true,
 
10748                 // @option updateInterval: Number = 200
 
10749                 // Tiles will not update more than once every `updateInterval` milliseconds when panning.
 
10750                 updateInterval: 200,
 
10752                 // @option zIndex: Number = 1
 
10753                 // The explicit zIndex of the tile layer.
 
10756                 // @option bounds: LatLngBounds = undefined
 
10757                 // If set, tiles will only be loaded inside the set `LatLngBounds`.
 
10760                 // @option minZoom: Number = 0
 
10761                 // The minimum zoom level down to which this layer will be displayed (inclusive).
 
10764                 // @option maxZoom: Number = undefined
 
10765                 // The maximum zoom level up to which this layer will be displayed (inclusive).
 
10766                 maxZoom: undefined,
 
10768                 // @option maxNativeZoom: Number = undefined
 
10769                 // Maximum zoom number the tile source has available. If it is specified,
 
10770                 // the tiles on all zoom levels higher than `maxNativeZoom` will be loaded
 
10771                 // from `maxNativeZoom` level and auto-scaled.
 
10772                 maxNativeZoom: undefined,
 
10774                 // @option minNativeZoom: Number = undefined
 
10775                 // Minimum zoom number the tile source has available. If it is specified,
 
10776                 // the tiles on all zoom levels lower than `minNativeZoom` will be loaded
 
10777                 // from `minNativeZoom` level and auto-scaled.
 
10778                 minNativeZoom: undefined,
 
10780                 // @option noWrap: Boolean = false
 
10781                 // Whether the layer is wrapped around the antimeridian. If `true`, the
 
10782                 // GridLayer will only be displayed once at low zoom levels. Has no
 
10783                 // effect when the [map CRS](#map-crs) doesn't wrap around. Can be used
 
10784                 // in combination with [`bounds`](#gridlayer-bounds) to prevent requesting
 
10785                 // tiles outside the CRS limits.
 
10788                 // @option pane: String = 'tilePane'
 
10789                 // `Map pane` where the grid layer will be added.
 
10792                 // @option className: String = ''
 
10793                 // A custom class name to assign to the tile layer. Empty by default.
 
10796                 // @option keepBuffer: Number = 2
 
10797                 // When panning the map, keep this many rows and columns of tiles before unloading them.
 
10801         initialize: function (options) {
 
10802                 setOptions(this, options);
 
10805         onAdd: function () {
 
10806                 this._initContainer();
 
10815         beforeAdd: function (map) {
 
10816                 map._addZoomLimit(this);
 
10819         onRemove: function (map) {
 
10820                 this._removeAllTiles();
 
10821                 remove(this._container);
 
10822                 map._removeZoomLimit(this);
 
10823                 this._container = null;
 
10824                 this._tileZoom = undefined;
 
10827         // @method bringToFront: this
 
10828         // Brings the tile layer to the top of all tile layers.
 
10829         bringToFront: function () {
 
10831                         toFront(this._container);
 
10832                         this._setAutoZIndex(Math.max);
 
10837         // @method bringToBack: this
 
10838         // Brings the tile layer to the bottom of all tile layers.
 
10839         bringToBack: function () {
 
10841                         toBack(this._container);
 
10842                         this._setAutoZIndex(Math.min);
 
10847         // @method getContainer: HTMLElement
 
10848         // Returns the HTML element that contains the tiles for this layer.
 
10849         getContainer: function () {
 
10850                 return this._container;
 
10853         // @method setOpacity(opacity: Number): this
 
10854         // Changes the [opacity](#gridlayer-opacity) of the grid layer.
 
10855         setOpacity: function (opacity) {
 
10856                 this.options.opacity = opacity;
 
10857                 this._updateOpacity();
 
10861         // @method setZIndex(zIndex: Number): this
 
10862         // Changes the [zIndex](#gridlayer-zindex) of the grid layer.
 
10863         setZIndex: function (zIndex) {
 
10864                 this.options.zIndex = zIndex;
 
10865                 this._updateZIndex();
 
10870         // @method isLoading: Boolean
 
10871         // Returns `true` if any tile in the grid layer has not finished loading.
 
10872         isLoading: function () {
 
10873                 return this._loading;
 
10876         // @method redraw: this
 
10877         // Causes the layer to clear all the tiles and request them again.
 
10878         redraw: function () {
 
10880                         this._removeAllTiles();
 
10886         getEvents: function () {
 
10888                         viewprereset: this._invalidateAll,
 
10889                         viewreset: this._resetView,
 
10890                         zoom: this._resetView,
 
10891                         moveend: this._onMoveEnd
 
10894                 if (!this.options.updateWhenIdle) {
 
10895                         // update tiles on move, but not more often than once per given interval
 
10896                         if (!this._onMove) {
 
10897                                 this._onMove = throttle(this._onMoveEnd, this.options.updateInterval, this);
 
10900                         events.move = this._onMove;
 
10903                 if (this._zoomAnimated) {
 
10904                         events.zoomanim = this._animateZoom;
 
10910         // @section Extension methods
 
10911         // Layers extending `GridLayer` shall reimplement the following method.
 
10912         // @method createTile(coords: Object, done?: Function): HTMLElement
 
10913         // Called only internally, must be overridden by classes extending `GridLayer`.
 
10914         // Returns the `HTMLElement` corresponding to the given `coords`. If the `done` callback
 
10915         // is specified, it must be called when the tile has finished loading and drawing.
 
10916         createTile: function () {
 
10917                 return document.createElement('div');
 
10921         // @method getTileSize: Point
 
10922         // Normalizes the [tileSize option](#gridlayer-tilesize) into a point. Used by the `createTile()` method.
 
10923         getTileSize: function () {
 
10924                 var s = this.options.tileSize;
 
10925                 return s instanceof Point ? s : new Point(s, s);
 
10928         _updateZIndex: function () {
 
10929                 if (this._container && this.options.zIndex !== undefined && this.options.zIndex !== null) {
 
10930                         this._container.style.zIndex = this.options.zIndex;
 
10934         _setAutoZIndex: function (compare) {
 
10935                 // go through all other layers of the same pane, set zIndex to max + 1 (front) or min - 1 (back)
 
10937                 var layers = this.getPane().children,
 
10938                     edgeZIndex = -compare(-Infinity, Infinity); // -Infinity for max, Infinity for min
 
10940                 for (var i = 0, len = layers.length, zIndex; i < len; i++) {
 
10942                         zIndex = layers[i].style.zIndex;
 
10944                         if (layers[i] !== this._container && zIndex) {
 
10945                                 edgeZIndex = compare(edgeZIndex, +zIndex);
 
10949                 if (isFinite(edgeZIndex)) {
 
10950                         this.options.zIndex = edgeZIndex + compare(-1, 1);
 
10951                         this._updateZIndex();
 
10955         _updateOpacity: function () {
 
10956                 if (!this._map) { return; }
 
10958                 // IE doesn't inherit filter opacity properly, so we're forced to set it on tiles
 
10959                 if (ielt9) { return; }
 
10961                 setOpacity(this._container, this.options.opacity);
 
10963                 var now = +new Date(),
 
10967                 for (var key in this._tiles) {
 
10968                         var tile = this._tiles[key];
 
10969                         if (!tile.current || !tile.loaded) { continue; }
 
10971                         var fade = Math.min(1, (now - tile.loaded) / 200);
 
10973                         setOpacity(tile.el, fade);
 
10980                                         this._onOpaqueTile(tile);
 
10982                                 tile.active = true;
 
10986                 if (willPrune && !this._noPrune) { this._pruneTiles(); }
 
10989                         cancelAnimFrame(this._fadeFrame);
 
10990                         this._fadeFrame = requestAnimFrame(this._updateOpacity, this);
 
10994         _onOpaqueTile: falseFn,
 
10996         _initContainer: function () {
 
10997                 if (this._container) { return; }
 
10999                 this._container = create$1('div', 'leaflet-layer ' + (this.options.className || ''));
 
11000                 this._updateZIndex();
 
11002                 if (this.options.opacity < 1) {
 
11003                         this._updateOpacity();
 
11006                 this.getPane().appendChild(this._container);
 
11009         _updateLevels: function () {
 
11011                 var zoom = this._tileZoom,
 
11012                     maxZoom = this.options.maxZoom;
 
11014                 if (zoom === undefined) { return undefined; }
 
11016                 for (var z in this._levels) {
 
11017                         if (this._levels[z].el.children.length || z === zoom) {
 
11018                                 this._levels[z].el.style.zIndex = maxZoom - Math.abs(zoom - z);
 
11019                                 this._onUpdateLevel(z);
 
11021                                 remove(this._levels[z].el);
 
11022                                 this._removeTilesAtZoom(z);
 
11023                                 this._onRemoveLevel(z);
 
11024                                 delete this._levels[z];
 
11028                 var level = this._levels[zoom],
 
11032                         level = this._levels[zoom] = {};
 
11034                         level.el = create$1('div', 'leaflet-tile-container leaflet-zoom-animated', this._container);
 
11035                         level.el.style.zIndex = maxZoom;
 
11037                         level.origin = map.project(map.unproject(map.getPixelOrigin()), zoom).round();
 
11040                         this._setZoomTransform(level, map.getCenter(), map.getZoom());
 
11042                         // force the browser to consider the newly added element for transition
 
11043                         falseFn(level.el.offsetWidth);
 
11045                         this._onCreateLevel(level);
 
11048                 this._level = level;
 
11053         _onUpdateLevel: falseFn,
 
11055         _onRemoveLevel: falseFn,
 
11057         _onCreateLevel: falseFn,
 
11059         _pruneTiles: function () {
 
11066                 var zoom = this._map.getZoom();
 
11067                 if (zoom > this.options.maxZoom ||
 
11068                         zoom < this.options.minZoom) {
 
11069                         this._removeAllTiles();
 
11073                 for (key in this._tiles) {
 
11074                         tile = this._tiles[key];
 
11075                         tile.retain = tile.current;
 
11078                 for (key in this._tiles) {
 
11079                         tile = this._tiles[key];
 
11080                         if (tile.current && !tile.active) {
 
11081                                 var coords = tile.coords;
 
11082                                 if (!this._retainParent(coords.x, coords.y, coords.z, coords.z - 5)) {
 
11083                                         this._retainChildren(coords.x, coords.y, coords.z, coords.z + 2);
 
11088                 for (key in this._tiles) {
 
11089                         if (!this._tiles[key].retain) {
 
11090                                 this._removeTile(key);
 
11095         _removeTilesAtZoom: function (zoom) {
 
11096                 for (var key in this._tiles) {
 
11097                         if (this._tiles[key].coords.z !== zoom) {
 
11100                         this._removeTile(key);
 
11104         _removeAllTiles: function () {
 
11105                 for (var key in this._tiles) {
 
11106                         this._removeTile(key);
 
11110         _invalidateAll: function () {
 
11111                 for (var z in this._levels) {
 
11112                         remove(this._levels[z].el);
 
11113                         this._onRemoveLevel(z);
 
11114                         delete this._levels[z];
 
11116                 this._removeAllTiles();
 
11118                 this._tileZoom = undefined;
 
11121         _retainParent: function (x, y, z, minZoom) {
 
11122                 var x2 = Math.floor(x / 2),
 
11123                     y2 = Math.floor(y / 2),
 
11125                     coords2 = new Point(+x2, +y2);
 
11128                 var key = this._tileCoordsToKey(coords2),
 
11129                     tile = this._tiles[key];
 
11131                 if (tile && tile.active) {
 
11132                         tile.retain = true;
 
11135                 } else if (tile && tile.loaded) {
 
11136                         tile.retain = true;
 
11139                 if (z2 > minZoom) {
 
11140                         return this._retainParent(x2, y2, z2, minZoom);
 
11146         _retainChildren: function (x, y, z, maxZoom) {
 
11148                 for (var i = 2 * x; i < 2 * x + 2; i++) {
 
11149                         for (var j = 2 * y; j < 2 * y + 2; j++) {
 
11151                                 var coords = new Point(i, j);
 
11154                                 var key = this._tileCoordsToKey(coords),
 
11155                                     tile = this._tiles[key];
 
11157                                 if (tile && tile.active) {
 
11158                                         tile.retain = true;
 
11161                                 } else if (tile && tile.loaded) {
 
11162                                         tile.retain = true;
 
11165                                 if (z + 1 < maxZoom) {
 
11166                                         this._retainChildren(i, j, z + 1, maxZoom);
 
11172         _resetView: function (e) {
 
11173                 var animating = e && (e.pinch || e.flyTo);
 
11174                 this._setView(this._map.getCenter(), this._map.getZoom(), animating, animating);
 
11177         _animateZoom: function (e) {
 
11178                 this._setView(e.center, e.zoom, true, e.noUpdate);
 
11181         _clampZoom: function (zoom) {
 
11182                 var options = this.options;
 
11184                 if (undefined !== options.minNativeZoom && zoom < options.minNativeZoom) {
 
11185                         return options.minNativeZoom;
 
11188                 if (undefined !== options.maxNativeZoom && options.maxNativeZoom < zoom) {
 
11189                         return options.maxNativeZoom;
 
11195         _setView: function (center, zoom, noPrune, noUpdate) {
 
11196                 var tileZoom = this._clampZoom(Math.round(zoom));
 
11197                 if ((this.options.maxZoom !== undefined && tileZoom > this.options.maxZoom) ||
 
11198                     (this.options.minZoom !== undefined && tileZoom < this.options.minZoom)) {
 
11199                         tileZoom = undefined;
 
11202                 var tileZoomChanged = this.options.updateWhenZooming && (tileZoom !== this._tileZoom);
 
11204                 if (!noUpdate || tileZoomChanged) {
 
11206                         this._tileZoom = tileZoom;
 
11208                         if (this._abortLoading) {
 
11209                                 this._abortLoading();
 
11212                         this._updateLevels();
 
11215                         if (tileZoom !== undefined) {
 
11216                                 this._update(center);
 
11220                                 this._pruneTiles();
 
11223                         // Flag to prevent _updateOpacity from pruning tiles during
 
11224                         // a zoom anim or a pinch gesture
 
11225                         this._noPrune = !!noPrune;
 
11228                 this._setZoomTransforms(center, zoom);
 
11231         _setZoomTransforms: function (center, zoom) {
 
11232                 for (var i in this._levels) {
 
11233                         this._setZoomTransform(this._levels[i], center, zoom);
 
11237         _setZoomTransform: function (level, center, zoom) {
 
11238                 var scale = this._map.getZoomScale(zoom, level.zoom),
 
11239                     translate = level.origin.multiplyBy(scale)
 
11240                         .subtract(this._map._getNewPixelOrigin(center, zoom)).round();
 
11243                         setTransform(level.el, translate, scale);
 
11245                         setPosition(level.el, translate);
 
11249         _resetGrid: function () {
 
11250                 var map = this._map,
 
11251                     crs = map.options.crs,
 
11252                     tileSize = this._tileSize = this.getTileSize(),
 
11253                     tileZoom = this._tileZoom;
 
11255                 var bounds = this._map.getPixelWorldBounds(this._tileZoom);
 
11257                         this._globalTileRange = this._pxBoundsToTileRange(bounds);
 
11260                 this._wrapX = crs.wrapLng && !this.options.noWrap && [
 
11261                         Math.floor(map.project([0, crs.wrapLng[0]], tileZoom).x / tileSize.x),
 
11262                         Math.ceil(map.project([0, crs.wrapLng[1]], tileZoom).x / tileSize.y)
 
11264                 this._wrapY = crs.wrapLat && !this.options.noWrap && [
 
11265                         Math.floor(map.project([crs.wrapLat[0], 0], tileZoom).y / tileSize.x),
 
11266                         Math.ceil(map.project([crs.wrapLat[1], 0], tileZoom).y / tileSize.y)
 
11270         _onMoveEnd: function () {
 
11271                 if (!this._map || this._map._animatingZoom) { return; }
 
11276         _getTiledPixelBounds: function (center) {
 
11277                 var map = this._map,
 
11278                     mapZoom = map._animatingZoom ? Math.max(map._animateToZoom, map.getZoom()) : map.getZoom(),
 
11279                     scale = map.getZoomScale(mapZoom, this._tileZoom),
 
11280                     pixelCenter = map.project(center, this._tileZoom).floor(),
 
11281                     halfSize = map.getSize().divideBy(scale * 2);
 
11283                 return new Bounds(pixelCenter.subtract(halfSize), pixelCenter.add(halfSize));
 
11286         // Private method to load tiles in the grid's active zoom level according to map bounds
 
11287         _update: function (center) {
 
11288                 var map = this._map;
 
11289                 if (!map) { return; }
 
11290                 var zoom = this._clampZoom(map.getZoom());
 
11292                 if (center === undefined) { center = map.getCenter(); }
 
11293                 if (this._tileZoom === undefined) { return; }   // if out of minzoom/maxzoom
 
11295                 var pixelBounds = this._getTiledPixelBounds(center),
 
11296                     tileRange = this._pxBoundsToTileRange(pixelBounds),
 
11297                     tileCenter = tileRange.getCenter(),
 
11299                     margin = this.options.keepBuffer,
 
11300                     noPruneRange = new Bounds(tileRange.getBottomLeft().subtract([margin, -margin]),
 
11301                                               tileRange.getTopRight().add([margin, -margin]));
 
11303                 // Sanity check: panic if the tile range contains Infinity somewhere.
 
11304                 if (!(isFinite(tileRange.min.x) &&
 
11305                       isFinite(tileRange.min.y) &&
 
11306                       isFinite(tileRange.max.x) &&
 
11307                       isFinite(tileRange.max.y))) { throw new Error('Attempted to load an infinite number of tiles'); }
 
11309                 for (var key in this._tiles) {
 
11310                         var c = this._tiles[key].coords;
 
11311                         if (c.z !== this._tileZoom || !noPruneRange.contains(new Point(c.x, c.y))) {
 
11312                                 this._tiles[key].current = false;
 
11316                 // _update just loads more tiles. If the tile zoom level differs too much
 
11317                 // from the map's, let _setView reset levels and prune old tiles.
 
11318                 if (Math.abs(zoom - this._tileZoom) > 1) { this._setView(center, zoom); return; }
 
11320                 // create a queue of coordinates to load tiles from
 
11321                 for (var j = tileRange.min.y; j <= tileRange.max.y; j++) {
 
11322                         for (var i = tileRange.min.x; i <= tileRange.max.x; i++) {
 
11323                                 var coords = new Point(i, j);
 
11324                                 coords.z = this._tileZoom;
 
11326                                 if (!this._isValidTile(coords)) { continue; }
 
11328                                 var tile = this._tiles[this._tileCoordsToKey(coords)];
 
11330                                         tile.current = true;
 
11332                                         queue.push(coords);
 
11337                 // sort tile queue to load tiles in order of their distance to center
 
11338                 queue.sort(function (a, b) {
 
11339                         return a.distanceTo(tileCenter) - b.distanceTo(tileCenter);
 
11342                 if (queue.length !== 0) {
 
11343                         // if it's the first batch of tiles to load
 
11344                         if (!this._loading) {
 
11345                                 this._loading = true;
 
11346                                 // @event loading: Event
 
11347                                 // Fired when the grid layer starts loading tiles.
 
11348                                 this.fire('loading');
 
11351                         // create DOM fragment to append tiles in one batch
 
11352                         var fragment = document.createDocumentFragment();
 
11354                         for (i = 0; i < queue.length; i++) {
 
11355                                 this._addTile(queue[i], fragment);
 
11358                         this._level.el.appendChild(fragment);
 
11362         _isValidTile: function (coords) {
 
11363                 var crs = this._map.options.crs;
 
11365                 if (!crs.infinite) {
 
11366                         // don't load tile if it's out of bounds and not wrapped
 
11367                         var bounds = this._globalTileRange;
 
11368                         if ((!crs.wrapLng && (coords.x < bounds.min.x || coords.x > bounds.max.x)) ||
 
11369                             (!crs.wrapLat && (coords.y < bounds.min.y || coords.y > bounds.max.y))) { return false; }
 
11372                 if (!this.options.bounds) { return true; }
 
11374                 // don't load tile if it doesn't intersect the bounds in options
 
11375                 var tileBounds = this._tileCoordsToBounds(coords);
 
11376                 return toLatLngBounds(this.options.bounds).overlaps(tileBounds);
 
11379         _keyToBounds: function (key) {
 
11380                 return this._tileCoordsToBounds(this._keyToTileCoords(key));
 
11383         _tileCoordsToNwSe: function (coords) {
 
11384                 var map = this._map,
 
11385                     tileSize = this.getTileSize(),
 
11386                     nwPoint = coords.scaleBy(tileSize),
 
11387                     sePoint = nwPoint.add(tileSize),
 
11388                     nw = map.unproject(nwPoint, coords.z),
 
11389                     se = map.unproject(sePoint, coords.z);
 
11393         // converts tile coordinates to its geographical bounds
 
11394         _tileCoordsToBounds: function (coords) {
 
11395                 var bp = this._tileCoordsToNwSe(coords),
 
11396                     bounds = new LatLngBounds(bp[0], bp[1]);
 
11398                 if (!this.options.noWrap) {
 
11399                         bounds = this._map.wrapLatLngBounds(bounds);
 
11403         // converts tile coordinates to key for the tile cache
 
11404         _tileCoordsToKey: function (coords) {
 
11405                 return coords.x + ':' + coords.y + ':' + coords.z;
 
11408         // converts tile cache key to coordinates
 
11409         _keyToTileCoords: function (key) {
 
11410                 var k = key.split(':'),
 
11411                     coords = new Point(+k[0], +k[1]);
 
11416         _removeTile: function (key) {
 
11417                 var tile = this._tiles[key];
 
11418                 if (!tile) { return; }
 
11422                 delete this._tiles[key];
 
11424                 // @event tileunload: TileEvent
 
11425                 // Fired when a tile is removed (e.g. when a tile goes off the screen).
 
11426                 this.fire('tileunload', {
 
11428                         coords: this._keyToTileCoords(key)
 
11432         _initTile: function (tile) {
 
11433                 addClass(tile, 'leaflet-tile');
 
11435                 var tileSize = this.getTileSize();
 
11436                 tile.style.width = tileSize.x + 'px';
 
11437                 tile.style.height = tileSize.y + 'px';
 
11439                 tile.onselectstart = falseFn;
 
11440                 tile.onmousemove = falseFn;
 
11442                 // update opacity on tiles in IE7-8 because of filter inheritance problems
 
11443                 if (ielt9 && this.options.opacity < 1) {
 
11444                         setOpacity(tile, this.options.opacity);
 
11447                 // without this hack, tiles disappear after zoom on Chrome for Android
 
11448                 // https://github.com/Leaflet/Leaflet/issues/2078
 
11449                 if (android && !android23) {
 
11450                         tile.style.WebkitBackfaceVisibility = 'hidden';
 
11454         _addTile: function (coords, container) {
 
11455                 var tilePos = this._getTilePos(coords),
 
11456                     key = this._tileCoordsToKey(coords);
 
11458                 var tile = this.createTile(this._wrapCoords(coords), bind(this._tileReady, this, coords));
 
11460                 this._initTile(tile);
 
11462                 // if createTile is defined with a second argument ("done" callback),
 
11463                 // we know that tile is async and will be ready later; otherwise
 
11464                 if (this.createTile.length < 2) {
 
11465                         // mark tile as ready, but delay one frame for opacity animation to happen
 
11466                         requestAnimFrame(bind(this._tileReady, this, coords, null, tile));
 
11469                 setPosition(tile, tilePos);
 
11471                 // save tile in cache
 
11472                 this._tiles[key] = {
 
11478                 container.appendChild(tile);
 
11479                 // @event tileloadstart: TileEvent
 
11480                 // Fired when a tile is requested and starts loading.
 
11481                 this.fire('tileloadstart', {
 
11487         _tileReady: function (coords, err, tile) {
 
11489                         // @event tileerror: TileErrorEvent
 
11490                         // Fired when there is an error loading a tile.
 
11491                         this.fire('tileerror', {
 
11498                 var key = this._tileCoordsToKey(coords);
 
11500                 tile = this._tiles[key];
 
11501                 if (!tile) { return; }
 
11503                 tile.loaded = +new Date();
 
11504                 if (this._map._fadeAnimated) {
 
11505                         setOpacity(tile.el, 0);
 
11506                         cancelAnimFrame(this._fadeFrame);
 
11507                         this._fadeFrame = requestAnimFrame(this._updateOpacity, this);
 
11509                         tile.active = true;
 
11510                         this._pruneTiles();
 
11514                         addClass(tile.el, 'leaflet-tile-loaded');
 
11516                         // @event tileload: TileEvent
 
11517                         // Fired when a tile loads.
 
11518                         this.fire('tileload', {
 
11524                 if (this._noTilesToLoad()) {
 
11525                         this._loading = false;
 
11526                         // @event load: Event
 
11527                         // Fired when the grid layer loaded all visible tiles.
 
11530                         if (ielt9 || !this._map._fadeAnimated) {
 
11531                                 requestAnimFrame(this._pruneTiles, this);
 
11533                                 // Wait a bit more than 0.2 secs (the duration of the tile fade-in)
 
11534                                 // to trigger a pruning.
 
11535                                 setTimeout(bind(this._pruneTiles, this), 250);
 
11540         _getTilePos: function (coords) {
 
11541                 return coords.scaleBy(this.getTileSize()).subtract(this._level.origin);
 
11544         _wrapCoords: function (coords) {
 
11545                 var newCoords = new Point(
 
11546                         this._wrapX ? wrapNum(coords.x, this._wrapX) : coords.x,
 
11547                         this._wrapY ? wrapNum(coords.y, this._wrapY) : coords.y);
 
11548                 newCoords.z = coords.z;
 
11552         _pxBoundsToTileRange: function (bounds) {
 
11553                 var tileSize = this.getTileSize();
 
11555                         bounds.min.unscaleBy(tileSize).floor(),
 
11556                         bounds.max.unscaleBy(tileSize).ceil().subtract([1, 1]));
 
11559         _noTilesToLoad: function () {
 
11560                 for (var key in this._tiles) {
 
11561                         if (!this._tiles[key].loaded) { return false; }
 
11567 // @factory L.gridLayer(options?: GridLayer options)
 
11568 // Creates a new instance of GridLayer with the supplied options.
 
11569 function gridLayer(options) {
 
11570         return new GridLayer(options);
 
11575  * @inherits GridLayer
 
11577  * 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`.
 
11582  * 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);
 
11585  * @section URL template
 
11588  * A string of the following form:
 
11591  * 'http://{s}.somedomain.com/blabla/{z}/{x}/{y}{r}.png'
 
11594  * `{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.
 
11596  * You can use custom keys in the template, which will be [evaluated](#util-template) from TileLayer options, like this:
 
11599  * L.tileLayer('http://{s}.somedomain.com/{foo}/{z}/{x}/{y}.png', {foo: 'bar'});
 
11604 var TileLayer = GridLayer.extend({
 
11607         // @aka TileLayer options
 
11609                 // @option minZoom: Number = 0
 
11610                 // The minimum zoom level down to which this layer will be displayed (inclusive).
 
11613                 // @option maxZoom: Number = 18
 
11614                 // The maximum zoom level up to which this layer will be displayed (inclusive).
 
11617                 // @option subdomains: String|String[] = 'abc'
 
11618                 // 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.
 
11621                 // @option errorTileUrl: String = ''
 
11622                 // URL to the tile image to show in place of the tile that failed to load.
 
11625                 // @option zoomOffset: Number = 0
 
11626                 // The zoom number used in tile URLs will be offset with this value.
 
11629                 // @option tms: Boolean = false
 
11630                 // If `true`, inverses Y axis numbering for tiles (turn this on for [TMS](https://en.wikipedia.org/wiki/Tile_Map_Service) services).
 
11633                 // @option zoomReverse: Boolean = false
 
11634                 // If set to true, the zoom number used in tile URLs will be reversed (`maxZoom - zoom` instead of `zoom`)
 
11635                 zoomReverse: false,
 
11637                 // @option detectRetina: Boolean = false
 
11638                 // 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.
 
11639                 detectRetina: false,
 
11641                 // @option crossOrigin: Boolean|String = false
 
11642                 // Whether the crossOrigin attribute will be added to the tiles.
 
11643                 // 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.
 
11644                 // Refer to [CORS Settings](https://developer.mozilla.org/en-US/docs/Web/HTML/CORS_settings_attributes) for valid String values.
 
11648         initialize: function (url, options) {
 
11652                 options = setOptions(this, options);
 
11654                 // detecting retina displays, adjusting tileSize and zoom levels
 
11655                 if (options.detectRetina && retina && options.maxZoom > 0) {
 
11657                         options.tileSize = Math.floor(options.tileSize / 2);
 
11659                         if (!options.zoomReverse) {
 
11660                                 options.zoomOffset++;
 
11663                                 options.zoomOffset--;
 
11667                         options.minZoom = Math.max(0, options.minZoom);
 
11670                 if (typeof options.subdomains === 'string') {
 
11671                         options.subdomains = options.subdomains.split('');
 
11674                 // for https://github.com/Leaflet/Leaflet/issues/137
 
11676                         this.on('tileunload', this._onTileRemove);
 
11680         // @method setUrl(url: String, noRedraw?: Boolean): this
 
11681         // Updates the layer's URL template and redraws it (unless `noRedraw` is set to `true`).
 
11682         // If the URL does not change, the layer will not be redrawn unless
 
11683         // the noRedraw parameter is set to false.
 
11684         setUrl: function (url, noRedraw) {
 
11685                 if (this._url === url && noRedraw === undefined) {
 
11697         // @method createTile(coords: Object, done?: Function): HTMLElement
 
11698         // Called only internally, overrides GridLayer's [`createTile()`](#gridlayer-createtile)
 
11699         // to return an `<img>` HTML element with the appropriate image URL given `coords`. The `done`
 
11700         // callback is called when the tile has been loaded.
 
11701         createTile: function (coords, done) {
 
11702                 var tile = document.createElement('img');
 
11704                 on(tile, 'load', bind(this._tileOnLoad, this, done, tile));
 
11705                 on(tile, 'error', bind(this._tileOnError, this, done, tile));
 
11707                 if (this.options.crossOrigin || this.options.crossOrigin === '') {
 
11708                         tile.crossOrigin = this.options.crossOrigin === true ? '' : this.options.crossOrigin;
 
11712                  Alt tag is set to empty string to keep screen readers from reading URL and for compliance reasons
 
11713                  http://www.w3.org/TR/WCAG20-TECHS/H67
 
11718                  Set role="presentation" to force screen readers to ignore this
 
11719                  https://www.w3.org/TR/wai-aria/roles#textalternativecomputation
 
11721                 tile.setAttribute('role', 'presentation');
 
11723                 tile.src = this.getTileUrl(coords);
 
11728         // @section Extension methods
 
11730         // Layers extending `TileLayer` might reimplement the following method.
 
11731         // @method getTileUrl(coords: Object): String
 
11732         // Called only internally, returns the URL for a tile given its coordinates.
 
11733         // Classes extending `TileLayer` can override this function to provide custom tile URL naming schemes.
 
11734         getTileUrl: function (coords) {
 
11736                         r: retina ? '@2x' : '',
 
11737                         s: this._getSubdomain(coords),
 
11740                         z: this._getZoomForUrl()
 
11742                 if (this._map && !this._map.options.crs.infinite) {
 
11743                         var invertedY = this._globalTileRange.max.y - coords.y;
 
11744                         if (this.options.tms) {
 
11745                                 data['y'] = invertedY;
 
11747                         data['-y'] = invertedY;
 
11750                 return template(this._url, extend(data, this.options));
 
11753         _tileOnLoad: function (done, tile) {
 
11754                 // For https://github.com/Leaflet/Leaflet/issues/3332
 
11756                         setTimeout(bind(done, this, null, tile), 0);
 
11762         _tileOnError: function (done, tile, e) {
 
11763                 var errorUrl = this.options.errorTileUrl;
 
11764                 if (errorUrl && tile.getAttribute('src') !== errorUrl) {
 
11765                         tile.src = errorUrl;
 
11770         _onTileRemove: function (e) {
 
11771                 e.tile.onload = null;
 
11774         _getZoomForUrl: function () {
 
11775                 var zoom = this._tileZoom,
 
11776                 maxZoom = this.options.maxZoom,
 
11777                 zoomReverse = this.options.zoomReverse,
 
11778                 zoomOffset = this.options.zoomOffset;
 
11781                         zoom = maxZoom - zoom;
 
11784                 return zoom + zoomOffset;
 
11787         _getSubdomain: function (tilePoint) {
 
11788                 var index = Math.abs(tilePoint.x + tilePoint.y) % this.options.subdomains.length;
 
11789                 return this.options.subdomains[index];
 
11792         // stops loading all tiles in the background layer
 
11793         _abortLoading: function () {
 
11795                 for (i in this._tiles) {
 
11796                         if (this._tiles[i].coords.z !== this._tileZoom) {
 
11797                                 tile = this._tiles[i].el;
 
11799                                 tile.onload = falseFn;
 
11800                                 tile.onerror = falseFn;
 
11802                                 if (!tile.complete) {
 
11803                                         tile.src = emptyImageUrl;
 
11805                                         delete this._tiles[i];
 
11811         _removeTile: function (key) {
 
11812                 var tile = this._tiles[key];
 
11813                 if (!tile) { return; }
 
11815                 // Cancels any pending http requests associated with the tile
 
11816                 // unless we're on Android's stock browser,
 
11817                 // see https://github.com/Leaflet/Leaflet/issues/137
 
11818                 if (!androidStock) {
 
11819                         tile.el.setAttribute('src', emptyImageUrl);
 
11822                 return GridLayer.prototype._removeTile.call(this, key);
 
11825         _tileReady: function (coords, err, tile) {
 
11826                 if (!this._map || (tile && tile.getAttribute('src') === emptyImageUrl)) {
 
11830                 return GridLayer.prototype._tileReady.call(this, coords, err, tile);
 
11835 // @factory L.tilelayer(urlTemplate: String, options?: TileLayer options)
 
11836 // Instantiates a tile layer object given a `URL template` and optionally an options object.
 
11838 function tileLayer(url, options) {
 
11839         return new TileLayer(url, options);
 
11843  * @class TileLayer.WMS
 
11844  * @inherits TileLayer
 
11845  * @aka L.TileLayer.WMS
 
11846  * Used to display [WMS](https://en.wikipedia.org/wiki/Web_Map_Service) services as tile layers on the map. Extends `TileLayer`.
 
11851  * var nexrad = L.tileLayer.wms("http://mesonet.agron.iastate.edu/cgi-bin/wms/nexrad/n0r.cgi", {
 
11852  *      layers: 'nexrad-n0r-900913',
 
11853  *      format: 'image/png',
 
11854  *      transparent: true,
 
11855  *      attribution: "Weather data © 2012 IEM Nexrad"
 
11860 var TileLayerWMS = TileLayer.extend({
 
11863         // @aka TileLayer.WMS options
 
11864         // If any custom options not documented here are used, they will be sent to the
 
11865         // WMS server as extra parameters in each request URL. This can be useful for
 
11866         // [non-standard vendor WMS parameters](http://docs.geoserver.org/stable/en/user/services/wms/vendor.html).
 
11867         defaultWmsParams: {
 
11871                 // @option layers: String = ''
 
11872                 // **(required)** Comma-separated list of WMS layers to show.
 
11875                 // @option styles: String = ''
 
11876                 // Comma-separated list of WMS styles.
 
11879                 // @option format: String = 'image/jpeg'
 
11880                 // WMS image format (use `'image/png'` for layers with transparency).
 
11881                 format: 'image/jpeg',
 
11883                 // @option transparent: Boolean = false
 
11884                 // If `true`, the WMS service will return images with transparency.
 
11885                 transparent: false,
 
11887                 // @option version: String = '1.1.1'
 
11888                 // Version of the WMS service to use
 
11893                 // @option crs: CRS = null
 
11894                 // Coordinate Reference System to use for the WMS requests, defaults to
 
11895                 // map CRS. Don't change this if you're not sure what it means.
 
11898                 // @option uppercase: Boolean = false
 
11899                 // If `true`, WMS request parameter keys will be uppercase.
 
11903         initialize: function (url, options) {
 
11907                 var wmsParams = extend({}, this.defaultWmsParams);
 
11909                 // all keys that are not TileLayer options go to WMS params
 
11910                 for (var i in options) {
 
11911                         if (!(i in this.options)) {
 
11912                                 wmsParams[i] = options[i];
 
11916                 options = setOptions(this, options);
 
11918                 var realRetina = options.detectRetina && retina ? 2 : 1;
 
11919                 var tileSize = this.getTileSize();
 
11920                 wmsParams.width = tileSize.x * realRetina;
 
11921                 wmsParams.height = tileSize.y * realRetina;
 
11923                 this.wmsParams = wmsParams;
 
11926         onAdd: function (map) {
 
11928                 this._crs = this.options.crs || map.options.crs;
 
11929                 this._wmsVersion = parseFloat(this.wmsParams.version);
 
11931                 var projectionKey = this._wmsVersion >= 1.3 ? 'crs' : 'srs';
 
11932                 this.wmsParams[projectionKey] = this._crs.code;
 
11934                 TileLayer.prototype.onAdd.call(this, map);
 
11937         getTileUrl: function (coords) {
 
11939                 var tileBounds = this._tileCoordsToNwSe(coords),
 
11941                     bounds = toBounds(crs.project(tileBounds[0]), crs.project(tileBounds[1])),
 
11944                     bbox = (this._wmsVersion >= 1.3 && this._crs === EPSG4326 ?
 
11945                     [min.y, min.x, max.y, max.x] :
 
11946                     [min.x, min.y, max.x, max.y]).join(','),
 
11947                     url = TileLayer.prototype.getTileUrl.call(this, coords);
 
11949                         getParamString(this.wmsParams, url, this.options.uppercase) +
 
11950                         (this.options.uppercase ? '&BBOX=' : '&bbox=') + bbox;
 
11953         // @method setParams(params: Object, noRedraw?: Boolean): this
 
11954         // Merges an object with the new parameters and re-requests tiles on the current screen (unless `noRedraw` was set to true).
 
11955         setParams: function (params, noRedraw) {
 
11957                 extend(this.wmsParams, params);
 
11968 // @factory L.tileLayer.wms(baseUrl: String, options: TileLayer.WMS options)
 
11969 // Instantiates a WMS tile layer object given a base URL of the WMS service and a WMS parameters/options object.
 
11970 function tileLayerWMS(url, options) {
 
11971         return new TileLayerWMS(url, options);
 
11974 TileLayer.WMS = TileLayerWMS;
 
11975 tileLayer.wms = tileLayerWMS;
 
11982  * Base class for vector renderer implementations (`SVG`, `Canvas`). Handles the
 
11983  * DOM container of the renderer, its bounds, and its zoom animation.
 
11985  * A `Renderer` works as an implicit layer group for all `Path`s - the renderer
 
11986  * itself can be added or removed to the map. All paths use a renderer, which can
 
11987  * be implicit (the map will decide the type of renderer and use it automatically)
 
11988  * or explicit (using the [`renderer`](#path-renderer) option of the path).
 
11990  * Do not use this class directly, use `SVG` and `Canvas` instead.
 
11992  * @event update: Event
 
11993  * Fired when the renderer updates its bounds, center and zoom, for example when
 
11994  * its map has moved
 
11997 var Renderer = Layer.extend({
 
12000         // @aka Renderer options
 
12002                 // @option padding: Number = 0.1
 
12003                 // How much to extend the clip area around the map view (relative to its size)
 
12004                 // e.g. 0.1 would be 10% of map view in each direction
 
12007                 // @option tolerance: Number = 0
 
12008                 // How much to extend click tolerance round a path/object on the map
 
12012         initialize: function (options) {
 
12013                 setOptions(this, options);
 
12015                 this._layers = this._layers || {};
 
12018         onAdd: function () {
 
12019                 if (!this._container) {
 
12020                         this._initContainer(); // defined by renderer implementations
 
12022                         if (this._zoomAnimated) {
 
12023                                 addClass(this._container, 'leaflet-zoom-animated');
 
12027                 this.getPane().appendChild(this._container);
 
12029                 this.on('update', this._updatePaths, this);
 
12032         onRemove: function () {
 
12033                 this.off('update', this._updatePaths, this);
 
12034                 this._destroyContainer();
 
12037         getEvents: function () {
 
12039                         viewreset: this._reset,
 
12040                         zoom: this._onZoom,
 
12041                         moveend: this._update,
 
12042                         zoomend: this._onZoomEnd
 
12044                 if (this._zoomAnimated) {
 
12045                         events.zoomanim = this._onAnimZoom;
 
12050         _onAnimZoom: function (ev) {
 
12051                 this._updateTransform(ev.center, ev.zoom);
 
12054         _onZoom: function () {
 
12055                 this._updateTransform(this._map.getCenter(), this._map.getZoom());
 
12058         _updateTransform: function (center, zoom) {
 
12059                 var scale = this._map.getZoomScale(zoom, this._zoom),
 
12060                     position = getPosition(this._container),
 
12061                     viewHalf = this._map.getSize().multiplyBy(0.5 + this.options.padding),
 
12062                     currentCenterPoint = this._map.project(this._center, zoom),
 
12063                     destCenterPoint = this._map.project(center, zoom),
 
12064                     centerOffset = destCenterPoint.subtract(currentCenterPoint),
 
12066                     topLeftOffset = viewHalf.multiplyBy(-scale).add(position).add(viewHalf).subtract(centerOffset);
 
12069                         setTransform(this._container, topLeftOffset, scale);
 
12071                         setPosition(this._container, topLeftOffset);
 
12075         _reset: function () {
 
12077                 this._updateTransform(this._center, this._zoom);
 
12079                 for (var id in this._layers) {
 
12080                         this._layers[id]._reset();
 
12084         _onZoomEnd: function () {
 
12085                 for (var id in this._layers) {
 
12086                         this._layers[id]._project();
 
12090         _updatePaths: function () {
 
12091                 for (var id in this._layers) {
 
12092                         this._layers[id]._update();
 
12096         _update: function () {
 
12097                 // Update pixel bounds of renderer container (for positioning/sizing/clipping later)
 
12098                 // Subclasses are responsible of firing the 'update' event.
 
12099                 var p = this.options.padding,
 
12100                     size = this._map.getSize(),
 
12101                     min = this._map.containerPointToLayerPoint(size.multiplyBy(-p)).round();
 
12103                 this._bounds = new Bounds(min, min.add(size.multiplyBy(1 + p * 2)).round());
 
12105                 this._center = this._map.getCenter();
 
12106                 this._zoom = this._map.getZoom();
 
12112  * @inherits Renderer
 
12115  * Allows vector layers to be displayed with [`<canvas>`](https://developer.mozilla.org/docs/Web/API/Canvas_API).
 
12116  * Inherits `Renderer`.
 
12118  * Due to [technical limitations](http://caniuse.com/#search=canvas), Canvas is not
 
12119  * available in all web browsers, notably IE8, and overlapping geometries might
 
12120  * not display properly in some edge cases.
 
12124  * Use Canvas by default for all paths in the map:
 
12127  * var map = L.map('map', {
 
12128  *      renderer: L.canvas()
 
12132  * Use a Canvas renderer with extra padding for specific vector geometries:
 
12135  * var map = L.map('map');
 
12136  * var myRenderer = L.canvas({ padding: 0.5 });
 
12137  * var line = L.polyline( coordinates, { renderer: myRenderer } );
 
12138  * var circle = L.circle( center, { renderer: myRenderer } );
 
12142 var Canvas = Renderer.extend({
 
12143         getEvents: function () {
 
12144                 var events = Renderer.prototype.getEvents.call(this);
 
12145                 events.viewprereset = this._onViewPreReset;
 
12149         _onViewPreReset: function () {
 
12150                 // Set a flag so that a viewprereset+moveend+viewreset only updates&redraws once
 
12151                 this._postponeUpdatePaths = true;
 
12154         onAdd: function () {
 
12155                 Renderer.prototype.onAdd.call(this);
 
12157                 // Redraw vectors since canvas is cleared upon removal,
 
12158                 // in case of removing the renderer itself from the map.
 
12162         _initContainer: function () {
 
12163                 var container = this._container = document.createElement('canvas');
 
12165                 on(container, 'mousemove', this._onMouseMove, this);
 
12166                 on(container, 'click dblclick mousedown mouseup contextmenu', this._onClick, this);
 
12167                 on(container, 'mouseout', this._handleMouseOut, this);
 
12169                 this._ctx = container.getContext('2d');
 
12172         _destroyContainer: function () {
 
12173                 cancelAnimFrame(this._redrawRequest);
 
12175                 remove(this._container);
 
12176                 off(this._container);
 
12177                 delete this._container;
 
12180         _updatePaths: function () {
 
12181                 if (this._postponeUpdatePaths) { return; }
 
12184                 this._redrawBounds = null;
 
12185                 for (var id in this._layers) {
 
12186                         layer = this._layers[id];
 
12192         _update: function () {
 
12193                 if (this._map._animatingZoom && this._bounds) { return; }
 
12195                 Renderer.prototype._update.call(this);
 
12197                 var b = this._bounds,
 
12198                     container = this._container,
 
12199                     size = b.getSize(),
 
12200                     m = retina ? 2 : 1;
 
12202                 setPosition(container, b.min);
 
12204                 // set canvas size (also clearing it); use double size on retina
 
12205                 container.width = m * size.x;
 
12206                 container.height = m * size.y;
 
12207                 container.style.width = size.x + 'px';
 
12208                 container.style.height = size.y + 'px';
 
12211                         this._ctx.scale(2, 2);
 
12214                 // translate so we use the same path coordinates after canvas element moves
 
12215                 this._ctx.translate(-b.min.x, -b.min.y);
 
12217                 // Tell paths to redraw themselves
 
12218                 this.fire('update');
 
12221         _reset: function () {
 
12222                 Renderer.prototype._reset.call(this);
 
12224                 if (this._postponeUpdatePaths) {
 
12225                         this._postponeUpdatePaths = false;
 
12226                         this._updatePaths();
 
12230         _initPath: function (layer) {
 
12231                 this._updateDashArray(layer);
 
12232                 this._layers[stamp(layer)] = layer;
 
12234                 var order = layer._order = {
 
12236                         prev: this._drawLast,
 
12239                 if (this._drawLast) { this._drawLast.next = order; }
 
12240                 this._drawLast = order;
 
12241                 this._drawFirst = this._drawFirst || this._drawLast;
 
12244         _addPath: function (layer) {
 
12245                 this._requestRedraw(layer);
 
12248         _removePath: function (layer) {
 
12249                 var order = layer._order;
 
12250                 var next = order.next;
 
12251                 var prev = order.prev;
 
12256                         this._drawLast = prev;
 
12261                         this._drawFirst = next;
 
12264                 delete layer._order;
 
12266                 delete this._layers[stamp(layer)];
 
12268                 this._requestRedraw(layer);
 
12271         _updatePath: function (layer) {
 
12272                 // Redraw the union of the layer's old pixel
 
12273                 // bounds and the new pixel bounds.
 
12274                 this._extendRedrawBounds(layer);
 
12277                 // The redraw will extend the redraw bounds
 
12278                 // with the new pixel bounds.
 
12279                 this._requestRedraw(layer);
 
12282         _updateStyle: function (layer) {
 
12283                 this._updateDashArray(layer);
 
12284                 this._requestRedraw(layer);
 
12287         _updateDashArray: function (layer) {
 
12288                 if (typeof layer.options.dashArray === 'string') {
 
12289                         var parts = layer.options.dashArray.split(/[, ]+/),
 
12293                         for (i = 0; i < parts.length; i++) {
 
12294                                 dashValue = Number(parts[i]);
 
12295                                 // Ignore dash array containing invalid lengths
 
12296                                 if (isNaN(dashValue)) { return; }
 
12297                                 dashArray.push(dashValue);
 
12299                         layer.options._dashArray = dashArray;
 
12301                         layer.options._dashArray = layer.options.dashArray;
 
12305         _requestRedraw: function (layer) {
 
12306                 if (!this._map) { return; }
 
12308                 this._extendRedrawBounds(layer);
 
12309                 this._redrawRequest = this._redrawRequest || requestAnimFrame(this._redraw, this);
 
12312         _extendRedrawBounds: function (layer) {
 
12313                 if (layer._pxBounds) {
 
12314                         var padding = (layer.options.weight || 0) + 1;
 
12315                         this._redrawBounds = this._redrawBounds || new Bounds();
 
12316                         this._redrawBounds.extend(layer._pxBounds.min.subtract([padding, padding]));
 
12317                         this._redrawBounds.extend(layer._pxBounds.max.add([padding, padding]));
 
12321         _redraw: function () {
 
12322                 this._redrawRequest = null;
 
12324                 if (this._redrawBounds) {
 
12325                         this._redrawBounds.min._floor();
 
12326                         this._redrawBounds.max._ceil();
 
12329                 this._clear(); // clear layers in redraw bounds
 
12330                 this._draw(); // draw layers
 
12332                 this._redrawBounds = null;
 
12335         _clear: function () {
 
12336                 var bounds = this._redrawBounds;
 
12338                         var size = bounds.getSize();
 
12339                         this._ctx.clearRect(bounds.min.x, bounds.min.y, size.x, size.y);
 
12341                         this._ctx.clearRect(0, 0, this._container.width, this._container.height);
 
12345         _draw: function () {
 
12346                 var layer, bounds = this._redrawBounds;
 
12349                         var size = bounds.getSize();
 
12350                         this._ctx.beginPath();
 
12351                         this._ctx.rect(bounds.min.x, bounds.min.y, size.x, size.y);
 
12355                 this._drawing = true;
 
12357                 for (var order = this._drawFirst; order; order = order.next) {
 
12358                         layer = order.layer;
 
12359                         if (!bounds || (layer._pxBounds && layer._pxBounds.intersects(bounds))) {
 
12360                                 layer._updatePath();
 
12364                 this._drawing = false;
 
12366                 this._ctx.restore();  // Restore state before clipping.
 
12369         _updatePoly: function (layer, closed) {
 
12370                 if (!this._drawing) { return; }
 
12373                     parts = layer._parts,
 
12374                     len = parts.length,
 
12377                 if (!len) { return; }
 
12381                 for (i = 0; i < len; i++) {
 
12382                         for (j = 0, len2 = parts[i].length; j < len2; j++) {
 
12384                                 ctx[j ? 'lineTo' : 'moveTo'](p.x, p.y);
 
12391                 this._fillStroke(ctx, layer);
 
12393                 // TODO optimization: 1 fill/stroke for all features with equal style instead of 1 for each feature
 
12396         _updateCircle: function (layer) {
 
12398                 if (!this._drawing || layer._empty()) { return; }
 
12400                 var p = layer._point,
 
12402                     r = Math.max(Math.round(layer._radius), 1),
 
12403                     s = (Math.max(Math.round(layer._radiusY), 1) || r) / r;
 
12411                 ctx.arc(p.x, p.y / s, r, 0, Math.PI * 2, false);
 
12417                 this._fillStroke(ctx, layer);
 
12420         _fillStroke: function (ctx, layer) {
 
12421                 var options = layer.options;
 
12423                 if (options.fill) {
 
12424                         ctx.globalAlpha = options.fillOpacity;
 
12425                         ctx.fillStyle = options.fillColor || options.color;
 
12426                         ctx.fill(options.fillRule || 'evenodd');
 
12429                 if (options.stroke && options.weight !== 0) {
 
12430                         if (ctx.setLineDash) {
 
12431                                 ctx.setLineDash(layer.options && layer.options._dashArray || []);
 
12433                         ctx.globalAlpha = options.opacity;
 
12434                         ctx.lineWidth = options.weight;
 
12435                         ctx.strokeStyle = options.color;
 
12436                         ctx.lineCap = options.lineCap;
 
12437                         ctx.lineJoin = options.lineJoin;
 
12442         // Canvas obviously doesn't have mouse events for individual drawn objects,
 
12443         // so we emulate that by calculating what's under the mouse on mousemove/click manually
 
12445         _onClick: function (e) {
 
12446                 var point = this._map.mouseEventToLayerPoint(e), layer, clickedLayer;
 
12448                 for (var order = this._drawFirst; order; order = order.next) {
 
12449                         layer = order.layer;
 
12450                         if (layer.options.interactive && layer._containsPoint(point) && !this._map._draggableMoved(layer)) {
 
12451                                 clickedLayer = layer;
 
12454                 if (clickedLayer)  {
 
12456                         this._fireEvent([clickedLayer], e);
 
12460         _onMouseMove: function (e) {
 
12461                 if (!this._map || this._map.dragging.moving() || this._map._animatingZoom) { return; }
 
12463                 var point = this._map.mouseEventToLayerPoint(e);
 
12464                 this._handleMouseHover(e, point);
 
12468         _handleMouseOut: function (e) {
 
12469                 var layer = this._hoveredLayer;
 
12471                         // if we're leaving the layer, fire mouseout
 
12472                         removeClass(this._container, 'leaflet-interactive');
 
12473                         this._fireEvent([layer], e, 'mouseout');
 
12474                         this._hoveredLayer = null;
 
12475                         this._mouseHoverThrottled = false;
 
12479         _handleMouseHover: function (e, point) {
 
12480                 if (this._mouseHoverThrottled) {
 
12484                 var layer, candidateHoveredLayer;
 
12486                 for (var order = this._drawFirst; order; order = order.next) {
 
12487                         layer = order.layer;
 
12488                         if (layer.options.interactive && layer._containsPoint(point)) {
 
12489                                 candidateHoveredLayer = layer;
 
12493                 if (candidateHoveredLayer !== this._hoveredLayer) {
 
12494                         this._handleMouseOut(e);
 
12496                         if (candidateHoveredLayer) {
 
12497                                 addClass(this._container, 'leaflet-interactive'); // change cursor
 
12498                                 this._fireEvent([candidateHoveredLayer], e, 'mouseover');
 
12499                                 this._hoveredLayer = candidateHoveredLayer;
 
12503                 if (this._hoveredLayer) {
 
12504                         this._fireEvent([this._hoveredLayer], e);
 
12507                 this._mouseHoverThrottled = true;
 
12508                 setTimeout(L.bind(function () {
 
12509                         this._mouseHoverThrottled = false;
 
12513         _fireEvent: function (layers, e, type) {
 
12514                 this._map._fireDOMEvent(e, type || e.type, layers);
 
12517         _bringToFront: function (layer) {
 
12518                 var order = layer._order;
 
12520                 if (!order) { return; }
 
12522                 var next = order.next;
 
12523                 var prev = order.prev;
 
12534                         // Update first entry unless this is the
 
12536                         this._drawFirst = next;
 
12539                 order.prev = this._drawLast;
 
12540                 this._drawLast.next = order;
 
12543                 this._drawLast = order;
 
12545                 this._requestRedraw(layer);
 
12548         _bringToBack: function (layer) {
 
12549                 var order = layer._order;
 
12551                 if (!order) { return; }
 
12553                 var next = order.next;
 
12554                 var prev = order.prev;
 
12565                         // Update last entry unless this is the
 
12567                         this._drawLast = prev;
 
12572                 order.next = this._drawFirst;
 
12573                 this._drawFirst.prev = order;
 
12574                 this._drawFirst = order;
 
12576                 this._requestRedraw(layer);
 
12580 // @factory L.canvas(options?: Renderer options)
 
12581 // Creates a Canvas renderer with the given options.
 
12582 function canvas$1(options) {
 
12583         return canvas ? new Canvas(options) : null;
 
12587  * Thanks to Dmitry Baranovsky and his Raphael library for inspiration!
 
12591 var vmlCreate = (function () {
 
12593                 document.namespaces.add('lvml', 'urn:schemas-microsoft-com:vml');
 
12594                 return function (name) {
 
12595                         return document.createElement('<lvml:' + name + ' class="lvml">');
 
12598                 return function (name) {
 
12599                         return document.createElement('<' + name + ' xmlns="urn:schemas-microsoft.com:vml" class="lvml">');
 
12609  * VML was deprecated in 2012, which means VML functionality exists only for backwards compatibility
 
12610  * with old versions of Internet Explorer.
 
12613 // mixin to redefine some SVG methods to handle VML syntax which is similar but with some differences
 
12616         _initContainer: function () {
 
12617                 this._container = create$1('div', 'leaflet-vml-container');
 
12620         _update: function () {
 
12621                 if (this._map._animatingZoom) { return; }
 
12622                 Renderer.prototype._update.call(this);
 
12623                 this.fire('update');
 
12626         _initPath: function (layer) {
 
12627                 var container = layer._container = vmlCreate('shape');
 
12629                 addClass(container, 'leaflet-vml-shape ' + (this.options.className || ''));
 
12631                 container.coordsize = '1 1';
 
12633                 layer._path = vmlCreate('path');
 
12634                 container.appendChild(layer._path);
 
12636                 this._updateStyle(layer);
 
12637                 this._layers[stamp(layer)] = layer;
 
12640         _addPath: function (layer) {
 
12641                 var container = layer._container;
 
12642                 this._container.appendChild(container);
 
12644                 if (layer.options.interactive) {
 
12645                         layer.addInteractiveTarget(container);
 
12649         _removePath: function (layer) {
 
12650                 var container = layer._container;
 
12652                 layer.removeInteractiveTarget(container);
 
12653                 delete this._layers[stamp(layer)];
 
12656         _updateStyle: function (layer) {
 
12657                 var stroke = layer._stroke,
 
12658                     fill = layer._fill,
 
12659                     options = layer.options,
 
12660                     container = layer._container;
 
12662                 container.stroked = !!options.stroke;
 
12663                 container.filled = !!options.fill;
 
12665                 if (options.stroke) {
 
12667                                 stroke = layer._stroke = vmlCreate('stroke');
 
12669                         container.appendChild(stroke);
 
12670                         stroke.weight = options.weight + 'px';
 
12671                         stroke.color = options.color;
 
12672                         stroke.opacity = options.opacity;
 
12674                         if (options.dashArray) {
 
12675                                 stroke.dashStyle = isArray(options.dashArray) ?
 
12676                                     options.dashArray.join(' ') :
 
12677                                     options.dashArray.replace(/( *, *)/g, ' ');
 
12679                                 stroke.dashStyle = '';
 
12681                         stroke.endcap = options.lineCap.replace('butt', 'flat');
 
12682                         stroke.joinstyle = options.lineJoin;
 
12684                 } else if (stroke) {
 
12685                         container.removeChild(stroke);
 
12686                         layer._stroke = null;
 
12689                 if (options.fill) {
 
12691                                 fill = layer._fill = vmlCreate('fill');
 
12693                         container.appendChild(fill);
 
12694                         fill.color = options.fillColor || options.color;
 
12695                         fill.opacity = options.fillOpacity;
 
12698                         container.removeChild(fill);
 
12699                         layer._fill = null;
 
12703         _updateCircle: function (layer) {
 
12704                 var p = layer._point.round(),
 
12705                     r = Math.round(layer._radius),
 
12706                     r2 = Math.round(layer._radiusY || r);
 
12708                 this._setPath(layer, layer._empty() ? 'M0 0' :
 
12709                         'AL ' + p.x + ',' + p.y + ' ' + r + ',' + r2 + ' 0,' + (65535 * 360));
 
12712         _setPath: function (layer, path) {
 
12713                 layer._path.v = path;
 
12716         _bringToFront: function (layer) {
 
12717                 toFront(layer._container);
 
12720         _bringToBack: function (layer) {
 
12721                 toBack(layer._container);
 
12725 var create$2 = vml ? vmlCreate : svgCreate;
 
12729  * @inherits Renderer
 
12732  * Allows vector layers to be displayed with [SVG](https://developer.mozilla.org/docs/Web/SVG).
 
12733  * Inherits `Renderer`.
 
12735  * Due to [technical limitations](http://caniuse.com/#search=svg), SVG is not
 
12736  * available in all web browsers, notably Android 2.x and 3.x.
 
12738  * Although SVG is not available on IE7 and IE8, these browsers support
 
12739  * [VML](https://en.wikipedia.org/wiki/Vector_Markup_Language)
 
12740  * (a now deprecated technology), and the SVG renderer will fall back to VML in
 
12745  * Use SVG by default for all paths in the map:
 
12748  * var map = L.map('map', {
 
12749  *      renderer: L.svg()
 
12753  * Use a SVG renderer with extra padding for specific vector geometries:
 
12756  * var map = L.map('map');
 
12757  * var myRenderer = L.svg({ padding: 0.5 });
 
12758  * var line = L.polyline( coordinates, { renderer: myRenderer } );
 
12759  * var circle = L.circle( center, { renderer: myRenderer } );
 
12763 var SVG = Renderer.extend({
 
12765         getEvents: function () {
 
12766                 var events = Renderer.prototype.getEvents.call(this);
 
12767                 events.zoomstart = this._onZoomStart;
 
12771         _initContainer: function () {
 
12772                 this._container = create$2('svg');
 
12774                 // makes it possible to click through svg root; we'll reset it back in individual paths
 
12775                 this._container.setAttribute('pointer-events', 'none');
 
12777                 this._rootGroup = create$2('g');
 
12778                 this._container.appendChild(this._rootGroup);
 
12781         _destroyContainer: function () {
 
12782                 remove(this._container);
 
12783                 off(this._container);
 
12784                 delete this._container;
 
12785                 delete this._rootGroup;
 
12786                 delete this._svgSize;
 
12789         _onZoomStart: function () {
 
12790                 // Drag-then-pinch interactions might mess up the center and zoom.
 
12791                 // In this case, the easiest way to prevent this is re-do the renderer
 
12792                 //   bounds and padding when the zooming starts.
 
12796         _update: function () {
 
12797                 if (this._map._animatingZoom && this._bounds) { return; }
 
12799                 Renderer.prototype._update.call(this);
 
12801                 var b = this._bounds,
 
12802                     size = b.getSize(),
 
12803                     container = this._container;
 
12805                 // set size of svg-container if changed
 
12806                 if (!this._svgSize || !this._svgSize.equals(size)) {
 
12807                         this._svgSize = size;
 
12808                         container.setAttribute('width', size.x);
 
12809                         container.setAttribute('height', size.y);
 
12812                 // movement: update container viewBox so that we don't have to change coordinates of individual layers
 
12813                 setPosition(container, b.min);
 
12814                 container.setAttribute('viewBox', [b.min.x, b.min.y, size.x, size.y].join(' '));
 
12816                 this.fire('update');
 
12819         // methods below are called by vector layers implementations
 
12821         _initPath: function (layer) {
 
12822                 var path = layer._path = create$2('path');
 
12825                 // @option className: String = null
 
12826                 // Custom class name set on an element. Only for SVG renderer.
 
12827                 if (layer.options.className) {
 
12828                         addClass(path, layer.options.className);
 
12831                 if (layer.options.interactive) {
 
12832                         addClass(path, 'leaflet-interactive');
 
12835                 this._updateStyle(layer);
 
12836                 this._layers[stamp(layer)] = layer;
 
12839         _addPath: function (layer) {
 
12840                 if (!this._rootGroup) { this._initContainer(); }
 
12841                 this._rootGroup.appendChild(layer._path);
 
12842                 layer.addInteractiveTarget(layer._path);
 
12845         _removePath: function (layer) {
 
12846                 remove(layer._path);
 
12847                 layer.removeInteractiveTarget(layer._path);
 
12848                 delete this._layers[stamp(layer)];
 
12851         _updatePath: function (layer) {
 
12856         _updateStyle: function (layer) {
 
12857                 var path = layer._path,
 
12858                     options = layer.options;
 
12860                 if (!path) { return; }
 
12862                 if (options.stroke) {
 
12863                         path.setAttribute('stroke', options.color);
 
12864                         path.setAttribute('stroke-opacity', options.opacity);
 
12865                         path.setAttribute('stroke-width', options.weight);
 
12866                         path.setAttribute('stroke-linecap', options.lineCap);
 
12867                         path.setAttribute('stroke-linejoin', options.lineJoin);
 
12869                         if (options.dashArray) {
 
12870                                 path.setAttribute('stroke-dasharray', options.dashArray);
 
12872                                 path.removeAttribute('stroke-dasharray');
 
12875                         if (options.dashOffset) {
 
12876                                 path.setAttribute('stroke-dashoffset', options.dashOffset);
 
12878                                 path.removeAttribute('stroke-dashoffset');
 
12881                         path.setAttribute('stroke', 'none');
 
12884                 if (options.fill) {
 
12885                         path.setAttribute('fill', options.fillColor || options.color);
 
12886                         path.setAttribute('fill-opacity', options.fillOpacity);
 
12887                         path.setAttribute('fill-rule', options.fillRule || 'evenodd');
 
12889                         path.setAttribute('fill', 'none');
 
12893         _updatePoly: function (layer, closed) {
 
12894                 this._setPath(layer, pointsToPath(layer._parts, closed));
 
12897         _updateCircle: function (layer) {
 
12898                 var p = layer._point,
 
12899                     r = Math.max(Math.round(layer._radius), 1),
 
12900                     r2 = Math.max(Math.round(layer._radiusY), 1) || r,
 
12901                     arc = 'a' + r + ',' + r2 + ' 0 1,0 ';
 
12903                 // drawing a circle with two half-arcs
 
12904                 var d = layer._empty() ? 'M0 0' :
 
12905                         'M' + (p.x - r) + ',' + p.y +
 
12906                         arc + (r * 2) + ',0 ' +
 
12907                         arc + (-r * 2) + ',0 ';
 
12909                 this._setPath(layer, d);
 
12912         _setPath: function (layer, path) {
 
12913                 layer._path.setAttribute('d', path);
 
12916         // SVG does not have the concept of zIndex so we resort to changing the DOM order of elements
 
12917         _bringToFront: function (layer) {
 
12918                 toFront(layer._path);
 
12921         _bringToBack: function (layer) {
 
12922                 toBack(layer._path);
 
12927         SVG.include(vmlMixin);
 
12931 // @factory L.svg(options?: Renderer options)
 
12932 // Creates a SVG renderer with the given options.
 
12933 function svg$1(options) {
 
12934         return svg || vml ? new SVG(options) : null;
 
12938         // @namespace Map; @method getRenderer(layer: Path): Renderer
 
12939         // Returns the instance of `Renderer` that should be used to render the given
 
12940         // `Path`. It will ensure that the `renderer` options of the map and paths
 
12941         // are respected, and that the renderers do exist on the map.
 
12942         getRenderer: function (layer) {
 
12943                 // @namespace Path; @option renderer: Renderer
 
12944                 // Use this specific instance of `Renderer` for this path. Takes
 
12945                 // precedence over the map's [default renderer](#map-renderer).
 
12946                 var renderer = layer.options.renderer || this._getPaneRenderer(layer.options.pane) || this.options.renderer || this._renderer;
 
12949                         renderer = this._renderer = this._createRenderer();
 
12952                 if (!this.hasLayer(renderer)) {
 
12953                         this.addLayer(renderer);
 
12958         _getPaneRenderer: function (name) {
 
12959                 if (name === 'overlayPane' || name === undefined) {
 
12963                 var renderer = this._paneRenderers[name];
 
12964                 if (renderer === undefined) {
 
12965                         renderer = this._createRenderer({pane: name});
 
12966                         this._paneRenderers[name] = renderer;
 
12971         _createRenderer: function (options) {
 
12972                 // @namespace Map; @option preferCanvas: Boolean = false
 
12973                 // Whether `Path`s should be rendered on a `Canvas` renderer.
 
12974                 // By default, all `Path`s are rendered in a `SVG` renderer.
 
12975                 return (this.options.preferCanvas && canvas$1(options)) || svg$1(options);
 
12980  * L.Rectangle extends Polygon and creates a rectangle when passed a LatLngBounds object.
 
12986  * @inherits Polygon
 
12988  * A class for drawing rectangle overlays on a map. Extends `Polygon`.
 
12993  * // define rectangle geographical bounds
 
12994  * var bounds = [[54.559322, -5.767822], [56.1210604, -3.021240]];
 
12996  * // create an orange rectangle
 
12997  * L.rectangle(bounds, {color: "#ff7800", weight: 1}).addTo(map);
 
12999  * // zoom the map to the rectangle bounds
 
13000  * map.fitBounds(bounds);
 
13006 var Rectangle = Polygon.extend({
 
13007         initialize: function (latLngBounds, options) {
 
13008                 Polygon.prototype.initialize.call(this, this._boundsToLatLngs(latLngBounds), options);
 
13011         // @method setBounds(latLngBounds: LatLngBounds): this
 
13012         // Redraws the rectangle with the passed bounds.
 
13013         setBounds: function (latLngBounds) {
 
13014                 return this.setLatLngs(this._boundsToLatLngs(latLngBounds));
 
13017         _boundsToLatLngs: function (latLngBounds) {
 
13018                 latLngBounds = toLatLngBounds(latLngBounds);
 
13020                         latLngBounds.getSouthWest(),
 
13021                         latLngBounds.getNorthWest(),
 
13022                         latLngBounds.getNorthEast(),
 
13023                         latLngBounds.getSouthEast()
 
13029 // @factory L.rectangle(latLngBounds: LatLngBounds, options?: Polyline options)
 
13030 function rectangle(latLngBounds, options) {
 
13031         return new Rectangle(latLngBounds, options);
 
13034 SVG.create = create$2;
 
13035 SVG.pointsToPath = pointsToPath;
 
13037 GeoJSON.geometryToLayer = geometryToLayer;
 
13038 GeoJSON.coordsToLatLng = coordsToLatLng;
 
13039 GeoJSON.coordsToLatLngs = coordsToLatLngs;
 
13040 GeoJSON.latLngToCoords = latLngToCoords;
 
13041 GeoJSON.latLngsToCoords = latLngsToCoords;
 
13042 GeoJSON.getFeature = getFeature;
 
13043 GeoJSON.asFeature = asFeature;
 
13046  * L.Handler.BoxZoom is used to add shift-drag zoom interaction to the map
 
13047  * (zoom to a selected bounding box), enabled by default.
 
13051 // @section Interaction Options
 
13053         // @option boxZoom: Boolean = true
 
13054         // Whether the map can be zoomed to a rectangular area specified by
 
13055         // dragging the mouse while pressing the shift key.
 
13059 var BoxZoom = Handler.extend({
 
13060         initialize: function (map) {
 
13062                 this._container = map._container;
 
13063                 this._pane = map._panes.overlayPane;
 
13064                 this._resetStateTimeout = 0;
 
13065                 map.on('unload', this._destroy, this);
 
13068         addHooks: function () {
 
13069                 on(this._container, 'mousedown', this._onMouseDown, this);
 
13072         removeHooks: function () {
 
13073                 off(this._container, 'mousedown', this._onMouseDown, this);
 
13076         moved: function () {
 
13077                 return this._moved;
 
13080         _destroy: function () {
 
13081                 remove(this._pane);
 
13085         _resetState: function () {
 
13086                 this._resetStateTimeout = 0;
 
13087                 this._moved = false;
 
13090         _clearDeferredResetState: function () {
 
13091                 if (this._resetStateTimeout !== 0) {
 
13092                         clearTimeout(this._resetStateTimeout);
 
13093                         this._resetStateTimeout = 0;
 
13097         _onMouseDown: function (e) {
 
13098                 if (!e.shiftKey || ((e.which !== 1) && (e.button !== 1))) { return false; }
 
13100                 // Clear the deferred resetState if it hasn't executed yet, otherwise it
 
13101                 // will interrupt the interaction and orphan a box element in the container.
 
13102                 this._clearDeferredResetState();
 
13103                 this._resetState();
 
13105                 disableTextSelection();
 
13106                 disableImageDrag();
 
13108                 this._startPoint = this._map.mouseEventToContainerPoint(e);
 
13112                         mousemove: this._onMouseMove,
 
13113                         mouseup: this._onMouseUp,
 
13114                         keydown: this._onKeyDown
 
13118         _onMouseMove: function (e) {
 
13119                 if (!this._moved) {
 
13120                         this._moved = true;
 
13122                         this._box = create$1('div', 'leaflet-zoom-box', this._container);
 
13123                         addClass(this._container, 'leaflet-crosshair');
 
13125                         this._map.fire('boxzoomstart');
 
13128                 this._point = this._map.mouseEventToContainerPoint(e);
 
13130                 var bounds = new Bounds(this._point, this._startPoint),
 
13131                     size = bounds.getSize();
 
13133                 setPosition(this._box, bounds.min);
 
13135                 this._box.style.width  = size.x + 'px';
 
13136                 this._box.style.height = size.y + 'px';
 
13139         _finish: function () {
 
13142                         removeClass(this._container, 'leaflet-crosshair');
 
13145                 enableTextSelection();
 
13150                         mousemove: this._onMouseMove,
 
13151                         mouseup: this._onMouseUp,
 
13152                         keydown: this._onKeyDown
 
13156         _onMouseUp: function (e) {
 
13157                 if ((e.which !== 1) && (e.button !== 1)) { return; }
 
13161                 if (!this._moved) { return; }
 
13162                 // Postpone to next JS tick so internal click event handling
 
13163                 // still see it as "moved".
 
13164                 this._clearDeferredResetState();
 
13165                 this._resetStateTimeout = setTimeout(bind(this._resetState, this), 0);
 
13167                 var bounds = new LatLngBounds(
 
13168                         this._map.containerPointToLatLng(this._startPoint),
 
13169                         this._map.containerPointToLatLng(this._point));
 
13173                         .fire('boxzoomend', {boxZoomBounds: bounds});
 
13176         _onKeyDown: function (e) {
 
13177                 if (e.keyCode === 27) {
 
13183 // @section Handlers
 
13184 // @property boxZoom: Handler
 
13185 // Box (shift-drag with mouse) zoom handler.
 
13186 Map.addInitHook('addHandler', 'boxZoom', BoxZoom);
 
13189  * L.Handler.DoubleClickZoom is used to handle double-click zoom on the map, enabled by default.
 
13193 // @section Interaction Options
 
13196         // @option doubleClickZoom: Boolean|String = true
 
13197         // Whether the map can be zoomed in by double clicking on it and
 
13198         // zoomed out by double clicking while holding shift. If passed
 
13199         // `'center'`, double-click zoom will zoom to the center of the
 
13200         //  view regardless of where the mouse was.
 
13201         doubleClickZoom: true
 
13204 var DoubleClickZoom = Handler.extend({
 
13205         addHooks: function () {
 
13206                 this._map.on('dblclick', this._onDoubleClick, this);
 
13209         removeHooks: function () {
 
13210                 this._map.off('dblclick', this._onDoubleClick, this);
 
13213         _onDoubleClick: function (e) {
 
13214                 var map = this._map,
 
13215                     oldZoom = map.getZoom(),
 
13216                     delta = map.options.zoomDelta,
 
13217                     zoom = e.originalEvent.shiftKey ? oldZoom - delta : oldZoom + delta;
 
13219                 if (map.options.doubleClickZoom === 'center') {
 
13222                         map.setZoomAround(e.containerPoint, zoom);
 
13227 // @section Handlers
 
13229 // Map properties include interaction handlers that allow you to control
 
13230 // interaction behavior in runtime, enabling or disabling certain features such
 
13231 // as dragging or touch zoom (see `Handler` methods). For example:
 
13234 // map.doubleClickZoom.disable();
 
13237 // @property doubleClickZoom: Handler
 
13238 // Double click zoom handler.
 
13239 Map.addInitHook('addHandler', 'doubleClickZoom', DoubleClickZoom);
 
13242  * L.Handler.MapDrag is used to make the map draggable (with panning inertia), enabled by default.
 
13246 // @section Interaction Options
 
13248         // @option dragging: Boolean = true
 
13249         // Whether the map be draggable with mouse/touch or not.
 
13252         // @section Panning Inertia Options
 
13253         // @option inertia: Boolean = *
 
13254         // If enabled, panning of the map will have an inertia effect where
 
13255         // the map builds momentum while dragging and continues moving in
 
13256         // the same direction for some time. Feels especially nice on touch
 
13257         // devices. Enabled by default unless running on old Android devices.
 
13258         inertia: !android23,
 
13260         // @option inertiaDeceleration: Number = 3000
 
13261         // The rate with which the inertial movement slows down, in pixels/second².
 
13262         inertiaDeceleration: 3400, // px/s^2
 
13264         // @option inertiaMaxSpeed: Number = Infinity
 
13265         // Max speed of the inertial movement, in pixels/second.
 
13266         inertiaMaxSpeed: Infinity, // px/s
 
13268         // @option easeLinearity: Number = 0.2
 
13269         easeLinearity: 0.2,
 
13271         // TODO refactor, move to CRS
 
13272         // @option worldCopyJump: Boolean = false
 
13273         // With this option enabled, the map tracks when you pan to another "copy"
 
13274         // of the world and seamlessly jumps to the original one so that all overlays
 
13275         // like markers and vector layers are still visible.
 
13276         worldCopyJump: false,
 
13278         // @option maxBoundsViscosity: Number = 0.0
 
13279         // If `maxBounds` is set, this option will control how solid the bounds
 
13280         // are when dragging the map around. The default value of `0.0` allows the
 
13281         // user to drag outside the bounds at normal speed, higher values will
 
13282         // slow down map dragging outside bounds, and `1.0` makes the bounds fully
 
13283         // solid, preventing the user from dragging outside the bounds.
 
13284         maxBoundsViscosity: 0.0
 
13287 var Drag = Handler.extend({
 
13288         addHooks: function () {
 
13289                 if (!this._draggable) {
 
13290                         var map = this._map;
 
13292                         this._draggable = new Draggable(map._mapPane, map._container);
 
13294                         this._draggable.on({
 
13295                                 dragstart: this._onDragStart,
 
13296                                 drag: this._onDrag,
 
13297                                 dragend: this._onDragEnd
 
13300                         this._draggable.on('predrag', this._onPreDragLimit, this);
 
13301                         if (map.options.worldCopyJump) {
 
13302                                 this._draggable.on('predrag', this._onPreDragWrap, this);
 
13303                                 map.on('zoomend', this._onZoomEnd, this);
 
13305                                 map.whenReady(this._onZoomEnd, this);
 
13308                 addClass(this._map._container, 'leaflet-grab leaflet-touch-drag');
 
13309                 this._draggable.enable();
 
13310                 this._positions = [];
 
13314         removeHooks: function () {
 
13315                 removeClass(this._map._container, 'leaflet-grab');
 
13316                 removeClass(this._map._container, 'leaflet-touch-drag');
 
13317                 this._draggable.disable();
 
13320         moved: function () {
 
13321                 return this._draggable && this._draggable._moved;
 
13324         moving: function () {
 
13325                 return this._draggable && this._draggable._moving;
 
13328         _onDragStart: function () {
 
13329                 var map = this._map;
 
13332                 if (this._map.options.maxBounds && this._map.options.maxBoundsViscosity) {
 
13333                         var bounds = toLatLngBounds(this._map.options.maxBounds);
 
13335                         this._offsetLimit = toBounds(
 
13336                                 this._map.latLngToContainerPoint(bounds.getNorthWest()).multiplyBy(-1),
 
13337                                 this._map.latLngToContainerPoint(bounds.getSouthEast()).multiplyBy(-1)
 
13338                                         .add(this._map.getSize()));
 
13340                         this._viscosity = Math.min(1.0, Math.max(0.0, this._map.options.maxBoundsViscosity));
 
13342                         this._offsetLimit = null;
 
13347                     .fire('dragstart');
 
13349                 if (map.options.inertia) {
 
13350                         this._positions = [];
 
13355         _onDrag: function (e) {
 
13356                 if (this._map.options.inertia) {
 
13357                         var time = this._lastTime = +new Date(),
 
13358                             pos = this._lastPos = this._draggable._absPos || this._draggable._newPos;
 
13360                         this._positions.push(pos);
 
13361                         this._times.push(time);
 
13363                         this._prunePositions(time);
 
13371         _prunePositions: function (time) {
 
13372                 while (this._positions.length > 1 && time - this._times[0] > 50) {
 
13373                         this._positions.shift();
 
13374                         this._times.shift();
 
13378         _onZoomEnd: function () {
 
13379                 var pxCenter = this._map.getSize().divideBy(2),
 
13380                     pxWorldCenter = this._map.latLngToLayerPoint([0, 0]);
 
13382                 this._initialWorldOffset = pxWorldCenter.subtract(pxCenter).x;
 
13383                 this._worldWidth = this._map.getPixelWorldBounds().getSize().x;
 
13386         _viscousLimit: function (value, threshold) {
 
13387                 return value - (value - threshold) * this._viscosity;
 
13390         _onPreDragLimit: function () {
 
13391                 if (!this._viscosity || !this._offsetLimit) { return; }
 
13393                 var offset = this._draggable._newPos.subtract(this._draggable._startPos);
 
13395                 var limit = this._offsetLimit;
 
13396                 if (offset.x < limit.min.x) { offset.x = this._viscousLimit(offset.x, limit.min.x); }
 
13397                 if (offset.y < limit.min.y) { offset.y = this._viscousLimit(offset.y, limit.min.y); }
 
13398                 if (offset.x > limit.max.x) { offset.x = this._viscousLimit(offset.x, limit.max.x); }
 
13399                 if (offset.y > limit.max.y) { offset.y = this._viscousLimit(offset.y, limit.max.y); }
 
13401                 this._draggable._newPos = this._draggable._startPos.add(offset);
 
13404         _onPreDragWrap: function () {
 
13405                 // TODO refactor to be able to adjust map pane position after zoom
 
13406                 var worldWidth = this._worldWidth,
 
13407                     halfWidth = Math.round(worldWidth / 2),
 
13408                     dx = this._initialWorldOffset,
 
13409                     x = this._draggable._newPos.x,
 
13410                     newX1 = (x - halfWidth + dx) % worldWidth + halfWidth - dx,
 
13411                     newX2 = (x + halfWidth + dx) % worldWidth - halfWidth - dx,
 
13412                     newX = Math.abs(newX1 + dx) < Math.abs(newX2 + dx) ? newX1 : newX2;
 
13414                 this._draggable._absPos = this._draggable._newPos.clone();
 
13415                 this._draggable._newPos.x = newX;
 
13418         _onDragEnd: function (e) {
 
13419                 var map = this._map,
 
13420                     options = map.options,
 
13422                     noInertia = !options.inertia || this._times.length < 2;
 
13424                 map.fire('dragend', e);
 
13427                         map.fire('moveend');
 
13430                         this._prunePositions(+new Date());
 
13432                         var direction = this._lastPos.subtract(this._positions[0]),
 
13433                             duration = (this._lastTime - this._times[0]) / 1000,
 
13434                             ease = options.easeLinearity,
 
13436                             speedVector = direction.multiplyBy(ease / duration),
 
13437                             speed = speedVector.distanceTo([0, 0]),
 
13439                             limitedSpeed = Math.min(options.inertiaMaxSpeed, speed),
 
13440                             limitedSpeedVector = speedVector.multiplyBy(limitedSpeed / speed),
 
13442                             decelerationDuration = limitedSpeed / (options.inertiaDeceleration * ease),
 
13443                             offset = limitedSpeedVector.multiplyBy(-decelerationDuration / 2).round();
 
13445                         if (!offset.x && !offset.y) {
 
13446                                 map.fire('moveend');
 
13449                                 offset = map._limitOffset(offset, map.options.maxBounds);
 
13451                                 requestAnimFrame(function () {
 
13452                                         map.panBy(offset, {
 
13453                                                 duration: decelerationDuration,
 
13454                                                 easeLinearity: ease,
 
13464 // @section Handlers
 
13465 // @property dragging: Handler
 
13466 // Map dragging handler (by both mouse and touch).
 
13467 Map.addInitHook('addHandler', 'dragging', Drag);
 
13470  * L.Map.Keyboard is handling keyboard interaction with the map, enabled by default.
 
13474 // @section Keyboard Navigation Options
 
13476         // @option keyboard: Boolean = true
 
13477         // Makes the map focusable and allows users to navigate the map with keyboard
 
13478         // arrows and `+`/`-` keys.
 
13481         // @option keyboardPanDelta: Number = 80
 
13482         // Amount of pixels to pan when pressing an arrow key.
 
13483         keyboardPanDelta: 80
 
13486 var Keyboard = Handler.extend({
 
13493                 zoomIn:  [187, 107, 61, 171],
 
13494                 zoomOut: [189, 109, 54, 173]
 
13497         initialize: function (map) {
 
13500                 this._setPanDelta(map.options.keyboardPanDelta);
 
13501                 this._setZoomDelta(map.options.zoomDelta);
 
13504         addHooks: function () {
 
13505                 var container = this._map._container;
 
13507                 // make the container focusable by tabbing
 
13508                 if (container.tabIndex <= 0) {
 
13509                         container.tabIndex = '0';
 
13513                         focus: this._onFocus,
 
13514                         blur: this._onBlur,
 
13515                         mousedown: this._onMouseDown
 
13519                         focus: this._addHooks,
 
13520                         blur: this._removeHooks
 
13524         removeHooks: function () {
 
13525                 this._removeHooks();
 
13527                 off(this._map._container, {
 
13528                         focus: this._onFocus,
 
13529                         blur: this._onBlur,
 
13530                         mousedown: this._onMouseDown
 
13534                         focus: this._addHooks,
 
13535                         blur: this._removeHooks
 
13539         _onMouseDown: function () {
 
13540                 if (this._focused) { return; }
 
13542                 var body = document.body,
 
13543                     docEl = document.documentElement,
 
13544                     top = body.scrollTop || docEl.scrollTop,
 
13545                     left = body.scrollLeft || docEl.scrollLeft;
 
13547                 this._map._container.focus();
 
13549                 window.scrollTo(left, top);
 
13552         _onFocus: function () {
 
13553                 this._focused = true;
 
13554                 this._map.fire('focus');
 
13557         _onBlur: function () {
 
13558                 this._focused = false;
 
13559                 this._map.fire('blur');
 
13562         _setPanDelta: function (panDelta) {
 
13563                 var keys = this._panKeys = {},
 
13564                     codes = this.keyCodes,
 
13567                 for (i = 0, len = codes.left.length; i < len; i++) {
 
13568                         keys[codes.left[i]] = [-1 * panDelta, 0];
 
13570                 for (i = 0, len = codes.right.length; i < len; i++) {
 
13571                         keys[codes.right[i]] = [panDelta, 0];
 
13573                 for (i = 0, len = codes.down.length; i < len; i++) {
 
13574                         keys[codes.down[i]] = [0, panDelta];
 
13576                 for (i = 0, len = codes.up.length; i < len; i++) {
 
13577                         keys[codes.up[i]] = [0, -1 * panDelta];
 
13581         _setZoomDelta: function (zoomDelta) {
 
13582                 var keys = this._zoomKeys = {},
 
13583                     codes = this.keyCodes,
 
13586                 for (i = 0, len = codes.zoomIn.length; i < len; i++) {
 
13587                         keys[codes.zoomIn[i]] = zoomDelta;
 
13589                 for (i = 0, len = codes.zoomOut.length; i < len; i++) {
 
13590                         keys[codes.zoomOut[i]] = -zoomDelta;
 
13594         _addHooks: function () {
 
13595                 on(document, 'keydown', this._onKeyDown, this);
 
13598         _removeHooks: function () {
 
13599                 off(document, 'keydown', this._onKeyDown, this);
 
13602         _onKeyDown: function (e) {
 
13603                 if (e.altKey || e.ctrlKey || e.metaKey) { return; }
 
13605                 var key = e.keyCode,
 
13609                 if (key in this._panKeys) {
 
13610                         if (!map._panAnim || !map._panAnim._inProgress) {
 
13611                                 offset = this._panKeys[key];
 
13613                                         offset = toPoint(offset).multiplyBy(3);
 
13618                                 if (map.options.maxBounds) {
 
13619                                         map.panInsideBounds(map.options.maxBounds);
 
13622                 } else if (key in this._zoomKeys) {
 
13623                         map.setZoom(map.getZoom() + (e.shiftKey ? 3 : 1) * this._zoomKeys[key]);
 
13625                 } else if (key === 27 && map._popup && map._popup.options.closeOnEscapeKey) {
 
13636 // @section Handlers
 
13637 // @section Handlers
 
13638 // @property keyboard: Handler
 
13639 // Keyboard navigation handler.
 
13640 Map.addInitHook('addHandler', 'keyboard', Keyboard);
 
13643  * L.Handler.ScrollWheelZoom is used by L.Map to enable mouse scroll wheel zoom on the map.
 
13647 // @section Interaction Options
 
13649         // @section Mousewheel options
 
13650         // @option scrollWheelZoom: Boolean|String = true
 
13651         // Whether the map can be zoomed by using the mouse wheel. If passed `'center'`,
 
13652         // it will zoom to the center of the view regardless of where the mouse was.
 
13653         scrollWheelZoom: true,
 
13655         // @option wheelDebounceTime: Number = 40
 
13656         // Limits the rate at which a wheel can fire (in milliseconds). By default
 
13657         // user can't zoom via wheel more often than once per 40 ms.
 
13658         wheelDebounceTime: 40,
 
13660         // @option wheelPxPerZoomLevel: Number = 60
 
13661         // How many scroll pixels (as reported by [L.DomEvent.getWheelDelta](#domevent-getwheeldelta))
 
13662         // mean a change of one full zoom level. Smaller values will make wheel-zooming
 
13663         // faster (and vice versa).
 
13664         wheelPxPerZoomLevel: 60
 
13667 var ScrollWheelZoom = Handler.extend({
 
13668         addHooks: function () {
 
13669                 on(this._map._container, 'mousewheel', this._onWheelScroll, this);
 
13674         removeHooks: function () {
 
13675                 off(this._map._container, 'mousewheel', this._onWheelScroll, this);
 
13678         _onWheelScroll: function (e) {
 
13679                 var delta = getWheelDelta(e);
 
13681                 var debounce = this._map.options.wheelDebounceTime;
 
13683                 this._delta += delta;
 
13684                 this._lastMousePos = this._map.mouseEventToContainerPoint(e);
 
13686                 if (!this._startTime) {
 
13687                         this._startTime = +new Date();
 
13690                 var left = Math.max(debounce - (+new Date() - this._startTime), 0);
 
13692                 clearTimeout(this._timer);
 
13693                 this._timer = setTimeout(bind(this._performZoom, this), left);
 
13698         _performZoom: function () {
 
13699                 var map = this._map,
 
13700                     zoom = map.getZoom(),
 
13701                     snap = this._map.options.zoomSnap || 0;
 
13703                 map._stop(); // stop panning and fly animations if any
 
13705                 // map the delta with a sigmoid function to -4..4 range leaning on -1..1
 
13706                 var d2 = this._delta / (this._map.options.wheelPxPerZoomLevel * 4),
 
13707                     d3 = 4 * Math.log(2 / (1 + Math.exp(-Math.abs(d2)))) / Math.LN2,
 
13708                     d4 = snap ? Math.ceil(d3 / snap) * snap : d3,
 
13709                     delta = map._limitZoom(zoom + (this._delta > 0 ? d4 : -d4)) - zoom;
 
13712                 this._startTime = null;
 
13714                 if (!delta) { return; }
 
13716                 if (map.options.scrollWheelZoom === 'center') {
 
13717                         map.setZoom(zoom + delta);
 
13719                         map.setZoomAround(this._lastMousePos, zoom + delta);
 
13724 // @section Handlers
 
13725 // @property scrollWheelZoom: Handler
 
13726 // Scroll wheel zoom handler.
 
13727 Map.addInitHook('addHandler', 'scrollWheelZoom', ScrollWheelZoom);
 
13730  * L.Map.Tap is used to enable mobile hacks like quick taps and long hold.
 
13734 // @section Interaction Options
 
13736         // @section Touch interaction options
 
13737         // @option tap: Boolean = true
 
13738         // Enables mobile hacks for supporting instant taps (fixing 200ms click
 
13739         // delay on iOS/Android) and touch holds (fired as `contextmenu` events).
 
13742         // @option tapTolerance: Number = 15
 
13743         // The max number of pixels a user can shift his finger during touch
 
13744         // for it to be considered a valid tap.
 
13748 var Tap = Handler.extend({
 
13749         addHooks: function () {
 
13750                 on(this._map._container, 'touchstart', this._onDown, this);
 
13753         removeHooks: function () {
 
13754                 off(this._map._container, 'touchstart', this._onDown, this);
 
13757         _onDown: function (e) {
 
13758                 if (!e.touches) { return; }
 
13762                 this._fireClick = true;
 
13764                 // don't simulate click or track longpress if more than 1 touch
 
13765                 if (e.touches.length > 1) {
 
13766                         this._fireClick = false;
 
13767                         clearTimeout(this._holdTimeout);
 
13771                 var first = e.touches[0],
 
13774                 this._startPos = this._newPos = new Point(first.clientX, first.clientY);
 
13776                 // if touching a link, highlight it
 
13777                 if (el.tagName && el.tagName.toLowerCase() === 'a') {
 
13778                         addClass(el, 'leaflet-active');
 
13781                 // simulate long hold but setting a timeout
 
13782                 this._holdTimeout = setTimeout(bind(function () {
 
13783                         if (this._isTapValid()) {
 
13784                                 this._fireClick = false;
 
13786                                 this._simulateEvent('contextmenu', first);
 
13790                 this._simulateEvent('mousedown', first);
 
13793                         touchmove: this._onMove,
 
13794                         touchend: this._onUp
 
13798         _onUp: function (e) {
 
13799                 clearTimeout(this._holdTimeout);
 
13802                         touchmove: this._onMove,
 
13803                         touchend: this._onUp
 
13806                 if (this._fireClick && e && e.changedTouches) {
 
13808                         var first = e.changedTouches[0],
 
13811                         if (el && el.tagName && el.tagName.toLowerCase() === 'a') {
 
13812                                 removeClass(el, 'leaflet-active');
 
13815                         this._simulateEvent('mouseup', first);
 
13817                         // simulate click if the touch didn't move too much
 
13818                         if (this._isTapValid()) {
 
13819                                 this._simulateEvent('click', first);
 
13824         _isTapValid: function () {
 
13825                 return this._newPos.distanceTo(this._startPos) <= this._map.options.tapTolerance;
 
13828         _onMove: function (e) {
 
13829                 var first = e.touches[0];
 
13830                 this._newPos = new Point(first.clientX, first.clientY);
 
13831                 this._simulateEvent('mousemove', first);
 
13834         _simulateEvent: function (type, e) {
 
13835                 var simulatedEvent = document.createEvent('MouseEvents');
 
13837                 simulatedEvent._simulated = true;
 
13838                 e.target._simulatedClick = true;
 
13840                 simulatedEvent.initMouseEvent(
 
13841                         type, true, true, window, 1,
 
13842                         e.screenX, e.screenY,
 
13843                         e.clientX, e.clientY,
 
13844                         false, false, false, false, 0, null);
 
13846                 e.target.dispatchEvent(simulatedEvent);
 
13850 // @section Handlers
 
13851 // @property tap: Handler
 
13852 // Mobile touch hacks (quick tap and touch hold) handler.
 
13853 if (touch && !pointer) {
 
13854         Map.addInitHook('addHandler', 'tap', Tap);
 
13858  * L.Handler.TouchZoom is used by L.Map to add pinch zoom on supported mobile browsers.
 
13862 // @section Interaction Options
 
13864         // @section Touch interaction options
 
13865         // @option touchZoom: Boolean|String = *
 
13866         // Whether the map can be zoomed by touch-dragging with two fingers. If
 
13867         // passed `'center'`, it will zoom to the center of the view regardless of
 
13868         // where the touch events (fingers) were. Enabled for touch-capable web
 
13869         // browsers except for old Androids.
 
13870         touchZoom: touch && !android23,
 
13872         // @option bounceAtZoomLimits: Boolean = true
 
13873         // Set it to false if you don't want the map to zoom beyond min/max zoom
 
13874         // and then bounce back when pinch-zooming.
 
13875         bounceAtZoomLimits: true
 
13878 var TouchZoom = Handler.extend({
 
13879         addHooks: function () {
 
13880                 addClass(this._map._container, 'leaflet-touch-zoom');
 
13881                 on(this._map._container, 'touchstart', this._onTouchStart, this);
 
13884         removeHooks: function () {
 
13885                 removeClass(this._map._container, 'leaflet-touch-zoom');
 
13886                 off(this._map._container, 'touchstart', this._onTouchStart, this);
 
13889         _onTouchStart: function (e) {
 
13890                 var map = this._map;
 
13891                 if (!e.touches || e.touches.length !== 2 || map._animatingZoom || this._zooming) { return; }
 
13893                 var p1 = map.mouseEventToContainerPoint(e.touches[0]),
 
13894                     p2 = map.mouseEventToContainerPoint(e.touches[1]);
 
13896                 this._centerPoint = map.getSize()._divideBy(2);
 
13897                 this._startLatLng = map.containerPointToLatLng(this._centerPoint);
 
13898                 if (map.options.touchZoom !== 'center') {
 
13899                         this._pinchStartLatLng = map.containerPointToLatLng(p1.add(p2)._divideBy(2));
 
13902                 this._startDist = p1.distanceTo(p2);
 
13903                 this._startZoom = map.getZoom();
 
13905                 this._moved = false;
 
13906                 this._zooming = true;
 
13910                 on(document, 'touchmove', this._onTouchMove, this);
 
13911                 on(document, 'touchend', this._onTouchEnd, this);
 
13916         _onTouchMove: function (e) {
 
13917                 if (!e.touches || e.touches.length !== 2 || !this._zooming) { return; }
 
13919                 var map = this._map,
 
13920                     p1 = map.mouseEventToContainerPoint(e.touches[0]),
 
13921                     p2 = map.mouseEventToContainerPoint(e.touches[1]),
 
13922                     scale = p1.distanceTo(p2) / this._startDist;
 
13924                 this._zoom = map.getScaleZoom(scale, this._startZoom);
 
13926                 if (!map.options.bounceAtZoomLimits && (
 
13927                         (this._zoom < map.getMinZoom() && scale < 1) ||
 
13928                         (this._zoom > map.getMaxZoom() && scale > 1))) {
 
13929                         this._zoom = map._limitZoom(this._zoom);
 
13932                 if (map.options.touchZoom === 'center') {
 
13933                         this._center = this._startLatLng;
 
13934                         if (scale === 1) { return; }
 
13936                         // Get delta from pinch to center, so centerLatLng is delta applied to initial pinchLatLng
 
13937                         var delta = p1._add(p2)._divideBy(2)._subtract(this._centerPoint);
 
13938                         if (scale === 1 && delta.x === 0 && delta.y === 0) { return; }
 
13939                         this._center = map.unproject(map.project(this._pinchStartLatLng, this._zoom).subtract(delta), this._zoom);
 
13942                 if (!this._moved) {
 
13943                         map._moveStart(true, false);
 
13944                         this._moved = true;
 
13947                 cancelAnimFrame(this._animRequest);
 
13949                 var moveFn = bind(map._move, map, this._center, this._zoom, {pinch: true, round: false});
 
13950                 this._animRequest = requestAnimFrame(moveFn, this, true);
 
13955         _onTouchEnd: function () {
 
13956                 if (!this._moved || !this._zooming) {
 
13957                         this._zooming = false;
 
13961                 this._zooming = false;
 
13962                 cancelAnimFrame(this._animRequest);
 
13964                 off(document, 'touchmove', this._onTouchMove);
 
13965                 off(document, 'touchend', this._onTouchEnd);
 
13967                 // Pinch updates GridLayers' levels only when zoomSnap is off, so zoomSnap becomes noUpdate.
 
13968                 if (this._map.options.zoomAnimation) {
 
13969                         this._map._animateZoom(this._center, this._map._limitZoom(this._zoom), true, this._map.options.zoomSnap);
 
13971                         this._map._resetView(this._center, this._map._limitZoom(this._zoom));
 
13976 // @section Handlers
 
13977 // @property touchZoom: Handler
 
13978 // Touch zoom handler.
 
13979 Map.addInitHook('addHandler', 'touchZoom', TouchZoom);
 
13981 Map.BoxZoom = BoxZoom;
 
13982 Map.DoubleClickZoom = DoubleClickZoom;
 
13984 Map.Keyboard = Keyboard;
 
13985 Map.ScrollWheelZoom = ScrollWheelZoom;
 
13987 Map.TouchZoom = TouchZoom;
 
13989 Object.freeze = freeze;
 
13991 exports.version = version;
 
13992 exports.Control = Control;
 
13993 exports.control = control;
 
13994 exports.Browser = Browser;
 
13995 exports.Evented = Evented;
 
13996 exports.Mixin = Mixin;
 
13997 exports.Util = Util;
 
13998 exports.Class = Class;
 
13999 exports.Handler = Handler;
 
14000 exports.extend = extend;
 
14001 exports.bind = bind;
 
14002 exports.stamp = stamp;
 
14003 exports.setOptions = setOptions;
 
14004 exports.DomEvent = DomEvent;
 
14005 exports.DomUtil = DomUtil;
 
14006 exports.PosAnimation = PosAnimation;
 
14007 exports.Draggable = Draggable;
 
14008 exports.LineUtil = LineUtil;
 
14009 exports.PolyUtil = PolyUtil;
 
14010 exports.Point = Point;
 
14011 exports.point = toPoint;
 
14012 exports.Bounds = Bounds;
 
14013 exports.bounds = toBounds;
 
14014 exports.Transformation = Transformation;
 
14015 exports.transformation = toTransformation;
 
14016 exports.Projection = index;
 
14017 exports.LatLng = LatLng;
 
14018 exports.latLng = toLatLng;
 
14019 exports.LatLngBounds = LatLngBounds;
 
14020 exports.latLngBounds = toLatLngBounds;
 
14022 exports.GeoJSON = GeoJSON;
 
14023 exports.geoJSON = geoJSON;
 
14024 exports.geoJson = geoJson;
 
14025 exports.Layer = Layer;
 
14026 exports.LayerGroup = LayerGroup;
 
14027 exports.layerGroup = layerGroup;
 
14028 exports.FeatureGroup = FeatureGroup;
 
14029 exports.featureGroup = featureGroup;
 
14030 exports.ImageOverlay = ImageOverlay;
 
14031 exports.imageOverlay = imageOverlay;
 
14032 exports.VideoOverlay = VideoOverlay;
 
14033 exports.videoOverlay = videoOverlay;
 
14034 exports.SVGOverlay = SVGOverlay;
 
14035 exports.svgOverlay = svgOverlay;
 
14036 exports.DivOverlay = DivOverlay;
 
14037 exports.Popup = Popup;
 
14038 exports.popup = popup;
 
14039 exports.Tooltip = Tooltip;
 
14040 exports.tooltip = tooltip;
 
14041 exports.Icon = Icon;
 
14042 exports.icon = icon;
 
14043 exports.DivIcon = DivIcon;
 
14044 exports.divIcon = divIcon;
 
14045 exports.Marker = Marker;
 
14046 exports.marker = marker;
 
14047 exports.TileLayer = TileLayer;
 
14048 exports.tileLayer = tileLayer;
 
14049 exports.GridLayer = GridLayer;
 
14050 exports.gridLayer = gridLayer;
 
14052 exports.svg = svg$1;
 
14053 exports.Renderer = Renderer;
 
14054 exports.Canvas = Canvas;
 
14055 exports.canvas = canvas$1;
 
14056 exports.Path = Path;
 
14057 exports.CircleMarker = CircleMarker;
 
14058 exports.circleMarker = circleMarker;
 
14059 exports.Circle = Circle;
 
14060 exports.circle = circle;
 
14061 exports.Polyline = Polyline;
 
14062 exports.polyline = polyline;
 
14063 exports.Polygon = Polygon;
 
14064 exports.polygon = polygon;
 
14065 exports.Rectangle = Rectangle;
 
14066 exports.rectangle = rectangle;
 
14068 exports.map = createMap;
 
14070 var oldL = window.L;
 
14071 exports.noConflict = function() {
 
14076 // Always export us to window global (see #2364)
 
14077 window.L = exports;
 
14080 //# sourceMappingURL=leaflet-src.js.map