2  * Leaflet 1.4.0, a JS library for interactive maps. http://leafletjs.com
 
   3  * (c) 2010-2018 Vladimir Agafonkin, (c) 2010-2011 CloudMade
 
   6 (function (global, factory) {
 
   7         typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) :
 
   8         typeof define === 'function' && define.amd ? define(['exports'], factory) :
 
   9         (factory((global.L = {})));
 
  10 }(this, (function (exports) { 'use strict';
 
  12 var version = "1.4.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.
 
 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 SphericalMercator = {
 
1654         MAX_LATITUDE: 85.0511287798,
 
1656         project: function (latlng) {
 
1657                 var d = Math.PI / 180,
 
1658                     max = this.MAX_LATITUDE,
 
1659                     lat = Math.max(Math.min(max, latlng.lat), -max),
 
1660                     sin = Math.sin(lat * d);
 
1663                         this.R * latlng.lng * d,
 
1664                         this.R * Math.log((1 + sin) / (1 - sin)) / 2);
 
1667         unproject: function (point) {
 
1668                 var d = 180 / Math.PI;
 
1671                         (2 * Math.atan(Math.exp(point.y / this.R)) - (Math.PI / 2)) * d,
 
1672                         point.x * d / this.R);
 
1675         bounds: (function () {
 
1676                 var d = 6378137 * Math.PI;
 
1677                 return new Bounds([-d, -d], [d, d]);
 
1682  * @class Transformation
 
1683  * @aka L.Transformation
 
1685  * Represents an affine transformation: a set of coefficients `a`, `b`, `c`, `d`
 
1686  * for transforming a point of a form `(x, y)` into `(a*x + b, c*y + d)` and doing
 
1687  * the reverse. Used by Leaflet in its projections code.
 
1692  * var transformation = L.transformation(2, 5, -1, 10),
 
1693  *      p = L.point(1, 2),
 
1694  *      p2 = transformation.transform(p), //  L.point(7, 8)
 
1695  *      p3 = transformation.untransform(p2); //  L.point(1, 2)
 
1700 // factory new L.Transformation(a: Number, b: Number, c: Number, d: Number)
 
1701 // Creates a `Transformation` object with the given coefficients.
 
1702 function Transformation(a, b, c, d) {
 
1704                 // use array properties
 
1717 Transformation.prototype = {
 
1718         // @method transform(point: Point, scale?: Number): Point
 
1719         // Returns a transformed point, optionally multiplied by the given scale.
 
1720         // Only accepts actual `L.Point` instances, not arrays.
 
1721         transform: function (point, scale) { // (Point, Number) -> Point
 
1722                 return this._transform(point.clone(), scale);
 
1725         // destructive transform (faster)
 
1726         _transform: function (point, scale) {
 
1728                 point.x = scale * (this._a * point.x + this._b);
 
1729                 point.y = scale * (this._c * point.y + this._d);
 
1733         // @method untransform(point: Point, scale?: Number): Point
 
1734         // Returns the reverse transformation of the given point, optionally divided
 
1735         // by the given scale. Only accepts actual `L.Point` instances, not arrays.
 
1736         untransform: function (point, scale) {
 
1739                         (point.x / scale - this._b) / this._a,
 
1740                         (point.y / scale - this._d) / this._c);
 
1744 // factory L.transformation(a: Number, b: Number, c: Number, d: Number)
 
1746 // @factory L.transformation(a: Number, b: Number, c: Number, d: Number)
 
1747 // Instantiates a Transformation object with the given coefficients.
 
1750 // @factory L.transformation(coefficients: Array): Transformation
 
1751 // Expects an coefficients array of the form
 
1752 // `[a: Number, b: Number, c: Number, d: Number]`.
 
1754 function toTransformation(a, b, c, d) {
 
1755         return new Transformation(a, b, c, d);
 
1760  * @crs L.CRS.EPSG3857
 
1762  * The most common CRS for online maps, used by almost all free and commercial
 
1763  * tile providers. Uses Spherical Mercator projection. Set in by default in
 
1764  * Map's `crs` option.
 
1767 var EPSG3857 = extend({}, Earth, {
 
1769         projection: SphericalMercator,
 
1771         transformation: (function () {
 
1772                 var scale = 0.5 / (Math.PI * SphericalMercator.R);
 
1773                 return toTransformation(scale, 0.5, -scale, 0.5);
 
1777 var EPSG900913 = extend({}, EPSG3857, {
 
1781 // @namespace SVG; @section
 
1782 // There are several static functions which can be called without instantiating L.SVG:
 
1784 // @function create(name: String): SVGElement
 
1785 // Returns a instance of [SVGElement](https://developer.mozilla.org/docs/Web/API/SVGElement),
 
1786 // corresponding to the class name passed. For example, using 'line' will return
 
1787 // an instance of [SVGLineElement](https://developer.mozilla.org/docs/Web/API/SVGLineElement).
 
1788 function svgCreate(name) {
 
1789         return document.createElementNS('http://www.w3.org/2000/svg', name);
 
1792 // @function pointsToPath(rings: Point[], closed: Boolean): String
 
1793 // Generates a SVG path string for multiple rings, with each ring turning
 
1794 // into "M..L..L.." instructions
 
1795 function pointsToPath(rings, closed) {
 
1797         i, j, len, len2, points, p;
 
1799         for (i = 0, len = rings.length; i < len; i++) {
 
1802                 for (j = 0, len2 = points.length; j < len2; j++) {
 
1804                         str += (j ? 'L' : 'M') + p.x + ' ' + p.y;
 
1807                 // closes the ring for polygons; "x" is VML syntax
 
1808                 str += closed ? (svg ? 'z' : 'x') : '';
 
1811         // SVG complains about empty path strings
 
1812         return str || 'M0 0';
 
1816  * @namespace Browser
 
1819  * A namespace with static properties for browser/feature detection used by Leaflet internally.
 
1824  * if (L.Browser.ielt9) {
 
1825  *   alert('Upgrade your browser, dude!');
 
1830 var style$1 = document.documentElement.style;
 
1832 // @property ie: Boolean; `true` for all Internet Explorer versions (not Edge).
 
1833 var ie = 'ActiveXObject' in window;
 
1835 // @property ielt9: Boolean; `true` for Internet Explorer versions less than 9.
 
1836 var ielt9 = ie && !document.addEventListener;
 
1838 // @property edge: Boolean; `true` for the Edge web browser.
 
1839 var edge = 'msLaunchUri' in navigator && !('documentMode' in document);
 
1841 // @property webkit: Boolean;
 
1842 // `true` for webkit-based browsers like Chrome and Safari (including mobile versions).
 
1843 var webkit = userAgentContains('webkit');
 
1845 // @property android: Boolean
 
1846 // `true` for any browser running on an Android platform.
 
1847 var android = userAgentContains('android');
 
1849 // @property android23: Boolean; `true` for browsers running on Android 2 or Android 3.
 
1850 var android23 = userAgentContains('android 2') || userAgentContains('android 3');
 
1852 /* See https://stackoverflow.com/a/17961266 for details on detecting stock Android */
 
1853 var webkitVer = parseInt(/WebKit\/([0-9]+)|$/.exec(navigator.userAgent)[1], 10); // also matches AppleWebKit
 
1854 // @property androidStock: Boolean; `true` for the Android stock browser (i.e. not Chrome)
 
1855 var androidStock = android && userAgentContains('Google') && webkitVer < 537 && !('AudioNode' in window);
 
1857 // @property opera: Boolean; `true` for the Opera browser
 
1858 var opera = !!window.opera;
 
1860 // @property chrome: Boolean; `true` for the Chrome browser.
 
1861 var chrome = userAgentContains('chrome');
 
1863 // @property gecko: Boolean; `true` for gecko-based browsers like Firefox.
 
1864 var gecko = userAgentContains('gecko') && !webkit && !opera && !ie;
 
1866 // @property safari: Boolean; `true` for the Safari browser.
 
1867 var safari = !chrome && userAgentContains('safari');
 
1869 var phantom = userAgentContains('phantom');
 
1871 // @property opera12: Boolean
 
1872 // `true` for the Opera browser supporting CSS transforms (version 12 or later).
 
1873 var opera12 = 'OTransition' in style$1;
 
1875 // @property win: Boolean; `true` when the browser is running in a Windows platform
 
1876 var win = navigator.platform.indexOf('Win') === 0;
 
1878 // @property ie3d: Boolean; `true` for all Internet Explorer versions supporting CSS transforms.
 
1879 var ie3d = ie && ('transition' in style$1);
 
1881 // @property webkit3d: Boolean; `true` for webkit-based browsers supporting CSS transforms.
 
1882 var webkit3d = ('WebKitCSSMatrix' in window) && ('m11' in new window.WebKitCSSMatrix()) && !android23;
 
1884 // @property gecko3d: Boolean; `true` for gecko-based browsers supporting CSS transforms.
 
1885 var gecko3d = 'MozPerspective' in style$1;
 
1887 // @property any3d: Boolean
 
1888 // `true` for all browsers supporting CSS transforms.
 
1889 var any3d = !window.L_DISABLE_3D && (ie3d || webkit3d || gecko3d) && !opera12 && !phantom;
 
1891 // @property mobile: Boolean; `true` for all browsers running in a mobile device.
 
1892 var mobile = typeof orientation !== 'undefined' || userAgentContains('mobile');
 
1894 // @property mobileWebkit: Boolean; `true` for all webkit-based browsers in a mobile device.
 
1895 var mobileWebkit = mobile && webkit;
 
1897 // @property mobileWebkit3d: Boolean
 
1898 // `true` for all webkit-based browsers in a mobile device supporting CSS transforms.
 
1899 var mobileWebkit3d = mobile && webkit3d;
 
1901 // @property msPointer: Boolean
 
1902 // `true` for browsers implementing the Microsoft touch events model (notably IE10).
 
1903 var msPointer = !window.PointerEvent && window.MSPointerEvent;
 
1905 // @property pointer: Boolean
 
1906 // `true` for all browsers supporting [pointer events](https://msdn.microsoft.com/en-us/library/dn433244%28v=vs.85%29.aspx).
 
1907 var pointer = !!(window.PointerEvent || msPointer);
 
1909 // @property touch: Boolean
 
1910 // `true` for all browsers supporting [touch events](https://developer.mozilla.org/docs/Web/API/Touch_events).
 
1911 // This does not necessarily mean that the browser is running in a computer with
 
1912 // a touchscreen, it only means that the browser is capable of understanding
 
1914 var touch = !window.L_NO_TOUCH && (pointer || 'ontouchstart' in window ||
 
1915                 (window.DocumentTouch && document instanceof window.DocumentTouch));
 
1917 // @property mobileOpera: Boolean; `true` for the Opera browser in a mobile device.
 
1918 var mobileOpera = mobile && opera;
 
1920 // @property mobileGecko: Boolean
 
1921 // `true` for gecko-based browsers running in a mobile device.
 
1922 var mobileGecko = mobile && gecko;
 
1924 // @property retina: Boolean
 
1925 // `true` for browsers on a high-resolution "retina" screen or on any screen when browser's display zoom is more than 100%.
 
1926 var retina = (window.devicePixelRatio || (window.screen.deviceXDPI / window.screen.logicalXDPI)) > 1;
 
1929 // @property canvas: Boolean
 
1930 // `true` when the browser supports [`<canvas>`](https://developer.mozilla.org/docs/Web/API/Canvas_API).
 
1931 var canvas = (function () {
 
1932         return !!document.createElement('canvas').getContext;
 
1935 // @property svg: Boolean
 
1936 // `true` when the browser supports [SVG](https://developer.mozilla.org/docs/Web/SVG).
 
1937 var svg = !!(document.createElementNS && svgCreate('svg').createSVGRect);
 
1939 // @property vml: Boolean
 
1940 // `true` if the browser supports [VML](https://en.wikipedia.org/wiki/Vector_Markup_Language).
 
1941 var vml = !svg && (function () {
 
1943                 var div = document.createElement('div');
 
1944                 div.innerHTML = '<v:shape adj="1"/>';
 
1946                 var shape = div.firstChild;
 
1947                 shape.style.behavior = 'url(#default#VML)';
 
1949                 return shape && (typeof shape.adj === 'object');
 
1957 function userAgentContains(str) {
 
1958         return navigator.userAgent.toLowerCase().indexOf(str) >= 0;
 
1962 var Browser = (Object.freeze || Object)({
 
1968         android23: android23,
 
1969         androidStock: androidStock,
 
1982         mobileWebkit: mobileWebkit,
 
1983         mobileWebkit3d: mobileWebkit3d,
 
1984         msPointer: msPointer,
 
1987         mobileOpera: mobileOpera,
 
1988         mobileGecko: mobileGecko,
 
1996  * Extends L.DomEvent to provide touch support for Internet Explorer and Windows-based devices.
 
2000 var POINTER_DOWN =   msPointer ? 'MSPointerDown'   : 'pointerdown';
 
2001 var POINTER_MOVE =   msPointer ? 'MSPointerMove'   : 'pointermove';
 
2002 var POINTER_UP =     msPointer ? 'MSPointerUp'     : 'pointerup';
 
2003 var POINTER_CANCEL = msPointer ? 'MSPointerCancel' : 'pointercancel';
 
2004 var TAG_WHITE_LIST = ['INPUT', 'SELECT', 'OPTION'];
 
2007 var _pointerDocListener = false;
 
2009 // DomEvent.DoubleTap needs to know about this
 
2010 var _pointersCount = 0;
 
2012 // Provides a touch events wrapper for (ms)pointer events.
 
2013 // ref http://www.w3.org/TR/pointerevents/ https://www.w3.org/Bugs/Public/show_bug.cgi?id=22890
 
2015 function addPointerListener(obj, type, handler, id) {
 
2016         if (type === 'touchstart') {
 
2017                 _addPointerStart(obj, handler, id);
 
2019         } else if (type === 'touchmove') {
 
2020                 _addPointerMove(obj, handler, id);
 
2022         } else if (type === 'touchend') {
 
2023                 _addPointerEnd(obj, handler, id);
 
2029 function removePointerListener(obj, type, id) {
 
2030         var handler = obj['_leaflet_' + type + id];
 
2032         if (type === 'touchstart') {
 
2033                 obj.removeEventListener(POINTER_DOWN, handler, false);
 
2035         } else if (type === 'touchmove') {
 
2036                 obj.removeEventListener(POINTER_MOVE, handler, false);
 
2038         } else if (type === 'touchend') {
 
2039                 obj.removeEventListener(POINTER_UP, handler, false);
 
2040                 obj.removeEventListener(POINTER_CANCEL, handler, false);
 
2046 function _addPointerStart(obj, handler, id) {
 
2047         var onDown = bind(function (e) {
 
2048                 if (e.pointerType !== 'mouse' && e.MSPOINTER_TYPE_MOUSE && e.pointerType !== e.MSPOINTER_TYPE_MOUSE) {
 
2049                         // In IE11, some touch events needs to fire for form controls, or
 
2050                         // the controls will stop working. We keep a whitelist of tag names that
 
2051                         // need these events. For other target tags, we prevent default on the event.
 
2052                         if (TAG_WHITE_LIST.indexOf(e.target.tagName) < 0) {
 
2059                 _handlePointer(e, handler);
 
2062         obj['_leaflet_touchstart' + id] = onDown;
 
2063         obj.addEventListener(POINTER_DOWN, onDown, false);
 
2065         // need to keep track of what pointers and how many are active to provide e.touches emulation
 
2066         if (!_pointerDocListener) {
 
2067                 // we listen documentElement as any drags that end by moving the touch off the screen get fired there
 
2068                 document.documentElement.addEventListener(POINTER_DOWN, _globalPointerDown, true);
 
2069                 document.documentElement.addEventListener(POINTER_MOVE, _globalPointerMove, true);
 
2070                 document.documentElement.addEventListener(POINTER_UP, _globalPointerUp, true);
 
2071                 document.documentElement.addEventListener(POINTER_CANCEL, _globalPointerUp, true);
 
2073                 _pointerDocListener = true;
 
2077 function _globalPointerDown(e) {
 
2078         _pointers[e.pointerId] = e;
 
2082 function _globalPointerMove(e) {
 
2083         if (_pointers[e.pointerId]) {
 
2084                 _pointers[e.pointerId] = e;
 
2088 function _globalPointerUp(e) {
 
2089         delete _pointers[e.pointerId];
 
2093 function _handlePointer(e, handler) {
 
2095         for (var i in _pointers) {
 
2096                 e.touches.push(_pointers[i]);
 
2098         e.changedTouches = [e];
 
2103 function _addPointerMove(obj, handler, id) {
 
2104         var onMove = function (e) {
 
2105                 // don't fire touch moves when mouse isn't down
 
2106                 if ((e.pointerType === e.MSPOINTER_TYPE_MOUSE || e.pointerType === 'mouse') && e.buttons === 0) { return; }
 
2108                 _handlePointer(e, handler);
 
2111         obj['_leaflet_touchmove' + id] = onMove;
 
2112         obj.addEventListener(POINTER_MOVE, onMove, false);
 
2115 function _addPointerEnd(obj, handler, id) {
 
2116         var onUp = function (e) {
 
2117                 _handlePointer(e, handler);
 
2120         obj['_leaflet_touchend' + id] = onUp;
 
2121         obj.addEventListener(POINTER_UP, onUp, false);
 
2122         obj.addEventListener(POINTER_CANCEL, onUp, false);
 
2126  * Extends the event handling code with double tap support for mobile browsers.
 
2129 var _touchstart = msPointer ? 'MSPointerDown' : pointer ? 'pointerdown' : 'touchstart';
 
2130 var _touchend = msPointer ? 'MSPointerUp' : pointer ? 'pointerup' : 'touchend';
 
2131 var _pre = '_leaflet_';
 
2133 // inspired by Zepto touch code by Thomas Fuchs
 
2134 function addDoubleTapListener(obj, handler, id) {
 
2139         function onTouchStart(e) {
 
2143                         if ((!edge) || e.pointerType === 'mouse') { return; }
 
2144                         count = _pointersCount;
 
2146                         count = e.touches.length;
 
2149                 if (count > 1) { return; }
 
2151                 var now = Date.now(),
 
2152                     delta = now - (last || now);
 
2154                 touch$$1 = e.touches ? e.touches[0] : e;
 
2155                 doubleTap = (delta > 0 && delta <= delay);
 
2159         function onTouchEnd(e) {
 
2160                 if (doubleTap && !touch$$1.cancelBubble) {
 
2162                                 if ((!edge) || e.pointerType === 'mouse') { return; }
 
2163                                 // work around .type being readonly with MSPointer* events
 
2167                                 for (i in touch$$1) {
 
2169                                         newTouch[i] = prop && prop.bind ? prop.bind(touch$$1) : prop;
 
2171                                 touch$$1 = newTouch;
 
2173                         touch$$1.type = 'dblclick';
 
2179         obj[_pre + _touchstart + id] = onTouchStart;
 
2180         obj[_pre + _touchend + id] = onTouchEnd;
 
2181         obj[_pre + 'dblclick' + id] = handler;
 
2183         obj.addEventListener(_touchstart, onTouchStart, false);
 
2184         obj.addEventListener(_touchend, onTouchEnd, false);
 
2186         // On some platforms (notably, chrome<55 on win10 + touchscreen + mouse),
 
2187         // the browser doesn't fire touchend/pointerup events but does fire
 
2188         // native dblclicks. See #4127.
 
2189         // Edge 14 also fires native dblclicks, but only for pointerType mouse, see #5180.
 
2190         obj.addEventListener('dblclick', handler, false);
 
2195 function removeDoubleTapListener(obj, id) {
 
2196         var touchstart = obj[_pre + _touchstart + id],
 
2197             touchend = obj[_pre + _touchend + id],
 
2198             dblclick = obj[_pre + 'dblclick' + id];
 
2200         obj.removeEventListener(_touchstart, touchstart, false);
 
2201         obj.removeEventListener(_touchend, touchend, false);
 
2203                 obj.removeEventListener('dblclick', dblclick, false);
 
2210  * @namespace DomUtil
 
2212  * Utility functions to work with the [DOM](https://developer.mozilla.org/docs/Web/API/Document_Object_Model)
 
2213  * tree, used by Leaflet internally.
 
2215  * Most functions expecting or returning a `HTMLElement` also work for
 
2216  * SVG elements. The only difference is that classes refer to CSS classes
 
2217  * in HTML and SVG classes in SVG.
 
2221 // @property TRANSFORM: String
 
2222 // Vendor-prefixed transform style name (e.g. `'webkitTransform'` for WebKit).
 
2223 var TRANSFORM = testProp(
 
2224         ['transform', 'webkitTransform', 'OTransform', 'MozTransform', 'msTransform']);
 
2226 // webkitTransition comes first because some browser versions that drop vendor prefix don't do
 
2227 // the same for the transitionend event, in particular the Android 4.1 stock browser
 
2229 // @property TRANSITION: String
 
2230 // Vendor-prefixed transition style name.
 
2231 var TRANSITION = testProp(
 
2232         ['webkitTransition', 'transition', 'OTransition', 'MozTransition', 'msTransition']);
 
2234 // @property TRANSITION_END: String
 
2235 // Vendor-prefixed transitionend event name.
 
2236 var TRANSITION_END =
 
2237         TRANSITION === 'webkitTransition' || TRANSITION === 'OTransition' ? TRANSITION + 'End' : 'transitionend';
 
2240 // @function get(id: String|HTMLElement): HTMLElement
 
2241 // Returns an element given its DOM id, or returns the element itself
 
2242 // if it was passed directly.
 
2244         return typeof id === 'string' ? document.getElementById(id) : id;
 
2247 // @function getStyle(el: HTMLElement, styleAttrib: String): String
 
2248 // Returns the value for a certain style attribute on an element,
 
2249 // including computed values or values set through CSS.
 
2250 function getStyle(el, style) {
 
2251         var value = el.style[style] || (el.currentStyle && el.currentStyle[style]);
 
2253         if ((!value || value === 'auto') && document.defaultView) {
 
2254                 var css = document.defaultView.getComputedStyle(el, null);
 
2255                 value = css ? css[style] : null;
 
2257         return value === 'auto' ? null : value;
 
2260 // @function create(tagName: String, className?: String, container?: HTMLElement): HTMLElement
 
2261 // Creates an HTML element with `tagName`, sets its class to `className`, and optionally appends it to `container` element.
 
2262 function create$1(tagName, className, container) {
 
2263         var el = document.createElement(tagName);
 
2264         el.className = className || '';
 
2267                 container.appendChild(el);
 
2272 // @function remove(el: HTMLElement)
 
2273 // Removes `el` from its parent element
 
2274 function remove(el) {
 
2275         var parent = el.parentNode;
 
2277                 parent.removeChild(el);
 
2281 // @function empty(el: HTMLElement)
 
2282 // Removes all of `el`'s children elements from `el`
 
2283 function empty(el) {
 
2284         while (el.firstChild) {
 
2285                 el.removeChild(el.firstChild);
 
2289 // @function toFront(el: HTMLElement)
 
2290 // Makes `el` the last child of its parent, so it renders in front of the other children.
 
2291 function toFront(el) {
 
2292         var parent = el.parentNode;
 
2293         if (parent && parent.lastChild !== el) {
 
2294                 parent.appendChild(el);
 
2298 // @function toBack(el: HTMLElement)
 
2299 // Makes `el` the first child of its parent, so it renders behind the other children.
 
2300 function toBack(el) {
 
2301         var parent = el.parentNode;
 
2302         if (parent && parent.firstChild !== el) {
 
2303                 parent.insertBefore(el, parent.firstChild);
 
2307 // @function hasClass(el: HTMLElement, name: String): Boolean
 
2308 // Returns `true` if the element's class attribute contains `name`.
 
2309 function hasClass(el, name) {
 
2310         if (el.classList !== undefined) {
 
2311                 return el.classList.contains(name);
 
2313         var className = getClass(el);
 
2314         return className.length > 0 && new RegExp('(^|\\s)' + name + '(\\s|$)').test(className);
 
2317 // @function addClass(el: HTMLElement, name: String)
 
2318 // Adds `name` to the element's class attribute.
 
2319 function addClass(el, name) {
 
2320         if (el.classList !== undefined) {
 
2321                 var classes = splitWords(name);
 
2322                 for (var i = 0, len = classes.length; i < len; i++) {
 
2323                         el.classList.add(classes[i]);
 
2325         } else if (!hasClass(el, name)) {
 
2326                 var className = getClass(el);
 
2327                 setClass(el, (className ? className + ' ' : '') + name);
 
2331 // @function removeClass(el: HTMLElement, name: String)
 
2332 // Removes `name` from the element's class attribute.
 
2333 function removeClass(el, name) {
 
2334         if (el.classList !== undefined) {
 
2335                 el.classList.remove(name);
 
2337                 setClass(el, trim((' ' + getClass(el) + ' ').replace(' ' + name + ' ', ' ')));
 
2341 // @function setClass(el: HTMLElement, name: String)
 
2342 // Sets the element's class.
 
2343 function setClass(el, name) {
 
2344         if (el.className.baseVal === undefined) {
 
2345                 el.className = name;
 
2347                 // in case of SVG element
 
2348                 el.className.baseVal = name;
 
2352 // @function getClass(el: HTMLElement): String
 
2353 // Returns the element's class.
 
2354 function getClass(el) {
 
2355         // Check if the element is an SVGElementInstance and use the correspondingElement instead
 
2356         // (Required for linked SVG elements in IE11.)
 
2357         if (el.correspondingElement) {
 
2358                 el = el.correspondingElement;
 
2360         return el.className.baseVal === undefined ? el.className : el.className.baseVal;
 
2363 // @function setOpacity(el: HTMLElement, opacity: Number)
 
2364 // Set the opacity of an element (including old IE support).
 
2365 // `opacity` must be a number from `0` to `1`.
 
2366 function setOpacity(el, value) {
 
2367         if ('opacity' in el.style) {
 
2368                 el.style.opacity = value;
 
2369         } else if ('filter' in el.style) {
 
2370                 _setOpacityIE(el, value);
 
2374 function _setOpacityIE(el, value) {
 
2376             filterName = 'DXImageTransform.Microsoft.Alpha';
 
2378         // filters collection throws an error if we try to retrieve a filter that doesn't exist
 
2380                 filter = el.filters.item(filterName);
 
2382                 // don't set opacity to 1 if we haven't already set an opacity,
 
2383                 // it isn't needed and breaks transparent pngs.
 
2384                 if (value === 1) { return; }
 
2387         value = Math.round(value * 100);
 
2390                 filter.Enabled = (value !== 100);
 
2391                 filter.Opacity = value;
 
2393                 el.style.filter += ' progid:' + filterName + '(opacity=' + value + ')';
 
2397 // @function testProp(props: String[]): String|false
 
2398 // Goes through the array of style names and returns the first name
 
2399 // that is a valid style name for an element. If no such name is found,
 
2400 // it returns false. Useful for vendor-prefixed styles like `transform`.
 
2401 function testProp(props) {
 
2402         var style = document.documentElement.style;
 
2404         for (var i = 0; i < props.length; i++) {
 
2405                 if (props[i] in style) {
 
2412 // @function setTransform(el: HTMLElement, offset: Point, scale?: Number)
 
2413 // Resets the 3D CSS transform of `el` so it is translated by `offset` pixels
 
2414 // and optionally scaled by `scale`. Does not have an effect if the
 
2415 // browser doesn't support 3D CSS transforms.
 
2416 function setTransform(el, offset, scale) {
 
2417         var pos = offset || new Point(0, 0);
 
2419         el.style[TRANSFORM] =
 
2421                         'translate(' + pos.x + 'px,' + pos.y + 'px)' :
 
2422                         'translate3d(' + pos.x + 'px,' + pos.y + 'px,0)') +
 
2423                 (scale ? ' scale(' + scale + ')' : '');
 
2426 // @function setPosition(el: HTMLElement, position: Point)
 
2427 // Sets the position of `el` to coordinates specified by `position`,
 
2428 // using CSS translate or top/left positioning depending on the browser
 
2429 // (used by Leaflet internally to position its layers).
 
2430 function setPosition(el, point) {
 
2433         el._leaflet_pos = point;
 
2437                 setTransform(el, point);
 
2439                 el.style.left = point.x + 'px';
 
2440                 el.style.top = point.y + 'px';
 
2444 // @function getPosition(el: HTMLElement): Point
 
2445 // Returns the coordinates of an element previously positioned with setPosition.
 
2446 function getPosition(el) {
 
2447         // this method is only used for elements previously positioned using setPosition,
 
2448         // so it's safe to cache the position for performance
 
2450         return el._leaflet_pos || new Point(0, 0);
 
2453 // @function disableTextSelection()
 
2454 // Prevents the user from generating `selectstart` DOM events, usually generated
 
2455 // when the user drags the mouse through a page with text. Used internally
 
2456 // by Leaflet to override the behaviour of any click-and-drag interaction on
 
2457 // the map. Affects drag interactions on the whole document.
 
2459 // @function enableTextSelection()
 
2460 // Cancels the effects of a previous [`L.DomUtil.disableTextSelection`](#domutil-disabletextselection).
 
2461 var disableTextSelection;
 
2462 var enableTextSelection;
 
2464 if ('onselectstart' in document) {
 
2465         disableTextSelection = function () {
 
2466                 on(window, 'selectstart', preventDefault);
 
2468         enableTextSelection = function () {
 
2469                 off(window, 'selectstart', preventDefault);
 
2472         var userSelectProperty = testProp(
 
2473                 ['userSelect', 'WebkitUserSelect', 'OUserSelect', 'MozUserSelect', 'msUserSelect']);
 
2475         disableTextSelection = function () {
 
2476                 if (userSelectProperty) {
 
2477                         var style = document.documentElement.style;
 
2478                         _userSelect = style[userSelectProperty];
 
2479                         style[userSelectProperty] = 'none';
 
2482         enableTextSelection = function () {
 
2483                 if (userSelectProperty) {
 
2484                         document.documentElement.style[userSelectProperty] = _userSelect;
 
2485                         _userSelect = undefined;
 
2490 // @function disableImageDrag()
 
2491 // As [`L.DomUtil.disableTextSelection`](#domutil-disabletextselection), but
 
2492 // for `dragstart` DOM events, usually generated when the user drags an image.
 
2493 function disableImageDrag() {
 
2494         on(window, 'dragstart', preventDefault);
 
2497 // @function enableImageDrag()
 
2498 // Cancels the effects of a previous [`L.DomUtil.disableImageDrag`](#domutil-disabletextselection).
 
2499 function enableImageDrag() {
 
2500         off(window, 'dragstart', preventDefault);
 
2503 var _outlineElement;
 
2505 // @function preventOutline(el: HTMLElement)
 
2506 // Makes the [outline](https://developer.mozilla.org/docs/Web/CSS/outline)
 
2507 // of the element `el` invisible. Used internally by Leaflet to prevent
 
2508 // focusable elements from displaying an outline when the user performs a
 
2509 // drag interaction on them.
 
2510 function preventOutline(element) {
 
2511         while (element.tabIndex === -1) {
 
2512                 element = element.parentNode;
 
2514         if (!element.style) { return; }
 
2516         _outlineElement = element;
 
2517         _outlineStyle = element.style.outline;
 
2518         element.style.outline = 'none';
 
2519         on(window, 'keydown', restoreOutline);
 
2522 // @function restoreOutline()
 
2523 // Cancels the effects of a previous [`L.DomUtil.preventOutline`]().
 
2524 function restoreOutline() {
 
2525         if (!_outlineElement) { return; }
 
2526         _outlineElement.style.outline = _outlineStyle;
 
2527         _outlineElement = undefined;
 
2528         _outlineStyle = undefined;
 
2529         off(window, 'keydown', restoreOutline);
 
2532 // @function getSizedParentNode(el: HTMLElement): HTMLElement
 
2533 // Finds the closest parent node which size (width and height) is not null.
 
2534 function getSizedParentNode(element) {
 
2536                 element = element.parentNode;
 
2537         } while ((!element.offsetWidth || !element.offsetHeight) && element !== document.body);
 
2541 // @function getScale(el: HTMLElement): Object
 
2542 // Computes the CSS scale currently applied on the element.
 
2543 // Returns an object with `x` and `y` members as horizontal and vertical scales respectively,
 
2544 // and `boundingClientRect` as the result of [`getBoundingClientRect()`](https://developer.mozilla.org/en-US/docs/Web/API/Element/getBoundingClientRect).
 
2545 function getScale(element) {
 
2546         var rect = element.getBoundingClientRect(); // Read-only in old browsers.
 
2549                 x: rect.width / element.offsetWidth || 1,
 
2550                 y: rect.height / element.offsetHeight || 1,
 
2551                 boundingClientRect: rect
 
2556 var DomUtil = (Object.freeze || Object)({
 
2557         TRANSFORM: TRANSFORM,
 
2558         TRANSITION: TRANSITION,
 
2559         TRANSITION_END: TRANSITION_END,
 
2569         removeClass: removeClass,
 
2572         setOpacity: setOpacity,
 
2574         setTransform: setTransform,
 
2575         setPosition: setPosition,
 
2576         getPosition: getPosition,
 
2577         disableTextSelection: disableTextSelection,
 
2578         enableTextSelection: enableTextSelection,
 
2579         disableImageDrag: disableImageDrag,
 
2580         enableImageDrag: enableImageDrag,
 
2581         preventOutline: preventOutline,
 
2582         restoreOutline: restoreOutline,
 
2583         getSizedParentNode: getSizedParentNode,
 
2588  * @namespace DomEvent
 
2589  * Utility functions to work with the [DOM events](https://developer.mozilla.org/docs/Web/API/Event), used by Leaflet internally.
 
2592 // Inspired by John Resig, Dean Edwards and YUI addEvent implementations.
 
2594 // @function on(el: HTMLElement, types: String, fn: Function, context?: Object): this
 
2595 // Adds a listener function (`fn`) to a particular DOM event type of the
 
2596 // element `el`. You can optionally specify the context of the listener
 
2597 // (object the `this` keyword will point to). You can also pass several
 
2598 // space-separated types (e.g. `'click dblclick'`).
 
2601 // @function on(el: HTMLElement, eventMap: Object, context?: Object): this
 
2602 // Adds a set of type/listener pairs, e.g. `{click: onClick, mousemove: onMouseMove}`
 
2603 function on(obj, types, fn, context) {
 
2605         if (typeof types === 'object') {
 
2606                 for (var type in types) {
 
2607                         addOne(obj, type, types[type], fn);
 
2610                 types = splitWords(types);
 
2612                 for (var i = 0, len = types.length; i < len; i++) {
 
2613                         addOne(obj, types[i], fn, context);
 
2620 var eventsKey = '_leaflet_events';
 
2622 // @function off(el: HTMLElement, types: String, fn: Function, context?: Object): this
 
2623 // Removes a previously added listener function.
 
2624 // Note that if you passed a custom context to on, you must pass the same
 
2625 // context to `off` in order to remove the listener.
 
2628 // @function off(el: HTMLElement, eventMap: Object, context?: Object): this
 
2629 // Removes a set of type/listener pairs, e.g. `{click: onClick, mousemove: onMouseMove}`
 
2630 function off(obj, types, fn, context) {
 
2632         if (typeof types === 'object') {
 
2633                 for (var type in types) {
 
2634                         removeOne(obj, type, types[type], fn);
 
2637                 types = splitWords(types);
 
2639                 for (var i = 0, len = types.length; i < len; i++) {
 
2640                         removeOne(obj, types[i], fn, context);
 
2643                 for (var j in obj[eventsKey]) {
 
2644                         removeOne(obj, j, obj[eventsKey][j]);
 
2646                 delete obj[eventsKey];
 
2652 function addOne(obj, type, fn, context) {
 
2653         var id = type + stamp(fn) + (context ? '_' + stamp(context) : '');
 
2655         if (obj[eventsKey] && obj[eventsKey][id]) { return this; }
 
2657         var handler = function (e) {
 
2658                 return fn.call(context || obj, e || window.event);
 
2661         var originalHandler = handler;
 
2663         if (pointer && type.indexOf('touch') === 0) {
 
2664                 // Needs DomEvent.Pointer.js
 
2665                 addPointerListener(obj, type, handler, id);
 
2667         } else if (touch && (type === 'dblclick') && addDoubleTapListener &&
 
2668                    !(pointer && chrome)) {
 
2669                 // Chrome >55 does not need the synthetic dblclicks from addDoubleTapListener
 
2671                 addDoubleTapListener(obj, handler, id);
 
2673         } else if ('addEventListener' in obj) {
 
2675                 if (type === 'mousewheel') {
 
2676                         obj.addEventListener('onwheel' in obj ? 'wheel' : 'mousewheel', handler, false);
 
2678                 } else if ((type === 'mouseenter') || (type === 'mouseleave')) {
 
2679                         handler = function (e) {
 
2680                                 e = e || window.event;
 
2681                                 if (isExternalTarget(obj, e)) {
 
2685                         obj.addEventListener(type === 'mouseenter' ? 'mouseover' : 'mouseout', handler, false);
 
2688                         if (type === 'click' && android) {
 
2689                                 handler = function (e) {
 
2690                                         filterClick(e, originalHandler);
 
2693                         obj.addEventListener(type, handler, false);
 
2696         } else if ('attachEvent' in obj) {
 
2697                 obj.attachEvent('on' + type, handler);
 
2700         obj[eventsKey] = obj[eventsKey] || {};
 
2701         obj[eventsKey][id] = handler;
 
2704 function removeOne(obj, type, fn, context) {
 
2706         var id = type + stamp(fn) + (context ? '_' + stamp(context) : ''),
 
2707             handler = obj[eventsKey] && obj[eventsKey][id];
 
2709         if (!handler) { return this; }
 
2711         if (pointer && type.indexOf('touch') === 0) {
 
2712                 removePointerListener(obj, type, id);
 
2714         } else if (touch && (type === 'dblclick') && removeDoubleTapListener &&
 
2715                    !(pointer && chrome)) {
 
2716                 removeDoubleTapListener(obj, id);
 
2718         } else if ('removeEventListener' in obj) {
 
2720                 if (type === 'mousewheel') {
 
2721                         obj.removeEventListener('onwheel' in obj ? 'wheel' : 'mousewheel', handler, false);
 
2724                         obj.removeEventListener(
 
2725                                 type === 'mouseenter' ? 'mouseover' :
 
2726                                 type === 'mouseleave' ? 'mouseout' : type, handler, false);
 
2729         } else if ('detachEvent' in obj) {
 
2730                 obj.detachEvent('on' + type, handler);
 
2733         obj[eventsKey][id] = null;
 
2736 // @function stopPropagation(ev: DOMEvent): this
 
2737 // Stop the given event from propagation to parent elements. Used inside the listener functions:
 
2739 // L.DomEvent.on(div, 'click', function (ev) {
 
2740 //      L.DomEvent.stopPropagation(ev);
 
2743 function stopPropagation(e) {
 
2745         if (e.stopPropagation) {
 
2746                 e.stopPropagation();
 
2747         } else if (e.originalEvent) {  // In case of Leaflet event.
 
2748                 e.originalEvent._stopped = true;
 
2750                 e.cancelBubble = true;
 
2757 // @function disableScrollPropagation(el: HTMLElement): this
 
2758 // Adds `stopPropagation` to the element's `'mousewheel'` events (plus browser variants).
 
2759 function disableScrollPropagation(el) {
 
2760         addOne(el, 'mousewheel', stopPropagation);
 
2764 // @function disableClickPropagation(el: HTMLElement): this
 
2765 // Adds `stopPropagation` to the element's `'click'`, `'doubleclick'`,
 
2766 // `'mousedown'` and `'touchstart'` events (plus browser variants).
 
2767 function disableClickPropagation(el) {
 
2768         on(el, 'mousedown touchstart dblclick', stopPropagation);
 
2769         addOne(el, 'click', fakeStop);
 
2773 // @function preventDefault(ev: DOMEvent): this
 
2774 // Prevents the default action of the DOM Event `ev` from happening (such as
 
2775 // following a link in the href of the a element, or doing a POST request
 
2776 // with page reload when a `<form>` is submitted).
 
2777 // Use it inside listener functions.
 
2778 function preventDefault(e) {
 
2779         if (e.preventDefault) {
 
2782                 e.returnValue = false;
 
2787 // @function stop(ev: DOMEvent): this
 
2788 // Does `stopPropagation` and `preventDefault` at the same time.
 
2795 // @function getMousePosition(ev: DOMEvent, container?: HTMLElement): Point
 
2796 // Gets normalized mouse position from a DOM event relative to the
 
2797 // `container` (border excluded) or to the whole page if not specified.
 
2798 function getMousePosition(e, container) {
 
2800                 return new Point(e.clientX, e.clientY);
 
2803         var scale = getScale(container),
 
2804             offset = scale.boundingClientRect; // left and top  values are in page scale (like the event clientX/Y)
 
2807                 // offset.left/top values are in page scale (like clientX/Y),
 
2808                 // whereas clientLeft/Top (border width) values are the original values (before CSS scale applies).
 
2809                 (e.clientX - offset.left) / scale.x - container.clientLeft,
 
2810                 (e.clientY - offset.top) / scale.y - container.clientTop
 
2814 // Chrome on Win scrolls double the pixels as in other platforms (see #4538),
 
2815 // and Firefox scrolls device pixels, not CSS pixels
 
2817         (win && chrome) ? 2 * window.devicePixelRatio :
 
2818         gecko ? window.devicePixelRatio : 1;
 
2820 // @function getWheelDelta(ev: DOMEvent): Number
 
2821 // Gets normalized wheel delta from a mousewheel DOM event, in vertical
 
2822 // pixels scrolled (negative if scrolling down).
 
2823 // Events from pointing devices without precise scrolling are mapped to
 
2824 // a best guess of 60 pixels.
 
2825 function getWheelDelta(e) {
 
2826         return (edge) ? e.wheelDeltaY / 2 : // Don't trust window-geometry-based delta
 
2827                (e.deltaY && e.deltaMode === 0) ? -e.deltaY / wheelPxFactor : // Pixels
 
2828                (e.deltaY && e.deltaMode === 1) ? -e.deltaY * 20 : // Lines
 
2829                (e.deltaY && e.deltaMode === 2) ? -e.deltaY * 60 : // Pages
 
2830                (e.deltaX || e.deltaZ) ? 0 :     // Skip horizontal/depth wheel events
 
2831                e.wheelDelta ? (e.wheelDeltaY || e.wheelDelta) / 2 : // Legacy IE pixels
 
2832                (e.detail && Math.abs(e.detail) < 32765) ? -e.detail * 20 : // Legacy Moz lines
 
2833                e.detail ? e.detail / -32765 * 60 : // Legacy Moz pages
 
2837 var skipEvents = {};
 
2839 function fakeStop(e) {
 
2840         // fakes stopPropagation by setting a special event flag, checked/reset with skipped(e)
 
2841         skipEvents[e.type] = true;
 
2844 function skipped(e) {
 
2845         var events = skipEvents[e.type];
 
2846         // reset when checking, as it's only used in map container and propagates outside of the map
 
2847         skipEvents[e.type] = false;
 
2851 // check if element really left/entered the event target (for mouseenter/mouseleave)
 
2852 function isExternalTarget(el, e) {
 
2854         var related = e.relatedTarget;
 
2856         if (!related) { return true; }
 
2859                 while (related && (related !== el)) {
 
2860                         related = related.parentNode;
 
2865         return (related !== el);
 
2870 // this is a horrible workaround for a bug in Android where a single touch triggers two click events
 
2871 function filterClick(e, handler) {
 
2872         var timeStamp = (e.timeStamp || (e.originalEvent && e.originalEvent.timeStamp)),
 
2873             elapsed = lastClick && (timeStamp - lastClick);
 
2875         // are they closer together than 500ms yet more than 100ms?
 
2876         // Android typically triggers them ~300ms apart while multiple listeners
 
2877         // on the same event should be triggered far faster;
 
2878         // or check if click is simulated on the element, and if it is, reject any non-simulated events
 
2880         if ((elapsed && elapsed > 100 && elapsed < 500) || (e.target._simulatedClick && !e._simulated)) {
 
2884         lastClick = timeStamp;
 
2892 var DomEvent = (Object.freeze || Object)({
 
2895         stopPropagation: stopPropagation,
 
2896         disableScrollPropagation: disableScrollPropagation,
 
2897         disableClickPropagation: disableClickPropagation,
 
2898         preventDefault: preventDefault,
 
2900         getMousePosition: getMousePosition,
 
2901         getWheelDelta: getWheelDelta,
 
2904         isExternalTarget: isExternalTarget,
 
2910  * @class PosAnimation
 
2911  * @aka L.PosAnimation
 
2913  * Used internally for panning animations, utilizing CSS3 Transitions for modern browsers and a timer fallback for IE6-9.
 
2917  * var fx = new L.PosAnimation();
 
2918  * fx.run(el, [300, 500], 0.5);
 
2921  * @constructor L.PosAnimation()
 
2922  * Creates a `PosAnimation` object.
 
2926 var PosAnimation = Evented.extend({
 
2928         // @method run(el: HTMLElement, newPos: Point, duration?: Number, easeLinearity?: Number)
 
2929         // Run an animation of a given element to a new position, optionally setting
 
2930         // duration in seconds (`0.25` by default) and easing linearity factor (3rd
 
2931         // argument of the [cubic bezier curve](http://cubic-bezier.com/#0,0,.5,1),
 
2932         // `0.5` by default).
 
2933         run: function (el, newPos, duration, easeLinearity) {
 
2937                 this._inProgress = true;
 
2938                 this._duration = duration || 0.25;
 
2939                 this._easeOutPower = 1 / Math.max(easeLinearity || 0.5, 0.2);
 
2941                 this._startPos = getPosition(el);
 
2942                 this._offset = newPos.subtract(this._startPos);
 
2943                 this._startTime = +new Date();
 
2945                 // @event start: Event
 
2946                 // Fired when the animation starts
 
2953         // Stops the animation (if currently running).
 
2955                 if (!this._inProgress) { return; }
 
2961         _animate: function () {
 
2963                 this._animId = requestAnimFrame(this._animate, this);
 
2967         _step: function (round) {
 
2968                 var elapsed = (+new Date()) - this._startTime,
 
2969                     duration = this._duration * 1000;
 
2971                 if (elapsed < duration) {
 
2972                         this._runFrame(this._easeOut(elapsed / duration), round);
 
2979         _runFrame: function (progress, round) {
 
2980                 var pos = this._startPos.add(this._offset.multiplyBy(progress));
 
2984                 setPosition(this._el, pos);
 
2986                 // @event step: Event
 
2987                 // Fired continuously during the animation.
 
2991         _complete: function () {
 
2992                 cancelAnimFrame(this._animId);
 
2994                 this._inProgress = false;
 
2995                 // @event end: Event
 
2996                 // Fired when the animation ends.
 
3000         _easeOut: function (t) {
 
3001                 return 1 - Math.pow(1 - t, this._easeOutPower);
 
3010  * The central class of the API — it is used to create a map on a page and manipulate it.
 
3015  * // initialize the map on the "map" div with a given center and zoom
 
3016  * var map = L.map('map', {
 
3017  *      center: [51.505, -0.09],
 
3024 var Map = Evented.extend({
 
3027                 // @section Map State Options
 
3028                 // @option crs: CRS = L.CRS.EPSG3857
 
3029                 // The [Coordinate Reference System](#crs) to use. Don't change this if you're not
 
3030                 // sure what it means.
 
3033                 // @option center: LatLng = undefined
 
3034                 // Initial geographic center of the map
 
3037                 // @option zoom: Number = undefined
 
3038                 // Initial map zoom level
 
3041                 // @option minZoom: Number = *
 
3042                 // Minimum zoom level of the map.
 
3043                 // If not specified and at least one `GridLayer` or `TileLayer` is in the map,
 
3044                 // the lowest of their `minZoom` options will be used instead.
 
3047                 // @option maxZoom: Number = *
 
3048                 // Maximum zoom level of the map.
 
3049                 // If not specified and at least one `GridLayer` or `TileLayer` is in the map,
 
3050                 // the highest of their `maxZoom` options will be used instead.
 
3053                 // @option layers: Layer[] = []
 
3054                 // Array of layers that will be added to the map initially
 
3057                 // @option maxBounds: LatLngBounds = null
 
3058                 // When this option is set, the map restricts the view to the given
 
3059                 // geographical bounds, bouncing the user back if the user tries to pan
 
3060                 // outside the view. To set the restriction dynamically, use
 
3061                 // [`setMaxBounds`](#map-setmaxbounds) method.
 
3062                 maxBounds: undefined,
 
3064                 // @option renderer: Renderer = *
 
3065                 // The default method for drawing vector layers on the map. `L.SVG`
 
3066                 // or `L.Canvas` by default depending on browser support.
 
3067                 renderer: undefined,
 
3070                 // @section Animation Options
 
3071                 // @option zoomAnimation: Boolean = true
 
3072                 // Whether the map zoom animation is enabled. By default it's enabled
 
3073                 // in all browsers that support CSS3 Transitions except Android.
 
3074                 zoomAnimation: true,
 
3076                 // @option zoomAnimationThreshold: Number = 4
 
3077                 // Won't animate zoom if the zoom difference exceeds this value.
 
3078                 zoomAnimationThreshold: 4,
 
3080                 // @option fadeAnimation: Boolean = true
 
3081                 // Whether the tile fade animation is enabled. By default it's enabled
 
3082                 // in all browsers that support CSS3 Transitions except Android.
 
3083                 fadeAnimation: true,
 
3085                 // @option markerZoomAnimation: Boolean = true
 
3086                 // Whether markers animate their zoom with the zoom animation, if disabled
 
3087                 // they will disappear for the length of the animation. By default it's
 
3088                 // enabled in all browsers that support CSS3 Transitions except Android.
 
3089                 markerZoomAnimation: true,
 
3091                 // @option transform3DLimit: Number = 2^23
 
3092                 // Defines the maximum size of a CSS translation transform. The default
 
3093                 // value should not be changed unless a web browser positions layers in
 
3094                 // the wrong place after doing a large `panBy`.
 
3095                 transform3DLimit: 8388608, // Precision limit of a 32-bit float
 
3097                 // @section Interaction Options
 
3098                 // @option zoomSnap: Number = 1
 
3099                 // Forces the map's zoom level to always be a multiple of this, particularly
 
3100                 // right after a [`fitBounds()`](#map-fitbounds) or a pinch-zoom.
 
3101                 // By default, the zoom level snaps to the nearest integer; lower values
 
3102                 // (e.g. `0.5` or `0.1`) allow for greater granularity. A value of `0`
 
3103                 // means the zoom level will not be snapped after `fitBounds` or a pinch-zoom.
 
3106                 // @option zoomDelta: Number = 1
 
3107                 // Controls how much the map's zoom level will change after a
 
3108                 // [`zoomIn()`](#map-zoomin), [`zoomOut()`](#map-zoomout), pressing `+`
 
3109                 // or `-` on the keyboard, or using the [zoom controls](#control-zoom).
 
3110                 // Values smaller than `1` (e.g. `0.5`) allow for greater granularity.
 
3113                 // @option trackResize: Boolean = true
 
3114                 // Whether the map automatically handles browser window resize to update itself.
 
3118         initialize: function (id, options) { // (HTMLElement or String, Object)
 
3119                 options = setOptions(this, options);
 
3121                 // Make sure to assign internal flags at the beginning,
 
3122                 // to avoid inconsistent state in some edge cases.
 
3123                 this._handlers = [];
 
3125                 this._zoomBoundLayers = {};
 
3126                 this._sizeChanged = true;
 
3128                 this._initContainer(id);
 
3131                 // hack for https://github.com/Leaflet/Leaflet/issues/1980
 
3132                 this._onResize = bind(this._onResize, this);
 
3136                 if (options.maxBounds) {
 
3137                         this.setMaxBounds(options.maxBounds);
 
3140                 if (options.zoom !== undefined) {
 
3141                         this._zoom = this._limitZoom(options.zoom);
 
3144                 if (options.center && options.zoom !== undefined) {
 
3145                         this.setView(toLatLng(options.center), options.zoom, {reset: true});
 
3148                 this.callInitHooks();
 
3150                 // don't animate on browsers without hardware-accelerated transitions or old Android/Opera
 
3151                 this._zoomAnimated = TRANSITION && any3d && !mobileOpera &&
 
3152                                 this.options.zoomAnimation;
 
3154                 // zoom transitions run with the same duration for all layers, so if one of transitionend events
 
3155                 // happens after starting zoom animation (propagating to the map pane), we know that it ended globally
 
3156                 if (this._zoomAnimated) {
 
3157                         this._createAnimProxy();
 
3158                         on(this._proxy, TRANSITION_END, this._catchTransitionEnd, this);
 
3161                 this._addLayers(this.options.layers);
 
3165         // @section Methods for modifying map state
 
3167         // @method setView(center: LatLng, zoom: Number, options?: Zoom/pan options): this
 
3168         // Sets the view of the map (geographical center and zoom) with the given
 
3169         // animation options.
 
3170         setView: function (center, zoom, options) {
 
3172                 zoom = zoom === undefined ? this._zoom : this._limitZoom(zoom);
 
3173                 center = this._limitCenter(toLatLng(center), zoom, this.options.maxBounds);
 
3174                 options = options || {};
 
3178                 if (this._loaded && !options.reset && options !== true) {
 
3180                         if (options.animate !== undefined) {
 
3181                                 options.zoom = extend({animate: options.animate}, options.zoom);
 
3182                                 options.pan = extend({animate: options.animate, duration: options.duration}, options.pan);
 
3185                         // try animating pan or zoom
 
3186                         var moved = (this._zoom !== zoom) ?
 
3187                                 this._tryAnimatedZoom && this._tryAnimatedZoom(center, zoom, options.zoom) :
 
3188                                 this._tryAnimatedPan(center, options.pan);
 
3191                                 // prevent resize handler call, the view will refresh after animation anyway
 
3192                                 clearTimeout(this._sizeTimer);
 
3197                 // animation didn't start, just reset the map view
 
3198                 this._resetView(center, zoom);
 
3203         // @method setZoom(zoom: Number, options?: Zoom/pan options): this
 
3204         // Sets the zoom of the map.
 
3205         setZoom: function (zoom, options) {
 
3206                 if (!this._loaded) {
 
3210                 return this.setView(this.getCenter(), zoom, {zoom: options});
 
3213         // @method zoomIn(delta?: Number, options?: Zoom options): this
 
3214         // Increases the zoom of the map by `delta` ([`zoomDelta`](#map-zoomdelta) by default).
 
3215         zoomIn: function (delta, options) {
 
3216                 delta = delta || (any3d ? this.options.zoomDelta : 1);
 
3217                 return this.setZoom(this._zoom + delta, options);
 
3220         // @method zoomOut(delta?: Number, options?: Zoom options): this
 
3221         // Decreases the zoom of the map by `delta` ([`zoomDelta`](#map-zoomdelta) by default).
 
3222         zoomOut: function (delta, options) {
 
3223                 delta = delta || (any3d ? this.options.zoomDelta : 1);
 
3224                 return this.setZoom(this._zoom - delta, options);
 
3227         // @method setZoomAround(latlng: LatLng, zoom: Number, options: Zoom options): this
 
3228         // Zooms the map while keeping a specified geographical point on the map
 
3229         // stationary (e.g. used internally for scroll zoom and double-click zoom).
 
3231         // @method setZoomAround(offset: Point, zoom: Number, options: Zoom options): this
 
3232         // Zooms the map while keeping a specified pixel on the map (relative to the top-left corner) stationary.
 
3233         setZoomAround: function (latlng, zoom, options) {
 
3234                 var scale = this.getZoomScale(zoom),
 
3235                     viewHalf = this.getSize().divideBy(2),
 
3236                     containerPoint = latlng instanceof Point ? latlng : this.latLngToContainerPoint(latlng),
 
3238                     centerOffset = containerPoint.subtract(viewHalf).multiplyBy(1 - 1 / scale),
 
3239                     newCenter = this.containerPointToLatLng(viewHalf.add(centerOffset));
 
3241                 return this.setView(newCenter, zoom, {zoom: options});
 
3244         _getBoundsCenterZoom: function (bounds, options) {
 
3246                 options = options || {};
 
3247                 bounds = bounds.getBounds ? bounds.getBounds() : toLatLngBounds(bounds);
 
3249                 var paddingTL = toPoint(options.paddingTopLeft || options.padding || [0, 0]),
 
3250                     paddingBR = toPoint(options.paddingBottomRight || options.padding || [0, 0]),
 
3252                     zoom = this.getBoundsZoom(bounds, false, paddingTL.add(paddingBR));
 
3254                 zoom = (typeof options.maxZoom === 'number') ? Math.min(options.maxZoom, zoom) : zoom;
 
3256                 if (zoom === Infinity) {
 
3258                                 center: bounds.getCenter(),
 
3263                 var paddingOffset = paddingBR.subtract(paddingTL).divideBy(2),
 
3265                     swPoint = this.project(bounds.getSouthWest(), zoom),
 
3266                     nePoint = this.project(bounds.getNorthEast(), zoom),
 
3267                     center = this.unproject(swPoint.add(nePoint).divideBy(2).add(paddingOffset), zoom);
 
3275         // @method fitBounds(bounds: LatLngBounds, options?: fitBounds options): this
 
3276         // Sets a map view that contains the given geographical bounds with the
 
3277         // maximum zoom level possible.
 
3278         fitBounds: function (bounds, options) {
 
3280                 bounds = toLatLngBounds(bounds);
 
3282                 if (!bounds.isValid()) {
 
3283                         throw new Error('Bounds are not valid.');
 
3286                 var target = this._getBoundsCenterZoom(bounds, options);
 
3287                 return this.setView(target.center, target.zoom, options);
 
3290         // @method fitWorld(options?: fitBounds options): this
 
3291         // Sets a map view that mostly contains the whole world with the maximum
 
3292         // zoom level possible.
 
3293         fitWorld: function (options) {
 
3294                 return this.fitBounds([[-90, -180], [90, 180]], options);
 
3297         // @method panTo(latlng: LatLng, options?: Pan options): this
 
3298         // Pans the map to a given center.
 
3299         panTo: function (center, options) { // (LatLng)
 
3300                 return this.setView(center, this._zoom, {pan: options});
 
3303         // @method panBy(offset: Point, options?: Pan options): this
 
3304         // Pans the map by a given number of pixels (animated).
 
3305         panBy: function (offset, options) {
 
3306                 offset = toPoint(offset).round();
 
3307                 options = options || {};
 
3309                 if (!offset.x && !offset.y) {
 
3310                         return this.fire('moveend');
 
3312                 // If we pan too far, Chrome gets issues with tiles
 
3313                 // and makes them disappear or appear in the wrong place (slightly offset) #2602
 
3314                 if (options.animate !== true && !this.getSize().contains(offset)) {
 
3315                         this._resetView(this.unproject(this.project(this.getCenter()).add(offset)), this.getZoom());
 
3319                 if (!this._panAnim) {
 
3320                         this._panAnim = new PosAnimation();
 
3323                                 'step': this._onPanTransitionStep,
 
3324                                 'end': this._onPanTransitionEnd
 
3328                 // don't fire movestart if animating inertia
 
3329                 if (!options.noMoveStart) {
 
3330                         this.fire('movestart');
 
3333                 // animate pan unless animate: false specified
 
3334                 if (options.animate !== false) {
 
3335                         addClass(this._mapPane, 'leaflet-pan-anim');
 
3337                         var newPos = this._getMapPanePos().subtract(offset).round();
 
3338                         this._panAnim.run(this._mapPane, newPos, options.duration || 0.25, options.easeLinearity);
 
3340                         this._rawPanBy(offset);
 
3341                         this.fire('move').fire('moveend');
 
3347         // @method flyTo(latlng: LatLng, zoom?: Number, options?: Zoom/pan options): this
 
3348         // Sets the view of the map (geographical center and zoom) performing a smooth
 
3349         // pan-zoom animation.
 
3350         flyTo: function (targetCenter, targetZoom, options) {
 
3352                 options = options || {};
 
3353                 if (options.animate === false || !any3d) {
 
3354                         return this.setView(targetCenter, targetZoom, options);
 
3359                 var from = this.project(this.getCenter()),
 
3360                     to = this.project(targetCenter),
 
3361                     size = this.getSize(),
 
3362                     startZoom = this._zoom;
 
3364                 targetCenter = toLatLng(targetCenter);
 
3365                 targetZoom = targetZoom === undefined ? startZoom : targetZoom;
 
3367                 var w0 = Math.max(size.x, size.y),
 
3368                     w1 = w0 * this.getZoomScale(startZoom, targetZoom),
 
3369                     u1 = (to.distanceTo(from)) || 1,
 
3374                         var s1 = i ? -1 : 1,
 
3376                             t1 = w1 * w1 - w0 * w0 + s1 * rho2 * rho2 * u1 * u1,
 
3377                             b1 = 2 * s2 * rho2 * u1,
 
3379                             sq = Math.sqrt(b * b + 1) - b;
 
3381                             // workaround for floating point precision bug when sq = 0, log = -Infinite,
 
3382                             // thus triggering an infinite loop in flyTo
 
3383                             var log = sq < 0.000000001 ? -18 : Math.log(sq);
 
3388                 function sinh(n) { return (Math.exp(n) - Math.exp(-n)) / 2; }
 
3389                 function cosh(n) { return (Math.exp(n) + Math.exp(-n)) / 2; }
 
3390                 function tanh(n) { return sinh(n) / cosh(n); }
 
3394                 function w(s) { return w0 * (cosh(r0) / cosh(r0 + rho * s)); }
 
3395                 function u(s) { return w0 * (cosh(r0) * tanh(r0 + rho * s) - sinh(r0)) / rho2; }
 
3397                 function easeOut(t) { return 1 - Math.pow(1 - t, 1.5); }
 
3399                 var start = Date.now(),
 
3400                     S = (r(1) - r0) / rho,
 
3401                     duration = options.duration ? 1000 * options.duration : 1000 * S * 0.8;
 
3404                         var t = (Date.now() - start) / duration,
 
3408                                 this._flyToFrame = requestAnimFrame(frame, this);
 
3411                                         this.unproject(from.add(to.subtract(from).multiplyBy(u(s) / u1)), startZoom),
 
3412                                         this.getScaleZoom(w0 / w(s), startZoom),
 
3417                                         ._move(targetCenter, targetZoom)
 
3422                 this._moveStart(true, options.noMoveStart);
 
3428         // @method flyToBounds(bounds: LatLngBounds, options?: fitBounds options): this
 
3429         // Sets the view of the map with a smooth animation like [`flyTo`](#map-flyto),
 
3430         // but takes a bounds parameter like [`fitBounds`](#map-fitbounds).
 
3431         flyToBounds: function (bounds, options) {
 
3432                 var target = this._getBoundsCenterZoom(bounds, options);
 
3433                 return this.flyTo(target.center, target.zoom, options);
 
3436         // @method setMaxBounds(bounds: Bounds): this
 
3437         // Restricts the map view to the given bounds (see the [maxBounds](#map-maxbounds) option).
 
3438         setMaxBounds: function (bounds) {
 
3439                 bounds = toLatLngBounds(bounds);
 
3441                 if (!bounds.isValid()) {
 
3442                         this.options.maxBounds = null;
 
3443                         return this.off('moveend', this._panInsideMaxBounds);
 
3444                 } else if (this.options.maxBounds) {
 
3445                         this.off('moveend', this._panInsideMaxBounds);
 
3448                 this.options.maxBounds = bounds;
 
3451                         this._panInsideMaxBounds();
 
3454                 return this.on('moveend', this._panInsideMaxBounds);
 
3457         // @method setMinZoom(zoom: Number): this
 
3458         // Sets the lower limit for the available zoom levels (see the [minZoom](#map-minzoom) option).
 
3459         setMinZoom: function (zoom) {
 
3460                 var oldZoom = this.options.minZoom;
 
3461                 this.options.minZoom = zoom;
 
3463                 if (this._loaded && oldZoom !== zoom) {
 
3464                         this.fire('zoomlevelschange');
 
3466                         if (this.getZoom() < this.options.minZoom) {
 
3467                                 return this.setZoom(zoom);
 
3474         // @method setMaxZoom(zoom: Number): this
 
3475         // Sets the upper limit for the available zoom levels (see the [maxZoom](#map-maxzoom) option).
 
3476         setMaxZoom: function (zoom) {
 
3477                 var oldZoom = this.options.maxZoom;
 
3478                 this.options.maxZoom = zoom;
 
3480                 if (this._loaded && oldZoom !== zoom) {
 
3481                         this.fire('zoomlevelschange');
 
3483                         if (this.getZoom() > this.options.maxZoom) {
 
3484                                 return this.setZoom(zoom);
 
3491         // @method panInsideBounds(bounds: LatLngBounds, options?: Pan options): this
 
3492         // 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.
 
3493         panInsideBounds: function (bounds, options) {
 
3494                 this._enforcingBounds = true;
 
3495                 var center = this.getCenter(),
 
3496                     newCenter = this._limitCenter(center, this._zoom, toLatLngBounds(bounds));
 
3498                 if (!center.equals(newCenter)) {
 
3499                         this.panTo(newCenter, options);
 
3502                 this._enforcingBounds = false;
 
3506         // @method panInside(latlng: LatLng, options?: options): this
 
3507         // Pans the map the minimum amount to make the `latlng` visible. Use
 
3508         // `padding`, `paddingTopLeft` and `paddingTopRight` options to fit
 
3509         // the display to more restricted bounds, like [`fitBounds`](#map-fitbounds).
 
3510         // If `latlng` is already within the (optionally padded) display bounds,
 
3511         // the map will not be panned.
 
3512         panInside: function (latlng, options) {
 
3513                 options = options || {};
 
3515                 var paddingTL = toPoint(options.paddingTopLeft || options.padding || [0, 0]),
 
3516                     paddingBR = toPoint(options.paddingBottomRight || options.padding || [0, 0]),
 
3517                     center = this.getCenter(),
 
3518                     pixelCenter = this.project(center),
 
3519                     pixelPoint = this.project(latlng),
 
3520                     pixelBounds = this.getPixelBounds(),
 
3521                     halfPixelBounds = pixelBounds.getSize().divideBy(2),
 
3522                     paddedBounds = toBounds([pixelBounds.min.add(paddingTL), pixelBounds.max.subtract(paddingBR)]);
 
3524                 if (!paddedBounds.contains(pixelPoint)) {
 
3525                         this._enforcingBounds = true;
 
3526                         var diff = pixelCenter.subtract(pixelPoint),
 
3527                             newCenter = toPoint(pixelPoint.x + diff.x, pixelPoint.y + diff.y);
 
3529                         if (pixelPoint.x < paddedBounds.min.x || pixelPoint.x > paddedBounds.max.x) {
 
3530                                 newCenter.x = pixelCenter.x - diff.x;
 
3532                                         newCenter.x += halfPixelBounds.x - paddingTL.x;
 
3534                                         newCenter.x -= halfPixelBounds.x - paddingBR.x;
 
3537                         if (pixelPoint.y < paddedBounds.min.y || pixelPoint.y > paddedBounds.max.y) {
 
3538                                 newCenter.y = pixelCenter.y - diff.y;
 
3540                                         newCenter.y += halfPixelBounds.y - paddingTL.y;
 
3542                                         newCenter.y -= halfPixelBounds.y - paddingBR.y;
 
3545                         this.panTo(this.unproject(newCenter), options);
 
3546                         this._enforcingBounds = false;
 
3551         // @method invalidateSize(options: Zoom/pan options): this
 
3552         // Checks if the map container size changed and updates the map if so —
 
3553         // call it after you've changed the map size dynamically, also animating
 
3554         // pan by default. If `options.pan` is `false`, panning will not occur.
 
3555         // If `options.debounceMoveend` is `true`, it will delay `moveend` event so
 
3556         // that it doesn't happen often even if the method is called many
 
3560         // @method invalidateSize(animate: Boolean): this
 
3561         // Checks if the map container size changed and updates the map if so —
 
3562         // call it after you've changed the map size dynamically, also animating
 
3564         invalidateSize: function (options) {
 
3565                 if (!this._loaded) { return this; }
 
3570                 }, options === true ? {animate: true} : options);
 
3572                 var oldSize = this.getSize();
 
3573                 this._sizeChanged = true;
 
3574                 this._lastCenter = null;
 
3576                 var newSize = this.getSize(),
 
3577                     oldCenter = oldSize.divideBy(2).round(),
 
3578                     newCenter = newSize.divideBy(2).round(),
 
3579                     offset = oldCenter.subtract(newCenter);
 
3581                 if (!offset.x && !offset.y) { return this; }
 
3583                 if (options.animate && options.pan) {
 
3588                                 this._rawPanBy(offset);
 
3593                         if (options.debounceMoveend) {
 
3594                                 clearTimeout(this._sizeTimer);
 
3595                                 this._sizeTimer = setTimeout(bind(this.fire, this, 'moveend'), 200);
 
3597                                 this.fire('moveend');
 
3601                 // @section Map state change events
 
3602                 // @event resize: ResizeEvent
 
3603                 // Fired when the map is resized.
 
3604                 return this.fire('resize', {
 
3610         // @section Methods for modifying map state
 
3611         // @method stop(): this
 
3612         // Stops the currently running `panTo` or `flyTo` animation, if any.
 
3614                 this.setZoom(this._limitZoom(this._zoom));
 
3615                 if (!this.options.zoomSnap) {
 
3616                         this.fire('viewreset');
 
3618                 return this._stop();
 
3621         // @section Geolocation methods
 
3622         // @method locate(options?: Locate options): this
 
3623         // Tries to locate the user using the Geolocation API, firing a [`locationfound`](#map-locationfound)
 
3624         // event with location data on success or a [`locationerror`](#map-locationerror) event on failure,
 
3625         // and optionally sets the map view to the user's location with respect to
 
3626         // detection accuracy (or to the world view if geolocation failed).
 
3627         // Note that, if your page doesn't use HTTPS, this method will fail in
 
3628         // modern browsers ([Chrome 50 and newer](https://sites.google.com/a/chromium.org/dev/Home/chromium-security/deprecating-powerful-features-on-insecure-origins))
 
3629         // See `Locate options` for more details.
 
3630         locate: function (options) {
 
3632                 options = this._locateOptions = extend({
 
3636                         // maxZoom: <Number>
 
3638                         // enableHighAccuracy: false
 
3641                 if (!('geolocation' in navigator)) {
 
3642                         this._handleGeolocationError({
 
3644                                 message: 'Geolocation not supported.'
 
3649                 var onResponse = bind(this._handleGeolocationResponse, this),
 
3650                     onError = bind(this._handleGeolocationError, this);
 
3652                 if (options.watch) {
 
3653                         this._locationWatchId =
 
3654                                 navigator.geolocation.watchPosition(onResponse, onError, options);
 
3656                         navigator.geolocation.getCurrentPosition(onResponse, onError, options);
 
3661         // @method stopLocate(): this
 
3662         // Stops watching location previously initiated by `map.locate({watch: true})`
 
3663         // and aborts resetting the map view if map.locate was called with
 
3664         // `{setView: true}`.
 
3665         stopLocate: function () {
 
3666                 if (navigator.geolocation && navigator.geolocation.clearWatch) {
 
3667                         navigator.geolocation.clearWatch(this._locationWatchId);
 
3669                 if (this._locateOptions) {
 
3670                         this._locateOptions.setView = false;
 
3675         _handleGeolocationError: function (error) {
 
3677                     message = error.message ||
 
3678                             (c === 1 ? 'permission denied' :
 
3679                             (c === 2 ? 'position unavailable' : 'timeout'));
 
3681                 if (this._locateOptions.setView && !this._loaded) {
 
3685                 // @section Location events
 
3686                 // @event locationerror: ErrorEvent
 
3687                 // Fired when geolocation (using the [`locate`](#map-locate) method) failed.
 
3688                 this.fire('locationerror', {
 
3690                         message: 'Geolocation error: ' + message + '.'
 
3694         _handleGeolocationResponse: function (pos) {
 
3695                 var lat = pos.coords.latitude,
 
3696                     lng = pos.coords.longitude,
 
3697                     latlng = new LatLng(lat, lng),
 
3698                     bounds = latlng.toBounds(pos.coords.accuracy * 2),
 
3699                     options = this._locateOptions;
 
3701                 if (options.setView) {
 
3702                         var zoom = this.getBoundsZoom(bounds);
 
3703                         this.setView(latlng, options.maxZoom ? Math.min(zoom, options.maxZoom) : zoom);
 
3709                         timestamp: pos.timestamp
 
3712                 for (var i in pos.coords) {
 
3713                         if (typeof pos.coords[i] === 'number') {
 
3714                                 data[i] = pos.coords[i];
 
3718                 // @event locationfound: LocationEvent
 
3719                 // Fired when geolocation (using the [`locate`](#map-locate) method)
 
3720                 // went successfully.
 
3721                 this.fire('locationfound', data);
 
3724         // TODO Appropriate docs section?
 
3725         // @section Other Methods
 
3726         // @method addHandler(name: String, HandlerClass: Function): this
 
3727         // Adds a new `Handler` to the map, given its name and constructor function.
 
3728         addHandler: function (name, HandlerClass) {
 
3729                 if (!HandlerClass) { return this; }
 
3731                 var handler = this[name] = new HandlerClass(this);
 
3733                 this._handlers.push(handler);
 
3735                 if (this.options[name]) {
 
3742         // @method remove(): this
 
3743         // Destroys the map and clears all related event listeners.
 
3744         remove: function () {
 
3746                 this._initEvents(true);
 
3748                 if (this._containerId !== this._container._leaflet_id) {
 
3749                         throw new Error('Map container is being reused by another instance');
 
3753                         // throws error in IE6-8
 
3754                         delete this._container._leaflet_id;
 
3755                         delete this._containerId;
 
3758                         this._container._leaflet_id = undefined;
 
3760                         this._containerId = undefined;
 
3763                 if (this._locationWatchId !== undefined) {
 
3769                 remove(this._mapPane);
 
3771                 if (this._clearControlPos) {
 
3772                         this._clearControlPos();
 
3774                 if (this._resizeRequest) {
 
3775                         cancelAnimFrame(this._resizeRequest);
 
3776                         this._resizeRequest = null;
 
3779                 this._clearHandlers();
 
3782                         // @section Map state change events
 
3783                         // @event unload: Event
 
3784                         // Fired when the map is destroyed with [remove](#map-remove) method.
 
3785                         this.fire('unload');
 
3789                 for (i in this._layers) {
 
3790                         this._layers[i].remove();
 
3792                 for (i in this._panes) {
 
3793                         remove(this._panes[i]);
 
3798                 delete this._mapPane;
 
3799                 delete this._renderer;
 
3804         // @section Other Methods
 
3805         // @method createPane(name: String, container?: HTMLElement): HTMLElement
 
3806         // Creates a new [map pane](#map-pane) with the given name if it doesn't exist already,
 
3807         // then returns it. The pane is created as a child of `container`, or
 
3808         // as a child of the main map pane if not set.
 
3809         createPane: function (name, container) {
 
3810                 var className = 'leaflet-pane' + (name ? ' leaflet-' + name.replace('Pane', '') + '-pane' : ''),
 
3811                     pane = create$1('div', className, container || this._mapPane);
 
3814                         this._panes[name] = pane;
 
3819         // @section Methods for Getting Map State
 
3821         // @method getCenter(): LatLng
 
3822         // Returns the geographical center of the map view
 
3823         getCenter: function () {
 
3824                 this._checkIfLoaded();
 
3826                 if (this._lastCenter && !this._moved()) {
 
3827                         return this._lastCenter;
 
3829                 return this.layerPointToLatLng(this._getCenterLayerPoint());
 
3832         // @method getZoom(): Number
 
3833         // Returns the current zoom level of the map view
 
3834         getZoom: function () {
 
3838         // @method getBounds(): LatLngBounds
 
3839         // Returns the geographical bounds visible in the current map view
 
3840         getBounds: function () {
 
3841                 var bounds = this.getPixelBounds(),
 
3842                     sw = this.unproject(bounds.getBottomLeft()),
 
3843                     ne = this.unproject(bounds.getTopRight());
 
3845                 return new LatLngBounds(sw, ne);
 
3848         // @method getMinZoom(): Number
 
3849         // 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.
 
3850         getMinZoom: function () {
 
3851                 return this.options.minZoom === undefined ? this._layersMinZoom || 0 : this.options.minZoom;
 
3854         // @method getMaxZoom(): Number
 
3855         // Returns the maximum zoom level of the map (if set in the `maxZoom` option of the map or of any layers).
 
3856         getMaxZoom: function () {
 
3857                 return this.options.maxZoom === undefined ?
 
3858                         (this._layersMaxZoom === undefined ? Infinity : this._layersMaxZoom) :
 
3859                         this.options.maxZoom;
 
3862         // @method getBoundsZoom(bounds: LatLngBounds, inside?: Boolean, padding?: Point): Number
 
3863         // Returns the maximum zoom level on which the given bounds fit to the map
 
3864         // view in its entirety. If `inside` (optional) is set to `true`, the method
 
3865         // instead returns the minimum zoom level on which the map view fits into
 
3866         // the given bounds in its entirety.
 
3867         getBoundsZoom: function (bounds, inside, padding) { // (LatLngBounds[, Boolean, Point]) -> Number
 
3868                 bounds = toLatLngBounds(bounds);
 
3869                 padding = toPoint(padding || [0, 0]);
 
3871                 var zoom = this.getZoom() || 0,
 
3872                     min = this.getMinZoom(),
 
3873                     max = this.getMaxZoom(),
 
3874                     nw = bounds.getNorthWest(),
 
3875                     se = bounds.getSouthEast(),
 
3876                     size = this.getSize().subtract(padding),
 
3877                     boundsSize = toBounds(this.project(se, zoom), this.project(nw, zoom)).getSize(),
 
3878                     snap = any3d ? this.options.zoomSnap : 1,
 
3879                     scalex = size.x / boundsSize.x,
 
3880                     scaley = size.y / boundsSize.y,
 
3881                     scale = inside ? Math.max(scalex, scaley) : Math.min(scalex, scaley);
 
3883                 zoom = this.getScaleZoom(scale, zoom);
 
3886                         zoom = Math.round(zoom / (snap / 100)) * (snap / 100); // don't jump if within 1% of a snap level
 
3887                         zoom = inside ? Math.ceil(zoom / snap) * snap : Math.floor(zoom / snap) * snap;
 
3890                 return Math.max(min, Math.min(max, zoom));
 
3893         // @method getSize(): Point
 
3894         // Returns the current size of the map container (in pixels).
 
3895         getSize: function () {
 
3896                 if (!this._size || this._sizeChanged) {
 
3897                         this._size = new Point(
 
3898                                 this._container.clientWidth || 0,
 
3899                                 this._container.clientHeight || 0);
 
3901                         this._sizeChanged = false;
 
3903                 return this._size.clone();
 
3906         // @method getPixelBounds(): Bounds
 
3907         // Returns the bounds of the current map view in projected pixel
 
3908         // coordinates (sometimes useful in layer and overlay implementations).
 
3909         getPixelBounds: function (center, zoom) {
 
3910                 var topLeftPoint = this._getTopLeftPoint(center, zoom);
 
3911                 return new Bounds(topLeftPoint, topLeftPoint.add(this.getSize()));
 
3914         // TODO: Check semantics - isn't the pixel origin the 0,0 coord relative to
 
3915         // the map pane? "left point of the map layer" can be confusing, specially
 
3916         // since there can be negative offsets.
 
3917         // @method getPixelOrigin(): Point
 
3918         // Returns the projected pixel coordinates of the top left point of
 
3919         // the map layer (useful in custom layer and overlay implementations).
 
3920         getPixelOrigin: function () {
 
3921                 this._checkIfLoaded();
 
3922                 return this._pixelOrigin;
 
3925         // @method getPixelWorldBounds(zoom?: Number): Bounds
 
3926         // Returns the world's bounds in pixel coordinates for zoom level `zoom`.
 
3927         // If `zoom` is omitted, the map's current zoom level is used.
 
3928         getPixelWorldBounds: function (zoom) {
 
3929                 return this.options.crs.getProjectedBounds(zoom === undefined ? this.getZoom() : zoom);
 
3932         // @section Other Methods
 
3934         // @method getPane(pane: String|HTMLElement): HTMLElement
 
3935         // Returns a [map pane](#map-pane), given its name or its HTML element (its identity).
 
3936         getPane: function (pane) {
 
3937                 return typeof pane === 'string' ? this._panes[pane] : pane;
 
3940         // @method getPanes(): Object
 
3941         // Returns a plain object containing the names of all [panes](#map-pane) as keys and
 
3942         // the panes as values.
 
3943         getPanes: function () {
 
3947         // @method getContainer: HTMLElement
 
3948         // Returns the HTML element that contains the map.
 
3949         getContainer: function () {
 
3950                 return this._container;
 
3954         // @section Conversion Methods
 
3956         // @method getZoomScale(toZoom: Number, fromZoom: Number): Number
 
3957         // Returns the scale factor to be applied to a map transition from zoom level
 
3958         // `fromZoom` to `toZoom`. Used internally to help with zoom animations.
 
3959         getZoomScale: function (toZoom, fromZoom) {
 
3960                 // TODO replace with universal implementation after refactoring projections
 
3961                 var crs = this.options.crs;
 
3962                 fromZoom = fromZoom === undefined ? this._zoom : fromZoom;
 
3963                 return crs.scale(toZoom) / crs.scale(fromZoom);
 
3966         // @method getScaleZoom(scale: Number, fromZoom: Number): Number
 
3967         // Returns the zoom level that the map would end up at, if it is at `fromZoom`
 
3968         // level and everything is scaled by a factor of `scale`. Inverse of
 
3969         // [`getZoomScale`](#map-getZoomScale).
 
3970         getScaleZoom: function (scale, fromZoom) {
 
3971                 var crs = this.options.crs;
 
3972                 fromZoom = fromZoom === undefined ? this._zoom : fromZoom;
 
3973                 var zoom = crs.zoom(scale * crs.scale(fromZoom));
 
3974                 return isNaN(zoom) ? Infinity : zoom;
 
3977         // @method project(latlng: LatLng, zoom: Number): Point
 
3978         // Projects a geographical coordinate `LatLng` according to the projection
 
3979         // of the map's CRS, then scales it according to `zoom` and the CRS's
 
3980         // `Transformation`. The result is pixel coordinate relative to
 
3982         project: function (latlng, zoom) {
 
3983                 zoom = zoom === undefined ? this._zoom : zoom;
 
3984                 return this.options.crs.latLngToPoint(toLatLng(latlng), zoom);
 
3987         // @method unproject(point: Point, zoom: Number): LatLng
 
3988         // Inverse of [`project`](#map-project).
 
3989         unproject: function (point, zoom) {
 
3990                 zoom = zoom === undefined ? this._zoom : zoom;
 
3991                 return this.options.crs.pointToLatLng(toPoint(point), zoom);
 
3994         // @method layerPointToLatLng(point: Point): LatLng
 
3995         // Given a pixel coordinate relative to the [origin pixel](#map-getpixelorigin),
 
3996         // returns the corresponding geographical coordinate (for the current zoom level).
 
3997         layerPointToLatLng: function (point) {
 
3998                 var projectedPoint = toPoint(point).add(this.getPixelOrigin());
 
3999                 return this.unproject(projectedPoint);
 
4002         // @method latLngToLayerPoint(latlng: LatLng): Point
 
4003         // Given a geographical coordinate, returns the corresponding pixel coordinate
 
4004         // relative to the [origin pixel](#map-getpixelorigin).
 
4005         latLngToLayerPoint: function (latlng) {
 
4006                 var projectedPoint = this.project(toLatLng(latlng))._round();
 
4007                 return projectedPoint._subtract(this.getPixelOrigin());
 
4010         // @method wrapLatLng(latlng: LatLng): LatLng
 
4011         // Returns a `LatLng` where `lat` and `lng` has been wrapped according to the
 
4012         // map's CRS's `wrapLat` and `wrapLng` properties, if they are outside the
 
4014         // By default this means longitude is wrapped around the dateline so its
 
4015         // value is between -180 and +180 degrees.
 
4016         wrapLatLng: function (latlng) {
 
4017                 return this.options.crs.wrapLatLng(toLatLng(latlng));
 
4020         // @method wrapLatLngBounds(bounds: LatLngBounds): LatLngBounds
 
4021         // Returns a `LatLngBounds` with the same size as the given one, ensuring that
 
4022         // its center is within the CRS's bounds.
 
4023         // By default this means the center longitude is wrapped around the dateline so its
 
4024         // value is between -180 and +180 degrees, and the majority of the bounds
 
4025         // overlaps the CRS's bounds.
 
4026         wrapLatLngBounds: function (latlng) {
 
4027                 return this.options.crs.wrapLatLngBounds(toLatLngBounds(latlng));
 
4030         // @method distance(latlng1: LatLng, latlng2: LatLng): Number
 
4031         // Returns the distance between two geographical coordinates according to
 
4032         // the map's CRS. By default this measures distance in meters.
 
4033         distance: function (latlng1, latlng2) {
 
4034                 return this.options.crs.distance(toLatLng(latlng1), toLatLng(latlng2));
 
4037         // @method containerPointToLayerPoint(point: Point): Point
 
4038         // Given a pixel coordinate relative to the map container, returns the corresponding
 
4039         // pixel coordinate relative to the [origin pixel](#map-getpixelorigin).
 
4040         containerPointToLayerPoint: function (point) { // (Point)
 
4041                 return toPoint(point).subtract(this._getMapPanePos());
 
4044         // @method layerPointToContainerPoint(point: Point): Point
 
4045         // Given a pixel coordinate relative to the [origin pixel](#map-getpixelorigin),
 
4046         // returns the corresponding pixel coordinate relative to the map container.
 
4047         layerPointToContainerPoint: function (point) { // (Point)
 
4048                 return toPoint(point).add(this._getMapPanePos());
 
4051         // @method containerPointToLatLng(point: Point): LatLng
 
4052         // Given a pixel coordinate relative to the map container, returns
 
4053         // the corresponding geographical coordinate (for the current zoom level).
 
4054         containerPointToLatLng: function (point) {
 
4055                 var layerPoint = this.containerPointToLayerPoint(toPoint(point));
 
4056                 return this.layerPointToLatLng(layerPoint);
 
4059         // @method latLngToContainerPoint(latlng: LatLng): Point
 
4060         // Given a geographical coordinate, returns the corresponding pixel coordinate
 
4061         // relative to the map container.
 
4062         latLngToContainerPoint: function (latlng) {
 
4063                 return this.layerPointToContainerPoint(this.latLngToLayerPoint(toLatLng(latlng)));
 
4066         // @method mouseEventToContainerPoint(ev: MouseEvent): Point
 
4067         // Given a MouseEvent object, returns the pixel coordinate relative to the
 
4068         // map container where the event took place.
 
4069         mouseEventToContainerPoint: function (e) {
 
4070                 return getMousePosition(e, this._container);
 
4073         // @method mouseEventToLayerPoint(ev: MouseEvent): Point
 
4074         // Given a MouseEvent object, returns the pixel coordinate relative to
 
4075         // the [origin pixel](#map-getpixelorigin) where the event took place.
 
4076         mouseEventToLayerPoint: function (e) {
 
4077                 return this.containerPointToLayerPoint(this.mouseEventToContainerPoint(e));
 
4080         // @method mouseEventToLatLng(ev: MouseEvent): LatLng
 
4081         // Given a MouseEvent object, returns geographical coordinate where the
 
4082         // event took place.
 
4083         mouseEventToLatLng: function (e) { // (MouseEvent)
 
4084                 return this.layerPointToLatLng(this.mouseEventToLayerPoint(e));
 
4088         // map initialization methods
 
4090         _initContainer: function (id) {
 
4091                 var container = this._container = get(id);
 
4094                         throw new Error('Map container not found.');
 
4095                 } else if (container._leaflet_id) {
 
4096                         throw new Error('Map container is already initialized.');
 
4099                 on(container, 'scroll', this._onScroll, this);
 
4100                 this._containerId = stamp(container);
 
4103         _initLayout: function () {
 
4104                 var container = this._container;
 
4106                 this._fadeAnimated = this.options.fadeAnimation && any3d;
 
4108                 addClass(container, 'leaflet-container' +
 
4109                         (touch ? ' leaflet-touch' : '') +
 
4110                         (retina ? ' leaflet-retina' : '') +
 
4111                         (ielt9 ? ' leaflet-oldie' : '') +
 
4112                         (safari ? ' leaflet-safari' : '') +
 
4113                         (this._fadeAnimated ? ' leaflet-fade-anim' : ''));
 
4115                 var position = getStyle(container, 'position');
 
4117                 if (position !== 'absolute' && position !== 'relative' && position !== 'fixed') {
 
4118                         container.style.position = 'relative';
 
4123                 if (this._initControlPos) {
 
4124                         this._initControlPos();
 
4128         _initPanes: function () {
 
4129                 var panes = this._panes = {};
 
4130                 this._paneRenderers = {};
 
4134                 // Panes are DOM elements used to control the ordering of layers on the map. You
 
4135                 // can access panes with [`map.getPane`](#map-getpane) or
 
4136                 // [`map.getPanes`](#map-getpanes) methods. New panes can be created with the
 
4137                 // [`map.createPane`](#map-createpane) method.
 
4139                 // Every map has the following default panes that differ only in zIndex.
 
4141                 // @pane mapPane: HTMLElement = 'auto'
 
4142                 // Pane that contains all other map panes
 
4144                 this._mapPane = this.createPane('mapPane', this._container);
 
4145                 setPosition(this._mapPane, new Point(0, 0));
 
4147                 // @pane tilePane: HTMLElement = 200
 
4148                 // Pane for `GridLayer`s and `TileLayer`s
 
4149                 this.createPane('tilePane');
 
4150                 // @pane overlayPane: HTMLElement = 400
 
4151                 // Pane for vectors (`Path`s, like `Polyline`s and `Polygon`s), `ImageOverlay`s and `VideoOverlay`s
 
4152                 this.createPane('shadowPane');
 
4153                 // @pane shadowPane: HTMLElement = 500
 
4154                 // Pane for overlay shadows (e.g. `Marker` shadows)
 
4155                 this.createPane('overlayPane');
 
4156                 // @pane markerPane: HTMLElement = 600
 
4157                 // Pane for `Icon`s of `Marker`s
 
4158                 this.createPane('markerPane');
 
4159                 // @pane tooltipPane: HTMLElement = 650
 
4160                 // Pane for `Tooltip`s.
 
4161                 this.createPane('tooltipPane');
 
4162                 // @pane popupPane: HTMLElement = 700
 
4163                 // Pane for `Popup`s.
 
4164                 this.createPane('popupPane');
 
4166                 if (!this.options.markerZoomAnimation) {
 
4167                         addClass(panes.markerPane, 'leaflet-zoom-hide');
 
4168                         addClass(panes.shadowPane, 'leaflet-zoom-hide');
 
4173         // private methods that modify map state
 
4175         // @section Map state change events
 
4176         _resetView: function (center, zoom) {
 
4177                 setPosition(this._mapPane, new Point(0, 0));
 
4179                 var loading = !this._loaded;
 
4180                 this._loaded = true;
 
4181                 zoom = this._limitZoom(zoom);
 
4183                 this.fire('viewprereset');
 
4185                 var zoomChanged = this._zoom !== zoom;
 
4187                         ._moveStart(zoomChanged, false)
 
4188                         ._move(center, zoom)
 
4189                         ._moveEnd(zoomChanged);
 
4191                 // @event viewreset: Event
 
4192                 // Fired when the map needs to redraw its content (this usually happens
 
4193                 // on map zoom or load). Very useful for creating custom overlays.
 
4194                 this.fire('viewreset');
 
4196                 // @event load: Event
 
4197                 // Fired when the map is initialized (when its center and zoom are set
 
4198                 // for the first time).
 
4204         _moveStart: function (zoomChanged, noMoveStart) {
 
4205                 // @event zoomstart: Event
 
4206                 // Fired when the map zoom is about to change (e.g. before zoom animation).
 
4207                 // @event movestart: Event
 
4208                 // Fired when the view of the map starts changing (e.g. user starts dragging the map).
 
4210                         this.fire('zoomstart');
 
4213                         this.fire('movestart');
 
4218         _move: function (center, zoom, data) {
 
4219                 if (zoom === undefined) {
 
4222                 var zoomChanged = this._zoom !== zoom;
 
4225                 this._lastCenter = center;
 
4226                 this._pixelOrigin = this._getNewPixelOrigin(center);
 
4228                 // @event zoom: Event
 
4229                 // Fired repeatedly during any change in zoom level, including zoom
 
4230                 // and fly animations.
 
4231                 if (zoomChanged || (data && data.pinch)) {      // Always fire 'zoom' if pinching because #3530
 
4232                         this.fire('zoom', data);
 
4235                 // @event move: Event
 
4236                 // Fired repeatedly during any movement of the map, including pan and
 
4238                 return this.fire('move', data);
 
4241         _moveEnd: function (zoomChanged) {
 
4242                 // @event zoomend: Event
 
4243                 // Fired when the map has changed, after any animations.
 
4245                         this.fire('zoomend');
 
4248                 // @event moveend: Event
 
4249                 // Fired when the center of the map stops changing (e.g. user stopped
 
4250                 // dragging the map).
 
4251                 return this.fire('moveend');
 
4254         _stop: function () {
 
4255                 cancelAnimFrame(this._flyToFrame);
 
4256                 if (this._panAnim) {
 
4257                         this._panAnim.stop();
 
4262         _rawPanBy: function (offset) {
 
4263                 setPosition(this._mapPane, this._getMapPanePos().subtract(offset));
 
4266         _getZoomSpan: function () {
 
4267                 return this.getMaxZoom() - this.getMinZoom();
 
4270         _panInsideMaxBounds: function () {
 
4271                 if (!this._enforcingBounds) {
 
4272                         this.panInsideBounds(this.options.maxBounds);
 
4276         _checkIfLoaded: function () {
 
4277                 if (!this._loaded) {
 
4278                         throw new Error('Set map center and zoom first.');
 
4282         // DOM event handling
 
4284         // @section Interaction events
 
4285         _initEvents: function (remove$$1) {
 
4287                 this._targets[stamp(this._container)] = this;
 
4289                 var onOff = remove$$1 ? off : on;
 
4291                 // @event click: MouseEvent
 
4292                 // Fired when the user clicks (or taps) the map.
 
4293                 // @event dblclick: MouseEvent
 
4294                 // Fired when the user double-clicks (or double-taps) the map.
 
4295                 // @event mousedown: MouseEvent
 
4296                 // Fired when the user pushes the mouse button on the map.
 
4297                 // @event mouseup: MouseEvent
 
4298                 // Fired when the user releases the mouse button on the map.
 
4299                 // @event mouseover: MouseEvent
 
4300                 // Fired when the mouse enters the map.
 
4301                 // @event mouseout: MouseEvent
 
4302                 // Fired when the mouse leaves the map.
 
4303                 // @event mousemove: MouseEvent
 
4304                 // Fired while the mouse moves over the map.
 
4305                 // @event contextmenu: MouseEvent
 
4306                 // Fired when the user pushes the right mouse button on the map, prevents
 
4307                 // default browser context menu from showing if there are listeners on
 
4308                 // this event. Also fired on mobile when the user holds a single touch
 
4309                 // for a second (also called long press).
 
4310                 // @event keypress: KeyboardEvent
 
4311                 // Fired when the user presses a key from the keyboard while the map is focused.
 
4312                 onOff(this._container, 'click dblclick mousedown mouseup ' +
 
4313                         'mouseover mouseout mousemove contextmenu keypress', this._handleDOMEvent, this);
 
4315                 if (this.options.trackResize) {
 
4316                         onOff(window, 'resize', this._onResize, this);
 
4319                 if (any3d && this.options.transform3DLimit) {
 
4320                         (remove$$1 ? this.off : this.on).call(this, 'moveend', this._onMoveEnd);
 
4324         _onResize: function () {
 
4325                 cancelAnimFrame(this._resizeRequest);
 
4326                 this._resizeRequest = requestAnimFrame(
 
4327                         function () { this.invalidateSize({debounceMoveend: true}); }, this);
 
4330         _onScroll: function () {
 
4331                 this._container.scrollTop  = 0;
 
4332                 this._container.scrollLeft = 0;
 
4335         _onMoveEnd: function () {
 
4336                 var pos = this._getMapPanePos();
 
4337                 if (Math.max(Math.abs(pos.x), Math.abs(pos.y)) >= this.options.transform3DLimit) {
 
4338                         // https://bugzilla.mozilla.org/show_bug.cgi?id=1203873 but Webkit also have
 
4339                         // a pixel offset on very high values, see: http://jsfiddle.net/dg6r5hhb/
 
4340                         this._resetView(this.getCenter(), this.getZoom());
 
4344         _findEventTargets: function (e, type) {
 
4347                     isHover = type === 'mouseout' || type === 'mouseover',
 
4348                     src = e.target || e.srcElement,
 
4352                         target = this._targets[stamp(src)];
 
4353                         if (target && (type === 'click' || type === 'preclick') && !e._simulated && this._draggableMoved(target)) {
 
4354                                 // Prevent firing click after you just dragged an object.
 
4358                         if (target && target.listens(type, true)) {
 
4359                                 if (isHover && !isExternalTarget(src, e)) { break; }
 
4360                                 targets.push(target);
 
4361                                 if (isHover) { break; }
 
4363                         if (src === this._container) { break; }
 
4364                         src = src.parentNode;
 
4366                 if (!targets.length && !dragging && !isHover && isExternalTarget(src, e)) {
 
4372         _handleDOMEvent: function (e) {
 
4373                 if (!this._loaded || skipped(e)) { return; }
 
4377                 if (type === 'mousedown' || type === 'keypress') {
 
4378                         // prevents outline when clicking on keyboard-focusable element
 
4379                         preventOutline(e.target || e.srcElement);
 
4382                 this._fireDOMEvent(e, type);
 
4385         _mouseEvents: ['click', 'dblclick', 'mouseover', 'mouseout', 'contextmenu'],
 
4387         _fireDOMEvent: function (e, type, targets) {
 
4389                 if (e.type === 'click') {
 
4390                         // Fire a synthetic 'preclick' event which propagates up (mainly for closing popups).
 
4391                         // @event preclick: MouseEvent
 
4392                         // Fired before mouse click on the map (sometimes useful when you
 
4393                         // want something to happen on click before any existing click
 
4394                         // handlers start running).
 
4395                         var synth = extend({}, e);
 
4396                         synth.type = 'preclick';
 
4397                         this._fireDOMEvent(synth, synth.type, targets);
 
4400                 if (e._stopped) { return; }
 
4402                 // Find the layer the event is propagating from and its parents.
 
4403                 targets = (targets || []).concat(this._findEventTargets(e, type));
 
4405                 if (!targets.length) { return; }
 
4407                 var target = targets[0];
 
4408                 if (type === 'contextmenu' && target.listens(type, true)) {
 
4416                 if (e.type !== 'keypress') {
 
4417                         var isMarker = target.getLatLng && (!target._radius || target._radius <= 10);
 
4418                         data.containerPoint = isMarker ?
 
4419                                 this.latLngToContainerPoint(target.getLatLng()) : this.mouseEventToContainerPoint(e);
 
4420                         data.layerPoint = this.containerPointToLayerPoint(data.containerPoint);
 
4421                         data.latlng = isMarker ? target.getLatLng() : this.layerPointToLatLng(data.layerPoint);
 
4424                 for (var i = 0; i < targets.length; i++) {
 
4425                         targets[i].fire(type, data, true);
 
4426                         if (data.originalEvent._stopped ||
 
4427                                 (targets[i].options.bubblingMouseEvents === false && indexOf(this._mouseEvents, type) !== -1)) { return; }
 
4431         _draggableMoved: function (obj) {
 
4432                 obj = obj.dragging && obj.dragging.enabled() ? obj : this;
 
4433                 return (obj.dragging && obj.dragging.moved()) || (this.boxZoom && this.boxZoom.moved());
 
4436         _clearHandlers: function () {
 
4437                 for (var i = 0, len = this._handlers.length; i < len; i++) {
 
4438                         this._handlers[i].disable();
 
4442         // @section Other Methods
 
4444         // @method whenReady(fn: Function, context?: Object): this
 
4445         // Runs the given function `fn` when the map gets initialized with
 
4446         // a view (center and zoom) and at least one layer, or immediately
 
4447         // if it's already initialized, optionally passing a function context.
 
4448         whenReady: function (callback, context) {
 
4450                         callback.call(context || this, {target: this});
 
4452                         this.on('load', callback, context);
 
4458         // private methods for getting map state
 
4460         _getMapPanePos: function () {
 
4461                 return getPosition(this._mapPane) || new Point(0, 0);
 
4464         _moved: function () {
 
4465                 var pos = this._getMapPanePos();
 
4466                 return pos && !pos.equals([0, 0]);
 
4469         _getTopLeftPoint: function (center, zoom) {
 
4470                 var pixelOrigin = center && zoom !== undefined ?
 
4471                         this._getNewPixelOrigin(center, zoom) :
 
4472                         this.getPixelOrigin();
 
4473                 return pixelOrigin.subtract(this._getMapPanePos());
 
4476         _getNewPixelOrigin: function (center, zoom) {
 
4477                 var viewHalf = this.getSize()._divideBy(2);
 
4478                 return this.project(center, zoom)._subtract(viewHalf)._add(this._getMapPanePos())._round();
 
4481         _latLngToNewLayerPoint: function (latlng, zoom, center) {
 
4482                 var topLeft = this._getNewPixelOrigin(center, zoom);
 
4483                 return this.project(latlng, zoom)._subtract(topLeft);
 
4486         _latLngBoundsToNewLayerBounds: function (latLngBounds, zoom, center) {
 
4487                 var topLeft = this._getNewPixelOrigin(center, zoom);
 
4489                         this.project(latLngBounds.getSouthWest(), zoom)._subtract(topLeft),
 
4490                         this.project(latLngBounds.getNorthWest(), zoom)._subtract(topLeft),
 
4491                         this.project(latLngBounds.getSouthEast(), zoom)._subtract(topLeft),
 
4492                         this.project(latLngBounds.getNorthEast(), zoom)._subtract(topLeft)
 
4496         // layer point of the current center
 
4497         _getCenterLayerPoint: function () {
 
4498                 return this.containerPointToLayerPoint(this.getSize()._divideBy(2));
 
4501         // offset of the specified place to the current center in pixels
 
4502         _getCenterOffset: function (latlng) {
 
4503                 return this.latLngToLayerPoint(latlng).subtract(this._getCenterLayerPoint());
 
4506         // adjust center for view to get inside bounds
 
4507         _limitCenter: function (center, zoom, bounds) {
 
4509                 if (!bounds) { return center; }
 
4511                 var centerPoint = this.project(center, zoom),
 
4512                     viewHalf = this.getSize().divideBy(2),
 
4513                     viewBounds = new Bounds(centerPoint.subtract(viewHalf), centerPoint.add(viewHalf)),
 
4514                     offset = this._getBoundsOffset(viewBounds, bounds, zoom);
 
4516                 // If offset is less than a pixel, ignore.
 
4517                 // This prevents unstable projections from getting into
 
4518                 // an infinite loop of tiny offsets.
 
4519                 if (offset.round().equals([0, 0])) {
 
4523                 return this.unproject(centerPoint.add(offset), zoom);
 
4526         // adjust offset for view to get inside bounds
 
4527         _limitOffset: function (offset, bounds) {
 
4528                 if (!bounds) { return offset; }
 
4530                 var viewBounds = this.getPixelBounds(),
 
4531                     newBounds = new Bounds(viewBounds.min.add(offset), viewBounds.max.add(offset));
 
4533                 return offset.add(this._getBoundsOffset(newBounds, bounds));
 
4536         // returns offset needed for pxBounds to get inside maxBounds at a specified zoom
 
4537         _getBoundsOffset: function (pxBounds, maxBounds, zoom) {
 
4538                 var projectedMaxBounds = toBounds(
 
4539                         this.project(maxBounds.getNorthEast(), zoom),
 
4540                         this.project(maxBounds.getSouthWest(), zoom)
 
4542                     minOffset = projectedMaxBounds.min.subtract(pxBounds.min),
 
4543                     maxOffset = projectedMaxBounds.max.subtract(pxBounds.max),
 
4545                     dx = this._rebound(minOffset.x, -maxOffset.x),
 
4546                     dy = this._rebound(minOffset.y, -maxOffset.y);
 
4548                 return new Point(dx, dy);
 
4551         _rebound: function (left, right) {
 
4552                 return left + right > 0 ?
 
4553                         Math.round(left - right) / 2 :
 
4554                         Math.max(0, Math.ceil(left)) - Math.max(0, Math.floor(right));
 
4557         _limitZoom: function (zoom) {
 
4558                 var min = this.getMinZoom(),
 
4559                     max = this.getMaxZoom(),
 
4560                     snap = any3d ? this.options.zoomSnap : 1;
 
4562                         zoom = Math.round(zoom / snap) * snap;
 
4564                 return Math.max(min, Math.min(max, zoom));
 
4567         _onPanTransitionStep: function () {
 
4571         _onPanTransitionEnd: function () {
 
4572                 removeClass(this._mapPane, 'leaflet-pan-anim');
 
4573                 this.fire('moveend');
 
4576         _tryAnimatedPan: function (center, options) {
 
4577                 // difference between the new and current centers in pixels
 
4578                 var offset = this._getCenterOffset(center)._trunc();
 
4580                 // don't animate too far unless animate: true specified in options
 
4581                 if ((options && options.animate) !== true && !this.getSize().contains(offset)) { return false; }
 
4583                 this.panBy(offset, options);
 
4588         _createAnimProxy: function () {
 
4590                 var proxy = this._proxy = create$1('div', 'leaflet-proxy leaflet-zoom-animated');
 
4591                 this._panes.mapPane.appendChild(proxy);
 
4593                 this.on('zoomanim', function (e) {
 
4594                         var prop = TRANSFORM,
 
4595                             transform = this._proxy.style[prop];
 
4597                         setTransform(this._proxy, this.project(e.center, e.zoom), this.getZoomScale(e.zoom, 1));
 
4599                         // workaround for case when transform is the same and so transitionend event is not fired
 
4600                         if (transform === this._proxy.style[prop] && this._animatingZoom) {
 
4601                                 this._onZoomTransitionEnd();
 
4605                 this.on('load moveend', function () {
 
4606                         var c = this.getCenter(),
 
4608                         setTransform(this._proxy, this.project(c, z), this.getZoomScale(z, 1));
 
4611                 this._on('unload', this._destroyAnimProxy, this);
 
4614         _destroyAnimProxy: function () {
 
4615                 remove(this._proxy);
 
4619         _catchTransitionEnd: function (e) {
 
4620                 if (this._animatingZoom && e.propertyName.indexOf('transform') >= 0) {
 
4621                         this._onZoomTransitionEnd();
 
4625         _nothingToAnimate: function () {
 
4626                 return !this._container.getElementsByClassName('leaflet-zoom-animated').length;
 
4629         _tryAnimatedZoom: function (center, zoom, options) {
 
4631                 if (this._animatingZoom) { return true; }
 
4633                 options = options || {};
 
4635                 // don't animate if disabled, not supported or zoom difference is too large
 
4636                 if (!this._zoomAnimated || options.animate === false || this._nothingToAnimate() ||
 
4637                         Math.abs(zoom - this._zoom) > this.options.zoomAnimationThreshold) { return false; }
 
4639                 // offset is the pixel coords of the zoom origin relative to the current center
 
4640                 var scale = this.getZoomScale(zoom),
 
4641                     offset = this._getCenterOffset(center)._divideBy(1 - 1 / scale);
 
4643                 // don't animate if the zoom origin isn't within one screen from the current center, unless forced
 
4644                 if (options.animate !== true && !this.getSize().contains(offset)) { return false; }
 
4646                 requestAnimFrame(function () {
 
4648                             ._moveStart(true, false)
 
4649                             ._animateZoom(center, zoom, true);
 
4655         _animateZoom: function (center, zoom, startAnim, noUpdate) {
 
4656                 if (!this._mapPane) { return; }
 
4659                         this._animatingZoom = true;
 
4661                         // remember what center/zoom to set after animation
 
4662                         this._animateToCenter = center;
 
4663                         this._animateToZoom = zoom;
 
4665                         addClass(this._mapPane, 'leaflet-zoom-anim');
 
4668                 // @event zoomanim: ZoomAnimEvent
 
4669                 // Fired at least once per zoom animation. For continous zoom, like pinch zooming, fired once per frame during zoom.
 
4670                 this.fire('zoomanim', {
 
4676                 // Work around webkit not firing 'transitionend', see https://github.com/Leaflet/Leaflet/issues/3689, 2693
 
4677                 setTimeout(bind(this._onZoomTransitionEnd, this), 250);
 
4680         _onZoomTransitionEnd: function () {
 
4681                 if (!this._animatingZoom) { return; }
 
4683                 if (this._mapPane) {
 
4684                         removeClass(this._mapPane, 'leaflet-zoom-anim');
 
4687                 this._animatingZoom = false;
 
4689                 this._move(this._animateToCenter, this._animateToZoom);
 
4691                 // This anim frame should prevent an obscure iOS webkit tile loading race condition.
 
4692                 requestAnimFrame(function () {
 
4693                         this._moveEnd(true);
 
4700 // @factory L.map(id: String, options?: Map options)
 
4701 // Instantiates a map object given the DOM ID of a `<div>` element
 
4702 // and optionally an object literal with `Map options`.
 
4705 // @factory L.map(el: HTMLElement, options?: Map options)
 
4706 // Instantiates a map object given an instance of a `<div>` HTML element
 
4707 // and optionally an object literal with `Map options`.
 
4708 function createMap(id, options) {
 
4709         return new Map(id, options);
 
4717  * L.Control is a base class for implementing map controls. Handles positioning.
 
4718  * All other controls extend from this class.
 
4721 var Control = Class.extend({
 
4723         // @aka Control options
 
4725                 // @option position: String = 'topright'
 
4726                 // The position of the control (one of the map corners). Possible values are `'topleft'`,
 
4727                 // `'topright'`, `'bottomleft'` or `'bottomright'`
 
4728                 position: 'topright'
 
4731         initialize: function (options) {
 
4732                 setOptions(this, options);
 
4736          * Classes extending L.Control will inherit the following methods:
 
4738          * @method getPosition: string
 
4739          * Returns the position of the control.
 
4741         getPosition: function () {
 
4742                 return this.options.position;
 
4745         // @method setPosition(position: string): this
 
4746         // Sets the position of the control.
 
4747         setPosition: function (position) {
 
4748                 var map = this._map;
 
4751                         map.removeControl(this);
 
4754                 this.options.position = position;
 
4757                         map.addControl(this);
 
4763         // @method getContainer: HTMLElement
 
4764         // Returns the HTMLElement that contains the control.
 
4765         getContainer: function () {
 
4766                 return this._container;
 
4769         // @method addTo(map: Map): this
 
4770         // Adds the control to the given map.
 
4771         addTo: function (map) {
 
4775                 var container = this._container = this.onAdd(map),
 
4776                     pos = this.getPosition(),
 
4777                     corner = map._controlCorners[pos];
 
4779                 addClass(container, 'leaflet-control');
 
4781                 if (pos.indexOf('bottom') !== -1) {
 
4782                         corner.insertBefore(container, corner.firstChild);
 
4784                         corner.appendChild(container);
 
4790         // @method remove: this
 
4791         // Removes the control from the map it is currently active on.
 
4792         remove: function () {
 
4797                 remove(this._container);
 
4799                 if (this.onRemove) {
 
4800                         this.onRemove(this._map);
 
4808         _refocusOnMap: function (e) {
 
4809                 // if map exists and event is not a keyboard event
 
4810                 if (this._map && e && e.screenX > 0 && e.screenY > 0) {
 
4811                         this._map.getContainer().focus();
 
4816 var control = function (options) {
 
4817         return new Control(options);
 
4820 /* @section Extension methods
 
4823  * Every control should extend from `L.Control` and (re-)implement the following methods.
 
4825  * @method onAdd(map: Map): HTMLElement
 
4826  * Should return the container DOM element for the control and add listeners on relevant map events. Called on [`control.addTo(map)`](#control-addTo).
 
4828  * @method onRemove(map: Map)
 
4829  * Optional method. Should contain all clean up code that removes the listeners previously added in [`onAdd`](#control-onadd). Called on [`control.remove()`](#control-remove).
 
4833  * @section Methods for Layers and Controls
 
4836         // @method addControl(control: Control): this
 
4837         // Adds the given control to the map
 
4838         addControl: function (control) {
 
4839                 control.addTo(this);
 
4843         // @method removeControl(control: Control): this
 
4844         // Removes the given control from the map
 
4845         removeControl: function (control) {
 
4850         _initControlPos: function () {
 
4851                 var corners = this._controlCorners = {},
 
4853                     container = this._controlContainer =
 
4854                             create$1('div', l + 'control-container', this._container);
 
4856                 function createCorner(vSide, hSide) {
 
4857                         var className = l + vSide + ' ' + l + hSide;
 
4859                         corners[vSide + hSide] = create$1('div', className, container);
 
4862                 createCorner('top', 'left');
 
4863                 createCorner('top', 'right');
 
4864                 createCorner('bottom', 'left');
 
4865                 createCorner('bottom', 'right');
 
4868         _clearControlPos: function () {
 
4869                 for (var i in this._controlCorners) {
 
4870                         remove(this._controlCorners[i]);
 
4872                 remove(this._controlContainer);
 
4873                 delete this._controlCorners;
 
4874                 delete this._controlContainer;
 
4879  * @class Control.Layers
 
4880  * @aka L.Control.Layers
 
4883  * 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`.
 
4888  * var baseLayers = {
 
4890  *      "OpenStreetMap": osm
 
4895  *      "Roads": roadsLayer
 
4898  * L.control.layers(baseLayers, overlays).addTo(map);
 
4901  * The `baseLayers` and `overlays` parameters are object literals with layer names as keys and `Layer` objects as values:
 
4905  *     "<someName1>": layer1,
 
4906  *     "<someName2>": layer2
 
4910  * The layer names can contain HTML, which allows you to add additional styling to the items:
 
4913  * {"<img src='my-layer-icon' /> <span class='my-layer-item'>My Layer</span>": myLayer}
 
4917 var Layers = Control.extend({
 
4919         // @aka Control.Layers options
 
4921                 // @option collapsed: Boolean = true
 
4922                 // If `true`, the control will be collapsed into an icon and expanded on mouse hover or touch.
 
4924                 position: 'topright',
 
4926                 // @option autoZIndex: Boolean = true
 
4927                 // 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.
 
4930                 // @option hideSingleBase: Boolean = false
 
4931                 // If `true`, the base layers in the control will be hidden when there is only one.
 
4932                 hideSingleBase: false,
 
4934                 // @option sortLayers: Boolean = false
 
4935                 // Whether to sort the layers. When `false`, layers will keep the order
 
4936                 // in which they were added to the control.
 
4939                 // @option sortFunction: Function = *
 
4940                 // A [compare function](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Array/sort)
 
4941                 // that will be used for sorting the layers, when `sortLayers` is `true`.
 
4942                 // The function receives both the `L.Layer` instances and their names, as in
 
4943                 // `sortFunction(layerA, layerB, nameA, nameB)`.
 
4944                 // By default, it sorts layers alphabetically by their name.
 
4945                 sortFunction: function (layerA, layerB, nameA, nameB) {
 
4946                         return nameA < nameB ? -1 : (nameB < nameA ? 1 : 0);
 
4950         initialize: function (baseLayers, overlays, options) {
 
4951                 setOptions(this, options);
 
4953                 this._layerControlInputs = [];
 
4955                 this._lastZIndex = 0;
 
4956                 this._handlingClick = false;
 
4958                 for (var i in baseLayers) {
 
4959                         this._addLayer(baseLayers[i], i);
 
4962                 for (i in overlays) {
 
4963                         this._addLayer(overlays[i], i, true);
 
4967         onAdd: function (map) {
 
4972                 map.on('zoomend', this._checkDisabledLayers, this);
 
4974                 for (var i = 0; i < this._layers.length; i++) {
 
4975                         this._layers[i].layer.on('add remove', this._onLayerChange, this);
 
4978                 return this._container;
 
4981         addTo: function (map) {
 
4982                 Control.prototype.addTo.call(this, map);
 
4983                 // Trigger expand after Layers Control has been inserted into DOM so that is now has an actual height.
 
4984                 return this._expandIfNotCollapsed();
 
4987         onRemove: function () {
 
4988                 this._map.off('zoomend', this._checkDisabledLayers, this);
 
4990                 for (var i = 0; i < this._layers.length; i++) {
 
4991                         this._layers[i].layer.off('add remove', this._onLayerChange, this);
 
4995         // @method addBaseLayer(layer: Layer, name: String): this
 
4996         // Adds a base layer (radio button entry) with the given name to the control.
 
4997         addBaseLayer: function (layer, name) {
 
4998                 this._addLayer(layer, name);
 
4999                 return (this._map) ? this._update() : this;
 
5002         // @method addOverlay(layer: Layer, name: String): this
 
5003         // Adds an overlay (checkbox entry) with the given name to the control.
 
5004         addOverlay: function (layer, name) {
 
5005                 this._addLayer(layer, name, true);
 
5006                 return (this._map) ? this._update() : this;
 
5009         // @method removeLayer(layer: Layer): this
 
5010         // Remove the given layer from the control.
 
5011         removeLayer: function (layer) {
 
5012                 layer.off('add remove', this._onLayerChange, this);
 
5014                 var obj = this._getLayer(stamp(layer));
 
5016                         this._layers.splice(this._layers.indexOf(obj), 1);
 
5018                 return (this._map) ? this._update() : this;
 
5021         // @method expand(): this
 
5022         // Expand the control container if collapsed.
 
5023         expand: function () {
 
5024                 addClass(this._container, 'leaflet-control-layers-expanded');
 
5025                 this._section.style.height = null;
 
5026                 var acceptableHeight = this._map.getSize().y - (this._container.offsetTop + 50);
 
5027                 if (acceptableHeight < this._section.clientHeight) {
 
5028                         addClass(this._section, 'leaflet-control-layers-scrollbar');
 
5029                         this._section.style.height = acceptableHeight + 'px';
 
5031                         removeClass(this._section, 'leaflet-control-layers-scrollbar');
 
5033                 this._checkDisabledLayers();
 
5037         // @method collapse(): this
 
5038         // Collapse the control container if expanded.
 
5039         collapse: function () {
 
5040                 removeClass(this._container, 'leaflet-control-layers-expanded');
 
5044         _initLayout: function () {
 
5045                 var className = 'leaflet-control-layers',
 
5046                     container = this._container = create$1('div', className),
 
5047                     collapsed = this.options.collapsed;
 
5049                 // makes this work on IE touch devices by stopping it from firing a mouseout event when the touch is released
 
5050                 container.setAttribute('aria-haspopup', true);
 
5052                 disableClickPropagation(container);
 
5053                 disableScrollPropagation(container);
 
5055                 var section = this._section = create$1('section', className + '-list');
 
5058                         this._map.on('click', this.collapse, this);
 
5062                                         mouseenter: this.expand,
 
5063                                         mouseleave: this.collapse
 
5068                 var link = this._layersLink = create$1('a', className + '-toggle', container);
 
5070                 link.title = 'Layers';
 
5073                         on(link, 'click', stop);
 
5074                         on(link, 'click', this.expand, this);
 
5076                         on(link, 'focus', this.expand, this);
 
5083                 this._baseLayersList = create$1('div', className + '-base', section);
 
5084                 this._separator = create$1('div', className + '-separator', section);
 
5085                 this._overlaysList = create$1('div', className + '-overlays', section);
 
5087                 container.appendChild(section);
 
5090         _getLayer: function (id) {
 
5091                 for (var i = 0; i < this._layers.length; i++) {
 
5093                         if (this._layers[i] && stamp(this._layers[i].layer) === id) {
 
5094                                 return this._layers[i];
 
5099         _addLayer: function (layer, name, overlay) {
 
5101                         layer.on('add remove', this._onLayerChange, this);
 
5110                 if (this.options.sortLayers) {
 
5111                         this._layers.sort(bind(function (a, b) {
 
5112                                 return this.options.sortFunction(a.layer, b.layer, a.name, b.name);
 
5116                 if (this.options.autoZIndex && layer.setZIndex) {
 
5118                         layer.setZIndex(this._lastZIndex);
 
5121                 this._expandIfNotCollapsed();
 
5124         _update: function () {
 
5125                 if (!this._container) { return this; }
 
5127                 empty(this._baseLayersList);
 
5128                 empty(this._overlaysList);
 
5130                 this._layerControlInputs = [];
 
5131                 var baseLayersPresent, overlaysPresent, i, obj, baseLayersCount = 0;
 
5133                 for (i = 0; i < this._layers.length; i++) {
 
5134                         obj = this._layers[i];
 
5136                         overlaysPresent = overlaysPresent || obj.overlay;
 
5137                         baseLayersPresent = baseLayersPresent || !obj.overlay;
 
5138                         baseLayersCount += !obj.overlay ? 1 : 0;
 
5141                 // Hide base layers section if there's only one layer.
 
5142                 if (this.options.hideSingleBase) {
 
5143                         baseLayersPresent = baseLayersPresent && baseLayersCount > 1;
 
5144                         this._baseLayersList.style.display = baseLayersPresent ? '' : 'none';
 
5147                 this._separator.style.display = overlaysPresent && baseLayersPresent ? '' : 'none';
 
5152         _onLayerChange: function (e) {
 
5153                 if (!this._handlingClick) {
 
5157                 var obj = this._getLayer(stamp(e.target));
 
5160                 // @section Layer events
 
5161                 // @event baselayerchange: LayersControlEvent
 
5162                 // Fired when the base layer is changed through the [layer control](#control-layers).
 
5163                 // @event overlayadd: LayersControlEvent
 
5164                 // Fired when an overlay is selected through the [layer control](#control-layers).
 
5165                 // @event overlayremove: LayersControlEvent
 
5166                 // Fired when an overlay is deselected through the [layer control](#control-layers).
 
5167                 // @namespace Control.Layers
 
5168                 var type = obj.overlay ?
 
5169                         (e.type === 'add' ? 'overlayadd' : 'overlayremove') :
 
5170                         (e.type === 'add' ? 'baselayerchange' : null);
 
5173                         this._map.fire(type, obj);
 
5177         // IE7 bugs out if you create a radio dynamically, so you have to do it this hacky way (see http://bit.ly/PqYLBe)
 
5178         _createRadioElement: function (name, checked) {
 
5180                 var radioHtml = '<input type="radio" class="leaflet-control-layers-selector" name="' +
 
5181                                 name + '"' + (checked ? ' checked="checked"' : '') + '/>';
 
5183                 var radioFragment = document.createElement('div');
 
5184                 radioFragment.innerHTML = radioHtml;
 
5186                 return radioFragment.firstChild;
 
5189         _addItem: function (obj) {
 
5190                 var label = document.createElement('label'),
 
5191                     checked = this._map.hasLayer(obj.layer),
 
5195                         input = document.createElement('input');
 
5196                         input.type = 'checkbox';
 
5197                         input.className = 'leaflet-control-layers-selector';
 
5198                         input.defaultChecked = checked;
 
5200                         input = this._createRadioElement('leaflet-base-layers', checked);
 
5203                 this._layerControlInputs.push(input);
 
5204                 input.layerId = stamp(obj.layer);
 
5206                 on(input, 'click', this._onInputClick, this);
 
5208                 var name = document.createElement('span');
 
5209                 name.innerHTML = ' ' + obj.name;
 
5211                 // Helps from preventing layer control flicker when checkboxes are disabled
 
5212                 // https://github.com/Leaflet/Leaflet/issues/2771
 
5213                 var holder = document.createElement('div');
 
5215                 label.appendChild(holder);
 
5216                 holder.appendChild(input);
 
5217                 holder.appendChild(name);
 
5219                 var container = obj.overlay ? this._overlaysList : this._baseLayersList;
 
5220                 container.appendChild(label);
 
5222                 this._checkDisabledLayers();
 
5226         _onInputClick: function () {
 
5227                 var inputs = this._layerControlInputs,
 
5229                 var addedLayers = [],
 
5232                 this._handlingClick = true;
 
5234                 for (var i = inputs.length - 1; i >= 0; i--) {
 
5236                         layer = this._getLayer(input.layerId).layer;
 
5238                         if (input.checked) {
 
5239                                 addedLayers.push(layer);
 
5240                         } else if (!input.checked) {
 
5241                                 removedLayers.push(layer);
 
5245                 // Bugfix issue 2318: Should remove all old layers before readding new ones
 
5246                 for (i = 0; i < removedLayers.length; i++) {
 
5247                         if (this._map.hasLayer(removedLayers[i])) {
 
5248                                 this._map.removeLayer(removedLayers[i]);
 
5251                 for (i = 0; i < addedLayers.length; i++) {
 
5252                         if (!this._map.hasLayer(addedLayers[i])) {
 
5253                                 this._map.addLayer(addedLayers[i]);
 
5257                 this._handlingClick = false;
 
5259                 this._refocusOnMap();
 
5262         _checkDisabledLayers: function () {
 
5263                 var inputs = this._layerControlInputs,
 
5266                     zoom = this._map.getZoom();
 
5268                 for (var i = inputs.length - 1; i >= 0; i--) {
 
5270                         layer = this._getLayer(input.layerId).layer;
 
5271                         input.disabled = (layer.options.minZoom !== undefined && zoom < layer.options.minZoom) ||
 
5272                                          (layer.options.maxZoom !== undefined && zoom > layer.options.maxZoom);
 
5277         _expandIfNotCollapsed: function () {
 
5278                 if (this._map && !this.options.collapsed) {
 
5284         _expand: function () {
 
5285                 // Backward compatibility, remove me in 1.1.
 
5286                 return this.expand();
 
5289         _collapse: function () {
 
5290                 // Backward compatibility, remove me in 1.1.
 
5291                 return this.collapse();
 
5297 // @factory L.control.layers(baselayers?: Object, overlays?: Object, options?: Control.Layers options)
 
5298 // Creates an attribution control with the given layers. Base layers will be switched with radio buttons, while overlays will be switched with checkboxes. Note that all base layers should be passed in the base layers object, but only one should be added to the map during map instantiation.
 
5299 var layers = function (baseLayers, overlays, options) {
 
5300         return new Layers(baseLayers, overlays, options);
 
5304  * @class Control.Zoom
 
5305  * @aka L.Control.Zoom
 
5308  * 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`.
 
5311 var Zoom = Control.extend({
 
5313         // @aka Control.Zoom options
 
5315                 position: 'topleft',
 
5317                 // @option zoomInText: String = '+'
 
5318                 // The text set on the 'zoom in' button.
 
5321                 // @option zoomInTitle: String = 'Zoom in'
 
5322                 // The title set on the 'zoom in' button.
 
5323                 zoomInTitle: 'Zoom in',
 
5325                 // @option zoomOutText: String = '−'
 
5326                 // The text set on the 'zoom out' button.
 
5327                 zoomOutText: '−',
 
5329                 // @option zoomOutTitle: String = 'Zoom out'
 
5330                 // The title set on the 'zoom out' button.
 
5331                 zoomOutTitle: 'Zoom out'
 
5334         onAdd: function (map) {
 
5335                 var zoomName = 'leaflet-control-zoom',
 
5336                     container = create$1('div', zoomName + ' leaflet-bar'),
 
5337                     options = this.options;
 
5339                 this._zoomInButton  = this._createButton(options.zoomInText, options.zoomInTitle,
 
5340                         zoomName + '-in',  container, this._zoomIn);
 
5341                 this._zoomOutButton = this._createButton(options.zoomOutText, options.zoomOutTitle,
 
5342                         zoomName + '-out', container, this._zoomOut);
 
5344                 this._updateDisabled();
 
5345                 map.on('zoomend zoomlevelschange', this._updateDisabled, this);
 
5350         onRemove: function (map) {
 
5351                 map.off('zoomend zoomlevelschange', this._updateDisabled, this);
 
5354         disable: function () {
 
5355                 this._disabled = true;
 
5356                 this._updateDisabled();
 
5360         enable: function () {
 
5361                 this._disabled = false;
 
5362                 this._updateDisabled();
 
5366         _zoomIn: function (e) {
 
5367                 if (!this._disabled && this._map._zoom < this._map.getMaxZoom()) {
 
5368                         this._map.zoomIn(this._map.options.zoomDelta * (e.shiftKey ? 3 : 1));
 
5372         _zoomOut: function (e) {
 
5373                 if (!this._disabled && this._map._zoom > this._map.getMinZoom()) {
 
5374                         this._map.zoomOut(this._map.options.zoomDelta * (e.shiftKey ? 3 : 1));
 
5378         _createButton: function (html, title, className, container, fn) {
 
5379                 var link = create$1('a', className, container);
 
5380                 link.innerHTML = html;
 
5385                  * Will force screen readers like VoiceOver to read this as "Zoom in - button"
 
5387                 link.setAttribute('role', 'button');
 
5388                 link.setAttribute('aria-label', title);
 
5390                 disableClickPropagation(link);
 
5391                 on(link, 'click', stop);
 
5392                 on(link, 'click', fn, this);
 
5393                 on(link, 'click', this._refocusOnMap, this);
 
5398         _updateDisabled: function () {
 
5399                 var map = this._map,
 
5400                     className = 'leaflet-disabled';
 
5402                 removeClass(this._zoomInButton, className);
 
5403                 removeClass(this._zoomOutButton, className);
 
5405                 if (this._disabled || map._zoom === map.getMinZoom()) {
 
5406                         addClass(this._zoomOutButton, className);
 
5408                 if (this._disabled || map._zoom === map.getMaxZoom()) {
 
5409                         addClass(this._zoomInButton, className);
 
5415 // @section Control options
 
5416 // @option zoomControl: Boolean = true
 
5417 // Whether a [zoom control](#control-zoom) is added to the map by default.
 
5422 Map.addInitHook(function () {
 
5423         if (this.options.zoomControl) {
 
5424                 // @section Controls
 
5425                 // @property zoomControl: Control.Zoom
 
5426                 // The default zoom control (only available if the
 
5427                 // [`zoomControl` option](#map-zoomcontrol) was `true` when creating the map).
 
5428                 this.zoomControl = new Zoom();
 
5429                 this.addControl(this.zoomControl);
 
5433 // @namespace Control.Zoom
 
5434 // @factory L.control.zoom(options: Control.Zoom options)
 
5435 // Creates a zoom control
 
5436 var zoom = function (options) {
 
5437         return new Zoom(options);
 
5441  * @class Control.Scale
 
5442  * @aka L.Control.Scale
 
5445  * 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`.
 
5450  * L.control.scale().addTo(map);
 
5454 var Scale = Control.extend({
 
5456         // @aka Control.Scale options
 
5458                 position: 'bottomleft',
 
5460                 // @option maxWidth: Number = 100
 
5461                 // Maximum width of the control in pixels. The width is set dynamically to show round values (e.g. 100, 200, 500).
 
5464                 // @option metric: Boolean = True
 
5465                 // Whether to show the metric scale line (m/km).
 
5468                 // @option imperial: Boolean = True
 
5469                 // Whether to show the imperial scale line (mi/ft).
 
5472                 // @option updateWhenIdle: Boolean = false
 
5473                 // If `true`, the control is updated on [`moveend`](#map-moveend), otherwise it's always up-to-date (updated on [`move`](#map-move)).
 
5476         onAdd: function (map) {
 
5477                 var className = 'leaflet-control-scale',
 
5478                     container = create$1('div', className),
 
5479                     options = this.options;
 
5481                 this._addScales(options, className + '-line', container);
 
5483                 map.on(options.updateWhenIdle ? 'moveend' : 'move', this._update, this);
 
5484                 map.whenReady(this._update, this);
 
5489         onRemove: function (map) {
 
5490                 map.off(this.options.updateWhenIdle ? 'moveend' : 'move', this._update, this);
 
5493         _addScales: function (options, className, container) {
 
5494                 if (options.metric) {
 
5495                         this._mScale = create$1('div', className, container);
 
5497                 if (options.imperial) {
 
5498                         this._iScale = create$1('div', className, container);
 
5502         _update: function () {
 
5503                 var map = this._map,
 
5504                     y = map.getSize().y / 2;
 
5506                 var maxMeters = map.distance(
 
5507                         map.containerPointToLatLng([0, y]),
 
5508                         map.containerPointToLatLng([this.options.maxWidth, y]));
 
5510                 this._updateScales(maxMeters);
 
5513         _updateScales: function (maxMeters) {
 
5514                 if (this.options.metric && maxMeters) {
 
5515                         this._updateMetric(maxMeters);
 
5517                 if (this.options.imperial && maxMeters) {
 
5518                         this._updateImperial(maxMeters);
 
5522         _updateMetric: function (maxMeters) {
 
5523                 var meters = this._getRoundNum(maxMeters),
 
5524                     label = meters < 1000 ? meters + ' m' : (meters / 1000) + ' km';
 
5526                 this._updateScale(this._mScale, label, meters / maxMeters);
 
5529         _updateImperial: function (maxMeters) {
 
5530                 var maxFeet = maxMeters * 3.2808399,
 
5531                     maxMiles, miles, feet;
 
5533                 if (maxFeet > 5280) {
 
5534                         maxMiles = maxFeet / 5280;
 
5535                         miles = this._getRoundNum(maxMiles);
 
5536                         this._updateScale(this._iScale, miles + ' mi', miles / maxMiles);
 
5539                         feet = this._getRoundNum(maxFeet);
 
5540                         this._updateScale(this._iScale, feet + ' ft', feet / maxFeet);
 
5544         _updateScale: function (scale, text, ratio) {
 
5545                 scale.style.width = Math.round(this.options.maxWidth * ratio) + 'px';
 
5546                 scale.innerHTML = text;
 
5549         _getRoundNum: function (num) {
 
5550                 var pow10 = Math.pow(10, (Math.floor(num) + '').length - 1),
 
5563 // @factory L.control.scale(options?: Control.Scale options)
 
5564 // Creates an scale control with the given options.
 
5565 var scale = function (options) {
 
5566         return new Scale(options);
 
5570  * @class Control.Attribution
 
5571  * @aka L.Control.Attribution
 
5574  * 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.
 
5577 var Attribution = Control.extend({
 
5579         // @aka Control.Attribution options
 
5581                 position: 'bottomright',
 
5583                 // @option prefix: String = 'Leaflet'
 
5584                 // The HTML text shown before the attributions. Pass `false` to disable.
 
5585                 prefix: '<a href="http://leafletjs.com" title="A JS library for interactive maps">Leaflet</a>'
 
5588         initialize: function (options) {
 
5589                 setOptions(this, options);
 
5591                 this._attributions = {};
 
5594         onAdd: function (map) {
 
5595                 map.attributionControl = this;
 
5596                 this._container = create$1('div', 'leaflet-control-attribution');
 
5597                 disableClickPropagation(this._container);
 
5599                 // TODO ugly, refactor
 
5600                 for (var i in map._layers) {
 
5601                         if (map._layers[i].getAttribution) {
 
5602                                 this.addAttribution(map._layers[i].getAttribution());
 
5608                 return this._container;
 
5611         // @method setPrefix(prefix: String): this
 
5612         // Sets the text before the attributions.
 
5613         setPrefix: function (prefix) {
 
5614                 this.options.prefix = prefix;
 
5619         // @method addAttribution(text: String): this
 
5620         // Adds an attribution text (e.g. `'Vector data © Mapbox'`).
 
5621         addAttribution: function (text) {
 
5622                 if (!text) { return this; }
 
5624                 if (!this._attributions[text]) {
 
5625                         this._attributions[text] = 0;
 
5627                 this._attributions[text]++;
 
5634         // @method removeAttribution(text: String): this
 
5635         // Removes an attribution text.
 
5636         removeAttribution: function (text) {
 
5637                 if (!text) { return this; }
 
5639                 if (this._attributions[text]) {
 
5640                         this._attributions[text]--;
 
5647         _update: function () {
 
5648                 if (!this._map) { return; }
 
5652                 for (var i in this._attributions) {
 
5653                         if (this._attributions[i]) {
 
5658                 var prefixAndAttribs = [];
 
5660                 if (this.options.prefix) {
 
5661                         prefixAndAttribs.push(this.options.prefix);
 
5663                 if (attribs.length) {
 
5664                         prefixAndAttribs.push(attribs.join(', '));
 
5667                 this._container.innerHTML = prefixAndAttribs.join(' | ');
 
5672 // @section Control options
 
5673 // @option attributionControl: Boolean = true
 
5674 // Whether a [attribution control](#control-attribution) is added to the map by default.
 
5676         attributionControl: true
 
5679 Map.addInitHook(function () {
 
5680         if (this.options.attributionControl) {
 
5681                 new Attribution().addTo(this);
 
5685 // @namespace Control.Attribution
 
5686 // @factory L.control.attribution(options: Control.Attribution options)
 
5687 // Creates an attribution control.
 
5688 var attribution = function (options) {
 
5689         return new Attribution(options);
 
5692 Control.Layers = Layers;
 
5693 Control.Zoom = Zoom;
 
5694 Control.Scale = Scale;
 
5695 Control.Attribution = Attribution;
 
5697 control.layers = layers;
 
5698 control.zoom = zoom;
 
5699 control.scale = scale;
 
5700 control.attribution = attribution;
 
5703         L.Handler is a base class for handler classes that are used internally to inject
 
5704         interaction features like dragging to classes like Map and Marker.
 
5709 // Abstract class for map interaction handlers
 
5711 var Handler = Class.extend({
 
5712         initialize: function (map) {
 
5716         // @method enable(): this
 
5717         // Enables the handler
 
5718         enable: function () {
 
5719                 if (this._enabled) { return this; }
 
5721                 this._enabled = true;
 
5726         // @method disable(): this
 
5727         // Disables the handler
 
5728         disable: function () {
 
5729                 if (!this._enabled) { return this; }
 
5731                 this._enabled = false;
 
5736         // @method enabled(): Boolean
 
5737         // Returns `true` if the handler is enabled
 
5738         enabled: function () {
 
5739                 return !!this._enabled;
 
5742         // @section Extension methods
 
5743         // Classes inheriting from `Handler` must implement the two following methods:
 
5744         // @method addHooks()
 
5745         // Called when the handler is enabled, should add event hooks.
 
5746         // @method removeHooks()
 
5747         // Called when the handler is disabled, should remove the event hooks added previously.
 
5750 // @section There is static function which can be called without instantiating L.Handler:
 
5751 // @function addTo(map: Map, name: String): this
 
5752 // Adds a new Handler to the given map with the given name.
 
5753 Handler.addTo = function (map, name) {
 
5754         map.addHandler(name, this);
 
5758 var Mixin = {Events: Events};
 
5765  * A class for making DOM elements draggable (including touch support).
 
5766  * Used internally for map and marker dragging. Only works for elements
 
5767  * that were positioned with [`L.DomUtil.setPosition`](#domutil-setposition).
 
5771  * var draggable = new L.Draggable(elementToDrag);
 
5772  * draggable.enable();
 
5776 var START = touch ? 'touchstart mousedown' : 'mousedown';
 
5778         mousedown: 'mouseup',
 
5779         touchstart: 'touchend',
 
5780         pointerdown: 'touchend',
 
5781         MSPointerDown: 'touchend'
 
5784         mousedown: 'mousemove',
 
5785         touchstart: 'touchmove',
 
5786         pointerdown: 'touchmove',
 
5787         MSPointerDown: 'touchmove'
 
5791 var Draggable = Evented.extend({
 
5795                 // @aka Draggable options
 
5796                 // @option clickTolerance: Number = 3
 
5797                 // The max number of pixels a user can shift the mouse pointer during a click
 
5798                 // for it to be considered a valid click (as opposed to a mouse drag).
 
5802         // @constructor L.Draggable(el: HTMLElement, dragHandle?: HTMLElement, preventOutline?: Boolean, options?: Draggable options)
 
5803         // Creates a `Draggable` object for moving `el` when you start dragging the `dragHandle` element (equals `el` itself by default).
 
5804         initialize: function (element, dragStartTarget, preventOutline$$1, options) {
 
5805                 setOptions(this, options);
 
5807                 this._element = element;
 
5808                 this._dragStartTarget = dragStartTarget || element;
 
5809                 this._preventOutline = preventOutline$$1;
 
5813         // Enables the dragging ability
 
5814         enable: function () {
 
5815                 if (this._enabled) { return; }
 
5817                 on(this._dragStartTarget, START, this._onDown, this);
 
5819                 this._enabled = true;
 
5822         // @method disable()
 
5823         // Disables the dragging ability
 
5824         disable: function () {
 
5825                 if (!this._enabled) { return; }
 
5827                 // If we're currently dragging this draggable,
 
5828                 // disabling it counts as first ending the drag.
 
5829                 if (Draggable._dragging === this) {
 
5833                 off(this._dragStartTarget, START, this._onDown, this);
 
5835                 this._enabled = false;
 
5836                 this._moved = false;
 
5839         _onDown: function (e) {
 
5840                 // Ignore simulated events, since we handle both touch and
 
5841                 // mouse explicitly; otherwise we risk getting duplicates of
 
5842                 // touch events, see #4315.
 
5843                 // Also ignore the event if disabled; this happens in IE11
 
5844                 // under some circumstances, see #3666.
 
5845                 if (e._simulated || !this._enabled) { return; }
 
5847                 this._moved = false;
 
5849                 if (hasClass(this._element, 'leaflet-zoom-anim')) { return; }
 
5851                 if (Draggable._dragging || e.shiftKey || ((e.which !== 1) && (e.button !== 1) && !e.touches)) { return; }
 
5852                 Draggable._dragging = this;  // Prevent dragging multiple objects at once.
 
5854                 if (this._preventOutline) {
 
5855                         preventOutline(this._element);
 
5859                 disableTextSelection();
 
5861                 if (this._moving) { return; }
 
5863                 // @event down: Event
 
5864                 // Fired when a drag is about to start.
 
5867                 var first = e.touches ? e.touches[0] : e,
 
5868                     sizedParent = getSizedParentNode(this._element);
 
5870                 this._startPoint = new Point(first.clientX, first.clientY);
 
5872                 // Cache the scale, so that we can continuously compensate for it during drag (_onMove).
 
5873                 this._parentScale = getScale(sizedParent);
 
5875                 on(document, MOVE[e.type], this._onMove, this);
 
5876                 on(document, END[e.type], this._onUp, this);
 
5879         _onMove: function (e) {
 
5880                 // Ignore simulated events, since we handle both touch and
 
5881                 // mouse explicitly; otherwise we risk getting duplicates of
 
5882                 // touch events, see #4315.
 
5883                 // Also ignore the event if disabled; this happens in IE11
 
5884                 // under some circumstances, see #3666.
 
5885                 if (e._simulated || !this._enabled) { return; }
 
5887                 if (e.touches && e.touches.length > 1) {
 
5892                 var first = (e.touches && e.touches.length === 1 ? e.touches[0] : e),
 
5893                     offset = new Point(first.clientX, first.clientY)._subtract(this._startPoint);
 
5895                 if (!offset.x && !offset.y) { return; }
 
5896                 if (Math.abs(offset.x) + Math.abs(offset.y) < this.options.clickTolerance) { return; }
 
5898                 // We assume that the parent container's position, border and scale do not change for the duration of the drag.
 
5899                 // Therefore there is no need to account for the position and border (they are eliminated by the subtraction)
 
5900                 // and we can use the cached value for the scale.
 
5901                 offset.x /= this._parentScale.x;
 
5902                 offset.y /= this._parentScale.y;
 
5907                         // @event dragstart: Event
 
5908                         // Fired when a drag starts
 
5909                         this.fire('dragstart');
 
5912                         this._startPos = getPosition(this._element).subtract(offset);
 
5914                         addClass(document.body, 'leaflet-dragging');
 
5916                         this._lastTarget = e.target || e.srcElement;
 
5917                         // IE and Edge do not give the <use> element, so fetch it
 
5919                         if ((window.SVGElementInstance) && (this._lastTarget instanceof SVGElementInstance)) {
 
5920                                 this._lastTarget = this._lastTarget.correspondingUseElement;
 
5922                         addClass(this._lastTarget, 'leaflet-drag-target');
 
5925                 this._newPos = this._startPos.add(offset);
 
5926                 this._moving = true;
 
5928                 cancelAnimFrame(this._animRequest);
 
5929                 this._lastEvent = e;
 
5930                 this._animRequest = requestAnimFrame(this._updatePosition, this, true);
 
5933         _updatePosition: function () {
 
5934                 var e = {originalEvent: this._lastEvent};
 
5936                 // @event predrag: Event
 
5937                 // Fired continuously during dragging *before* each corresponding
 
5938                 // update of the element's position.
 
5939                 this.fire('predrag', e);
 
5940                 setPosition(this._element, this._newPos);
 
5942                 // @event drag: Event
 
5943                 // Fired continuously during dragging.
 
5944                 this.fire('drag', e);
 
5947         _onUp: function (e) {
 
5948                 // Ignore simulated events, since we handle both touch and
 
5949                 // mouse explicitly; otherwise we risk getting duplicates of
 
5950                 // touch events, see #4315.
 
5951                 // Also ignore the event if disabled; this happens in IE11
 
5952                 // under some circumstances, see #3666.
 
5953                 if (e._simulated || !this._enabled) { return; }
 
5957         finishDrag: function () {
 
5958                 removeClass(document.body, 'leaflet-dragging');
 
5960                 if (this._lastTarget) {
 
5961                         removeClass(this._lastTarget, 'leaflet-drag-target');
 
5962                         this._lastTarget = null;
 
5965                 for (var i in MOVE) {
 
5966                         off(document, MOVE[i], this._onMove, this);
 
5967                         off(document, END[i], this._onUp, this);
 
5971                 enableTextSelection();
 
5973                 if (this._moved && this._moving) {
 
5974                         // ensure drag is not fired after dragend
 
5975                         cancelAnimFrame(this._animRequest);
 
5977                         // @event dragend: DragEndEvent
 
5978                         // Fired when the drag ends.
 
5979                         this.fire('dragend', {
 
5980                                 distance: this._newPos.distanceTo(this._startPos)
 
5984                 this._moving = false;
 
5985                 Draggable._dragging = false;
 
5991  * @namespace LineUtil
 
5993  * Various utility functions for polyline points processing, used by Leaflet internally to make polylines lightning-fast.
 
5996 // Simplify polyline with vertex reduction and Douglas-Peucker simplification.
 
5997 // Improves rendering performance dramatically by lessening the number of points to draw.
 
5999 // @function simplify(points: Point[], tolerance: Number): Point[]
 
6000 // Dramatically reduces the number of points in a polyline while retaining
 
6001 // its shape and returns a new array of simplified points, using the
 
6002 // [Douglas-Peucker algorithm](http://en.wikipedia.org/wiki/Douglas-Peucker_algorithm).
 
6003 // Used for a huge performance boost when processing/displaying Leaflet polylines for
 
6004 // each zoom level and also reducing visual noise. tolerance affects the amount of
 
6005 // simplification (lesser value means higher quality but slower and with more points).
 
6006 // Also released as a separated micro-library [Simplify.js](http://mourner.github.com/simplify-js/).
 
6007 function simplify(points, tolerance) {
 
6008         if (!tolerance || !points.length) {
 
6009                 return points.slice();
 
6012         var sqTolerance = tolerance * tolerance;
 
6014             // stage 1: vertex reduction
 
6015             points = _reducePoints(points, sqTolerance);
 
6017             // stage 2: Douglas-Peucker simplification
 
6018             points = _simplifyDP(points, sqTolerance);
 
6023 // @function pointToSegmentDistance(p: Point, p1: Point, p2: Point): Number
 
6024 // Returns the distance between point `p` and segment `p1` to `p2`.
 
6025 function pointToSegmentDistance(p, p1, p2) {
 
6026         return Math.sqrt(_sqClosestPointOnSegment(p, p1, p2, true));
 
6029 // @function closestPointOnSegment(p: Point, p1: Point, p2: Point): Number
 
6030 // Returns the closest point from a point `p` on a segment `p1` to `p2`.
 
6031 function closestPointOnSegment(p, p1, p2) {
 
6032         return _sqClosestPointOnSegment(p, p1, p2);
 
6035 // Douglas-Peucker simplification, see http://en.wikipedia.org/wiki/Douglas-Peucker_algorithm
 
6036 function _simplifyDP(points, sqTolerance) {
 
6038         var len = points.length,
 
6039             ArrayConstructor = typeof Uint8Array !== undefined + '' ? Uint8Array : Array,
 
6040             markers = new ArrayConstructor(len);
 
6042             markers[0] = markers[len - 1] = 1;
 
6044         _simplifyDPStep(points, markers, sqTolerance, 0, len - 1);
 
6049         for (i = 0; i < len; i++) {
 
6051                         newPoints.push(points[i]);
 
6058 function _simplifyDPStep(points, markers, sqTolerance, first, last) {
 
6063         for (i = first + 1; i <= last - 1; i++) {
 
6064                 sqDist = _sqClosestPointOnSegment(points[i], points[first], points[last], true);
 
6066                 if (sqDist > maxSqDist) {
 
6072         if (maxSqDist > sqTolerance) {
 
6075                 _simplifyDPStep(points, markers, sqTolerance, first, index);
 
6076                 _simplifyDPStep(points, markers, sqTolerance, index, last);
 
6080 // reduce points that are too close to each other to a single point
 
6081 function _reducePoints(points, sqTolerance) {
 
6082         var reducedPoints = [points[0]];
 
6084         for (var i = 1, prev = 0, len = points.length; i < len; i++) {
 
6085                 if (_sqDist(points[i], points[prev]) > sqTolerance) {
 
6086                         reducedPoints.push(points[i]);
 
6090         if (prev < len - 1) {
 
6091                 reducedPoints.push(points[len - 1]);
 
6093         return reducedPoints;
 
6098 // @function clipSegment(a: Point, b: Point, bounds: Bounds, useLastCode?: Boolean, round?: Boolean): Point[]|Boolean
 
6099 // Clips the segment a to b by rectangular bounds with the
 
6100 // [Cohen-Sutherland algorithm](https://en.wikipedia.org/wiki/Cohen%E2%80%93Sutherland_algorithm)
 
6101 // (modifying the segment points directly!). Used by Leaflet to only show polyline
 
6102 // points that are on the screen or near, increasing performance.
 
6103 function clipSegment(a, b, bounds, useLastCode, round) {
 
6104         var codeA = useLastCode ? _lastCode : _getBitCode(a, bounds),
 
6105             codeB = _getBitCode(b, bounds),
 
6107             codeOut, p, newCode;
 
6109             // save 2nd code to avoid calculating it on the next segment
 
6113                 // if a,b is inside the clip window (trivial accept)
 
6114                 if (!(codeA | codeB)) {
 
6118                 // if a,b is outside the clip window (trivial reject)
 
6119                 if (codeA & codeB) {
 
6124                 codeOut = codeA || codeB;
 
6125                 p = _getEdgeIntersection(a, b, codeOut, bounds, round);
 
6126                 newCode = _getBitCode(p, bounds);
 
6128                 if (codeOut === codeA) {
 
6138 function _getEdgeIntersection(a, b, code, bounds, round) {
 
6145         if (code & 8) { // top
 
6146                 x = a.x + dx * (max.y - a.y) / dy;
 
6149         } else if (code & 4) { // bottom
 
6150                 x = a.x + dx * (min.y - a.y) / dy;
 
6153         } else if (code & 2) { // right
 
6155                 y = a.y + dy * (max.x - a.x) / dx;
 
6157         } else if (code & 1) { // left
 
6159                 y = a.y + dy * (min.x - a.x) / dx;
 
6162         return new Point(x, y, round);
 
6165 function _getBitCode(p, bounds) {
 
6168         if (p.x < bounds.min.x) { // left
 
6170         } else if (p.x > bounds.max.x) { // right
 
6174         if (p.y < bounds.min.y) { // bottom
 
6176         } else if (p.y > bounds.max.y) { // top
 
6183 // square distance (to avoid unnecessary Math.sqrt calls)
 
6184 function _sqDist(p1, p2) {
 
6185         var dx = p2.x - p1.x,
 
6187         return dx * dx + dy * dy;
 
6190 // return closest point on segment or distance to that point
 
6191 function _sqClosestPointOnSegment(p, p1, p2, sqDist) {
 
6196             dot = dx * dx + dy * dy,
 
6200                 t = ((p.x - x) * dx + (p.y - y) * dy) / dot;
 
6214         return sqDist ? dx * dx + dy * dy : new Point(x, y);
 
6218 // @function isFlat(latlngs: LatLng[]): Boolean
 
6219 // Returns true if `latlngs` is a flat array, false is nested.
 
6220 function isFlat(latlngs) {
 
6221         return !isArray(latlngs[0]) || (typeof latlngs[0][0] !== 'object' && typeof latlngs[0][0] !== 'undefined');
 
6224 function _flat(latlngs) {
 
6225         console.warn('Deprecated use of _flat, please use L.LineUtil.isFlat instead.');
 
6226         return isFlat(latlngs);
 
6230 var LineUtil = (Object.freeze || Object)({
 
6232         pointToSegmentDistance: pointToSegmentDistance,
 
6233         closestPointOnSegment: closestPointOnSegment,
 
6234         clipSegment: clipSegment,
 
6235         _getEdgeIntersection: _getEdgeIntersection,
 
6236         _getBitCode: _getBitCode,
 
6237         _sqClosestPointOnSegment: _sqClosestPointOnSegment,
 
6243  * @namespace PolyUtil
 
6244  * Various utility functions for polygon geometries.
 
6247 /* @function clipPolygon(points: Point[], bounds: Bounds, round?: Boolean): Point[]
 
6248  * 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)).
 
6249  * Used by Leaflet to only show polygon points that are on the screen or near, increasing
 
6250  * performance. Note that polygon points needs different algorithm for clipping
 
6251  * than polyline, so there's a separate method for it.
 
6253 function clipPolygon(points, bounds, round) {
 
6255             edges = [1, 4, 2, 8],
 
6260         for (i = 0, len = points.length; i < len; i++) {
 
6261                 points[i]._code = _getBitCode(points[i], bounds);
 
6264         // for each edge (left, bottom, right, top)
 
6265         for (k = 0; k < 4; k++) {
 
6269                 for (i = 0, len = points.length, j = len - 1; i < len; j = i++) {
 
6273                         // if a is inside the clip window
 
6274                         if (!(a._code & edge)) {
 
6275                                 // if b is outside the clip window (a->b goes out of screen)
 
6276                                 if (b._code & edge) {
 
6277                                         p = _getEdgeIntersection(b, a, edge, bounds, round);
 
6278                                         p._code = _getBitCode(p, bounds);
 
6279                                         clippedPoints.push(p);
 
6281                                 clippedPoints.push(a);
 
6283                         // else if b is inside the clip window (a->b enters the screen)
 
6284                         } else if (!(b._code & edge)) {
 
6285                                 p = _getEdgeIntersection(b, a, edge, bounds, round);
 
6286                                 p._code = _getBitCode(p, bounds);
 
6287                                 clippedPoints.push(p);
 
6290                 points = clippedPoints;
 
6297 var PolyUtil = (Object.freeze || Object)({
 
6298         clipPolygon: clipPolygon
 
6302  * @namespace Projection
 
6304  * Leaflet comes with a set of already defined Projections out of the box:
 
6306  * @projection L.Projection.LonLat
 
6308  * Equirectangular, or Plate Carree projection — the most simple projection,
 
6309  * mostly used by GIS enthusiasts. Directly maps `x` as longitude, and `y` as
 
6310  * latitude. Also suitable for flat worlds, e.g. game maps. Used by the
 
6311  * `EPSG:4326` and `Simple` CRS.
 
6315         project: function (latlng) {
 
6316                 return new Point(latlng.lng, latlng.lat);
 
6319         unproject: function (point) {
 
6320                 return new LatLng(point.y, point.x);
 
6323         bounds: new Bounds([-180, -90], [180, 90])
 
6327  * @namespace Projection
 
6328  * @projection L.Projection.Mercator
 
6330  * Elliptical Mercator projection — more complex than Spherical Mercator. Takes into account that Earth is a geoid, not a perfect sphere. Used by the EPSG:3395 CRS.
 
6335         R_MINOR: 6356752.314245179,
 
6337         bounds: new Bounds([-20037508.34279, -15496570.73972], [20037508.34279, 18764656.23138]),
 
6339         project: function (latlng) {
 
6340                 var d = Math.PI / 180,
 
6343                     tmp = this.R_MINOR / r,
 
6344                     e = Math.sqrt(1 - tmp * tmp),
 
6345                     con = e * Math.sin(y);
 
6347                 var ts = Math.tan(Math.PI / 4 - y / 2) / Math.pow((1 - con) / (1 + con), e / 2);
 
6348                 y = -r * Math.log(Math.max(ts, 1E-10));
 
6350                 return new Point(latlng.lng * d * r, y);
 
6353         unproject: function (point) {
 
6354                 var d = 180 / Math.PI,
 
6356                     tmp = this.R_MINOR / r,
 
6357                     e = Math.sqrt(1 - tmp * tmp),
 
6358                     ts = Math.exp(-point.y / r),
 
6359                     phi = Math.PI / 2 - 2 * Math.atan(ts);
 
6361                 for (var i = 0, dphi = 0.1, con; i < 15 && Math.abs(dphi) > 1e-7; i++) {
 
6362                         con = e * Math.sin(phi);
 
6363                         con = Math.pow((1 - con) / (1 + con), e / 2);
 
6364                         dphi = Math.PI / 2 - 2 * Math.atan(ts * con) - phi;
 
6368                 return new LatLng(phi * d, point.x * d / r);
 
6375  * An object with methods for projecting geographical coordinates of the world onto
 
6376  * a flat surface (and back). See [Map projection](http://en.wikipedia.org/wiki/Map_projection).
 
6378  * @property bounds: Bounds
 
6379  * The bounds (specified in CRS units) where the projection is valid
 
6381  * @method project(latlng: LatLng): Point
 
6382  * Projects geographical coordinates into a 2D point.
 
6383  * Only accepts actual `L.LatLng` instances, not arrays.
 
6385  * @method unproject(point: Point): LatLng
 
6386  * The inverse of `project`. Projects a 2D point into a geographical location.
 
6387  * Only accepts actual `L.Point` instances, not arrays.
 
6389  * Note that the projection instances do not inherit from Leafet's `Class` object,
 
6390  * and can't be instantiated. Also, new classes can't inherit from them,
 
6391  * and methods can't be added to them with the `include` function.
 
6398 var index = (Object.freeze || Object)({
 
6401         SphericalMercator: SphericalMercator
 
6406  * @crs L.CRS.EPSG3395
 
6408  * Rarely used by some commercial tile providers. Uses Elliptical Mercator projection.
 
6410 var EPSG3395 = extend({}, Earth, {
 
6412         projection: Mercator,
 
6414         transformation: (function () {
 
6415                 var scale = 0.5 / (Math.PI * Mercator.R);
 
6416                 return toTransformation(scale, 0.5, -scale, 0.5);
 
6422  * @crs L.CRS.EPSG4326
 
6424  * A common CRS among GIS enthusiasts. Uses simple Equirectangular projection.
 
6426  * Leaflet 1.0.x complies with the [TMS coordinate scheme for EPSG:4326](https://wiki.osgeo.org/wiki/Tile_Map_Service_Specification#global-geodetic),
 
6427  * which is a breaking change from 0.7.x behaviour.  If you are using a `TileLayer`
 
6428  * with this CRS, ensure that there are two 256x256 pixel tiles covering the
 
6429  * whole earth at zoom level zero, and that the tile coordinate origin is (-180,+90),
 
6430  * or (-180,-90) for `TileLayer`s with [the `tms` option](#tilelayer-tms) set.
 
6433 var EPSG4326 = extend({}, Earth, {
 
6436         transformation: toTransformation(1 / 180, 1, -1 / 180, 0.5)
 
6443  * A simple CRS that maps longitude and latitude into `x` and `y` directly.
 
6444  * May be used for maps of flat surfaces (e.g. game maps). Note that the `y`
 
6445  * axis should still be inverted (going from bottom to top). `distance()` returns
 
6446  * simple euclidean distance.
 
6449 var Simple = extend({}, CRS, {
 
6451         transformation: toTransformation(1, 0, -1, 0),
 
6453         scale: function (zoom) {
 
6454                 return Math.pow(2, zoom);
 
6457         zoom: function (scale) {
 
6458                 return Math.log(scale) / Math.LN2;
 
6461         distance: function (latlng1, latlng2) {
 
6462                 var dx = latlng2.lng - latlng1.lng,
 
6463                     dy = latlng2.lat - latlng1.lat;
 
6465                 return Math.sqrt(dx * dx + dy * dy);
 
6472 CRS.EPSG3395 = EPSG3395;
 
6473 CRS.EPSG3857 = EPSG3857;
 
6474 CRS.EPSG900913 = EPSG900913;
 
6475 CRS.EPSG4326 = EPSG4326;
 
6476 CRS.Simple = Simple;
 
6484  * A set of methods from the Layer base class that all Leaflet layers use.
 
6485  * Inherits all methods, options and events from `L.Evented`.
 
6490  * var layer = L.Marker(latlng).addTo(map);
 
6496  * Fired after the layer is added to a map
 
6498  * @event remove: Event
 
6499  * Fired after the layer is removed from a map
 
6503 var Layer = Evented.extend({
 
6505         // Classes extending `L.Layer` will inherit the following options:
 
6507                 // @option pane: String = 'overlayPane'
 
6508                 // 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.
 
6509                 pane: 'overlayPane',
 
6511                 // @option attribution: String = null
 
6512                 // 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.
 
6515                 bubblingMouseEvents: true
 
6519          * Classes extending `L.Layer` will inherit the following methods:
 
6521          * @method addTo(map: Map|LayerGroup): this
 
6522          * Adds the layer to the given map or layer group.
 
6524         addTo: function (map) {
 
6529         // @method remove: this
 
6530         // Removes the layer from the map it is currently active on.
 
6531         remove: function () {
 
6532                 return this.removeFrom(this._map || this._mapToAdd);
 
6535         // @method removeFrom(map: Map): this
 
6536         // Removes the layer from the given map
 
6537         removeFrom: function (obj) {
 
6539                         obj.removeLayer(this);
 
6544         // @method getPane(name? : String): HTMLElement
 
6545         // Returns the `HTMLElement` representing the named pane on the map. If `name` is omitted, returns the pane for this layer.
 
6546         getPane: function (name) {
 
6547                 return this._map.getPane(name ? (this.options[name] || name) : this.options.pane);
 
6550         addInteractiveTarget: function (targetEl) {
 
6551                 this._map._targets[stamp(targetEl)] = this;
 
6555         removeInteractiveTarget: function (targetEl) {
 
6556                 delete this._map._targets[stamp(targetEl)];
 
6560         // @method getAttribution: String
 
6561         // Used by the `attribution control`, returns the [attribution option](#gridlayer-attribution).
 
6562         getAttribution: function () {
 
6563                 return this.options.attribution;
 
6566         _layerAdd: function (e) {
 
6569                 // check in case layer gets added and then removed before the map is ready
 
6570                 if (!map.hasLayer(this)) { return; }
 
6573                 this._zoomAnimated = map._zoomAnimated;
 
6575                 if (this.getEvents) {
 
6576                         var events = this.getEvents();
 
6577                         map.on(events, this);
 
6578                         this.once('remove', function () {
 
6579                                 map.off(events, this);
 
6585                 if (this.getAttribution && map.attributionControl) {
 
6586                         map.attributionControl.addAttribution(this.getAttribution());
 
6590                 map.fire('layeradd', {layer: this});
 
6594 /* @section Extension methods
 
6597  * Every layer should extend from `L.Layer` and (re-)implement the following methods.
 
6599  * @method onAdd(map: Map): this
 
6600  * 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).
 
6602  * @method onRemove(map: Map): this
 
6603  * 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).
 
6605  * @method getEvents(): Object
 
6606  * 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.
 
6608  * @method getAttribution(): String
 
6609  * This optional method should return a string containing HTML to be shown on the `Attribution control` whenever the layer is visible.
 
6611  * @method beforeAdd(map: Map): this
 
6612  * 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.
 
6617  * @section Layer events
 
6619  * @event layeradd: LayerEvent
 
6620  * Fired when a new layer is added to the map.
 
6622  * @event layerremove: LayerEvent
 
6623  * Fired when some layer is removed from the map
 
6625  * @section Methods for Layers and Controls
 
6628         // @method addLayer(layer: Layer): this
 
6629         // Adds the given layer to the map
 
6630         addLayer: function (layer) {
 
6631                 if (!layer._layerAdd) {
 
6632                         throw new Error('The provided object is not a Layer.');
 
6635                 var id = stamp(layer);
 
6636                 if (this._layers[id]) { return this; }
 
6637                 this._layers[id] = layer;
 
6639                 layer._mapToAdd = this;
 
6641                 if (layer.beforeAdd) {
 
6642                         layer.beforeAdd(this);
 
6645                 this.whenReady(layer._layerAdd, layer);
 
6650         // @method removeLayer(layer: Layer): this
 
6651         // Removes the given layer from the map.
 
6652         removeLayer: function (layer) {
 
6653                 var id = stamp(layer);
 
6655                 if (!this._layers[id]) { return this; }
 
6658                         layer.onRemove(this);
 
6661                 if (layer.getAttribution && this.attributionControl) {
 
6662                         this.attributionControl.removeAttribution(layer.getAttribution());
 
6665                 delete this._layers[id];
 
6668                         this.fire('layerremove', {layer: layer});
 
6669                         layer.fire('remove');
 
6672                 layer._map = layer._mapToAdd = null;
 
6677         // @method hasLayer(layer: Layer): Boolean
 
6678         // Returns `true` if the given layer is currently added to the map
 
6679         hasLayer: function (layer) {
 
6680                 return !!layer && (stamp(layer) in this._layers);
 
6683         /* @method eachLayer(fn: Function, context?: Object): this
 
6684          * Iterates over the layers of the map, optionally specifying context of the iterator function.
 
6686          * map.eachLayer(function(layer){
 
6687          *     layer.bindPopup('Hello');
 
6691         eachLayer: function (method, context) {
 
6692                 for (var i in this._layers) {
 
6693                         method.call(context, this._layers[i]);
 
6698         _addLayers: function (layers) {
 
6699                 layers = layers ? (isArray(layers) ? layers : [layers]) : [];
 
6701                 for (var i = 0, len = layers.length; i < len; i++) {
 
6702                         this.addLayer(layers[i]);
 
6706         _addZoomLimit: function (layer) {
 
6707                 if (isNaN(layer.options.maxZoom) || !isNaN(layer.options.minZoom)) {
 
6708                         this._zoomBoundLayers[stamp(layer)] = layer;
 
6709                         this._updateZoomLevels();
 
6713         _removeZoomLimit: function (layer) {
 
6714                 var id = stamp(layer);
 
6716                 if (this._zoomBoundLayers[id]) {
 
6717                         delete this._zoomBoundLayers[id];
 
6718                         this._updateZoomLevels();
 
6722         _updateZoomLevels: function () {
 
6723                 var minZoom = Infinity,
 
6724                     maxZoom = -Infinity,
 
6725                     oldZoomSpan = this._getZoomSpan();
 
6727                 for (var i in this._zoomBoundLayers) {
 
6728                         var options = this._zoomBoundLayers[i].options;
 
6730                         minZoom = options.minZoom === undefined ? minZoom : Math.min(minZoom, options.minZoom);
 
6731                         maxZoom = options.maxZoom === undefined ? maxZoom : Math.max(maxZoom, options.maxZoom);
 
6734                 this._layersMaxZoom = maxZoom === -Infinity ? undefined : maxZoom;
 
6735                 this._layersMinZoom = minZoom === Infinity ? undefined : minZoom;
 
6737                 // @section Map state change events
 
6738                 // @event zoomlevelschange: Event
 
6739                 // Fired when the number of zoomlevels on the map is changed due
 
6740                 // to adding or removing a layer.
 
6741                 if (oldZoomSpan !== this._getZoomSpan()) {
 
6742                         this.fire('zoomlevelschange');
 
6745                 if (this.options.maxZoom === undefined && this._layersMaxZoom && this.getZoom() > this._layersMaxZoom) {
 
6746                         this.setZoom(this._layersMaxZoom);
 
6748                 if (this.options.minZoom === undefined && this._layersMinZoom && this.getZoom() < this._layersMinZoom) {
 
6749                         this.setZoom(this._layersMinZoom);
 
6759  * Used to group several layers and handle them as one. If you add it to the map,
 
6760  * any layers added or removed from the group will be added/removed on the map as
 
6761  * well. Extends `Layer`.
 
6766  * L.layerGroup([marker1, marker2])
 
6767  *      .addLayer(polyline)
 
6772 var LayerGroup = Layer.extend({
 
6774         initialize: function (layers, options) {
 
6775                 setOptions(this, options);
 
6782                         for (i = 0, len = layers.length; i < len; i++) {
 
6783                                 this.addLayer(layers[i]);
 
6788         // @method addLayer(layer: Layer): this
 
6789         // Adds the given layer to the group.
 
6790         addLayer: function (layer) {
 
6791                 var id = this.getLayerId(layer);
 
6793                 this._layers[id] = layer;
 
6796                         this._map.addLayer(layer);
 
6802         // @method removeLayer(layer: Layer): this
 
6803         // Removes the given layer from the group.
 
6805         // @method removeLayer(id: Number): this
 
6806         // Removes the layer with the given internal ID from the group.
 
6807         removeLayer: function (layer) {
 
6808                 var id = layer in this._layers ? layer : this.getLayerId(layer);
 
6810                 if (this._map && this._layers[id]) {
 
6811                         this._map.removeLayer(this._layers[id]);
 
6814                 delete this._layers[id];
 
6819         // @method hasLayer(layer: Layer): Boolean
 
6820         // Returns `true` if the given layer is currently added to the group.
 
6822         // @method hasLayer(id: Number): Boolean
 
6823         // Returns `true` if the given internal ID is currently added to the group.
 
6824         hasLayer: function (layer) {
 
6825                 return !!layer && (layer in this._layers || this.getLayerId(layer) in this._layers);
 
6828         // @method clearLayers(): this
 
6829         // Removes all the layers from the group.
 
6830         clearLayers: function () {
 
6831                 return this.eachLayer(this.removeLayer, this);
 
6834         // @method invoke(methodName: String, …): this
 
6835         // Calls `methodName` on every layer contained in this group, passing any
 
6836         // additional parameters. Has no effect if the layers contained do not
 
6837         // implement `methodName`.
 
6838         invoke: function (methodName) {
 
6839                 var args = Array.prototype.slice.call(arguments, 1),
 
6842                 for (i in this._layers) {
 
6843                         layer = this._layers[i];
 
6845                         if (layer[methodName]) {
 
6846                                 layer[methodName].apply(layer, args);
 
6853         onAdd: function (map) {
 
6854                 this.eachLayer(map.addLayer, map);
 
6857         onRemove: function (map) {
 
6858                 this.eachLayer(map.removeLayer, map);
 
6861         // @method eachLayer(fn: Function, context?: Object): this
 
6862         // Iterates over the layers of the group, optionally specifying context of the iterator function.
 
6864         // group.eachLayer(function (layer) {
 
6865         //      layer.bindPopup('Hello');
 
6868         eachLayer: function (method, context) {
 
6869                 for (var i in this._layers) {
 
6870                         method.call(context, this._layers[i]);
 
6875         // @method getLayer(id: Number): Layer
 
6876         // Returns the layer with the given internal ID.
 
6877         getLayer: function (id) {
 
6878                 return this._layers[id];
 
6881         // @method getLayers(): Layer[]
 
6882         // Returns an array of all the layers added to the group.
 
6883         getLayers: function () {
 
6885                 this.eachLayer(layers.push, layers);
 
6889         // @method setZIndex(zIndex: Number): this
 
6890         // Calls `setZIndex` on every layer contained in this group, passing the z-index.
 
6891         setZIndex: function (zIndex) {
 
6892                 return this.invoke('setZIndex', zIndex);
 
6895         // @method getLayerId(layer: Layer): Number
 
6896         // Returns the internal ID for a layer
 
6897         getLayerId: function (layer) {
 
6898                 return stamp(layer);
 
6903 // @factory L.layerGroup(layers?: Layer[], options?: Object)
 
6904 // Create a layer group, optionally given an initial set of layers and an `options` object.
 
6905 var layerGroup = function (layers, options) {
 
6906         return new LayerGroup(layers, options);
 
6910  * @class FeatureGroup
 
6911  * @aka L.FeatureGroup
 
6912  * @inherits LayerGroup
 
6914  * Extended `LayerGroup` that makes it easier to do the same thing to all its member layers:
 
6915  *  * [`bindPopup`](#layer-bindpopup) binds a popup to all of the layers at once (likewise with [`bindTooltip`](#layer-bindtooltip))
 
6916  *  * Events are propagated to the `FeatureGroup`, so if the group has an event
 
6917  * handler, it will handle events from any of the layers. This includes mouse events
 
6918  * and custom events.
 
6919  *  * Has `layeradd` and `layerremove` events
 
6924  * L.featureGroup([marker1, marker2, polyline])
 
6925  *      .bindPopup('Hello world!')
 
6926  *      .on('click', function() { alert('Clicked on a member of the group!'); })
 
6931 var FeatureGroup = LayerGroup.extend({
 
6933         addLayer: function (layer) {
 
6934                 if (this.hasLayer(layer)) {
 
6938                 layer.addEventParent(this);
 
6940                 LayerGroup.prototype.addLayer.call(this, layer);
 
6942                 // @event layeradd: LayerEvent
 
6943                 // Fired when a layer is added to this `FeatureGroup`
 
6944                 return this.fire('layeradd', {layer: layer});
 
6947         removeLayer: function (layer) {
 
6948                 if (!this.hasLayer(layer)) {
 
6951                 if (layer in this._layers) {
 
6952                         layer = this._layers[layer];
 
6955                 layer.removeEventParent(this);
 
6957                 LayerGroup.prototype.removeLayer.call(this, layer);
 
6959                 // @event layerremove: LayerEvent
 
6960                 // Fired when a layer is removed from this `FeatureGroup`
 
6961                 return this.fire('layerremove', {layer: layer});
 
6964         // @method setStyle(style: Path options): this
 
6965         // Sets the given path options to each layer of the group that has a `setStyle` method.
 
6966         setStyle: function (style) {
 
6967                 return this.invoke('setStyle', style);
 
6970         // @method bringToFront(): this
 
6971         // Brings the layer group to the top of all other layers
 
6972         bringToFront: function () {
 
6973                 return this.invoke('bringToFront');
 
6976         // @method bringToBack(): this
 
6977         // Brings the layer group to the back of all other layers
 
6978         bringToBack: function () {
 
6979                 return this.invoke('bringToBack');
 
6982         // @method getBounds(): LatLngBounds
 
6983         // Returns the LatLngBounds of the Feature Group (created from bounds and coordinates of its children).
 
6984         getBounds: function () {
 
6985                 var bounds = new LatLngBounds();
 
6987                 for (var id in this._layers) {
 
6988                         var layer = this._layers[id];
 
6989                         bounds.extend(layer.getBounds ? layer.getBounds() : layer.getLatLng());
 
6995 // @factory L.featureGroup(layers: Layer[])
 
6996 // Create a feature group, optionally given an initial set of layers.
 
6997 var featureGroup = function (layers) {
 
6998         return new FeatureGroup(layers);
 
7005  * Represents an icon to provide when creating a marker.
 
7010  * var myIcon = L.icon({
 
7011  *     iconUrl: 'my-icon.png',
 
7012  *     iconRetinaUrl: 'my-icon@2x.png',
 
7013  *     iconSize: [38, 95],
 
7014  *     iconAnchor: [22, 94],
 
7015  *     popupAnchor: [-3, -76],
 
7016  *     shadowUrl: 'my-icon-shadow.png',
 
7017  *     shadowRetinaUrl: 'my-icon-shadow@2x.png',
 
7018  *     shadowSize: [68, 95],
 
7019  *     shadowAnchor: [22, 94]
 
7022  * L.marker([50.505, 30.57], {icon: myIcon}).addTo(map);
 
7025  * `L.Icon.Default` extends `L.Icon` and is the blue icon Leaflet uses for markers by default.
 
7029 var Icon = Class.extend({
 
7034          * @option iconUrl: String = null
 
7035          * **(required)** The URL to the icon image (absolute or relative to your script path).
 
7037          * @option iconRetinaUrl: String = null
 
7038          * The URL to a retina sized version of the icon image (absolute or relative to your
 
7039          * script path). Used for Retina screen devices.
 
7041          * @option iconSize: Point = null
 
7042          * Size of the icon image in pixels.
 
7044          * @option iconAnchor: Point = null
 
7045          * The coordinates of the "tip" of the icon (relative to its top left corner). The icon
 
7046          * will be aligned so that this point is at the marker's geographical location. Centered
 
7047          * by default if size is specified, also can be set in CSS with negative margins.
 
7049          * @option popupAnchor: Point = [0, 0]
 
7050          * The coordinates of the point from which popups will "open", relative to the icon anchor.
 
7052          * @option tooltipAnchor: Point = [0, 0]
 
7053          * The coordinates of the point from which tooltips will "open", relative to the icon anchor.
 
7055          * @option shadowUrl: String = null
 
7056          * The URL to the icon shadow image. If not specified, no shadow image will be created.
 
7058          * @option shadowRetinaUrl: String = null
 
7060          * @option shadowSize: Point = null
 
7061          * Size of the shadow image in pixels.
 
7063          * @option shadowAnchor: Point = null
 
7064          * The coordinates of the "tip" of the shadow (relative to its top left corner) (the same
 
7065          * as iconAnchor if not specified).
 
7067          * @option className: String = ''
 
7068          * A custom class name to assign to both icon and shadow images. Empty by default.
 
7072                 popupAnchor: [0, 0],
 
7073                 tooltipAnchor: [0, 0]
 
7076         initialize: function (options) {
 
7077                 setOptions(this, options);
 
7080         // @method createIcon(oldIcon?: HTMLElement): HTMLElement
 
7081         // Called internally when the icon has to be shown, returns a `<img>` HTML element
 
7082         // styled according to the options.
 
7083         createIcon: function (oldIcon) {
 
7084                 return this._createIcon('icon', oldIcon);
 
7087         // @method createShadow(oldIcon?: HTMLElement): HTMLElement
 
7088         // As `createIcon`, but for the shadow beneath it.
 
7089         createShadow: function (oldIcon) {
 
7090                 return this._createIcon('shadow', oldIcon);
 
7093         _createIcon: function (name, oldIcon) {
 
7094                 var src = this._getIconUrl(name);
 
7097                         if (name === 'icon') {
 
7098                                 throw new Error('iconUrl not set in Icon options (see the docs).');
 
7103                 var img = this._createImg(src, oldIcon && oldIcon.tagName === 'IMG' ? oldIcon : null);
 
7104                 this._setIconStyles(img, name);
 
7109         _setIconStyles: function (img, name) {
 
7110                 var options = this.options;
 
7111                 var sizeOption = options[name + 'Size'];
 
7113                 if (typeof sizeOption === 'number') {
 
7114                         sizeOption = [sizeOption, sizeOption];
 
7117                 var size = toPoint(sizeOption),
 
7118                     anchor = toPoint(name === 'shadow' && options.shadowAnchor || options.iconAnchor ||
 
7119                             size && size.divideBy(2, true));
 
7121                 img.className = 'leaflet-marker-' + name + ' ' + (options.className || '');
 
7124                         img.style.marginLeft = (-anchor.x) + 'px';
 
7125                         img.style.marginTop  = (-anchor.y) + 'px';
 
7129                         img.style.width  = size.x + 'px';
 
7130                         img.style.height = size.y + 'px';
 
7134         _createImg: function (src, el) {
 
7135                 el = el || document.createElement('img');
 
7140         _getIconUrl: function (name) {
 
7141                 return retina && this.options[name + 'RetinaUrl'] || this.options[name + 'Url'];
 
7146 // @factory L.icon(options: Icon options)
 
7147 // Creates an icon instance with the given options.
 
7148 function icon(options) {
 
7149         return new Icon(options);
 
7153  * @miniclass Icon.Default (Icon)
 
7154  * @aka L.Icon.Default
 
7157  * A trivial subclass of `Icon`, represents the icon to use in `Marker`s when
 
7158  * no icon is specified. Points to the blue marker image distributed with Leaflet
 
7161  * In order to customize the default icon, just change the properties of `L.Icon.Default.prototype.options`
 
7162  * (which is a set of `Icon options`).
 
7164  * If you want to _completely_ replace the default icon, override the
 
7165  * `L.Marker.prototype.options.icon` with your own icon instead.
 
7168 var IconDefault = Icon.extend({
 
7171                 iconUrl:       'marker-icon.png',
 
7172                 iconRetinaUrl: 'marker-icon-2x.png',
 
7173                 shadowUrl:     'marker-shadow.png',
 
7175                 iconAnchor:  [12, 41],
 
7176                 popupAnchor: [1, -34],
 
7177                 tooltipAnchor: [16, -28],
 
7178                 shadowSize:  [41, 41]
 
7181         _getIconUrl: function (name) {
 
7182                 if (!IconDefault.imagePath) {   // Deprecated, backwards-compatibility only
 
7183                         IconDefault.imagePath = this._detectIconPath();
 
7186                 // @option imagePath: String
 
7187                 // `Icon.Default` will try to auto-detect the location of the
 
7188                 // blue icon images. If you are placing these images in a non-standard
 
7189                 // way, set this option to point to the right path.
 
7190                 return (this.options.imagePath || IconDefault.imagePath) + Icon.prototype._getIconUrl.call(this, name);
 
7193         _detectIconPath: function () {
 
7194                 var el = create$1('div',  'leaflet-default-icon-path', document.body);
 
7195                 var path = getStyle(el, 'background-image') ||
 
7196                            getStyle(el, 'backgroundImage');     // IE8
 
7198                 document.body.removeChild(el);
 
7200                 if (path === null || path.indexOf('url') !== 0) {
 
7203                         path = path.replace(/^url\(["']?/, '').replace(/marker-icon\.png["']?\)$/, '');
 
7211  * L.Handler.MarkerDrag is used internally by L.Marker to make the markers draggable.
 
7215 /* @namespace Marker
 
7216  * @section Interaction handlers
 
7218  * 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:
 
7221  * marker.dragging.disable();
 
7224  * @property dragging: Handler
 
7225  * Marker dragging handler (by both mouse and touch). Only valid when the marker is on the map (Otherwise set [`marker.options.draggable`](#marker-draggable)).
 
7228 var MarkerDrag = Handler.extend({
 
7229         initialize: function (marker) {
 
7230                 this._marker = marker;
 
7233         addHooks: function () {
 
7234                 var icon = this._marker._icon;
 
7236                 if (!this._draggable) {
 
7237                         this._draggable = new Draggable(icon, icon, true);
 
7240                 this._draggable.on({
 
7241                         dragstart: this._onDragStart,
 
7242                         predrag: this._onPreDrag,
 
7244                         dragend: this._onDragEnd
 
7247                 addClass(icon, 'leaflet-marker-draggable');
 
7250         removeHooks: function () {
 
7251                 this._draggable.off({
 
7252                         dragstart: this._onDragStart,
 
7253                         predrag: this._onPreDrag,
 
7255                         dragend: this._onDragEnd
 
7258                 if (this._marker._icon) {
 
7259                         removeClass(this._marker._icon, 'leaflet-marker-draggable');
 
7263         moved: function () {
 
7264                 return this._draggable && this._draggable._moved;
 
7267         _adjustPan: function (e) {
 
7268                 var marker = this._marker,
 
7270                     speed = this._marker.options.autoPanSpeed,
 
7271                     padding = this._marker.options.autoPanPadding,
 
7272                     iconPos = getPosition(marker._icon),
 
7273                     bounds = map.getPixelBounds(),
 
7274                     origin = map.getPixelOrigin();
 
7276                 var panBounds = toBounds(
 
7277                         bounds.min._subtract(origin).add(padding),
 
7278                         bounds.max._subtract(origin).subtract(padding)
 
7281                 if (!panBounds.contains(iconPos)) {
 
7282                         // Compute incremental movement
 
7283                         var movement = toPoint(
 
7284                                 (Math.max(panBounds.max.x, iconPos.x) - panBounds.max.x) / (bounds.max.x - panBounds.max.x) -
 
7285                                 (Math.min(panBounds.min.x, iconPos.x) - panBounds.min.x) / (bounds.min.x - panBounds.min.x),
 
7287                                 (Math.max(panBounds.max.y, iconPos.y) - panBounds.max.y) / (bounds.max.y - panBounds.max.y) -
 
7288                                 (Math.min(panBounds.min.y, iconPos.y) - panBounds.min.y) / (bounds.min.y - panBounds.min.y)
 
7289                         ).multiplyBy(speed);
 
7291                         map.panBy(movement, {animate: false});
 
7293                         this._draggable._newPos._add(movement);
 
7294                         this._draggable._startPos._add(movement);
 
7296                         setPosition(marker._icon, this._draggable._newPos);
 
7299                         this._panRequest = requestAnimFrame(this._adjustPan.bind(this, e));
 
7303         _onDragStart: function () {
 
7304                 // @section Dragging events
 
7305                 // @event dragstart: Event
 
7306                 // Fired when the user starts dragging the marker.
 
7308                 // @event movestart: Event
 
7309                 // Fired when the marker starts moving (because of dragging).
 
7311                 this._oldLatLng = this._marker.getLatLng();
 
7318         _onPreDrag: function (e) {
 
7319                 if (this._marker.options.autoPan) {
 
7320                         cancelAnimFrame(this._panRequest);
 
7321                         this._panRequest = requestAnimFrame(this._adjustPan.bind(this, e));
 
7325         _onDrag: function (e) {
 
7326                 var marker = this._marker,
 
7327                     shadow = marker._shadow,
 
7328                     iconPos = getPosition(marker._icon),
 
7329                     latlng = marker._map.layerPointToLatLng(iconPos);
 
7331                 // update shadow position
 
7333                         setPosition(shadow, iconPos);
 
7336                 marker._latlng = latlng;
 
7338                 e.oldLatLng = this._oldLatLng;
 
7340                 // @event drag: Event
 
7341                 // Fired repeatedly while the user drags the marker.
 
7347         _onDragEnd: function (e) {
 
7348                 // @event dragend: DragEndEvent
 
7349                 // Fired when the user stops dragging the marker.
 
7351                  cancelAnimFrame(this._panRequest);
 
7353                 // @event moveend: Event
 
7354                 // Fired when the marker stops moving (because of dragging).
 
7355                 delete this._oldLatLng;
 
7358                     .fire('dragend', e);
 
7364  * @inherits Interactive layer
 
7366  * L.Marker is used to display clickable/draggable icons on the map. Extends `Layer`.
 
7371  * L.marker([50.5, 30.5]).addTo(map);
 
7375 var Marker = Layer.extend({
 
7378         // @aka Marker options
 
7380                 // @option icon: Icon = *
 
7381                 // Icon instance to use for rendering the marker.
 
7382                 // See [Icon documentation](#L.Icon) for details on how to customize the marker icon.
 
7383                 // If not specified, a common instance of `L.Icon.Default` is used.
 
7384                 icon: new IconDefault(),
 
7386                 // Option inherited from "Interactive layer" abstract class
 
7389                 // @option keyboard: Boolean = true
 
7390                 // Whether the marker can be tabbed to with a keyboard and clicked by pressing enter.
 
7393                 // @option title: String = ''
 
7394                 // Text for the browser tooltip that appear on marker hover (no tooltip by default).
 
7397                 // @option alt: String = ''
 
7398                 // Text for the `alt` attribute of the icon image (useful for accessibility).
 
7401                 // @option zIndexOffset: Number = 0
 
7402                 // 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).
 
7405                 // @option opacity: Number = 1.0
 
7406                 // The opacity of the marker.
 
7409                 // @option riseOnHover: Boolean = false
 
7410                 // If `true`, the marker will get on top of others when you hover the mouse over it.
 
7413                 // @option riseOffset: Number = 250
 
7414                 // The z-index offset used for the `riseOnHover` feature.
 
7417                 // @option pane: String = 'markerPane'
 
7418                 // `Map pane` where the markers icon will be added.
 
7421                 // @option bubblingMouseEvents: Boolean = false
 
7422                 // When `true`, a mouse event on this marker will trigger the same event on the map
 
7423                 // (unless [`L.DomEvent.stopPropagation`](#domevent-stoppropagation) is used).
 
7424                 bubblingMouseEvents: false,
 
7426                 // @section Draggable marker options
 
7427                 // @option draggable: Boolean = false
 
7428                 // Whether the marker is draggable with mouse/touch or not.
 
7431                 // @option autoPan: Boolean = false
 
7432                 // Whether to pan the map when dragging this marker near its edge or not.
 
7435                 // @option autoPanPadding: Point = Point(50, 50)
 
7436                 // Distance (in pixels to the left/right and to the top/bottom) of the
 
7437                 // map edge to start panning the map.
 
7438                 autoPanPadding: [50, 50],
 
7440                 // @option autoPanSpeed: Number = 10
 
7441                 // Number of pixels the map should pan by.
 
7447          * In addition to [shared layer methods](#Layer) like `addTo()` and `remove()` and [popup methods](#Popup) like bindPopup() you can also use the following methods:
 
7450         initialize: function (latlng, options) {
 
7451                 setOptions(this, options);
 
7452                 this._latlng = toLatLng(latlng);
 
7455         onAdd: function (map) {
 
7456                 this._zoomAnimated = this._zoomAnimated && map.options.markerZoomAnimation;
 
7458                 if (this._zoomAnimated) {
 
7459                         map.on('zoomanim', this._animateZoom, this);
 
7466         onRemove: function (map) {
 
7467                 if (this.dragging && this.dragging.enabled()) {
 
7468                         this.options.draggable = true;
 
7469                         this.dragging.removeHooks();
 
7471                 delete this.dragging;
 
7473                 if (this._zoomAnimated) {
 
7474                         map.off('zoomanim', this._animateZoom, this);
 
7478                 this._removeShadow();
 
7481         getEvents: function () {
 
7484                         viewreset: this.update
 
7488         // @method getLatLng: LatLng
 
7489         // Returns the current geographical position of the marker.
 
7490         getLatLng: function () {
 
7491                 return this._latlng;
 
7494         // @method setLatLng(latlng: LatLng): this
 
7495         // Changes the marker position to the given point.
 
7496         setLatLng: function (latlng) {
 
7497                 var oldLatLng = this._latlng;
 
7498                 this._latlng = toLatLng(latlng);
 
7501                 // @event move: Event
 
7502                 // 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`.
 
7503                 return this.fire('move', {oldLatLng: oldLatLng, latlng: this._latlng});
 
7506         // @method setZIndexOffset(offset: Number): this
 
7507         // Changes the [zIndex offset](#marker-zindexoffset) of the marker.
 
7508         setZIndexOffset: function (offset) {
 
7509                 this.options.zIndexOffset = offset;
 
7510                 return this.update();
 
7513         // @method setIcon(icon: Icon): this
 
7514         // Changes the marker icon.
 
7515         setIcon: function (icon) {
 
7517                 this.options.icon = icon;
 
7525                         this.bindPopup(this._popup, this._popup.options);
 
7531         getElement: function () {
 
7535         update: function () {
 
7537                 if (this._icon && this._map) {
 
7538                         var pos = this._map.latLngToLayerPoint(this._latlng).round();
 
7545         _initIcon: function () {
 
7546                 var options = this.options,
 
7547                     classToAdd = 'leaflet-zoom-' + (this._zoomAnimated ? 'animated' : 'hide');
 
7549                 var icon = options.icon.createIcon(this._icon),
 
7552                 // if we're not reusing the icon, remove the old one and init new one
 
7553                 if (icon !== this._icon) {
 
7559                         if (options.title) {
 
7560                                 icon.title = options.title;
 
7563                         if (icon.tagName === 'IMG') {
 
7564                                 icon.alt = options.alt || '';
 
7568                 addClass(icon, classToAdd);
 
7570                 if (options.keyboard) {
 
7571                         icon.tabIndex = '0';
 
7576                 if (options.riseOnHover) {
 
7578                                 mouseover: this._bringToFront,
 
7579                                 mouseout: this._resetZIndex
 
7583                 var newShadow = options.icon.createShadow(this._shadow),
 
7586                 if (newShadow !== this._shadow) {
 
7587                         this._removeShadow();
 
7592                         addClass(newShadow, classToAdd);
 
7595                 this._shadow = newShadow;
 
7598                 if (options.opacity < 1) {
 
7599                         this._updateOpacity();
 
7604                         this.getPane().appendChild(this._icon);
 
7606                 this._initInteraction();
 
7607                 if (newShadow && addShadow) {
 
7608                         this.getPane('shadowPane').appendChild(this._shadow);
 
7612         _removeIcon: function () {
 
7613                 if (this.options.riseOnHover) {
 
7615                                 mouseover: this._bringToFront,
 
7616                                 mouseout: this._resetZIndex
 
7621                 this.removeInteractiveTarget(this._icon);
 
7626         _removeShadow: function () {
 
7628                         remove(this._shadow);
 
7630                 this._shadow = null;
 
7633         _setPos: function (pos) {
 
7634                 setPosition(this._icon, pos);
 
7637                         setPosition(this._shadow, pos);
 
7640                 this._zIndex = pos.y + this.options.zIndexOffset;
 
7642                 this._resetZIndex();
 
7645         _updateZIndex: function (offset) {
 
7646                 this._icon.style.zIndex = this._zIndex + offset;
 
7649         _animateZoom: function (opt) {
 
7650                 var pos = this._map._latLngToNewLayerPoint(this._latlng, opt.zoom, opt.center).round();
 
7655         _initInteraction: function () {
 
7657                 if (!this.options.interactive) { return; }
 
7659                 addClass(this._icon, 'leaflet-interactive');
 
7661                 this.addInteractiveTarget(this._icon);
 
7664                         var draggable = this.options.draggable;
 
7665                         if (this.dragging) {
 
7666                                 draggable = this.dragging.enabled();
 
7667                                 this.dragging.disable();
 
7670                         this.dragging = new MarkerDrag(this);
 
7673                                 this.dragging.enable();
 
7678         // @method setOpacity(opacity: Number): this
 
7679         // Changes the opacity of the marker.
 
7680         setOpacity: function (opacity) {
 
7681                 this.options.opacity = opacity;
 
7683                         this._updateOpacity();
 
7689         _updateOpacity: function () {
 
7690                 var opacity = this.options.opacity;
 
7692                 setOpacity(this._icon, opacity);
 
7695                         setOpacity(this._shadow, opacity);
 
7699         _bringToFront: function () {
 
7700                 this._updateZIndex(this.options.riseOffset);
 
7703         _resetZIndex: function () {
 
7704                 this._updateZIndex(0);
 
7707         _getPopupAnchor: function () {
 
7708                 return this.options.icon.options.popupAnchor;
 
7711         _getTooltipAnchor: function () {
 
7712                 return this.options.icon.options.tooltipAnchor;
 
7717 // factory L.marker(latlng: LatLng, options? : Marker options)
 
7719 // @factory L.marker(latlng: LatLng, options? : Marker options)
 
7720 // Instantiates a Marker object given a geographical point and optionally an options object.
 
7721 function marker(latlng, options) {
 
7722         return new Marker(latlng, options);
 
7728  * @inherits Interactive layer
 
7730  * An abstract class that contains options and constants shared between vector
 
7731  * overlays (Polygon, Polyline, Circle). Do not use it directly. Extends `Layer`.
 
7734 var Path = Layer.extend({
 
7737         // @aka Path options
 
7739                 // @option stroke: Boolean = true
 
7740                 // Whether to draw stroke along the path. Set it to `false` to disable borders on polygons or circles.
 
7743                 // @option color: String = '#3388ff'
 
7747                 // @option weight: Number = 3
 
7748                 // Stroke width in pixels
 
7751                 // @option opacity: Number = 1.0
 
7755                 // @option lineCap: String= 'round'
 
7756                 // A string that defines [shape to be used at the end](https://developer.mozilla.org/docs/Web/SVG/Attribute/stroke-linecap) of the stroke.
 
7759                 // @option lineJoin: String = 'round'
 
7760                 // A string that defines [shape to be used at the corners](https://developer.mozilla.org/docs/Web/SVG/Attribute/stroke-linejoin) of the stroke.
 
7763                 // @option dashArray: String = null
 
7764                 // 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).
 
7767                 // @option dashOffset: String = null
 
7768                 // 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).
 
7771                 // @option fill: Boolean = depends
 
7772                 // Whether to fill the path with color. Set it to `false` to disable filling on polygons or circles.
 
7775                 // @option fillColor: String = *
 
7776                 // Fill color. Defaults to the value of the [`color`](#path-color) option
 
7779                 // @option fillOpacity: Number = 0.2
 
7783                 // @option fillRule: String = 'evenodd'
 
7784                 // A string that defines [how the inside of a shape](https://developer.mozilla.org/docs/Web/SVG/Attribute/fill-rule) is determined.
 
7785                 fillRule: 'evenodd',
 
7789                 // Option inherited from "Interactive layer" abstract class
 
7792                 // @option bubblingMouseEvents: Boolean = true
 
7793                 // When `true`, a mouse event on this path will trigger the same event on the map
 
7794                 // (unless [`L.DomEvent.stopPropagation`](#domevent-stoppropagation) is used).
 
7795                 bubblingMouseEvents: true
 
7798         beforeAdd: function (map) {
 
7799                 // Renderer is set here because we need to call renderer.getEvents
 
7800                 // before this.getEvents.
 
7801                 this._renderer = map.getRenderer(this);
 
7804         onAdd: function () {
 
7805                 this._renderer._initPath(this);
 
7807                 this._renderer._addPath(this);
 
7810         onRemove: function () {
 
7811                 this._renderer._removePath(this);
 
7814         // @method redraw(): this
 
7815         // Redraws the layer. Sometimes useful after you changed the coordinates that the path uses.
 
7816         redraw: function () {
 
7818                         this._renderer._updatePath(this);
 
7823         // @method setStyle(style: Path options): this
 
7824         // Changes the appearance of a Path based on the options in the `Path options` object.
 
7825         setStyle: function (style) {
 
7826                 setOptions(this, style);
 
7827                 if (this._renderer) {
 
7828                         this._renderer._updateStyle(this);
 
7833         // @method bringToFront(): this
 
7834         // Brings the layer to the top of all path layers.
 
7835         bringToFront: function () {
 
7836                 if (this._renderer) {
 
7837                         this._renderer._bringToFront(this);
 
7842         // @method bringToBack(): this
 
7843         // Brings the layer to the bottom of all path layers.
 
7844         bringToBack: function () {
 
7845                 if (this._renderer) {
 
7846                         this._renderer._bringToBack(this);
 
7851         getElement: function () {
 
7855         _reset: function () {
 
7856                 // defined in child classes
 
7861         _clickTolerance: function () {
 
7862                 // used when doing hit detection for Canvas layers
 
7863                 return (this.options.stroke ? this.options.weight / 2 : 0) + this._renderer.options.tolerance;
 
7868  * @class CircleMarker
 
7869  * @aka L.CircleMarker
 
7872  * A circle of a fixed size with radius specified in pixels. Extends `Path`.
 
7875 var CircleMarker = Path.extend({
 
7878         // @aka CircleMarker options
 
7882                 // @option radius: Number = 10
 
7883                 // Radius of the circle marker, in pixels
 
7887         initialize: function (latlng, options) {
 
7888                 setOptions(this, options);
 
7889                 this._latlng = toLatLng(latlng);
 
7890                 this._radius = this.options.radius;
 
7893         // @method setLatLng(latLng: LatLng): this
 
7894         // Sets the position of a circle marker to a new location.
 
7895         setLatLng: function (latlng) {
 
7896                 this._latlng = toLatLng(latlng);
 
7898                 return this.fire('move', {latlng: this._latlng});
 
7901         // @method getLatLng(): LatLng
 
7902         // Returns the current geographical position of the circle marker
 
7903         getLatLng: function () {
 
7904                 return this._latlng;
 
7907         // @method setRadius(radius: Number): this
 
7908         // Sets the radius of a circle marker. Units are in pixels.
 
7909         setRadius: function (radius) {
 
7910                 this.options.radius = this._radius = radius;
 
7911                 return this.redraw();
 
7914         // @method getRadius(): Number
 
7915         // Returns the current radius of the circle
 
7916         getRadius: function () {
 
7917                 return this._radius;
 
7920         setStyle : function (options) {
 
7921                 var radius = options && options.radius || this._radius;
 
7922                 Path.prototype.setStyle.call(this, options);
 
7923                 this.setRadius(radius);
 
7927         _project: function () {
 
7928                 this._point = this._map.latLngToLayerPoint(this._latlng);
 
7929                 this._updateBounds();
 
7932         _updateBounds: function () {
 
7933                 var r = this._radius,
 
7934                     r2 = this._radiusY || r,
 
7935                     w = this._clickTolerance(),
 
7936                     p = [r + w, r2 + w];
 
7937                 this._pxBounds = new Bounds(this._point.subtract(p), this._point.add(p));
 
7940         _update: function () {
 
7946         _updatePath: function () {
 
7947                 this._renderer._updateCircle(this);
 
7950         _empty: function () {
 
7951                 return this._radius && !this._renderer._bounds.intersects(this._pxBounds);
 
7954         // Needed by the `Canvas` renderer for interactivity
 
7955         _containsPoint: function (p) {
 
7956                 return p.distanceTo(this._point) <= this._radius + this._clickTolerance();
 
7961 // @factory L.circleMarker(latlng: LatLng, options?: CircleMarker options)
 
7962 // Instantiates a circle marker object given a geographical point, and an optional options object.
 
7963 function circleMarker(latlng, options) {
 
7964         return new CircleMarker(latlng, options);
 
7970  * @inherits CircleMarker
 
7972  * A class for drawing circle overlays on a map. Extends `CircleMarker`.
 
7974  * It's an approximation and starts to diverge from a real circle closer to poles (due to projection distortion).
 
7979  * L.circle([50.5, 30.5], {radius: 200}).addTo(map);
 
7983 var Circle = CircleMarker.extend({
 
7985         initialize: function (latlng, options, legacyOptions) {
 
7986                 if (typeof options === 'number') {
 
7987                         // Backwards compatibility with 0.7.x factory (latlng, radius, options?)
 
7988                         options = extend({}, legacyOptions, {radius: options});
 
7990                 setOptions(this, options);
 
7991                 this._latlng = toLatLng(latlng);
 
7993                 if (isNaN(this.options.radius)) { throw new Error('Circle radius cannot be NaN'); }
 
7996                 // @aka Circle options
 
7997                 // @option radius: Number; Radius of the circle, in meters.
 
7998                 this._mRadius = this.options.radius;
 
8001         // @method setRadius(radius: Number): this
 
8002         // Sets the radius of a circle. Units are in meters.
 
8003         setRadius: function (radius) {
 
8004                 this._mRadius = radius;
 
8005                 return this.redraw();
 
8008         // @method getRadius(): Number
 
8009         // Returns the current radius of a circle. Units are in meters.
 
8010         getRadius: function () {
 
8011                 return this._mRadius;
 
8014         // @method getBounds(): LatLngBounds
 
8015         // Returns the `LatLngBounds` of the path.
 
8016         getBounds: function () {
 
8017                 var half = [this._radius, this._radiusY || this._radius];
 
8019                 return new LatLngBounds(
 
8020                         this._map.layerPointToLatLng(this._point.subtract(half)),
 
8021                         this._map.layerPointToLatLng(this._point.add(half)));
 
8024         setStyle: Path.prototype.setStyle,
 
8026         _project: function () {
 
8028                 var lng = this._latlng.lng,
 
8029                     lat = this._latlng.lat,
 
8031                     crs = map.options.crs;
 
8033                 if (crs.distance === Earth.distance) {
 
8034                         var d = Math.PI / 180,
 
8035                             latR = (this._mRadius / Earth.R) / d,
 
8036                             top = map.project([lat + latR, lng]),
 
8037                             bottom = map.project([lat - latR, lng]),
 
8038                             p = top.add(bottom).divideBy(2),
 
8039                             lat2 = map.unproject(p).lat,
 
8040                             lngR = Math.acos((Math.cos(latR * d) - Math.sin(lat * d) * Math.sin(lat2 * d)) /
 
8041                                     (Math.cos(lat * d) * Math.cos(lat2 * d))) / d;
 
8043                         if (isNaN(lngR) || lngR === 0) {
 
8044                                 lngR = latR / Math.cos(Math.PI / 180 * lat); // Fallback for edge case, #2425
 
8047                         this._point = p.subtract(map.getPixelOrigin());
 
8048                         this._radius = isNaN(lngR) ? 0 : p.x - map.project([lat2, lng - lngR]).x;
 
8049                         this._radiusY = p.y - top.y;
 
8052                         var latlng2 = crs.unproject(crs.project(this._latlng).subtract([this._mRadius, 0]));
 
8054                         this._point = map.latLngToLayerPoint(this._latlng);
 
8055                         this._radius = this._point.x - map.latLngToLayerPoint(latlng2).x;
 
8058                 this._updateBounds();
 
8062 // @factory L.circle(latlng: LatLng, options?: Circle options)
 
8063 // Instantiates a circle object given a geographical point, and an options object
 
8064 // which contains the circle radius.
 
8066 // @factory L.circle(latlng: LatLng, radius: Number, options?: Circle options)
 
8067 // Obsolete way of instantiating a circle, for compatibility with 0.7.x code.
 
8068 // Do not use in new applications or plugins.
 
8069 function circle(latlng, options, legacyOptions) {
 
8070         return new Circle(latlng, options, legacyOptions);
 
8078  * A class for drawing polyline overlays on a map. Extends `Path`.
 
8083  * // create a red polyline from an array of LatLng points
 
8090  * var polyline = L.polyline(latlngs, {color: 'red'}).addTo(map);
 
8092  * // zoom the map to the polyline
 
8093  * map.fitBounds(polyline.getBounds());
 
8096  * You can also pass a multi-dimensional array to represent a `MultiPolyline` shape:
 
8099  * // create a red polyline from an array of arrays of LatLng points
 
8101  *      [[45.51, -122.68],
 
8112 var Polyline = Path.extend({
 
8115         // @aka Polyline options
 
8117                 // @option smoothFactor: Number = 1.0
 
8118                 // How much to simplify the polyline on each zoom level. More means
 
8119                 // better performance and smoother look, and less means more accurate representation.
 
8122                 // @option noClip: Boolean = false
 
8123                 // Disable polyline clipping.
 
8127         initialize: function (latlngs, options) {
 
8128                 setOptions(this, options);
 
8129                 this._setLatLngs(latlngs);
 
8132         // @method getLatLngs(): LatLng[]
 
8133         // Returns an array of the points in the path, or nested arrays of points in case of multi-polyline.
 
8134         getLatLngs: function () {
 
8135                 return this._latlngs;
 
8138         // @method setLatLngs(latlngs: LatLng[]): this
 
8139         // Replaces all the points in the polyline with the given array of geographical points.
 
8140         setLatLngs: function (latlngs) {
 
8141                 this._setLatLngs(latlngs);
 
8142                 return this.redraw();
 
8145         // @method isEmpty(): Boolean
 
8146         // Returns `true` if the Polyline has no LatLngs.
 
8147         isEmpty: function () {
 
8148                 return !this._latlngs.length;
 
8151         // @method closestLayerPoint(p: Point): Point
 
8152         // Returns the point closest to `p` on the Polyline.
 
8153         closestLayerPoint: function (p) {
 
8154                 var minDistance = Infinity,
 
8156                     closest = _sqClosestPointOnSegment,
 
8159                 for (var j = 0, jLen = this._parts.length; j < jLen; j++) {
 
8160                         var points = this._parts[j];
 
8162                         for (var i = 1, len = points.length; i < len; i++) {
 
8166                                 var sqDist = closest(p, p1, p2, true);
 
8168                                 if (sqDist < minDistance) {
 
8169                                         minDistance = sqDist;
 
8170                                         minPoint = closest(p, p1, p2);
 
8175                         minPoint.distance = Math.sqrt(minDistance);
 
8180         // @method getCenter(): LatLng
 
8181         // Returns the center ([centroid](http://en.wikipedia.org/wiki/Centroid)) of the polyline.
 
8182         getCenter: function () {
 
8183                 // throws error when not yet added to map as this center calculation requires projected coordinates
 
8185                         throw new Error('Must add layer to map before using getCenter()');
 
8188                 var i, halfDist, segDist, dist, p1, p2, ratio,
 
8189                     points = this._rings[0],
 
8190                     len = points.length;
 
8192                 if (!len) { return null; }
 
8194                 // polyline centroid algorithm; only uses the first ring if there are multiple
 
8196                 for (i = 0, halfDist = 0; i < len - 1; i++) {
 
8197                         halfDist += points[i].distanceTo(points[i + 1]) / 2;
 
8200                 // The line is so small in the current view that all points are on the same pixel.
 
8201                 if (halfDist === 0) {
 
8202                         return this._map.layerPointToLatLng(points[0]);
 
8205                 for (i = 0, dist = 0; i < len - 1; i++) {
 
8208                         segDist = p1.distanceTo(p2);
 
8211                         if (dist > halfDist) {
 
8212                                 ratio = (dist - halfDist) / segDist;
 
8213                                 return this._map.layerPointToLatLng([
 
8214                                         p2.x - ratio * (p2.x - p1.x),
 
8215                                         p2.y - ratio * (p2.y - p1.y)
 
8221         // @method getBounds(): LatLngBounds
 
8222         // Returns the `LatLngBounds` of the path.
 
8223         getBounds: function () {
 
8224                 return this._bounds;
 
8227         // @method addLatLng(latlng: LatLng, latlngs? LatLng[]): this
 
8228         // Adds a given point to the polyline. By default, adds to the first ring of
 
8229         // the polyline in case of a multi-polyline, but can be overridden by passing
 
8230         // a specific ring as a LatLng array (that you can earlier access with [`getLatLngs`](#polyline-getlatlngs)).
 
8231         addLatLng: function (latlng, latlngs) {
 
8232                 latlngs = latlngs || this._defaultShape();
 
8233                 latlng = toLatLng(latlng);
 
8234                 latlngs.push(latlng);
 
8235                 this._bounds.extend(latlng);
 
8236                 return this.redraw();
 
8239         _setLatLngs: function (latlngs) {
 
8240                 this._bounds = new LatLngBounds();
 
8241                 this._latlngs = this._convertLatLngs(latlngs);
 
8244         _defaultShape: function () {
 
8245                 return isFlat(this._latlngs) ? this._latlngs : this._latlngs[0];
 
8248         // recursively convert latlngs input into actual LatLng instances; calculate bounds along the way
 
8249         _convertLatLngs: function (latlngs) {
 
8251                     flat = isFlat(latlngs);
 
8253                 for (var i = 0, len = latlngs.length; i < len; i++) {
 
8255                                 result[i] = toLatLng(latlngs[i]);
 
8256                                 this._bounds.extend(result[i]);
 
8258                                 result[i] = this._convertLatLngs(latlngs[i]);
 
8265         _project: function () {
 
8266                 var pxBounds = new Bounds();
 
8268                 this._projectLatlngs(this._latlngs, this._rings, pxBounds);
 
8270                 var w = this._clickTolerance(),
 
8271                     p = new Point(w, w);
 
8273                 if (this._bounds.isValid() && pxBounds.isValid()) {
 
8274                         pxBounds.min._subtract(p);
 
8275                         pxBounds.max._add(p);
 
8276                         this._pxBounds = pxBounds;
 
8280         // recursively turns latlngs into a set of rings with projected coordinates
 
8281         _projectLatlngs: function (latlngs, result, projectedBounds) {
 
8282                 var flat = latlngs[0] instanceof LatLng,
 
8283                     len = latlngs.length,
 
8288                         for (i = 0; i < len; i++) {
 
8289                                 ring[i] = this._map.latLngToLayerPoint(latlngs[i]);
 
8290                                 projectedBounds.extend(ring[i]);
 
8294                         for (i = 0; i < len; i++) {
 
8295                                 this._projectLatlngs(latlngs[i], result, projectedBounds);
 
8300         // clip polyline by renderer bounds so that we have less to render for performance
 
8301         _clipPoints: function () {
 
8302                 var bounds = this._renderer._bounds;
 
8305                 if (!this._pxBounds || !this._pxBounds.intersects(bounds)) {
 
8309                 if (this.options.noClip) {
 
8310                         this._parts = this._rings;
 
8314                 var parts = this._parts,
 
8315                     i, j, k, len, len2, segment, points;
 
8317                 for (i = 0, k = 0, len = this._rings.length; i < len; i++) {
 
8318                         points = this._rings[i];
 
8320                         for (j = 0, len2 = points.length; j < len2 - 1; j++) {
 
8321                                 segment = clipSegment(points[j], points[j + 1], bounds, j, true);
 
8323                                 if (!segment) { continue; }
 
8325                                 parts[k] = parts[k] || [];
 
8326                                 parts[k].push(segment[0]);
 
8328                                 // if segment goes out of screen, or it's the last one, it's the end of the line part
 
8329                                 if ((segment[1] !== points[j + 1]) || (j === len2 - 2)) {
 
8330                                         parts[k].push(segment[1]);
 
8337         // simplify each clipped part of the polyline for performance
 
8338         _simplifyPoints: function () {
 
8339                 var parts = this._parts,
 
8340                     tolerance = this.options.smoothFactor;
 
8342                 for (var i = 0, len = parts.length; i < len; i++) {
 
8343                         parts[i] = simplify(parts[i], tolerance);
 
8347         _update: function () {
 
8348                 if (!this._map) { return; }
 
8351                 this._simplifyPoints();
 
8355         _updatePath: function () {
 
8356                 this._renderer._updatePoly(this);
 
8359         // Needed by the `Canvas` renderer for interactivity
 
8360         _containsPoint: function (p, closed) {
 
8361                 var i, j, k, len, len2, part,
 
8362                     w = this._clickTolerance();
 
8364                 if (!this._pxBounds || !this._pxBounds.contains(p)) { return false; }
 
8366                 // hit detection for polylines
 
8367                 for (i = 0, len = this._parts.length; i < len; i++) {
 
8368                         part = this._parts[i];
 
8370                         for (j = 0, len2 = part.length, k = len2 - 1; j < len2; k = j++) {
 
8371                                 if (!closed && (j === 0)) { continue; }
 
8373                                 if (pointToSegmentDistance(p, part[k], part[j]) <= w) {
 
8382 // @factory L.polyline(latlngs: LatLng[], options?: Polyline options)
 
8383 // Instantiates a polyline object given an array of geographical points and
 
8384 // optionally an options object. You can create a `Polyline` object with
 
8385 // multiple separate lines (`MultiPolyline`) by passing an array of arrays
 
8386 // of geographic points.
 
8387 function polyline(latlngs, options) {
 
8388         return new Polyline(latlngs, options);
 
8391 // Retrocompat. Allow plugins to support Leaflet versions before and after 1.1.
 
8392 Polyline._flat = _flat;
 
8397  * @inherits Polyline
 
8399  * A class for drawing polygon overlays on a map. Extends `Polyline`.
 
8401  * 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.
 
8407  * // create a red polygon from an array of LatLng points
 
8408  * var latlngs = [[37, -109.05],[41, -109.03],[41, -102.05],[37, -102.04]];
 
8410  * var polygon = L.polygon(latlngs, {color: 'red'}).addTo(map);
 
8412  * // zoom the map to the polygon
 
8413  * map.fitBounds(polygon.getBounds());
 
8416  * 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:
 
8420  *   [[37, -109.05],[41, -109.03],[41, -102.05],[37, -102.04]], // outer ring
 
8421  *   [[37.29, -108.58],[40.71, -108.58],[40.71, -102.50],[37.29, -102.50]] // hole
 
8425  * Additionally, you can pass a multi-dimensional array to represent a MultiPolygon shape.
 
8429  *   [ // first polygon
 
8430  *     [[37, -109.05],[41, -109.03],[41, -102.05],[37, -102.04]], // outer ring
 
8431  *     [[37.29, -108.58],[40.71, -108.58],[40.71, -102.50],[37.29, -102.50]] // hole
 
8433  *   [ // second polygon
 
8434  *     [[41, -111.03],[45, -111.04],[45, -104.05],[41, -104.05]]
 
8440 var Polygon = Polyline.extend({
 
8446         isEmpty: function () {
 
8447                 return !this._latlngs.length || !this._latlngs[0].length;
 
8450         getCenter: function () {
 
8451                 // throws error when not yet added to map as this center calculation requires projected coordinates
 
8453                         throw new Error('Must add layer to map before using getCenter()');
 
8456                 var i, j, p1, p2, f, area, x, y, center,
 
8457                     points = this._rings[0],
 
8458                     len = points.length;
 
8460                 if (!len) { return null; }
 
8462                 // polygon centroid algorithm; only uses the first ring if there are multiple
 
8466                 for (i = 0, j = len - 1; i < len; j = i++) {
 
8470                         f = p1.y * p2.x - p2.y * p1.x;
 
8471                         x += (p1.x + p2.x) * f;
 
8472                         y += (p1.y + p2.y) * f;
 
8477                         // Polygon is so small that all points are on same pixel.
 
8480                         center = [x / area, y / area];
 
8482                 return this._map.layerPointToLatLng(center);
 
8485         _convertLatLngs: function (latlngs) {
 
8486                 var result = Polyline.prototype._convertLatLngs.call(this, latlngs),
 
8487                     len = result.length;
 
8489                 // remove last point if it equals first one
 
8490                 if (len >= 2 && result[0] instanceof LatLng && result[0].equals(result[len - 1])) {
 
8496         _setLatLngs: function (latlngs) {
 
8497                 Polyline.prototype._setLatLngs.call(this, latlngs);
 
8498                 if (isFlat(this._latlngs)) {
 
8499                         this._latlngs = [this._latlngs];
 
8503         _defaultShape: function () {
 
8504                 return isFlat(this._latlngs[0]) ? this._latlngs[0] : this._latlngs[0][0];
 
8507         _clipPoints: function () {
 
8508                 // polygons need a different clipping algorithm so we redefine that
 
8510                 var bounds = this._renderer._bounds,
 
8511                     w = this.options.weight,
 
8512                     p = new Point(w, w);
 
8514                 // increase clip padding by stroke width to avoid stroke on clip edges
 
8515                 bounds = new Bounds(bounds.min.subtract(p), bounds.max.add(p));
 
8518                 if (!this._pxBounds || !this._pxBounds.intersects(bounds)) {
 
8522                 if (this.options.noClip) {
 
8523                         this._parts = this._rings;
 
8527                 for (var i = 0, len = this._rings.length, clipped; i < len; i++) {
 
8528                         clipped = clipPolygon(this._rings[i], bounds, true);
 
8529                         if (clipped.length) {
 
8530                                 this._parts.push(clipped);
 
8535         _updatePath: function () {
 
8536                 this._renderer._updatePoly(this, true);
 
8539         // Needed by the `Canvas` renderer for interactivity
 
8540         _containsPoint: function (p) {
 
8542                     part, p1, p2, i, j, k, len, len2;
 
8544                 if (!this._pxBounds || !this._pxBounds.contains(p)) { return false; }
 
8546                 // ray casting algorithm for detecting if point is in polygon
 
8547                 for (i = 0, len = this._parts.length; i < len; i++) {
 
8548                         part = this._parts[i];
 
8550                         for (j = 0, len2 = part.length, k = len2 - 1; j < len2; k = j++) {
 
8554                                 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)) {
 
8560                 // also check if it's on polygon stroke
 
8561                 return inside || Polyline.prototype._containsPoint.call(this, p, true);
 
8567 // @factory L.polygon(latlngs: LatLng[], options?: Polyline options)
 
8568 function polygon(latlngs, options) {
 
8569         return new Polygon(latlngs, options);
 
8575  * @inherits FeatureGroup
 
8577  * Represents a GeoJSON object or an array of GeoJSON objects. Allows you to parse
 
8578  * GeoJSON data and display it on the map. Extends `FeatureGroup`.
 
8584  *      style: function (feature) {
 
8585  *              return {color: feature.properties.color};
 
8587  * }).bindPopup(function (layer) {
 
8588  *      return layer.feature.properties.description;
 
8593 var GeoJSON = FeatureGroup.extend({
 
8596          * @aka GeoJSON options
 
8598          * @option pointToLayer: Function = *
 
8599          * A `Function` defining how GeoJSON points spawn Leaflet layers. It is internally
 
8600          * called when data is added, passing the GeoJSON point feature and its `LatLng`.
 
8601          * The default is to spawn a default `Marker`:
 
8603          * function(geoJsonPoint, latlng) {
 
8604          *      return L.marker(latlng);
 
8608          * @option style: Function = *
 
8609          * A `Function` defining the `Path options` for styling GeoJSON lines and polygons,
 
8610          * called internally when data is added.
 
8611          * The default value is to not override any defaults:
 
8613          * function (geoJsonFeature) {
 
8618          * @option onEachFeature: Function = *
 
8619          * A `Function` that will be called once for each created `Feature`, after it has
 
8620          * been created and styled. Useful for attaching events and popups to features.
 
8621          * The default is to do nothing with the newly created layers:
 
8623          * function (feature, layer) {}
 
8626          * @option filter: Function = *
 
8627          * A `Function` that will be used to decide whether to include a feature or not.
 
8628          * The default is to include all features:
 
8630          * function (geoJsonFeature) {
 
8634          * Note: dynamically changing the `filter` option will have effect only on newly
 
8635          * added data. It will _not_ re-evaluate already included features.
 
8637          * @option coordsToLatLng: Function = *
 
8638          * A `Function` that will be used for converting GeoJSON coordinates to `LatLng`s.
 
8639          * The default is the `coordsToLatLng` static method.
 
8642         initialize: function (geojson, options) {
 
8643                 setOptions(this, options);
 
8648                         this.addData(geojson);
 
8652         // @method addData( <GeoJSON> data ): this
 
8653         // Adds a GeoJSON object to the layer.
 
8654         addData: function (geojson) {
 
8655                 var features = isArray(geojson) ? geojson : geojson.features,
 
8659                         for (i = 0, len = features.length; i < len; i++) {
 
8660                                 // only add this if geometry or geometries are set and not null
 
8661                                 feature = features[i];
 
8662                                 if (feature.geometries || feature.geometry || feature.features || feature.coordinates) {
 
8663                                         this.addData(feature);
 
8669                 var options = this.options;
 
8671                 if (options.filter && !options.filter(geojson)) { return this; }
 
8673                 var layer = geometryToLayer(geojson, options);
 
8677                 layer.feature = asFeature(geojson);
 
8679                 layer.defaultOptions = layer.options;
 
8680                 this.resetStyle(layer);
 
8682                 if (options.onEachFeature) {
 
8683                         options.onEachFeature(geojson, layer);
 
8686                 return this.addLayer(layer);
 
8689         // @method resetStyle( <Path> layer ): this
 
8690         // Resets the given vector layer's style to the original GeoJSON style, useful for resetting style after hover events.
 
8691         resetStyle: function (layer) {
 
8692                 // reset any custom styles
 
8693                 layer.options = extend({}, layer.defaultOptions);
 
8694                 this._setLayerStyle(layer, this.options.style);
 
8698         // @method setStyle( <Function> style ): this
 
8699         // Changes styles of GeoJSON vector layers with the given style function.
 
8700         setStyle: function (style) {
 
8701                 return this.eachLayer(function (layer) {
 
8702                         this._setLayerStyle(layer, style);
 
8706         _setLayerStyle: function (layer, style) {
 
8707                 if (typeof style === 'function') {
 
8708                         style = style(layer.feature);
 
8710                 if (layer.setStyle) {
 
8711                         layer.setStyle(style);
 
8717 // There are several static functions which can be called without instantiating L.GeoJSON:
 
8719 // @function geometryToLayer(featureData: Object, options?: GeoJSON options): Layer
 
8720 // Creates a `Layer` from a given GeoJSON feature. Can use a custom
 
8721 // [`pointToLayer`](#geojson-pointtolayer) and/or [`coordsToLatLng`](#geojson-coordstolatlng)
 
8722 // functions if provided as options.
 
8723 function geometryToLayer(geojson, options) {
 
8725         var geometry = geojson.type === 'Feature' ? geojson.geometry : geojson,
 
8726             coords = geometry ? geometry.coordinates : null,
 
8728             pointToLayer = options && options.pointToLayer,
 
8729             _coordsToLatLng = options && options.coordsToLatLng || coordsToLatLng,
 
8730             latlng, latlngs, i, len;
 
8732         if (!coords && !geometry) {
 
8736         switch (geometry.type) {
 
8738                 latlng = _coordsToLatLng(coords);
 
8739                 return pointToLayer ? pointToLayer(geojson, latlng) : new Marker(latlng);
 
8742                 for (i = 0, len = coords.length; i < len; i++) {
 
8743                         latlng = _coordsToLatLng(coords[i]);
 
8744                         layers.push(pointToLayer ? pointToLayer(geojson, latlng) : new Marker(latlng));
 
8746                 return new FeatureGroup(layers);
 
8749         case 'MultiLineString':
 
8750                 latlngs = coordsToLatLngs(coords, geometry.type === 'LineString' ? 0 : 1, _coordsToLatLng);
 
8751                 return new Polyline(latlngs, options);
 
8754         case 'MultiPolygon':
 
8755                 latlngs = coordsToLatLngs(coords, geometry.type === 'Polygon' ? 1 : 2, _coordsToLatLng);
 
8756                 return new Polygon(latlngs, options);
 
8758         case 'GeometryCollection':
 
8759                 for (i = 0, len = geometry.geometries.length; i < len; i++) {
 
8760                         var layer = geometryToLayer({
 
8761                                 geometry: geometry.geometries[i],
 
8763                                 properties: geojson.properties
 
8770                 return new FeatureGroup(layers);
 
8773                 throw new Error('Invalid GeoJSON object.');
 
8777 // @function coordsToLatLng(coords: Array): LatLng
 
8778 // Creates a `LatLng` object from an array of 2 numbers (longitude, latitude)
 
8779 // or 3 numbers (longitude, latitude, altitude) used in GeoJSON for points.
 
8780 function coordsToLatLng(coords) {
 
8781         return new LatLng(coords[1], coords[0], coords[2]);
 
8784 // @function coordsToLatLngs(coords: Array, levelsDeep?: Number, coordsToLatLng?: Function): Array
 
8785 // Creates a multidimensional array of `LatLng`s from a GeoJSON coordinates array.
 
8786 // `levelsDeep` specifies the nesting level (0 is for an array of points, 1 for an array of arrays of points, etc., 0 by default).
 
8787 // Can use a custom [`coordsToLatLng`](#geojson-coordstolatlng) function.
 
8788 function coordsToLatLngs(coords, levelsDeep, _coordsToLatLng) {
 
8791         for (var i = 0, len = coords.length, latlng; i < len; i++) {
 
8792                 latlng = levelsDeep ?
 
8793                         coordsToLatLngs(coords[i], levelsDeep - 1, _coordsToLatLng) :
 
8794                         (_coordsToLatLng || coordsToLatLng)(coords[i]);
 
8796                 latlngs.push(latlng);
 
8802 // @function latLngToCoords(latlng: LatLng, precision?: Number): Array
 
8803 // Reverse of [`coordsToLatLng`](#geojson-coordstolatlng)
 
8804 function latLngToCoords(latlng, precision) {
 
8805         precision = typeof precision === 'number' ? precision : 6;
 
8806         return latlng.alt !== undefined ?
 
8807                 [formatNum(latlng.lng, precision), formatNum(latlng.lat, precision), formatNum(latlng.alt, precision)] :
 
8808                 [formatNum(latlng.lng, precision), formatNum(latlng.lat, precision)];
 
8811 // @function latLngsToCoords(latlngs: Array, levelsDeep?: Number, closed?: Boolean): Array
 
8812 // Reverse of [`coordsToLatLngs`](#geojson-coordstolatlngs)
 
8813 // `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.
 
8814 function latLngsToCoords(latlngs, levelsDeep, closed, precision) {
 
8817         for (var i = 0, len = latlngs.length; i < len; i++) {
 
8818                 coords.push(levelsDeep ?
 
8819                         latLngsToCoords(latlngs[i], levelsDeep - 1, closed, precision) :
 
8820                         latLngToCoords(latlngs[i], precision));
 
8823         if (!levelsDeep && closed) {
 
8824                 coords.push(coords[0]);
 
8830 function getFeature(layer, newGeometry) {
 
8831         return layer.feature ?
 
8832                 extend({}, layer.feature, {geometry: newGeometry}) :
 
8833                 asFeature(newGeometry);
 
8836 // @function asFeature(geojson: Object): Object
 
8837 // Normalize GeoJSON geometries/features into GeoJSON features.
 
8838 function asFeature(geojson) {
 
8839         if (geojson.type === 'Feature' || geojson.type === 'FeatureCollection') {
 
8850 var PointToGeoJSON = {
 
8851         toGeoJSON: function (precision) {
 
8852                 return getFeature(this, {
 
8854                         coordinates: latLngToCoords(this.getLatLng(), precision)
 
8859 // @namespace Marker
 
8860 // @method toGeoJSON(): Object
 
8861 // Returns a [`GeoJSON`](http://en.wikipedia.org/wiki/GeoJSON) representation of the marker (as a GeoJSON `Point` Feature).
 
8862 Marker.include(PointToGeoJSON);
 
8864 // @namespace CircleMarker
 
8865 // @method toGeoJSON(): Object
 
8866 // Returns a [`GeoJSON`](http://en.wikipedia.org/wiki/GeoJSON) representation of the circle marker (as a GeoJSON `Point` Feature).
 
8867 Circle.include(PointToGeoJSON);
 
8868 CircleMarker.include(PointToGeoJSON);
 
8871 // @namespace Polyline
 
8872 // @method toGeoJSON(): Object
 
8873 // Returns a [`GeoJSON`](http://en.wikipedia.org/wiki/GeoJSON) representation of the polyline (as a GeoJSON `LineString` or `MultiLineString` Feature).
 
8875         toGeoJSON: function (precision) {
 
8876                 var multi = !isFlat(this._latlngs);
 
8878                 var coords = latLngsToCoords(this._latlngs, multi ? 1 : 0, false, precision);
 
8880                 return getFeature(this, {
 
8881                         type: (multi ? 'Multi' : '') + 'LineString',
 
8887 // @namespace Polygon
 
8888 // @method toGeoJSON(): Object
 
8889 // Returns a [`GeoJSON`](http://en.wikipedia.org/wiki/GeoJSON) representation of the polygon (as a GeoJSON `Polygon` or `MultiPolygon` Feature).
 
8891         toGeoJSON: function (precision) {
 
8892                 var holes = !isFlat(this._latlngs),
 
8893                     multi = holes && !isFlat(this._latlngs[0]);
 
8895                 var coords = latLngsToCoords(this._latlngs, multi ? 2 : holes ? 1 : 0, true, precision);
 
8901                 return getFeature(this, {
 
8902                         type: (multi ? 'Multi' : '') + 'Polygon',
 
8909 // @namespace LayerGroup
 
8910 LayerGroup.include({
 
8911         toMultiPoint: function (precision) {
 
8914                 this.eachLayer(function (layer) {
 
8915                         coords.push(layer.toGeoJSON(precision).geometry.coordinates);
 
8918                 return getFeature(this, {
 
8924         // @method toGeoJSON(): Object
 
8925         // Returns a [`GeoJSON`](http://en.wikipedia.org/wiki/GeoJSON) representation of the layer group (as a GeoJSON `FeatureCollection`, `GeometryCollection`, or `MultiPoint`).
 
8926         toGeoJSON: function (precision) {
 
8928                 var type = this.feature && this.feature.geometry && this.feature.geometry.type;
 
8930                 if (type === 'MultiPoint') {
 
8931                         return this.toMultiPoint(precision);
 
8934                 var isGeometryCollection = type === 'GeometryCollection',
 
8937                 this.eachLayer(function (layer) {
 
8938                         if (layer.toGeoJSON) {
 
8939                                 var json = layer.toGeoJSON(precision);
 
8940                                 if (isGeometryCollection) {
 
8941                                         jsons.push(json.geometry);
 
8943                                         var feature = asFeature(json);
 
8944                                         // Squash nested feature collections
 
8945                                         if (feature.type === 'FeatureCollection') {
 
8946                                                 jsons.push.apply(jsons, feature.features);
 
8948                                                 jsons.push(feature);
 
8954                 if (isGeometryCollection) {
 
8955                         return getFeature(this, {
 
8957                                 type: 'GeometryCollection'
 
8962                         type: 'FeatureCollection',
 
8968 // @namespace GeoJSON
 
8969 // @factory L.geoJSON(geojson?: Object, options?: GeoJSON options)
 
8970 // Creates a GeoJSON layer. Optionally accepts an object in
 
8971 // [GeoJSON format](https://tools.ietf.org/html/rfc7946) to display on the map
 
8972 // (you can alternatively add it later with `addData` method) and an `options` object.
 
8973 function geoJSON(geojson, options) {
 
8974         return new GeoJSON(geojson, options);
 
8977 // Backward compatibility.
 
8978 var geoJson = geoJSON;
 
8981  * @class ImageOverlay
 
8982  * @aka L.ImageOverlay
 
8983  * @inherits Interactive layer
 
8985  * Used to load and display a single image over specific bounds of the map. Extends `Layer`.
 
8990  * var imageUrl = 'http://www.lib.utexas.edu/maps/historical/newark_nj_1922.jpg',
 
8991  *      imageBounds = [[40.712216, -74.22655], [40.773941, -74.12544]];
 
8992  * L.imageOverlay(imageUrl, imageBounds).addTo(map);
 
8996 var ImageOverlay = Layer.extend({
 
8999         // @aka ImageOverlay options
 
9001                 // @option opacity: Number = 1.0
 
9002                 // The opacity of the image overlay.
 
9005                 // @option alt: String = ''
 
9006                 // Text for the `alt` attribute of the image (useful for accessibility).
 
9009                 // @option interactive: Boolean = false
 
9010                 // If `true`, the image overlay will emit [mouse events](#interactive-layer) when clicked or hovered.
 
9013                 // @option crossOrigin: Boolean|String = false
 
9014                 // Whether the crossOrigin attribute will be added to the image.
 
9015                 // 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.
 
9016                 // Refer to [CORS Settings](https://developer.mozilla.org/en-US/docs/Web/HTML/CORS_settings_attributes) for valid String values.
 
9019                 // @option errorOverlayUrl: String = ''
 
9020                 // URL to the overlay image to show in place of the overlay that failed to load.
 
9021                 errorOverlayUrl: '',
 
9023                 // @option zIndex: Number = 1
 
9024                 // The explicit [zIndex](https://developer.mozilla.org/docs/Web/CSS/CSS_Positioning/Understanding_z_index) of the overlay layer.
 
9027                 // @option className: String = ''
 
9028                 // A custom class name to assign to the image. Empty by default.
 
9032         initialize: function (url, bounds, options) { // (String, LatLngBounds, Object)
 
9034                 this._bounds = toLatLngBounds(bounds);
 
9036                 setOptions(this, options);
 
9039         onAdd: function () {
 
9043                         if (this.options.opacity < 1) {
 
9044                                 this._updateOpacity();
 
9048                 if (this.options.interactive) {
 
9049                         addClass(this._image, 'leaflet-interactive');
 
9050                         this.addInteractiveTarget(this._image);
 
9053                 this.getPane().appendChild(this._image);
 
9057         onRemove: function () {
 
9058                 remove(this._image);
 
9059                 if (this.options.interactive) {
 
9060                         this.removeInteractiveTarget(this._image);
 
9064         // @method setOpacity(opacity: Number): this
 
9065         // Sets the opacity of the overlay.
 
9066         setOpacity: function (opacity) {
 
9067                 this.options.opacity = opacity;
 
9070                         this._updateOpacity();
 
9075         setStyle: function (styleOpts) {
 
9076                 if (styleOpts.opacity) {
 
9077                         this.setOpacity(styleOpts.opacity);
 
9082         // @method bringToFront(): this
 
9083         // Brings the layer to the top of all overlays.
 
9084         bringToFront: function () {
 
9086                         toFront(this._image);
 
9091         // @method bringToBack(): this
 
9092         // Brings the layer to the bottom of all overlays.
 
9093         bringToBack: function () {
 
9095                         toBack(this._image);
 
9100         // @method setUrl(url: String): this
 
9101         // Changes the URL of the image.
 
9102         setUrl: function (url) {
 
9106                         this._image.src = url;
 
9111         // @method setBounds(bounds: LatLngBounds): this
 
9112         // Update the bounds that this ImageOverlay covers
 
9113         setBounds: function (bounds) {
 
9114                 this._bounds = toLatLngBounds(bounds);
 
9122         getEvents: function () {
 
9125                         viewreset: this._reset
 
9128                 if (this._zoomAnimated) {
 
9129                         events.zoomanim = this._animateZoom;
 
9135         // @method setZIndex(value: Number): this
 
9136         // Changes the [zIndex](#imageoverlay-zindex) of the image overlay.
 
9137         setZIndex: function (value) {
 
9138                 this.options.zIndex = value;
 
9139                 this._updateZIndex();
 
9143         // @method getBounds(): LatLngBounds
 
9144         // Get the bounds that this ImageOverlay covers
 
9145         getBounds: function () {
 
9146                 return this._bounds;
 
9149         // @method getElement(): HTMLElement
 
9150         // Returns the instance of [`HTMLImageElement`](https://developer.mozilla.org/docs/Web/API/HTMLImageElement)
 
9151         // used by this overlay.
 
9152         getElement: function () {
 
9156         _initImage: function () {
 
9157                 var wasElementSupplied = this._url.tagName === 'IMG';
 
9158                 var img = this._image = wasElementSupplied ? this._url : create$1('img');
 
9160                 addClass(img, 'leaflet-image-layer');
 
9161                 if (this._zoomAnimated) { addClass(img, 'leaflet-zoom-animated'); }
 
9162                 if (this.options.className) { addClass(img, this.options.className); }
 
9164                 img.onselectstart = falseFn;
 
9165                 img.onmousemove = falseFn;
 
9167                 // @event load: Event
 
9168                 // Fired when the ImageOverlay layer has loaded its image
 
9169                 img.onload = bind(this.fire, this, 'load');
 
9170                 img.onerror = bind(this._overlayOnError, this, 'error');
 
9172                 if (this.options.crossOrigin || this.options.crossOrigin === '') {
 
9173                         img.crossOrigin = this.options.crossOrigin === true ? '' : this.options.crossOrigin;
 
9176                 if (this.options.zIndex) {
 
9177                         this._updateZIndex();
 
9180                 if (wasElementSupplied) {
 
9181                         this._url = img.src;
 
9185                 img.src = this._url;
 
9186                 img.alt = this.options.alt;
 
9189         _animateZoom: function (e) {
 
9190                 var scale = this._map.getZoomScale(e.zoom),
 
9191                     offset = this._map._latLngBoundsToNewLayerBounds(this._bounds, e.zoom, e.center).min;
 
9193                 setTransform(this._image, offset, scale);
 
9196         _reset: function () {
 
9197                 var image = this._image,
 
9198                     bounds = new Bounds(
 
9199                         this._map.latLngToLayerPoint(this._bounds.getNorthWest()),
 
9200                         this._map.latLngToLayerPoint(this._bounds.getSouthEast())),
 
9201                     size = bounds.getSize();
 
9203                 setPosition(image, bounds.min);
 
9205                 image.style.width  = size.x + 'px';
 
9206                 image.style.height = size.y + 'px';
 
9209         _updateOpacity: function () {
 
9210                 setOpacity(this._image, this.options.opacity);
 
9213         _updateZIndex: function () {
 
9214                 if (this._image && this.options.zIndex !== undefined && this.options.zIndex !== null) {
 
9215                         this._image.style.zIndex = this.options.zIndex;
 
9219         _overlayOnError: function () {
 
9220                 // @event error: Event
 
9221                 // Fired when the ImageOverlay layer fails to load its image
 
9224                 var errorUrl = this.options.errorOverlayUrl;
 
9225                 if (errorUrl && this._url !== errorUrl) {
 
9226                         this._url = errorUrl;
 
9227                         this._image.src = errorUrl;
 
9232 // @factory L.imageOverlay(imageUrl: String, bounds: LatLngBounds, options?: ImageOverlay options)
 
9233 // Instantiates an image overlay object given the URL of the image and the
 
9234 // geographical bounds it is tied to.
 
9235 var imageOverlay = function (url, bounds, options) {
 
9236         return new ImageOverlay(url, bounds, options);
 
9240  * @class VideoOverlay
 
9241  * @aka L.VideoOverlay
 
9242  * @inherits ImageOverlay
 
9244  * Used to load and display a video player over specific bounds of the map. Extends `ImageOverlay`.
 
9246  * A video overlay uses the [`<video>`](https://developer.mozilla.org/docs/Web/HTML/Element/video)
 
9252  * var videoUrl = 'https://www.mapbox.com/bites/00188/patricia_nasa.webm',
 
9253  *      videoBounds = [[ 32, -130], [ 13, -100]];
 
9254  * L.videoOverlay(videoUrl, videoBounds ).addTo(map);
 
9258 var VideoOverlay = ImageOverlay.extend({
 
9261         // @aka VideoOverlay options
 
9263                 // @option autoplay: Boolean = true
 
9264                 // Whether the video starts playing automatically when loaded.
 
9267                 // @option loop: Boolean = true
 
9268                 // Whether the video will loop back to the beginning when played.
 
9272         _initImage: function () {
 
9273                 var wasElementSupplied = this._url.tagName === 'VIDEO';
 
9274                 var vid = this._image = wasElementSupplied ? this._url : create$1('video');
 
9276                 addClass(vid, 'leaflet-image-layer');
 
9277                 if (this._zoomAnimated) { addClass(vid, 'leaflet-zoom-animated'); }
 
9279                 vid.onselectstart = falseFn;
 
9280                 vid.onmousemove = falseFn;
 
9282                 // @event load: Event
 
9283                 // Fired when the video has finished loading the first frame
 
9284                 vid.onloadeddata = bind(this.fire, this, 'load');
 
9286                 if (wasElementSupplied) {
 
9287                         var sourceElements = vid.getElementsByTagName('source');
 
9289                         for (var j = 0; j < sourceElements.length; j++) {
 
9290                                 sources.push(sourceElements[j].src);
 
9293                         this._url = (sourceElements.length > 0) ? sources : [vid.src];
 
9297                 if (!isArray(this._url)) { this._url = [this._url]; }
 
9299                 vid.autoplay = !!this.options.autoplay;
 
9300                 vid.loop = !!this.options.loop;
 
9301                 for (var i = 0; i < this._url.length; i++) {
 
9302                         var source = create$1('source');
 
9303                         source.src = this._url[i];
 
9304                         vid.appendChild(source);
 
9308         // @method getElement(): HTMLVideoElement
 
9309         // Returns the instance of [`HTMLVideoElement`](https://developer.mozilla.org/docs/Web/API/HTMLVideoElement)
 
9310         // used by this overlay.
 
9314 // @factory L.videoOverlay(video: String|Array|HTMLVideoElement, bounds: LatLngBounds, options?: VideoOverlay options)
 
9315 // Instantiates an image overlay object given the URL of the video (or array of URLs, or even a video element) and the
 
9316 // geographical bounds it is tied to.
 
9318 function videoOverlay(video, bounds, options) {
 
9319         return new VideoOverlay(video, bounds, options);
 
9326  * Base model for L.Popup and L.Tooltip. Inherit from it for custom popup like plugins.
 
9329 // @namespace DivOverlay
 
9330 var DivOverlay = Layer.extend({
 
9333         // @aka DivOverlay options
 
9335                 // @option offset: Point = Point(0, 7)
 
9336                 // The offset of the popup position. Useful to control the anchor
 
9337                 // of the popup when opening it on some overlays.
 
9340                 // @option className: String = ''
 
9341                 // A custom CSS class name to assign to the popup.
 
9344                 // @option pane: String = 'popupPane'
 
9345                 // `Map pane` where the popup will be added.
 
9349         initialize: function (options, source) {
 
9350                 setOptions(this, options);
 
9352                 this._source = source;
 
9355         onAdd: function (map) {
 
9356                 this._zoomAnimated = map._zoomAnimated;
 
9358                 if (!this._container) {
 
9362                 if (map._fadeAnimated) {
 
9363                         setOpacity(this._container, 0);
 
9366                 clearTimeout(this._removeTimeout);
 
9367                 this.getPane().appendChild(this._container);
 
9370                 if (map._fadeAnimated) {
 
9371                         setOpacity(this._container, 1);
 
9374                 this.bringToFront();
 
9377         onRemove: function (map) {
 
9378                 if (map._fadeAnimated) {
 
9379                         setOpacity(this._container, 0);
 
9380                         this._removeTimeout = setTimeout(bind(remove, undefined, this._container), 200);
 
9382                         remove(this._container);
 
9387         // @method getLatLng: LatLng
 
9388         // Returns the geographical point of popup.
 
9389         getLatLng: function () {
 
9390                 return this._latlng;
 
9393         // @method setLatLng(latlng: LatLng): this
 
9394         // Sets the geographical point where the popup will open.
 
9395         setLatLng: function (latlng) {
 
9396                 this._latlng = toLatLng(latlng);
 
9398                         this._updatePosition();
 
9404         // @method getContent: String|HTMLElement
 
9405         // Returns the content of the popup.
 
9406         getContent: function () {
 
9407                 return this._content;
 
9410         // @method setContent(htmlContent: String|HTMLElement|Function): this
 
9411         // 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.
 
9412         setContent: function (content) {
 
9413                 this._content = content;
 
9418         // @method getElement: String|HTMLElement
 
9419         // Alias for [getContent()](#popup-getcontent)
 
9420         getElement: function () {
 
9421                 return this._container;
 
9424         // @method update: null
 
9425         // Updates the popup content, layout and position. Useful for updating the popup after something inside changed, e.g. image loaded.
 
9426         update: function () {
 
9427                 if (!this._map) { return; }
 
9429                 this._container.style.visibility = 'hidden';
 
9431                 this._updateContent();
 
9432                 this._updateLayout();
 
9433                 this._updatePosition();
 
9435                 this._container.style.visibility = '';
 
9440         getEvents: function () {
 
9442                         zoom: this._updatePosition,
 
9443                         viewreset: this._updatePosition
 
9446                 if (this._zoomAnimated) {
 
9447                         events.zoomanim = this._animateZoom;
 
9452         // @method isOpen: Boolean
 
9453         // Returns `true` when the popup is visible on the map.
 
9454         isOpen: function () {
 
9455                 return !!this._map && this._map.hasLayer(this);
 
9458         // @method bringToFront: this
 
9459         // Brings this popup in front of other popups (in the same map pane).
 
9460         bringToFront: function () {
 
9462                         toFront(this._container);
 
9467         // @method bringToBack: this
 
9468         // Brings this popup to the back of other popups (in the same map pane).
 
9469         bringToBack: function () {
 
9471                         toBack(this._container);
 
9476         _updateContent: function () {
 
9477                 if (!this._content) { return; }
 
9479                 var node = this._contentNode;
 
9480                 var content = (typeof this._content === 'function') ? this._content(this._source || this) : this._content;
 
9482                 if (typeof content === 'string') {
 
9483                         node.innerHTML = content;
 
9485                         while (node.hasChildNodes()) {
 
9486                                 node.removeChild(node.firstChild);
 
9488                         node.appendChild(content);
 
9490                 this.fire('contentupdate');
 
9493         _updatePosition: function () {
 
9494                 if (!this._map) { return; }
 
9496                 var pos = this._map.latLngToLayerPoint(this._latlng),
 
9497                     offset = toPoint(this.options.offset),
 
9498                     anchor = this._getAnchor();
 
9500                 if (this._zoomAnimated) {
 
9501                         setPosition(this._container, pos.add(anchor));
 
9503                         offset = offset.add(pos).add(anchor);
 
9506                 var bottom = this._containerBottom = -offset.y,
 
9507                     left = this._containerLeft = -Math.round(this._containerWidth / 2) + offset.x;
 
9509                 // bottom position the popup in case the height of the popup changes (images loading etc)
 
9510                 this._container.style.bottom = bottom + 'px';
 
9511                 this._container.style.left = left + 'px';
 
9514         _getAnchor: function () {
 
9522  * @inherits DivOverlay
 
9524  * Used to open popups in certain places of the map. Use [Map.openPopup](#map-openpopup) to
 
9525  * open popups while making sure that only one popup is open at one time
 
9526  * (recommended for usability), or use [Map.addLayer](#map-addlayer) to open as many as you want.
 
9530  * If you want to just bind a popup to marker click and then open it, it's really easy:
 
9533  * marker.bindPopup(popupContent).openPopup();
 
9535  * Path overlays like polylines also have a `bindPopup` method.
 
9536  * Here's a more complicated way to open a popup on a map:
 
9539  * var popup = L.popup()
 
9540  *      .setLatLng(latlng)
 
9541  *      .setContent('<p>Hello world!<br />This is a nice popup.</p>')
 
9548 var Popup = DivOverlay.extend({
 
9551         // @aka Popup options
 
9553                 // @option maxWidth: Number = 300
 
9554                 // Max width of the popup, in pixels.
 
9557                 // @option minWidth: Number = 50
 
9558                 // Min width of the popup, in pixels.
 
9561                 // @option maxHeight: Number = null
 
9562                 // If set, creates a scrollable container of the given height
 
9563                 // inside a popup if its content exceeds it.
 
9566                 // @option autoPan: Boolean = true
 
9567                 // Set it to `false` if you don't want the map to do panning animation
 
9568                 // to fit the opened popup.
 
9571                 // @option autoPanPaddingTopLeft: Point = null
 
9572                 // The margin between the popup and the top left corner of the map
 
9573                 // view after autopanning was performed.
 
9574                 autoPanPaddingTopLeft: null,
 
9576                 // @option autoPanPaddingBottomRight: Point = null
 
9577                 // The margin between the popup and the bottom right corner of the map
 
9578                 // view after autopanning was performed.
 
9579                 autoPanPaddingBottomRight: null,
 
9581                 // @option autoPanPadding: Point = Point(5, 5)
 
9582                 // Equivalent of setting both top left and bottom right autopan padding to the same value.
 
9583                 autoPanPadding: [5, 5],
 
9585                 // @option keepInView: Boolean = false
 
9586                 // Set it to `true` if you want to prevent users from panning the popup
 
9587                 // off of the screen while it is open.
 
9590                 // @option closeButton: Boolean = true
 
9591                 // Controls the presence of a close button in the popup.
 
9594                 // @option autoClose: Boolean = true
 
9595                 // Set it to `false` if you want to override the default behavior of
 
9596                 // the popup closing when another popup is opened.
 
9599                 // @option closeOnEscapeKey: Boolean = true
 
9600                 // Set it to `false` if you want to override the default behavior of
 
9601                 // the ESC key for closing of the popup.
 
9602                 closeOnEscapeKey: true,
 
9604                 // @option closeOnClick: Boolean = *
 
9605                 // Set it if you want to override the default behavior of the popup closing when user clicks
 
9606                 // on the map. Defaults to the map's [`closePopupOnClick`](#map-closepopuponclick) option.
 
9608                 // @option className: String = ''
 
9609                 // A custom CSS class name to assign to the popup.
 
9614         // @method openOn(map: Map): this
 
9615         // Adds the popup to the map and closes the previous one. The same as `map.openPopup(popup)`.
 
9616         openOn: function (map) {
 
9617                 map.openPopup(this);
 
9621         onAdd: function (map) {
 
9622                 DivOverlay.prototype.onAdd.call(this, map);
 
9625                 // @section Popup events
 
9626                 // @event popupopen: PopupEvent
 
9627                 // Fired when a popup is opened in the map
 
9628                 map.fire('popupopen', {popup: this});
 
9632                         // @section Popup events
 
9633                         // @event popupopen: PopupEvent
 
9634                         // Fired when a popup bound to this layer is opened
 
9635                         this._source.fire('popupopen', {popup: this}, true);
 
9636                         // For non-path layers, we toggle the popup when clicking
 
9637                         // again the layer, so prevent the map to reopen it.
 
9638                         if (!(this._source instanceof Path)) {
 
9639                                 this._source.on('preclick', stopPropagation);
 
9644         onRemove: function (map) {
 
9645                 DivOverlay.prototype.onRemove.call(this, map);
 
9648                 // @section Popup events
 
9649                 // @event popupclose: PopupEvent
 
9650                 // Fired when a popup in the map is closed
 
9651                 map.fire('popupclose', {popup: this});
 
9655                         // @section Popup events
 
9656                         // @event popupclose: PopupEvent
 
9657                         // Fired when a popup bound to this layer is closed
 
9658                         this._source.fire('popupclose', {popup: this}, true);
 
9659                         if (!(this._source instanceof Path)) {
 
9660                                 this._source.off('preclick', stopPropagation);
 
9665         getEvents: function () {
 
9666                 var events = DivOverlay.prototype.getEvents.call(this);
 
9668                 if (this.options.closeOnClick !== undefined ? this.options.closeOnClick : this._map.options.closePopupOnClick) {
 
9669                         events.preclick = this._close;
 
9672                 if (this.options.keepInView) {
 
9673                         events.moveend = this._adjustPan;
 
9679         _close: function () {
 
9681                         this._map.closePopup(this);
 
9685         _initLayout: function () {
 
9686                 var prefix = 'leaflet-popup',
 
9687                     container = this._container = create$1('div',
 
9688                         prefix + ' ' + (this.options.className || '') +
 
9689                         ' leaflet-zoom-animated');
 
9691                 var wrapper = this._wrapper = create$1('div', prefix + '-content-wrapper', container);
 
9692                 this._contentNode = create$1('div', prefix + '-content', wrapper);
 
9694                 disableClickPropagation(wrapper);
 
9695                 disableScrollPropagation(this._contentNode);
 
9696                 on(wrapper, 'contextmenu', stopPropagation);
 
9698                 this._tipContainer = create$1('div', prefix + '-tip-container', container);
 
9699                 this._tip = create$1('div', prefix + '-tip', this._tipContainer);
 
9701                 if (this.options.closeButton) {
 
9702                         var closeButton = this._closeButton = create$1('a', prefix + '-close-button', container);
 
9703                         closeButton.href = '#close';
 
9704                         closeButton.innerHTML = '×';
 
9706                         on(closeButton, 'click', this._onCloseButtonClick, this);
 
9710         _updateLayout: function () {
 
9711                 var container = this._contentNode,
 
9712                     style = container.style;
 
9715                 style.whiteSpace = 'nowrap';
 
9717                 var width = container.offsetWidth;
 
9718                 width = Math.min(width, this.options.maxWidth);
 
9719                 width = Math.max(width, this.options.minWidth);
 
9721                 style.width = (width + 1) + 'px';
 
9722                 style.whiteSpace = '';
 
9726                 var height = container.offsetHeight,
 
9727                     maxHeight = this.options.maxHeight,
 
9728                     scrolledClass = 'leaflet-popup-scrolled';
 
9730                 if (maxHeight && height > maxHeight) {
 
9731                         style.height = maxHeight + 'px';
 
9732                         addClass(container, scrolledClass);
 
9734                         removeClass(container, scrolledClass);
 
9737                 this._containerWidth = this._container.offsetWidth;
 
9740         _animateZoom: function (e) {
 
9741                 var pos = this._map._latLngToNewLayerPoint(this._latlng, e.zoom, e.center),
 
9742                     anchor = this._getAnchor();
 
9743                 setPosition(this._container, pos.add(anchor));
 
9746         _adjustPan: function () {
 
9747                 if (!this.options.autoPan) { return; }
 
9748                 if (this._map._panAnim) { this._map._panAnim.stop(); }
 
9750                 var map = this._map,
 
9751                     marginBottom = parseInt(getStyle(this._container, 'marginBottom'), 10) || 0,
 
9752                     containerHeight = this._container.offsetHeight + marginBottom,
 
9753                     containerWidth = this._containerWidth,
 
9754                     layerPos = new Point(this._containerLeft, -containerHeight - this._containerBottom);
 
9756                 layerPos._add(getPosition(this._container));
 
9758                 var containerPos = map.layerPointToContainerPoint(layerPos),
 
9759                     padding = toPoint(this.options.autoPanPadding),
 
9760                     paddingTL = toPoint(this.options.autoPanPaddingTopLeft || padding),
 
9761                     paddingBR = toPoint(this.options.autoPanPaddingBottomRight || padding),
 
9762                     size = map.getSize(),
 
9766                 if (containerPos.x + containerWidth + paddingBR.x > size.x) { // right
 
9767                         dx = containerPos.x + containerWidth - size.x + paddingBR.x;
 
9769                 if (containerPos.x - dx - paddingTL.x < 0) { // left
 
9770                         dx = containerPos.x - paddingTL.x;
 
9772                 if (containerPos.y + containerHeight + paddingBR.y > size.y) { // bottom
 
9773                         dy = containerPos.y + containerHeight - size.y + paddingBR.y;
 
9775                 if (containerPos.y - dy - paddingTL.y < 0) { // top
 
9776                         dy = containerPos.y - paddingTL.y;
 
9780                 // @section Popup events
 
9781                 // @event autopanstart: Event
 
9782                 // Fired when the map starts autopanning when opening a popup.
 
9785                             .fire('autopanstart')
 
9790         _onCloseButtonClick: function (e) {
 
9795         _getAnchor: function () {
 
9796                 // Where should we anchor the popup on the source layer?
 
9797                 return toPoint(this._source && this._source._getPopupAnchor ? this._source._getPopupAnchor() : [0, 0]);
 
9803 // @factory L.popup(options?: Popup options, source?: Layer)
 
9804 // 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.
 
9805 var popup = function (options, source) {
 
9806         return new Popup(options, source);
 
9811  * @section Interaction Options
 
9812  * @option closePopupOnClick: Boolean = true
 
9813  * Set it to `false` if you don't want popups to close when user clicks the map.
 
9816         closePopupOnClick: true
 
9821 // @section Methods for Layers and Controls
 
9823         // @method openPopup(popup: Popup): this
 
9824         // Opens the specified popup while closing the previously opened (to make sure only one is opened at one time for usability).
 
9826         // @method openPopup(content: String|HTMLElement, latlng: LatLng, options?: Popup options): this
 
9827         // Creates a popup with the specified content and options and opens it in the given point on a map.
 
9828         openPopup: function (popup, latlng, options) {
 
9829                 if (!(popup instanceof Popup)) {
 
9830                         popup = new Popup(options).setContent(popup);
 
9834                         popup.setLatLng(latlng);
 
9837                 if (this.hasLayer(popup)) {
 
9841                 if (this._popup && this._popup.options.autoClose) {
 
9845                 this._popup = popup;
 
9846                 return this.addLayer(popup);
 
9849         // @method closePopup(popup?: Popup): this
 
9850         // Closes the popup previously opened with [openPopup](#map-openpopup) (or the given one).
 
9851         closePopup: function (popup) {
 
9852                 if (!popup || popup === this._popup) {
 
9853                         popup = this._popup;
 
9857                         this.removeLayer(popup);
 
9865  * @section Popup methods example
 
9867  * All layers share a set of methods convenient for binding popups to it.
 
9870  * var layer = L.Polygon(latlngs).bindPopup('Hi There!').addTo(map);
 
9871  * layer.openPopup();
 
9872  * layer.closePopup();
 
9875  * 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.
 
9878 // @section Popup methods
 
9881         // @method bindPopup(content: String|HTMLElement|Function|Popup, options?: Popup options): this
 
9882         // Binds a popup to the layer with the passed `content` and sets up the
 
9883         // necessary event listeners. If a `Function` is passed it will receive
 
9884         // the layer as the first argument and should return a `String` or `HTMLElement`.
 
9885         bindPopup: function (content, options) {
 
9887                 if (content instanceof Popup) {
 
9888                         setOptions(content, options);
 
9889                         this._popup = content;
 
9890                         content._source = this;
 
9892                         if (!this._popup || options) {
 
9893                                 this._popup = new Popup(options, this);
 
9895                         this._popup.setContent(content);
 
9898                 if (!this._popupHandlersAdded) {
 
9900                                 click: this._openPopup,
 
9901                                 keypress: this._onKeyPress,
 
9902                                 remove: this.closePopup,
 
9903                                 move: this._movePopup
 
9905                         this._popupHandlersAdded = true;
 
9911         // @method unbindPopup(): this
 
9912         // Removes the popup previously bound with `bindPopup`.
 
9913         unbindPopup: function () {
 
9916                                 click: this._openPopup,
 
9917                                 keypress: this._onKeyPress,
 
9918                                 remove: this.closePopup,
 
9919                                 move: this._movePopup
 
9921                         this._popupHandlersAdded = false;
 
9927         // @method openPopup(latlng?: LatLng): this
 
9928         // Opens the bound popup at the specified `latlng` or at the default popup anchor if no `latlng` is passed.
 
9929         openPopup: function (layer, latlng) {
 
9930                 if (!(layer instanceof Layer)) {
 
9935                 if (layer instanceof FeatureGroup) {
 
9936                         for (var id in this._layers) {
 
9937                                 layer = this._layers[id];
 
9943                         latlng = layer.getCenter ? layer.getCenter() : layer.getLatLng();
 
9946                 if (this._popup && this._map) {
 
9947                         // set popup source to this layer
 
9948                         this._popup._source = layer;
 
9950                         // update the popup (content, layout, ect...)
 
9951                         this._popup.update();
 
9953                         // open the popup on the map
 
9954                         this._map.openPopup(this._popup, latlng);
 
9960         // @method closePopup(): this
 
9961         // Closes the popup bound to this layer if it is open.
 
9962         closePopup: function () {
 
9964                         this._popup._close();
 
9969         // @method togglePopup(): this
 
9970         // Opens or closes the popup bound to this layer depending on its current state.
 
9971         togglePopup: function (target) {
 
9973                         if (this._popup._map) {
 
9976                                 this.openPopup(target);
 
9982         // @method isPopupOpen(): boolean
 
9983         // Returns `true` if the popup bound to this layer is currently open.
 
9984         isPopupOpen: function () {
 
9985                 return (this._popup ? this._popup.isOpen() : false);
 
9988         // @method setPopupContent(content: String|HTMLElement|Popup): this
 
9989         // Sets the content of the popup bound to this layer.
 
9990         setPopupContent: function (content) {
 
9992                         this._popup.setContent(content);
 
9997         // @method getPopup(): Popup
 
9998         // Returns the popup bound to this layer.
 
9999         getPopup: function () {
 
10000                 return this._popup;
 
10003         _openPopup: function (e) {
 
10004                 var layer = e.layer || e.target;
 
10006                 if (!this._popup) {
 
10014                 // prevent map click
 
10017                 // if this inherits from Path its a vector and we can just
 
10018                 // open the popup at the new location
 
10019                 if (layer instanceof Path) {
 
10020                         this.openPopup(e.layer || e.target, e.latlng);
 
10024                 // otherwise treat it like a marker and figure out
 
10025                 // if we should toggle it open/closed
 
10026                 if (this._map.hasLayer(this._popup) && this._popup._source === layer) {
 
10029                         this.openPopup(layer, e.latlng);
 
10033         _movePopup: function (e) {
 
10034                 this._popup.setLatLng(e.latlng);
 
10037         _onKeyPress: function (e) {
 
10038                 if (e.originalEvent.keyCode === 13) {
 
10039                         this._openPopup(e);
 
10046  * @inherits DivOverlay
 
10048  * Used to display small texts on top of map layers.
 
10053  * marker.bindTooltip("my tooltip text").openTooltip();
 
10055  * Note about tooltip offset. Leaflet takes two options in consideration
 
10056  * for computing tooltip offsetting:
 
10057  * - the `offset` Tooltip option: it defaults to [0, 0], and it's specific to one tooltip.
 
10058  *   Add a positive x offset to move the tooltip to the right, and a positive y offset to
 
10059  *   move it to the bottom. Negatives will move to the left and top.
 
10060  * - the `tooltipAnchor` Icon option: this will only be considered for Marker. You
 
10061  *   should adapt this value if you use a custom icon.
 
10065 // @namespace Tooltip
 
10066 var Tooltip = DivOverlay.extend({
 
10069         // @aka Tooltip options
 
10071                 // @option pane: String = 'tooltipPane'
 
10072                 // `Map pane` where the tooltip will be added.
 
10073                 pane: 'tooltipPane',
 
10075                 // @option offset: Point = Point(0, 0)
 
10076                 // Optional offset of the tooltip position.
 
10079                 // @option direction: String = 'auto'
 
10080                 // Direction where to open the tooltip. Possible values are: `right`, `left`,
 
10081                 // `top`, `bottom`, `center`, `auto`.
 
10082                 // `auto` will dynamically switch between `right` and `left` according to the tooltip
 
10083                 // position on the map.
 
10086                 // @option permanent: Boolean = false
 
10087                 // Whether to open the tooltip permanently or only on mouseover.
 
10090                 // @option sticky: Boolean = false
 
10091                 // If true, the tooltip will follow the mouse instead of being fixed at the feature center.
 
10094                 // @option interactive: Boolean = false
 
10095                 // If true, the tooltip will listen to the feature events.
 
10096                 interactive: false,
 
10098                 // @option opacity: Number = 0.9
 
10099                 // Tooltip container opacity.
 
10103         onAdd: function (map) {
 
10104                 DivOverlay.prototype.onAdd.call(this, map);
 
10105                 this.setOpacity(this.options.opacity);
 
10108                 // @section Tooltip events
 
10109                 // @event tooltipopen: TooltipEvent
 
10110                 // Fired when a tooltip is opened in the map.
 
10111                 map.fire('tooltipopen', {tooltip: this});
 
10113                 if (this._source) {
 
10114                         // @namespace Layer
 
10115                         // @section Tooltip events
 
10116                         // @event tooltipopen: TooltipEvent
 
10117                         // Fired when a tooltip bound to this layer is opened.
 
10118                         this._source.fire('tooltipopen', {tooltip: this}, true);
 
10122         onRemove: function (map) {
 
10123                 DivOverlay.prototype.onRemove.call(this, map);
 
10126                 // @section Tooltip events
 
10127                 // @event tooltipclose: TooltipEvent
 
10128                 // Fired when a tooltip in the map is closed.
 
10129                 map.fire('tooltipclose', {tooltip: this});
 
10131                 if (this._source) {
 
10132                         // @namespace Layer
 
10133                         // @section Tooltip events
 
10134                         // @event tooltipclose: TooltipEvent
 
10135                         // Fired when a tooltip bound to this layer is closed.
 
10136                         this._source.fire('tooltipclose', {tooltip: this}, true);
 
10140         getEvents: function () {
 
10141                 var events = DivOverlay.prototype.getEvents.call(this);
 
10143                 if (touch && !this.options.permanent) {
 
10144                         events.preclick = this._close;
 
10150         _close: function () {
 
10152                         this._map.closeTooltip(this);
 
10156         _initLayout: function () {
 
10157                 var prefix = 'leaflet-tooltip',
 
10158                     className = prefix + ' ' + (this.options.className || '') + ' leaflet-zoom-' + (this._zoomAnimated ? 'animated' : 'hide');
 
10160                 this._contentNode = this._container = create$1('div', className);
 
10163         _updateLayout: function () {},
 
10165         _adjustPan: function () {},
 
10167         _setPosition: function (pos) {
 
10168                 var map = this._map,
 
10169                     container = this._container,
 
10170                     centerPoint = map.latLngToContainerPoint(map.getCenter()),
 
10171                     tooltipPoint = map.layerPointToContainerPoint(pos),
 
10172                     direction = this.options.direction,
 
10173                     tooltipWidth = container.offsetWidth,
 
10174                     tooltipHeight = container.offsetHeight,
 
10175                     offset = toPoint(this.options.offset),
 
10176                     anchor = this._getAnchor();
 
10178                 if (direction === 'top') {
 
10179                         pos = pos.add(toPoint(-tooltipWidth / 2 + offset.x, -tooltipHeight + offset.y + anchor.y, true));
 
10180                 } else if (direction === 'bottom') {
 
10181                         pos = pos.subtract(toPoint(tooltipWidth / 2 - offset.x, -offset.y, true));
 
10182                 } else if (direction === 'center') {
 
10183                         pos = pos.subtract(toPoint(tooltipWidth / 2 + offset.x, tooltipHeight / 2 - anchor.y + offset.y, true));
 
10184                 } else if (direction === 'right' || direction === 'auto' && tooltipPoint.x < centerPoint.x) {
 
10185                         direction = 'right';
 
10186                         pos = pos.add(toPoint(offset.x + anchor.x, anchor.y - tooltipHeight / 2 + offset.y, true));
 
10188                         direction = 'left';
 
10189                         pos = pos.subtract(toPoint(tooltipWidth + anchor.x - offset.x, tooltipHeight / 2 - anchor.y - offset.y, true));
 
10192                 removeClass(container, 'leaflet-tooltip-right');
 
10193                 removeClass(container, 'leaflet-tooltip-left');
 
10194                 removeClass(container, 'leaflet-tooltip-top');
 
10195                 removeClass(container, 'leaflet-tooltip-bottom');
 
10196                 addClass(container, 'leaflet-tooltip-' + direction);
 
10197                 setPosition(container, pos);
 
10200         _updatePosition: function () {
 
10201                 var pos = this._map.latLngToLayerPoint(this._latlng);
 
10202                 this._setPosition(pos);
 
10205         setOpacity: function (opacity) {
 
10206                 this.options.opacity = opacity;
 
10208                 if (this._container) {
 
10209                         setOpacity(this._container, opacity);
 
10213         _animateZoom: function (e) {
 
10214                 var pos = this._map._latLngToNewLayerPoint(this._latlng, e.zoom, e.center);
 
10215                 this._setPosition(pos);
 
10218         _getAnchor: function () {
 
10219                 // Where should we anchor the tooltip on the source layer?
 
10220                 return toPoint(this._source && this._source._getTooltipAnchor && !this.options.sticky ? this._source._getTooltipAnchor() : [0, 0]);
 
10225 // @namespace Tooltip
 
10226 // @factory L.tooltip(options?: Tooltip options, source?: Layer)
 
10227 // 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.
 
10228 var tooltip = function (options, source) {
 
10229         return new Tooltip(options, source);
 
10233 // @section Methods for Layers and Controls
 
10236         // @method openTooltip(tooltip: Tooltip): this
 
10237         // Opens the specified tooltip.
 
10239         // @method openTooltip(content: String|HTMLElement, latlng: LatLng, options?: Tooltip options): this
 
10240         // Creates a tooltip with the specified content and options and open it.
 
10241         openTooltip: function (tooltip, latlng, options) {
 
10242                 if (!(tooltip instanceof Tooltip)) {
 
10243                         tooltip = new Tooltip(options).setContent(tooltip);
 
10247                         tooltip.setLatLng(latlng);
 
10250                 if (this.hasLayer(tooltip)) {
 
10254                 return this.addLayer(tooltip);
 
10257         // @method closeTooltip(tooltip?: Tooltip): this
 
10258         // Closes the tooltip given as parameter.
 
10259         closeTooltip: function (tooltip) {
 
10261                         this.removeLayer(tooltip);
 
10270  * @section Tooltip methods example
 
10272  * All layers share a set of methods convenient for binding tooltips to it.
 
10275  * var layer = L.Polygon(latlngs).bindTooltip('Hi There!').addTo(map);
 
10276  * layer.openTooltip();
 
10277  * layer.closeTooltip();
 
10281 // @section Tooltip methods
 
10284         // @method bindTooltip(content: String|HTMLElement|Function|Tooltip, options?: Tooltip options): this
 
10285         // Binds a tooltip to the layer with the passed `content` and sets up the
 
10286         // necessary event listeners. If a `Function` is passed it will receive
 
10287         // the layer as the first argument and should return a `String` or `HTMLElement`.
 
10288         bindTooltip: function (content, options) {
 
10290                 if (content instanceof Tooltip) {
 
10291                         setOptions(content, options);
 
10292                         this._tooltip = content;
 
10293                         content._source = this;
 
10295                         if (!this._tooltip || options) {
 
10296                                 this._tooltip = new Tooltip(options, this);
 
10298                         this._tooltip.setContent(content);
 
10302                 this._initTooltipInteractions();
 
10304                 if (this._tooltip.options.permanent && this._map && this._map.hasLayer(this)) {
 
10305                         this.openTooltip();
 
10311         // @method unbindTooltip(): this
 
10312         // Removes the tooltip previously bound with `bindTooltip`.
 
10313         unbindTooltip: function () {
 
10314                 if (this._tooltip) {
 
10315                         this._initTooltipInteractions(true);
 
10316                         this.closeTooltip();
 
10317                         this._tooltip = null;
 
10322         _initTooltipInteractions: function (remove$$1) {
 
10323                 if (!remove$$1 && this._tooltipHandlersAdded) { return; }
 
10324                 var onOff = remove$$1 ? 'off' : 'on',
 
10326                         remove: this.closeTooltip,
 
10327                         move: this._moveTooltip
 
10329                 if (!this._tooltip.options.permanent) {
 
10330                         events.mouseover = this._openTooltip;
 
10331                         events.mouseout = this.closeTooltip;
 
10332                         if (this._tooltip.options.sticky) {
 
10333                                 events.mousemove = this._moveTooltip;
 
10336                                 events.click = this._openTooltip;
 
10339                         events.add = this._openTooltip;
 
10341                 this[onOff](events);
 
10342                 this._tooltipHandlersAdded = !remove$$1;
 
10345         // @method openTooltip(latlng?: LatLng): this
 
10346         // Opens the bound tooltip at the specified `latlng` or at the default tooltip anchor if no `latlng` is passed.
 
10347         openTooltip: function (layer, latlng) {
 
10348                 if (!(layer instanceof Layer)) {
 
10353                 if (layer instanceof FeatureGroup) {
 
10354                         for (var id in this._layers) {
 
10355                                 layer = this._layers[id];
 
10361                         latlng = layer.getCenter ? layer.getCenter() : layer.getLatLng();
 
10364                 if (this._tooltip && this._map) {
 
10366                         // set tooltip source to this layer
 
10367                         this._tooltip._source = layer;
 
10369                         // update the tooltip (content, layout, ect...)
 
10370                         this._tooltip.update();
 
10372                         // open the tooltip on the map
 
10373                         this._map.openTooltip(this._tooltip, latlng);
 
10375                         // Tooltip container may not be defined if not permanent and never
 
10377                         if (this._tooltip.options.interactive && this._tooltip._container) {
 
10378                                 addClass(this._tooltip._container, 'leaflet-clickable');
 
10379                                 this.addInteractiveTarget(this._tooltip._container);
 
10386         // @method closeTooltip(): this
 
10387         // Closes the tooltip bound to this layer if it is open.
 
10388         closeTooltip: function () {
 
10389                 if (this._tooltip) {
 
10390                         this._tooltip._close();
 
10391                         if (this._tooltip.options.interactive && this._tooltip._container) {
 
10392                                 removeClass(this._tooltip._container, 'leaflet-clickable');
 
10393                                 this.removeInteractiveTarget(this._tooltip._container);
 
10399         // @method toggleTooltip(): this
 
10400         // Opens or closes the tooltip bound to this layer depending on its current state.
 
10401         toggleTooltip: function (target) {
 
10402                 if (this._tooltip) {
 
10403                         if (this._tooltip._map) {
 
10404                                 this.closeTooltip();
 
10406                                 this.openTooltip(target);
 
10412         // @method isTooltipOpen(): boolean
 
10413         // Returns `true` if the tooltip bound to this layer is currently open.
 
10414         isTooltipOpen: function () {
 
10415                 return this._tooltip.isOpen();
 
10418         // @method setTooltipContent(content: String|HTMLElement|Tooltip): this
 
10419         // Sets the content of the tooltip bound to this layer.
 
10420         setTooltipContent: function (content) {
 
10421                 if (this._tooltip) {
 
10422                         this._tooltip.setContent(content);
 
10427         // @method getTooltip(): Tooltip
 
10428         // Returns the tooltip bound to this layer.
 
10429         getTooltip: function () {
 
10430                 return this._tooltip;
 
10433         _openTooltip: function (e) {
 
10434                 var layer = e.layer || e.target;
 
10436                 if (!this._tooltip || !this._map) {
 
10439                 this.openTooltip(layer, this._tooltip.options.sticky ? e.latlng : undefined);
 
10442         _moveTooltip: function (e) {
 
10443                 var latlng = e.latlng, containerPoint, layerPoint;
 
10444                 if (this._tooltip.options.sticky && e.originalEvent) {
 
10445                         containerPoint = this._map.mouseEventToContainerPoint(e.originalEvent);
 
10446                         layerPoint = this._map.containerPointToLayerPoint(containerPoint);
 
10447                         latlng = this._map.layerPointToLatLng(layerPoint);
 
10449                 this._tooltip.setLatLng(latlng);
 
10458  * Represents a lightweight icon for markers that uses a simple `<div>`
 
10459  * element instead of an image. Inherits from `Icon` but ignores the `iconUrl` and shadow options.
 
10463  * var myIcon = L.divIcon({className: 'my-div-icon'});
 
10464  * // you can set .my-div-icon styles in CSS
 
10466  * L.marker([50.505, 30.57], {icon: myIcon}).addTo(map);
 
10469  * By default, it has a 'leaflet-div-icon' CSS class and is styled as a little white square with a shadow.
 
10472 var DivIcon = Icon.extend({
 
10475                 // @aka DivIcon options
 
10476                 iconSize: [12, 12], // also can be set through CSS
 
10478                 // iconAnchor: (Point),
 
10479                 // popupAnchor: (Point),
 
10481                 // @option html: String = ''
 
10482                 // Custom HTML code to put inside the div element, empty by default.
 
10485                 // @option bgPos: Point = [0, 0]
 
10486                 // Optional relative position of the background, in pixels
 
10489                 className: 'leaflet-div-icon'
 
10492         createIcon: function (oldIcon) {
 
10493                 var div = (oldIcon && oldIcon.tagName === 'DIV') ? oldIcon : document.createElement('div'),
 
10494                     options = this.options;
 
10496                 div.innerHTML = options.html !== false ? options.html : '';
 
10498                 if (options.bgPos) {
 
10499                         var bgPos = toPoint(options.bgPos);
 
10500                         div.style.backgroundPosition = (-bgPos.x) + 'px ' + (-bgPos.y) + 'px';
 
10502                 this._setIconStyles(div, 'icon');
 
10507         createShadow: function () {
 
10512 // @factory L.divIcon(options: DivIcon options)
 
10513 // Creates a `DivIcon` instance with the given options.
 
10514 function divIcon(options) {
 
10515         return new DivIcon(options);
 
10518 Icon.Default = IconDefault;
 
10525  * Generic class for handling a tiled grid of HTML elements. This is the base class for all tile layers and replaces `TileLayer.Canvas`.
 
10526  * 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.
 
10529  * @section Synchronous usage
 
10532  * 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.
 
10535  * var CanvasLayer = L.GridLayer.extend({
 
10536  *     createTile: function(coords){
 
10537  *         // create a <canvas> element for drawing
 
10538  *         var tile = L.DomUtil.create('canvas', 'leaflet-tile');
 
10540  *         // setup tile width and height according to the options
 
10541  *         var size = this.getTileSize();
 
10542  *         tile.width = size.x;
 
10543  *         tile.height = size.y;
 
10545  *         // get a canvas context and draw something on it using coords.x, coords.y and coords.z
 
10546  *         var ctx = tile.getContext('2d');
 
10548  *         // return the tile so it can be rendered on screen
 
10554  * @section Asynchronous usage
 
10557  * 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.
 
10560  * var CanvasLayer = L.GridLayer.extend({
 
10561  *     createTile: function(coords, done){
 
10564  *         // create a <canvas> element for drawing
 
10565  *         var tile = L.DomUtil.create('canvas', 'leaflet-tile');
 
10567  *         // setup tile width and height according to the options
 
10568  *         var size = this.getTileSize();
 
10569  *         tile.width = size.x;
 
10570  *         tile.height = size.y;
 
10572  *         // draw something asynchronously and pass the tile to the done() callback
 
10573  *         setTimeout(function() {
 
10574  *             done(error, tile);
 
10586 var GridLayer = Layer.extend({
 
10589         // @aka GridLayer options
 
10591                 // @option tileSize: Number|Point = 256
 
10592                 // Width and height of tiles in the grid. Use a number if width and height are equal, or `L.point(width, height)` otherwise.
 
10595                 // @option opacity: Number = 1.0
 
10596                 // Opacity of the tiles. Can be used in the `createTile()` function.
 
10599                 // @option updateWhenIdle: Boolean = (depends)
 
10600                 // Load new tiles only when panning ends.
 
10601                 // `true` by default on mobile browsers, in order to avoid too many requests and keep smooth navigation.
 
10602                 // `false` otherwise in order to display new tiles _during_ panning, since it is easy to pan outside the
 
10603                 // [`keepBuffer`](#gridlayer-keepbuffer) option in desktop browsers.
 
10604                 updateWhenIdle: mobile,
 
10606                 // @option updateWhenZooming: Boolean = true
 
10607                 // 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.
 
10608                 updateWhenZooming: true,
 
10610                 // @option updateInterval: Number = 200
 
10611                 // Tiles will not update more than once every `updateInterval` milliseconds when panning.
 
10612                 updateInterval: 200,
 
10614                 // @option zIndex: Number = 1
 
10615                 // The explicit zIndex of the tile layer.
 
10618                 // @option bounds: LatLngBounds = undefined
 
10619                 // If set, tiles will only be loaded inside the set `LatLngBounds`.
 
10622                 // @option minZoom: Number = 0
 
10623                 // The minimum zoom level down to which this layer will be displayed (inclusive).
 
10626                 // @option maxZoom: Number = undefined
 
10627                 // The maximum zoom level up to which this layer will be displayed (inclusive).
 
10628                 maxZoom: undefined,
 
10630                 // @option maxNativeZoom: Number = undefined
 
10631                 // Maximum zoom number the tile source has available. If it is specified,
 
10632                 // the tiles on all zoom levels higher than `maxNativeZoom` will be loaded
 
10633                 // from `maxNativeZoom` level and auto-scaled.
 
10634                 maxNativeZoom: undefined,
 
10636                 // @option minNativeZoom: Number = undefined
 
10637                 // Minimum zoom number the tile source has available. If it is specified,
 
10638                 // the tiles on all zoom levels lower than `minNativeZoom` will be loaded
 
10639                 // from `minNativeZoom` level and auto-scaled.
 
10640                 minNativeZoom: undefined,
 
10642                 // @option noWrap: Boolean = false
 
10643                 // Whether the layer is wrapped around the antimeridian. If `true`, the
 
10644                 // GridLayer will only be displayed once at low zoom levels. Has no
 
10645                 // effect when the [map CRS](#map-crs) doesn't wrap around. Can be used
 
10646                 // in combination with [`bounds`](#gridlayer-bounds) to prevent requesting
 
10647                 // tiles outside the CRS limits.
 
10650                 // @option pane: String = 'tilePane'
 
10651                 // `Map pane` where the grid layer will be added.
 
10654                 // @option className: String = ''
 
10655                 // A custom class name to assign to the tile layer. Empty by default.
 
10658                 // @option keepBuffer: Number = 2
 
10659                 // When panning the map, keep this many rows and columns of tiles before unloading them.
 
10663         initialize: function (options) {
 
10664                 setOptions(this, options);
 
10667         onAdd: function () {
 
10668                 this._initContainer();
 
10677         beforeAdd: function (map) {
 
10678                 map._addZoomLimit(this);
 
10681         onRemove: function (map) {
 
10682                 this._removeAllTiles();
 
10683                 remove(this._container);
 
10684                 map._removeZoomLimit(this);
 
10685                 this._container = null;
 
10686                 this._tileZoom = undefined;
 
10689         // @method bringToFront: this
 
10690         // Brings the tile layer to the top of all tile layers.
 
10691         bringToFront: function () {
 
10693                         toFront(this._container);
 
10694                         this._setAutoZIndex(Math.max);
 
10699         // @method bringToBack: this
 
10700         // Brings the tile layer to the bottom of all tile layers.
 
10701         bringToBack: function () {
 
10703                         toBack(this._container);
 
10704                         this._setAutoZIndex(Math.min);
 
10709         // @method getContainer: HTMLElement
 
10710         // Returns the HTML element that contains the tiles for this layer.
 
10711         getContainer: function () {
 
10712                 return this._container;
 
10715         // @method setOpacity(opacity: Number): this
 
10716         // Changes the [opacity](#gridlayer-opacity) of the grid layer.
 
10717         setOpacity: function (opacity) {
 
10718                 this.options.opacity = opacity;
 
10719                 this._updateOpacity();
 
10723         // @method setZIndex(zIndex: Number): this
 
10724         // Changes the [zIndex](#gridlayer-zindex) of the grid layer.
 
10725         setZIndex: function (zIndex) {
 
10726                 this.options.zIndex = zIndex;
 
10727                 this._updateZIndex();
 
10732         // @method isLoading: Boolean
 
10733         // Returns `true` if any tile in the grid layer has not finished loading.
 
10734         isLoading: function () {
 
10735                 return this._loading;
 
10738         // @method redraw: this
 
10739         // Causes the layer to clear all the tiles and request them again.
 
10740         redraw: function () {
 
10742                         this._removeAllTiles();
 
10748         getEvents: function () {
 
10750                         viewprereset: this._invalidateAll,
 
10751                         viewreset: this._resetView,
 
10752                         zoom: this._resetView,
 
10753                         moveend: this._onMoveEnd
 
10756                 if (!this.options.updateWhenIdle) {
 
10757                         // update tiles on move, but not more often than once per given interval
 
10758                         if (!this._onMove) {
 
10759                                 this._onMove = throttle(this._onMoveEnd, this.options.updateInterval, this);
 
10762                         events.move = this._onMove;
 
10765                 if (this._zoomAnimated) {
 
10766                         events.zoomanim = this._animateZoom;
 
10772         // @section Extension methods
 
10773         // Layers extending `GridLayer` shall reimplement the following method.
 
10774         // @method createTile(coords: Object, done?: Function): HTMLElement
 
10775         // Called only internally, must be overridden by classes extending `GridLayer`.
 
10776         // Returns the `HTMLElement` corresponding to the given `coords`. If the `done` callback
 
10777         // is specified, it must be called when the tile has finished loading and drawing.
 
10778         createTile: function () {
 
10779                 return document.createElement('div');
 
10783         // @method getTileSize: Point
 
10784         // Normalizes the [tileSize option](#gridlayer-tilesize) into a point. Used by the `createTile()` method.
 
10785         getTileSize: function () {
 
10786                 var s = this.options.tileSize;
 
10787                 return s instanceof Point ? s : new Point(s, s);
 
10790         _updateZIndex: function () {
 
10791                 if (this._container && this.options.zIndex !== undefined && this.options.zIndex !== null) {
 
10792                         this._container.style.zIndex = this.options.zIndex;
 
10796         _setAutoZIndex: function (compare) {
 
10797                 // go through all other layers of the same pane, set zIndex to max + 1 (front) or min - 1 (back)
 
10799                 var layers = this.getPane().children,
 
10800                     edgeZIndex = -compare(-Infinity, Infinity); // -Infinity for max, Infinity for min
 
10802                 for (var i = 0, len = layers.length, zIndex; i < len; i++) {
 
10804                         zIndex = layers[i].style.zIndex;
 
10806                         if (layers[i] !== this._container && zIndex) {
 
10807                                 edgeZIndex = compare(edgeZIndex, +zIndex);
 
10811                 if (isFinite(edgeZIndex)) {
 
10812                         this.options.zIndex = edgeZIndex + compare(-1, 1);
 
10813                         this._updateZIndex();
 
10817         _updateOpacity: function () {
 
10818                 if (!this._map) { return; }
 
10820                 // IE doesn't inherit filter opacity properly, so we're forced to set it on tiles
 
10821                 if (ielt9) { return; }
 
10823                 setOpacity(this._container, this.options.opacity);
 
10825                 var now = +new Date(),
 
10829                 for (var key in this._tiles) {
 
10830                         var tile = this._tiles[key];
 
10831                         if (!tile.current || !tile.loaded) { continue; }
 
10833                         var fade = Math.min(1, (now - tile.loaded) / 200);
 
10835                         setOpacity(tile.el, fade);
 
10842                                         this._onOpaqueTile(tile);
 
10844                                 tile.active = true;
 
10848                 if (willPrune && !this._noPrune) { this._pruneTiles(); }
 
10851                         cancelAnimFrame(this._fadeFrame);
 
10852                         this._fadeFrame = requestAnimFrame(this._updateOpacity, this);
 
10856         _onOpaqueTile: falseFn,
 
10858         _initContainer: function () {
 
10859                 if (this._container) { return; }
 
10861                 this._container = create$1('div', 'leaflet-layer ' + (this.options.className || ''));
 
10862                 this._updateZIndex();
 
10864                 if (this.options.opacity < 1) {
 
10865                         this._updateOpacity();
 
10868                 this.getPane().appendChild(this._container);
 
10871         _updateLevels: function () {
 
10873                 var zoom = this._tileZoom,
 
10874                     maxZoom = this.options.maxZoom;
 
10876                 if (zoom === undefined) { return undefined; }
 
10878                 for (var z in this._levels) {
 
10879                         if (this._levels[z].el.children.length || z === zoom) {
 
10880                                 this._levels[z].el.style.zIndex = maxZoom - Math.abs(zoom - z);
 
10881                                 this._onUpdateLevel(z);
 
10883                                 remove(this._levels[z].el);
 
10884                                 this._removeTilesAtZoom(z);
 
10885                                 this._onRemoveLevel(z);
 
10886                                 delete this._levels[z];
 
10890                 var level = this._levels[zoom],
 
10894                         level = this._levels[zoom] = {};
 
10896                         level.el = create$1('div', 'leaflet-tile-container leaflet-zoom-animated', this._container);
 
10897                         level.el.style.zIndex = maxZoom;
 
10899                         level.origin = map.project(map.unproject(map.getPixelOrigin()), zoom).round();
 
10902                         this._setZoomTransform(level, map.getCenter(), map.getZoom());
 
10904                         // force the browser to consider the newly added element for transition
 
10905                         falseFn(level.el.offsetWidth);
 
10907                         this._onCreateLevel(level);
 
10910                 this._level = level;
 
10915         _onUpdateLevel: falseFn,
 
10917         _onRemoveLevel: falseFn,
 
10919         _onCreateLevel: falseFn,
 
10921         _pruneTiles: function () {
 
10928                 var zoom = this._map.getZoom();
 
10929                 if (zoom > this.options.maxZoom ||
 
10930                         zoom < this.options.minZoom) {
 
10931                         this._removeAllTiles();
 
10935                 for (key in this._tiles) {
 
10936                         tile = this._tiles[key];
 
10937                         tile.retain = tile.current;
 
10940                 for (key in this._tiles) {
 
10941                         tile = this._tiles[key];
 
10942                         if (tile.current && !tile.active) {
 
10943                                 var coords = tile.coords;
 
10944                                 if (!this._retainParent(coords.x, coords.y, coords.z, coords.z - 5)) {
 
10945                                         this._retainChildren(coords.x, coords.y, coords.z, coords.z + 2);
 
10950                 for (key in this._tiles) {
 
10951                         if (!this._tiles[key].retain) {
 
10952                                 this._removeTile(key);
 
10957         _removeTilesAtZoom: function (zoom) {
 
10958                 for (var key in this._tiles) {
 
10959                         if (this._tiles[key].coords.z !== zoom) {
 
10962                         this._removeTile(key);
 
10966         _removeAllTiles: function () {
 
10967                 for (var key in this._tiles) {
 
10968                         this._removeTile(key);
 
10972         _invalidateAll: function () {
 
10973                 for (var z in this._levels) {
 
10974                         remove(this._levels[z].el);
 
10975                         this._onRemoveLevel(z);
 
10976                         delete this._levels[z];
 
10978                 this._removeAllTiles();
 
10980                 this._tileZoom = undefined;
 
10983         _retainParent: function (x, y, z, minZoom) {
 
10984                 var x2 = Math.floor(x / 2),
 
10985                     y2 = Math.floor(y / 2),
 
10987                     coords2 = new Point(+x2, +y2);
 
10990                 var key = this._tileCoordsToKey(coords2),
 
10991                     tile = this._tiles[key];
 
10993                 if (tile && tile.active) {
 
10994                         tile.retain = true;
 
10997                 } else if (tile && tile.loaded) {
 
10998                         tile.retain = true;
 
11001                 if (z2 > minZoom) {
 
11002                         return this._retainParent(x2, y2, z2, minZoom);
 
11008         _retainChildren: function (x, y, z, maxZoom) {
 
11010                 for (var i = 2 * x; i < 2 * x + 2; i++) {
 
11011                         for (var j = 2 * y; j < 2 * y + 2; j++) {
 
11013                                 var coords = new Point(i, j);
 
11016                                 var key = this._tileCoordsToKey(coords),
 
11017                                     tile = this._tiles[key];
 
11019                                 if (tile && tile.active) {
 
11020                                         tile.retain = true;
 
11023                                 } else if (tile && tile.loaded) {
 
11024                                         tile.retain = true;
 
11027                                 if (z + 1 < maxZoom) {
 
11028                                         this._retainChildren(i, j, z + 1, maxZoom);
 
11034         _resetView: function (e) {
 
11035                 var animating = e && (e.pinch || e.flyTo);
 
11036                 this._setView(this._map.getCenter(), this._map.getZoom(), animating, animating);
 
11039         _animateZoom: function (e) {
 
11040                 this._setView(e.center, e.zoom, true, e.noUpdate);
 
11043         _clampZoom: function (zoom) {
 
11044                 var options = this.options;
 
11046                 if (undefined !== options.minNativeZoom && zoom < options.minNativeZoom) {
 
11047                         return options.minNativeZoom;
 
11050                 if (undefined !== options.maxNativeZoom && options.maxNativeZoom < zoom) {
 
11051                         return options.maxNativeZoom;
 
11057         _setView: function (center, zoom, noPrune, noUpdate) {
 
11058                 var tileZoom = this._clampZoom(Math.round(zoom));
 
11059                 if ((this.options.maxZoom !== undefined && tileZoom > this.options.maxZoom) ||
 
11060                     (this.options.minZoom !== undefined && tileZoom < this.options.minZoom)) {
 
11061                         tileZoom = undefined;
 
11064                 var tileZoomChanged = this.options.updateWhenZooming && (tileZoom !== this._tileZoom);
 
11066                 if (!noUpdate || tileZoomChanged) {
 
11068                         this._tileZoom = tileZoom;
 
11070                         if (this._abortLoading) {
 
11071                                 this._abortLoading();
 
11074                         this._updateLevels();
 
11077                         if (tileZoom !== undefined) {
 
11078                                 this._update(center);
 
11082                                 this._pruneTiles();
 
11085                         // Flag to prevent _updateOpacity from pruning tiles during
 
11086                         // a zoom anim or a pinch gesture
 
11087                         this._noPrune = !!noPrune;
 
11090                 this._setZoomTransforms(center, zoom);
 
11093         _setZoomTransforms: function (center, zoom) {
 
11094                 for (var i in this._levels) {
 
11095                         this._setZoomTransform(this._levels[i], center, zoom);
 
11099         _setZoomTransform: function (level, center, zoom) {
 
11100                 var scale = this._map.getZoomScale(zoom, level.zoom),
 
11101                     translate = level.origin.multiplyBy(scale)
 
11102                         .subtract(this._map._getNewPixelOrigin(center, zoom)).round();
 
11105                         setTransform(level.el, translate, scale);
 
11107                         setPosition(level.el, translate);
 
11111         _resetGrid: function () {
 
11112                 var map = this._map,
 
11113                     crs = map.options.crs,
 
11114                     tileSize = this._tileSize = this.getTileSize(),
 
11115                     tileZoom = this._tileZoom;
 
11117                 var bounds = this._map.getPixelWorldBounds(this._tileZoom);
 
11119                         this._globalTileRange = this._pxBoundsToTileRange(bounds);
 
11122                 this._wrapX = crs.wrapLng && !this.options.noWrap && [
 
11123                         Math.floor(map.project([0, crs.wrapLng[0]], tileZoom).x / tileSize.x),
 
11124                         Math.ceil(map.project([0, crs.wrapLng[1]], tileZoom).x / tileSize.y)
 
11126                 this._wrapY = crs.wrapLat && !this.options.noWrap && [
 
11127                         Math.floor(map.project([crs.wrapLat[0], 0], tileZoom).y / tileSize.x),
 
11128                         Math.ceil(map.project([crs.wrapLat[1], 0], tileZoom).y / tileSize.y)
 
11132         _onMoveEnd: function () {
 
11133                 if (!this._map || this._map._animatingZoom) { return; }
 
11138         _getTiledPixelBounds: function (center) {
 
11139                 var map = this._map,
 
11140                     mapZoom = map._animatingZoom ? Math.max(map._animateToZoom, map.getZoom()) : map.getZoom(),
 
11141                     scale = map.getZoomScale(mapZoom, this._tileZoom),
 
11142                     pixelCenter = map.project(center, this._tileZoom).floor(),
 
11143                     halfSize = map.getSize().divideBy(scale * 2);
 
11145                 return new Bounds(pixelCenter.subtract(halfSize), pixelCenter.add(halfSize));
 
11148         // Private method to load tiles in the grid's active zoom level according to map bounds
 
11149         _update: function (center) {
 
11150                 var map = this._map;
 
11151                 if (!map) { return; }
 
11152                 var zoom = this._clampZoom(map.getZoom());
 
11154                 if (center === undefined) { center = map.getCenter(); }
 
11155                 if (this._tileZoom === undefined) { return; }   // if out of minzoom/maxzoom
 
11157                 var pixelBounds = this._getTiledPixelBounds(center),
 
11158                     tileRange = this._pxBoundsToTileRange(pixelBounds),
 
11159                     tileCenter = tileRange.getCenter(),
 
11161                     margin = this.options.keepBuffer,
 
11162                     noPruneRange = new Bounds(tileRange.getBottomLeft().subtract([margin, -margin]),
 
11163                                               tileRange.getTopRight().add([margin, -margin]));
 
11165                 // Sanity check: panic if the tile range contains Infinity somewhere.
 
11166                 if (!(isFinite(tileRange.min.x) &&
 
11167                       isFinite(tileRange.min.y) &&
 
11168                       isFinite(tileRange.max.x) &&
 
11169                       isFinite(tileRange.max.y))) { throw new Error('Attempted to load an infinite number of tiles'); }
 
11171                 for (var key in this._tiles) {
 
11172                         var c = this._tiles[key].coords;
 
11173                         if (c.z !== this._tileZoom || !noPruneRange.contains(new Point(c.x, c.y))) {
 
11174                                 this._tiles[key].current = false;
 
11178                 // _update just loads more tiles. If the tile zoom level differs too much
 
11179                 // from the map's, let _setView reset levels and prune old tiles.
 
11180                 if (Math.abs(zoom - this._tileZoom) > 1) { this._setView(center, zoom); return; }
 
11182                 // create a queue of coordinates to load tiles from
 
11183                 for (var j = tileRange.min.y; j <= tileRange.max.y; j++) {
 
11184                         for (var i = tileRange.min.x; i <= tileRange.max.x; i++) {
 
11185                                 var coords = new Point(i, j);
 
11186                                 coords.z = this._tileZoom;
 
11188                                 if (!this._isValidTile(coords)) { continue; }
 
11190                                 var tile = this._tiles[this._tileCoordsToKey(coords)];
 
11192                                         tile.current = true;
 
11194                                         queue.push(coords);
 
11199                 // sort tile queue to load tiles in order of their distance to center
 
11200                 queue.sort(function (a, b) {
 
11201                         return a.distanceTo(tileCenter) - b.distanceTo(tileCenter);
 
11204                 if (queue.length !== 0) {
 
11205                         // if it's the first batch of tiles to load
 
11206                         if (!this._loading) {
 
11207                                 this._loading = true;
 
11208                                 // @event loading: Event
 
11209                                 // Fired when the grid layer starts loading tiles.
 
11210                                 this.fire('loading');
 
11213                         // create DOM fragment to append tiles in one batch
 
11214                         var fragment = document.createDocumentFragment();
 
11216                         for (i = 0; i < queue.length; i++) {
 
11217                                 this._addTile(queue[i], fragment);
 
11220                         this._level.el.appendChild(fragment);
 
11224         _isValidTile: function (coords) {
 
11225                 var crs = this._map.options.crs;
 
11227                 if (!crs.infinite) {
 
11228                         // don't load tile if it's out of bounds and not wrapped
 
11229                         var bounds = this._globalTileRange;
 
11230                         if ((!crs.wrapLng && (coords.x < bounds.min.x || coords.x > bounds.max.x)) ||
 
11231                             (!crs.wrapLat && (coords.y < bounds.min.y || coords.y > bounds.max.y))) { return false; }
 
11234                 if (!this.options.bounds) { return true; }
 
11236                 // don't load tile if it doesn't intersect the bounds in options
 
11237                 var tileBounds = this._tileCoordsToBounds(coords);
 
11238                 return toLatLngBounds(this.options.bounds).overlaps(tileBounds);
 
11241         _keyToBounds: function (key) {
 
11242                 return this._tileCoordsToBounds(this._keyToTileCoords(key));
 
11245         _tileCoordsToNwSe: function (coords) {
 
11246                 var map = this._map,
 
11247                     tileSize = this.getTileSize(),
 
11248                     nwPoint = coords.scaleBy(tileSize),
 
11249                     sePoint = nwPoint.add(tileSize),
 
11250                     nw = map.unproject(nwPoint, coords.z),
 
11251                     se = map.unproject(sePoint, coords.z);
 
11255         // converts tile coordinates to its geographical bounds
 
11256         _tileCoordsToBounds: function (coords) {
 
11257                 var bp = this._tileCoordsToNwSe(coords),
 
11258                     bounds = new LatLngBounds(bp[0], bp[1]);
 
11260                 if (!this.options.noWrap) {
 
11261                         bounds = this._map.wrapLatLngBounds(bounds);
 
11265         // converts tile coordinates to key for the tile cache
 
11266         _tileCoordsToKey: function (coords) {
 
11267                 return coords.x + ':' + coords.y + ':' + coords.z;
 
11270         // converts tile cache key to coordinates
 
11271         _keyToTileCoords: function (key) {
 
11272                 var k = key.split(':'),
 
11273                     coords = new Point(+k[0], +k[1]);
 
11278         _removeTile: function (key) {
 
11279                 var tile = this._tiles[key];
 
11280                 if (!tile) { return; }
 
11284                 delete this._tiles[key];
 
11286                 // @event tileunload: TileEvent
 
11287                 // Fired when a tile is removed (e.g. when a tile goes off the screen).
 
11288                 this.fire('tileunload', {
 
11290                         coords: this._keyToTileCoords(key)
 
11294         _initTile: function (tile) {
 
11295                 addClass(tile, 'leaflet-tile');
 
11297                 var tileSize = this.getTileSize();
 
11298                 tile.style.width = tileSize.x + 'px';
 
11299                 tile.style.height = tileSize.y + 'px';
 
11301                 tile.onselectstart = falseFn;
 
11302                 tile.onmousemove = falseFn;
 
11304                 // update opacity on tiles in IE7-8 because of filter inheritance problems
 
11305                 if (ielt9 && this.options.opacity < 1) {
 
11306                         setOpacity(tile, this.options.opacity);
 
11309                 // without this hack, tiles disappear after zoom on Chrome for Android
 
11310                 // https://github.com/Leaflet/Leaflet/issues/2078
 
11311                 if (android && !android23) {
 
11312                         tile.style.WebkitBackfaceVisibility = 'hidden';
 
11316         _addTile: function (coords, container) {
 
11317                 var tilePos = this._getTilePos(coords),
 
11318                     key = this._tileCoordsToKey(coords);
 
11320                 var tile = this.createTile(this._wrapCoords(coords), bind(this._tileReady, this, coords));
 
11322                 this._initTile(tile);
 
11324                 // if createTile is defined with a second argument ("done" callback),
 
11325                 // we know that tile is async and will be ready later; otherwise
 
11326                 if (this.createTile.length < 2) {
 
11327                         // mark tile as ready, but delay one frame for opacity animation to happen
 
11328                         requestAnimFrame(bind(this._tileReady, this, coords, null, tile));
 
11331                 setPosition(tile, tilePos);
 
11333                 // save tile in cache
 
11334                 this._tiles[key] = {
 
11340                 container.appendChild(tile);
 
11341                 // @event tileloadstart: TileEvent
 
11342                 // Fired when a tile is requested and starts loading.
 
11343                 this.fire('tileloadstart', {
 
11349         _tileReady: function (coords, err, tile) {
 
11351                         // @event tileerror: TileErrorEvent
 
11352                         // Fired when there is an error loading a tile.
 
11353                         this.fire('tileerror', {
 
11360                 var key = this._tileCoordsToKey(coords);
 
11362                 tile = this._tiles[key];
 
11363                 if (!tile) { return; }
 
11365                 tile.loaded = +new Date();
 
11366                 if (this._map._fadeAnimated) {
 
11367                         setOpacity(tile.el, 0);
 
11368                         cancelAnimFrame(this._fadeFrame);
 
11369                         this._fadeFrame = requestAnimFrame(this._updateOpacity, this);
 
11371                         tile.active = true;
 
11372                         this._pruneTiles();
 
11376                         addClass(tile.el, 'leaflet-tile-loaded');
 
11378                         // @event tileload: TileEvent
 
11379                         // Fired when a tile loads.
 
11380                         this.fire('tileload', {
 
11386                 if (this._noTilesToLoad()) {
 
11387                         this._loading = false;
 
11388                         // @event load: Event
 
11389                         // Fired when the grid layer loaded all visible tiles.
 
11392                         if (ielt9 || !this._map._fadeAnimated) {
 
11393                                 requestAnimFrame(this._pruneTiles, this);
 
11395                                 // Wait a bit more than 0.2 secs (the duration of the tile fade-in)
 
11396                                 // to trigger a pruning.
 
11397                                 setTimeout(bind(this._pruneTiles, this), 250);
 
11402         _getTilePos: function (coords) {
 
11403                 return coords.scaleBy(this.getTileSize()).subtract(this._level.origin);
 
11406         _wrapCoords: function (coords) {
 
11407                 var newCoords = new Point(
 
11408                         this._wrapX ? wrapNum(coords.x, this._wrapX) : coords.x,
 
11409                         this._wrapY ? wrapNum(coords.y, this._wrapY) : coords.y);
 
11410                 newCoords.z = coords.z;
 
11414         _pxBoundsToTileRange: function (bounds) {
 
11415                 var tileSize = this.getTileSize();
 
11417                         bounds.min.unscaleBy(tileSize).floor(),
 
11418                         bounds.max.unscaleBy(tileSize).ceil().subtract([1, 1]));
 
11421         _noTilesToLoad: function () {
 
11422                 for (var key in this._tiles) {
 
11423                         if (!this._tiles[key].loaded) { return false; }
 
11429 // @factory L.gridLayer(options?: GridLayer options)
 
11430 // Creates a new instance of GridLayer with the supplied options.
 
11431 function gridLayer(options) {
 
11432         return new GridLayer(options);
 
11437  * @inherits GridLayer
 
11439  * 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`.
 
11444  * 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);
 
11447  * @section URL template
 
11450  * A string of the following form:
 
11453  * 'http://{s}.somedomain.com/blabla/{z}/{x}/{y}{r}.png'
 
11456  * `{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.
 
11458  * You can use custom keys in the template, which will be [evaluated](#util-template) from TileLayer options, like this:
 
11461  * L.tileLayer('http://{s}.somedomain.com/{foo}/{z}/{x}/{y}.png', {foo: 'bar'});
 
11466 var TileLayer = GridLayer.extend({
 
11469         // @aka TileLayer options
 
11471                 // @option minZoom: Number = 0
 
11472                 // The minimum zoom level down to which this layer will be displayed (inclusive).
 
11475                 // @option maxZoom: Number = 18
 
11476                 // The maximum zoom level up to which this layer will be displayed (inclusive).
 
11479                 // @option subdomains: String|String[] = 'abc'
 
11480                 // 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.
 
11483                 // @option errorTileUrl: String = ''
 
11484                 // URL to the tile image to show in place of the tile that failed to load.
 
11487                 // @option zoomOffset: Number = 0
 
11488                 // The zoom number used in tile URLs will be offset with this value.
 
11491                 // @option tms: Boolean = false
 
11492                 // If `true`, inverses Y axis numbering for tiles (turn this on for [TMS](https://en.wikipedia.org/wiki/Tile_Map_Service) services).
 
11495                 // @option zoomReverse: Boolean = false
 
11496                 // If set to true, the zoom number used in tile URLs will be reversed (`maxZoom - zoom` instead of `zoom`)
 
11497                 zoomReverse: false,
 
11499                 // @option detectRetina: Boolean = false
 
11500                 // 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.
 
11501                 detectRetina: false,
 
11503                 // @option crossOrigin: Boolean|String = false
 
11504                 // Whether the crossOrigin attribute will be added to the tiles.
 
11505                 // 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.
 
11506                 // Refer to [CORS Settings](https://developer.mozilla.org/en-US/docs/Web/HTML/CORS_settings_attributes) for valid String values.
 
11510         initialize: function (url, options) {
 
11514                 options = setOptions(this, options);
 
11516                 // detecting retina displays, adjusting tileSize and zoom levels
 
11517                 if (options.detectRetina && retina && options.maxZoom > 0) {
 
11519                         options.tileSize = Math.floor(options.tileSize / 2);
 
11521                         if (!options.zoomReverse) {
 
11522                                 options.zoomOffset++;
 
11525                                 options.zoomOffset--;
 
11529                         options.minZoom = Math.max(0, options.minZoom);
 
11532                 if (typeof options.subdomains === 'string') {
 
11533                         options.subdomains = options.subdomains.split('');
 
11536                 // for https://github.com/Leaflet/Leaflet/issues/137
 
11538                         this.on('tileunload', this._onTileRemove);
 
11542         // @method setUrl(url: String, noRedraw?: Boolean): this
 
11543         // Updates the layer's URL template and redraws it (unless `noRedraw` is set to `true`).
 
11544         // If the URL does not change, the layer will not be redrawn unless
 
11545         // the noRedraw parameter is set to false.
 
11546         setUrl: function (url, noRedraw) {
 
11547                 if (this._url === url && noRedraw === undefined) {
 
11559         // @method createTile(coords: Object, done?: Function): HTMLElement
 
11560         // Called only internally, overrides GridLayer's [`createTile()`](#gridlayer-createtile)
 
11561         // to return an `<img>` HTML element with the appropriate image URL given `coords`. The `done`
 
11562         // callback is called when the tile has been loaded.
 
11563         createTile: function (coords, done) {
 
11564                 var tile = document.createElement('img');
 
11566                 on(tile, 'load', bind(this._tileOnLoad, this, done, tile));
 
11567                 on(tile, 'error', bind(this._tileOnError, this, done, tile));
 
11569                 if (this.options.crossOrigin || this.options.crossOrigin === '') {
 
11570                         tile.crossOrigin = this.options.crossOrigin === true ? '' : this.options.crossOrigin;
 
11574                  Alt tag is set to empty string to keep screen readers from reading URL and for compliance reasons
 
11575                  http://www.w3.org/TR/WCAG20-TECHS/H67
 
11580                  Set role="presentation" to force screen readers to ignore this
 
11581                  https://www.w3.org/TR/wai-aria/roles#textalternativecomputation
 
11583                 tile.setAttribute('role', 'presentation');
 
11585                 tile.src = this.getTileUrl(coords);
 
11590         // @section Extension methods
 
11592         // Layers extending `TileLayer` might reimplement the following method.
 
11593         // @method getTileUrl(coords: Object): String
 
11594         // Called only internally, returns the URL for a tile given its coordinates.
 
11595         // Classes extending `TileLayer` can override this function to provide custom tile URL naming schemes.
 
11596         getTileUrl: function (coords) {
 
11598                         r: retina ? '@2x' : '',
 
11599                         s: this._getSubdomain(coords),
 
11602                         z: this._getZoomForUrl()
 
11604                 if (this._map && !this._map.options.crs.infinite) {
 
11605                         var invertedY = this._globalTileRange.max.y - coords.y;
 
11606                         if (this.options.tms) {
 
11607                                 data['y'] = invertedY;
 
11609                         data['-y'] = invertedY;
 
11612                 return template(this._url, extend(data, this.options));
 
11615         _tileOnLoad: function (done, tile) {
 
11616                 // For https://github.com/Leaflet/Leaflet/issues/3332
 
11618                         setTimeout(bind(done, this, null, tile), 0);
 
11624         _tileOnError: function (done, tile, e) {
 
11625                 var errorUrl = this.options.errorTileUrl;
 
11626                 if (errorUrl && tile.getAttribute('src') !== errorUrl) {
 
11627                         tile.src = errorUrl;
 
11632         _onTileRemove: function (e) {
 
11633                 e.tile.onload = null;
 
11636         _getZoomForUrl: function () {
 
11637                 var zoom = this._tileZoom,
 
11638                 maxZoom = this.options.maxZoom,
 
11639                 zoomReverse = this.options.zoomReverse,
 
11640                 zoomOffset = this.options.zoomOffset;
 
11643                         zoom = maxZoom - zoom;
 
11646                 return zoom + zoomOffset;
 
11649         _getSubdomain: function (tilePoint) {
 
11650                 var index = Math.abs(tilePoint.x + tilePoint.y) % this.options.subdomains.length;
 
11651                 return this.options.subdomains[index];
 
11654         // stops loading all tiles in the background layer
 
11655         _abortLoading: function () {
 
11657                 for (i in this._tiles) {
 
11658                         if (this._tiles[i].coords.z !== this._tileZoom) {
 
11659                                 tile = this._tiles[i].el;
 
11661                                 tile.onload = falseFn;
 
11662                                 tile.onerror = falseFn;
 
11664                                 if (!tile.complete) {
 
11665                                         tile.src = emptyImageUrl;
 
11667                                         delete this._tiles[i];
 
11673         _removeTile: function (key) {
 
11674                 var tile = this._tiles[key];
 
11675                 if (!tile) { return; }
 
11677                 // Cancels any pending http requests associated with the tile
 
11678                 // unless we're on Android's stock browser,
 
11679                 // see https://github.com/Leaflet/Leaflet/issues/137
 
11680                 if (!androidStock) {
 
11681                         tile.el.setAttribute('src', emptyImageUrl);
 
11684                 return GridLayer.prototype._removeTile.call(this, key);
 
11687         _tileReady: function (coords, err, tile) {
 
11688                 if (!this._map || (tile && tile.getAttribute('src') === emptyImageUrl)) {
 
11692                 return GridLayer.prototype._tileReady.call(this, coords, err, tile);
 
11697 // @factory L.tilelayer(urlTemplate: String, options?: TileLayer options)
 
11698 // Instantiates a tile layer object given a `URL template` and optionally an options object.
 
11700 function tileLayer(url, options) {
 
11701         return new TileLayer(url, options);
 
11705  * @class TileLayer.WMS
 
11706  * @inherits TileLayer
 
11707  * @aka L.TileLayer.WMS
 
11708  * Used to display [WMS](https://en.wikipedia.org/wiki/Web_Map_Service) services as tile layers on the map. Extends `TileLayer`.
 
11713  * var nexrad = L.tileLayer.wms("http://mesonet.agron.iastate.edu/cgi-bin/wms/nexrad/n0r.cgi", {
 
11714  *      layers: 'nexrad-n0r-900913',
 
11715  *      format: 'image/png',
 
11716  *      transparent: true,
 
11717  *      attribution: "Weather data © 2012 IEM Nexrad"
 
11722 var TileLayerWMS = TileLayer.extend({
 
11725         // @aka TileLayer.WMS options
 
11726         // If any custom options not documented here are used, they will be sent to the
 
11727         // WMS server as extra parameters in each request URL. This can be useful for
 
11728         // [non-standard vendor WMS parameters](http://docs.geoserver.org/stable/en/user/services/wms/vendor.html).
 
11729         defaultWmsParams: {
 
11733                 // @option layers: String = ''
 
11734                 // **(required)** Comma-separated list of WMS layers to show.
 
11737                 // @option styles: String = ''
 
11738                 // Comma-separated list of WMS styles.
 
11741                 // @option format: String = 'image/jpeg'
 
11742                 // WMS image format (use `'image/png'` for layers with transparency).
 
11743                 format: 'image/jpeg',
 
11745                 // @option transparent: Boolean = false
 
11746                 // If `true`, the WMS service will return images with transparency.
 
11747                 transparent: false,
 
11749                 // @option version: String = '1.1.1'
 
11750                 // Version of the WMS service to use
 
11755                 // @option crs: CRS = null
 
11756                 // Coordinate Reference System to use for the WMS requests, defaults to
 
11757                 // map CRS. Don't change this if you're not sure what it means.
 
11760                 // @option uppercase: Boolean = false
 
11761                 // If `true`, WMS request parameter keys will be uppercase.
 
11765         initialize: function (url, options) {
 
11769                 var wmsParams = extend({}, this.defaultWmsParams);
 
11771                 // all keys that are not TileLayer options go to WMS params
 
11772                 for (var i in options) {
 
11773                         if (!(i in this.options)) {
 
11774                                 wmsParams[i] = options[i];
 
11778                 options = setOptions(this, options);
 
11780                 var realRetina = options.detectRetina && retina ? 2 : 1;
 
11781                 var tileSize = this.getTileSize();
 
11782                 wmsParams.width = tileSize.x * realRetina;
 
11783                 wmsParams.height = tileSize.y * realRetina;
 
11785                 this.wmsParams = wmsParams;
 
11788         onAdd: function (map) {
 
11790                 this._crs = this.options.crs || map.options.crs;
 
11791                 this._wmsVersion = parseFloat(this.wmsParams.version);
 
11793                 var projectionKey = this._wmsVersion >= 1.3 ? 'crs' : 'srs';
 
11794                 this.wmsParams[projectionKey] = this._crs.code;
 
11796                 TileLayer.prototype.onAdd.call(this, map);
 
11799         getTileUrl: function (coords) {
 
11801                 var tileBounds = this._tileCoordsToNwSe(coords),
 
11803                     bounds = toBounds(crs.project(tileBounds[0]), crs.project(tileBounds[1])),
 
11806                     bbox = (this._wmsVersion >= 1.3 && this._crs === EPSG4326 ?
 
11807                     [min.y, min.x, max.y, max.x] :
 
11808                     [min.x, min.y, max.x, max.y]).join(','),
 
11809                     url = TileLayer.prototype.getTileUrl.call(this, coords);
 
11811                         getParamString(this.wmsParams, url, this.options.uppercase) +
 
11812                         (this.options.uppercase ? '&BBOX=' : '&bbox=') + bbox;
 
11815         // @method setParams(params: Object, noRedraw?: Boolean): this
 
11816         // Merges an object with the new parameters and re-requests tiles on the current screen (unless `noRedraw` was set to true).
 
11817         setParams: function (params, noRedraw) {
 
11819                 extend(this.wmsParams, params);
 
11830 // @factory L.tileLayer.wms(baseUrl: String, options: TileLayer.WMS options)
 
11831 // Instantiates a WMS tile layer object given a base URL of the WMS service and a WMS parameters/options object.
 
11832 function tileLayerWMS(url, options) {
 
11833         return new TileLayerWMS(url, options);
 
11836 TileLayer.WMS = TileLayerWMS;
 
11837 tileLayer.wms = tileLayerWMS;
 
11844  * Base class for vector renderer implementations (`SVG`, `Canvas`). Handles the
 
11845  * DOM container of the renderer, its bounds, and its zoom animation.
 
11847  * A `Renderer` works as an implicit layer group for all `Path`s - the renderer
 
11848  * itself can be added or removed to the map. All paths use a renderer, which can
 
11849  * be implicit (the map will decide the type of renderer and use it automatically)
 
11850  * or explicit (using the [`renderer`](#path-renderer) option of the path).
 
11852  * Do not use this class directly, use `SVG` and `Canvas` instead.
 
11854  * @event update: Event
 
11855  * Fired when the renderer updates its bounds, center and zoom, for example when
 
11856  * its map has moved
 
11859 var Renderer = Layer.extend({
 
11862         // @aka Renderer options
 
11864                 // @option padding: Number = 0.1
 
11865                 // How much to extend the clip area around the map view (relative to its size)
 
11866                 // e.g. 0.1 would be 10% of map view in each direction
 
11869                 // @option tolerance: Number = 0
 
11870                 // How much to extend click tolerance round a path/object on the map
 
11874         initialize: function (options) {
 
11875                 setOptions(this, options);
 
11877                 this._layers = this._layers || {};
 
11880         onAdd: function () {
 
11881                 if (!this._container) {
 
11882                         this._initContainer(); // defined by renderer implementations
 
11884                         if (this._zoomAnimated) {
 
11885                                 addClass(this._container, 'leaflet-zoom-animated');
 
11889                 this.getPane().appendChild(this._container);
 
11891                 this.on('update', this._updatePaths, this);
 
11894         onRemove: function () {
 
11895                 this.off('update', this._updatePaths, this);
 
11896                 this._destroyContainer();
 
11899         getEvents: function () {
 
11901                         viewreset: this._reset,
 
11902                         zoom: this._onZoom,
 
11903                         moveend: this._update,
 
11904                         zoomend: this._onZoomEnd
 
11906                 if (this._zoomAnimated) {
 
11907                         events.zoomanim = this._onAnimZoom;
 
11912         _onAnimZoom: function (ev) {
 
11913                 this._updateTransform(ev.center, ev.zoom);
 
11916         _onZoom: function () {
 
11917                 this._updateTransform(this._map.getCenter(), this._map.getZoom());
 
11920         _updateTransform: function (center, zoom) {
 
11921                 var scale = this._map.getZoomScale(zoom, this._zoom),
 
11922                     position = getPosition(this._container),
 
11923                     viewHalf = this._map.getSize().multiplyBy(0.5 + this.options.padding),
 
11924                     currentCenterPoint = this._map.project(this._center, zoom),
 
11925                     destCenterPoint = this._map.project(center, zoom),
 
11926                     centerOffset = destCenterPoint.subtract(currentCenterPoint),
 
11928                     topLeftOffset = viewHalf.multiplyBy(-scale).add(position).add(viewHalf).subtract(centerOffset);
 
11931                         setTransform(this._container, topLeftOffset, scale);
 
11933                         setPosition(this._container, topLeftOffset);
 
11937         _reset: function () {
 
11939                 this._updateTransform(this._center, this._zoom);
 
11941                 for (var id in this._layers) {
 
11942                         this._layers[id]._reset();
 
11946         _onZoomEnd: function () {
 
11947                 for (var id in this._layers) {
 
11948                         this._layers[id]._project();
 
11952         _updatePaths: function () {
 
11953                 for (var id in this._layers) {
 
11954                         this._layers[id]._update();
 
11958         _update: function () {
 
11959                 // Update pixel bounds of renderer container (for positioning/sizing/clipping later)
 
11960                 // Subclasses are responsible of firing the 'update' event.
 
11961                 var p = this.options.padding,
 
11962                     size = this._map.getSize(),
 
11963                     min = this._map.containerPointToLayerPoint(size.multiplyBy(-p)).round();
 
11965                 this._bounds = new Bounds(min, min.add(size.multiplyBy(1 + p * 2)).round());
 
11967                 this._center = this._map.getCenter();
 
11968                 this._zoom = this._map.getZoom();
 
11974  * @inherits Renderer
 
11977  * Allows vector layers to be displayed with [`<canvas>`](https://developer.mozilla.org/docs/Web/API/Canvas_API).
 
11978  * Inherits `Renderer`.
 
11980  * Due to [technical limitations](http://caniuse.com/#search=canvas), Canvas is not
 
11981  * available in all web browsers, notably IE8, and overlapping geometries might
 
11982  * not display properly in some edge cases.
 
11986  * Use Canvas by default for all paths in the map:
 
11989  * var map = L.map('map', {
 
11990  *      renderer: L.canvas()
 
11994  * Use a Canvas renderer with extra padding for specific vector geometries:
 
11997  * var map = L.map('map');
 
11998  * var myRenderer = L.canvas({ padding: 0.5 });
 
11999  * var line = L.polyline( coordinates, { renderer: myRenderer } );
 
12000  * var circle = L.circle( center, { renderer: myRenderer } );
 
12004 var Canvas = Renderer.extend({
 
12005         getEvents: function () {
 
12006                 var events = Renderer.prototype.getEvents.call(this);
 
12007                 events.viewprereset = this._onViewPreReset;
 
12011         _onViewPreReset: function () {
 
12012                 // Set a flag so that a viewprereset+moveend+viewreset only updates&redraws once
 
12013                 this._postponeUpdatePaths = true;
 
12016         onAdd: function () {
 
12017                 Renderer.prototype.onAdd.call(this);
 
12019                 // Redraw vectors since canvas is cleared upon removal,
 
12020                 // in case of removing the renderer itself from the map.
 
12024         _initContainer: function () {
 
12025                 var container = this._container = document.createElement('canvas');
 
12027                 on(container, 'mousemove', throttle(this._onMouseMove, 32, this), this);
 
12028                 on(container, 'click dblclick mousedown mouseup contextmenu', this._onClick, this);
 
12029                 on(container, 'mouseout', this._handleMouseOut, this);
 
12031                 this._ctx = container.getContext('2d');
 
12034         _destroyContainer: function () {
 
12035                 cancelAnimFrame(this._redrawRequest);
 
12037                 remove(this._container);
 
12038                 off(this._container);
 
12039                 delete this._container;
 
12042         _updatePaths: function () {
 
12043                 if (this._postponeUpdatePaths) { return; }
 
12046                 this._redrawBounds = null;
 
12047                 for (var id in this._layers) {
 
12048                         layer = this._layers[id];
 
12054         _update: function () {
 
12055                 if (this._map._animatingZoom && this._bounds) { return; }
 
12057                 Renderer.prototype._update.call(this);
 
12059                 var b = this._bounds,
 
12060                     container = this._container,
 
12061                     size = b.getSize(),
 
12062                     m = retina ? 2 : 1;
 
12064                 setPosition(container, b.min);
 
12066                 // set canvas size (also clearing it); use double size on retina
 
12067                 container.width = m * size.x;
 
12068                 container.height = m * size.y;
 
12069                 container.style.width = size.x + 'px';
 
12070                 container.style.height = size.y + 'px';
 
12073                         this._ctx.scale(2, 2);
 
12076                 // translate so we use the same path coordinates after canvas element moves
 
12077                 this._ctx.translate(-b.min.x, -b.min.y);
 
12079                 // Tell paths to redraw themselves
 
12080                 this.fire('update');
 
12083         _reset: function () {
 
12084                 Renderer.prototype._reset.call(this);
 
12086                 if (this._postponeUpdatePaths) {
 
12087                         this._postponeUpdatePaths = false;
 
12088                         this._updatePaths();
 
12092         _initPath: function (layer) {
 
12093                 this._updateDashArray(layer);
 
12094                 this._layers[stamp(layer)] = layer;
 
12096                 var order = layer._order = {
 
12098                         prev: this._drawLast,
 
12101                 if (this._drawLast) { this._drawLast.next = order; }
 
12102                 this._drawLast = order;
 
12103                 this._drawFirst = this._drawFirst || this._drawLast;
 
12106         _addPath: function (layer) {
 
12107                 this._requestRedraw(layer);
 
12110         _removePath: function (layer) {
 
12111                 var order = layer._order;
 
12112                 var next = order.next;
 
12113                 var prev = order.prev;
 
12118                         this._drawLast = prev;
 
12123                         this._drawFirst = next;
 
12126                 delete layer._order;
 
12128                 delete this._layers[stamp(layer)];
 
12130                 this._requestRedraw(layer);
 
12133         _updatePath: function (layer) {
 
12134                 // Redraw the union of the layer's old pixel
 
12135                 // bounds and the new pixel bounds.
 
12136                 this._extendRedrawBounds(layer);
 
12139                 // The redraw will extend the redraw bounds
 
12140                 // with the new pixel bounds.
 
12141                 this._requestRedraw(layer);
 
12144         _updateStyle: function (layer) {
 
12145                 this._updateDashArray(layer);
 
12146                 this._requestRedraw(layer);
 
12149         _updateDashArray: function (layer) {
 
12150                 if (typeof layer.options.dashArray === 'string') {
 
12151                         var parts = layer.options.dashArray.split(/[, ]+/),
 
12155                         for (i = 0; i < parts.length; i++) {
 
12156                                 dashValue = Number(parts[i]);
 
12157                                 // Ignore dash array containing invalid lengths
 
12158                                 if (isNaN(dashValue)) { return; }
 
12159                                 dashArray.push(dashValue);
 
12161                         layer.options._dashArray = dashArray;
 
12163                         layer.options._dashArray = layer.options.dashArray;
 
12167         _requestRedraw: function (layer) {
 
12168                 if (!this._map) { return; }
 
12170                 this._extendRedrawBounds(layer);
 
12171                 this._redrawRequest = this._redrawRequest || requestAnimFrame(this._redraw, this);
 
12174         _extendRedrawBounds: function (layer) {
 
12175                 if (layer._pxBounds) {
 
12176                         var padding = (layer.options.weight || 0) + 1;
 
12177                         this._redrawBounds = this._redrawBounds || new Bounds();
 
12178                         this._redrawBounds.extend(layer._pxBounds.min.subtract([padding, padding]));
 
12179                         this._redrawBounds.extend(layer._pxBounds.max.add([padding, padding]));
 
12183         _redraw: function () {
 
12184                 this._redrawRequest = null;
 
12186                 if (this._redrawBounds) {
 
12187                         this._redrawBounds.min._floor();
 
12188                         this._redrawBounds.max._ceil();
 
12191                 this._clear(); // clear layers in redraw bounds
 
12192                 this._draw(); // draw layers
 
12194                 this._redrawBounds = null;
 
12197         _clear: function () {
 
12198                 var bounds = this._redrawBounds;
 
12200                         var size = bounds.getSize();
 
12201                         this._ctx.clearRect(bounds.min.x, bounds.min.y, size.x, size.y);
 
12203                         this._ctx.clearRect(0, 0, this._container.width, this._container.height);
 
12207         _draw: function () {
 
12208                 var layer, bounds = this._redrawBounds;
 
12211                         var size = bounds.getSize();
 
12212                         this._ctx.beginPath();
 
12213                         this._ctx.rect(bounds.min.x, bounds.min.y, size.x, size.y);
 
12217                 this._drawing = true;
 
12219                 for (var order = this._drawFirst; order; order = order.next) {
 
12220                         layer = order.layer;
 
12221                         if (!bounds || (layer._pxBounds && layer._pxBounds.intersects(bounds))) {
 
12222                                 layer._updatePath();
 
12226                 this._drawing = false;
 
12228                 this._ctx.restore();  // Restore state before clipping.
 
12231         _updatePoly: function (layer, closed) {
 
12232                 if (!this._drawing) { return; }
 
12235                     parts = layer._parts,
 
12236                     len = parts.length,
 
12239                 if (!len) { return; }
 
12243                 for (i = 0; i < len; i++) {
 
12244                         for (j = 0, len2 = parts[i].length; j < len2; j++) {
 
12246                                 ctx[j ? 'lineTo' : 'moveTo'](p.x, p.y);
 
12253                 this._fillStroke(ctx, layer);
 
12255                 // TODO optimization: 1 fill/stroke for all features with equal style instead of 1 for each feature
 
12258         _updateCircle: function (layer) {
 
12260                 if (!this._drawing || layer._empty()) { return; }
 
12262                 var p = layer._point,
 
12264                     r = Math.max(Math.round(layer._radius), 1),
 
12265                     s = (Math.max(Math.round(layer._radiusY), 1) || r) / r;
 
12273                 ctx.arc(p.x, p.y / s, r, 0, Math.PI * 2, false);
 
12279                 this._fillStroke(ctx, layer);
 
12282         _fillStroke: function (ctx, layer) {
 
12283                 var options = layer.options;
 
12285                 if (options.fill) {
 
12286                         ctx.globalAlpha = options.fillOpacity;
 
12287                         ctx.fillStyle = options.fillColor || options.color;
 
12288                         ctx.fill(options.fillRule || 'evenodd');
 
12291                 if (options.stroke && options.weight !== 0) {
 
12292                         if (ctx.setLineDash) {
 
12293                                 ctx.setLineDash(layer.options && layer.options._dashArray || []);
 
12295                         ctx.globalAlpha = options.opacity;
 
12296                         ctx.lineWidth = options.weight;
 
12297                         ctx.strokeStyle = options.color;
 
12298                         ctx.lineCap = options.lineCap;
 
12299                         ctx.lineJoin = options.lineJoin;
 
12304         // Canvas obviously doesn't have mouse events for individual drawn objects,
 
12305         // so we emulate that by calculating what's under the mouse on mousemove/click manually
 
12307         _onClick: function (e) {
 
12308                 var point = this._map.mouseEventToLayerPoint(e), layer, clickedLayer;
 
12310                 for (var order = this._drawFirst; order; order = order.next) {
 
12311                         layer = order.layer;
 
12312                         if (layer.options.interactive && layer._containsPoint(point) && !this._map._draggableMoved(layer)) {
 
12313                                 clickedLayer = layer;
 
12316                 if (clickedLayer)  {
 
12318                         this._fireEvent([clickedLayer], e);
 
12322         _onMouseMove: function (e) {
 
12323                 if (!this._map || this._map.dragging.moving() || this._map._animatingZoom) { return; }
 
12325                 var point = this._map.mouseEventToLayerPoint(e);
 
12326                 this._handleMouseHover(e, point);
 
12330         _handleMouseOut: function (e) {
 
12331                 var layer = this._hoveredLayer;
 
12333                         // if we're leaving the layer, fire mouseout
 
12334                         removeClass(this._container, 'leaflet-interactive');
 
12335                         this._fireEvent([layer], e, 'mouseout');
 
12336                         this._hoveredLayer = null;
 
12340         _handleMouseHover: function (e, point) {
 
12341                 var layer, candidateHoveredLayer;
 
12343                 for (var order = this._drawFirst; order; order = order.next) {
 
12344                         layer = order.layer;
 
12345                         if (layer.options.interactive && layer._containsPoint(point)) {
 
12346                                 candidateHoveredLayer = layer;
 
12350                 if (candidateHoveredLayer !== this._hoveredLayer) {
 
12351                         this._handleMouseOut(e);
 
12353                         if (candidateHoveredLayer) {
 
12354                                 addClass(this._container, 'leaflet-interactive'); // change cursor
 
12355                                 this._fireEvent([candidateHoveredLayer], e, 'mouseover');
 
12356                                 this._hoveredLayer = candidateHoveredLayer;
 
12360                 if (this._hoveredLayer) {
 
12361                         this._fireEvent([this._hoveredLayer], e);
 
12365         _fireEvent: function (layers, e, type) {
 
12366                 this._map._fireDOMEvent(e, type || e.type, layers);
 
12369         _bringToFront: function (layer) {
 
12370                 var order = layer._order;
 
12372                 if (!order) { return; }
 
12374                 var next = order.next;
 
12375                 var prev = order.prev;
 
12386                         // Update first entry unless this is the
 
12388                         this._drawFirst = next;
 
12391                 order.prev = this._drawLast;
 
12392                 this._drawLast.next = order;
 
12395                 this._drawLast = order;
 
12397                 this._requestRedraw(layer);
 
12400         _bringToBack: function (layer) {
 
12401                 var order = layer._order;
 
12403                 if (!order) { return; }
 
12405                 var next = order.next;
 
12406                 var prev = order.prev;
 
12417                         // Update last entry unless this is the
 
12419                         this._drawLast = prev;
 
12424                 order.next = this._drawFirst;
 
12425                 this._drawFirst.prev = order;
 
12426                 this._drawFirst = order;
 
12428                 this._requestRedraw(layer);
 
12432 // @factory L.canvas(options?: Renderer options)
 
12433 // Creates a Canvas renderer with the given options.
 
12434 function canvas$1(options) {
 
12435         return canvas ? new Canvas(options) : null;
 
12439  * Thanks to Dmitry Baranovsky and his Raphael library for inspiration!
 
12443 var vmlCreate = (function () {
 
12445                 document.namespaces.add('lvml', 'urn:schemas-microsoft-com:vml');
 
12446                 return function (name) {
 
12447                         return document.createElement('<lvml:' + name + ' class="lvml">');
 
12450                 return function (name) {
 
12451                         return document.createElement('<' + name + ' xmlns="urn:schemas-microsoft.com:vml" class="lvml">');
 
12461  * VML was deprecated in 2012, which means VML functionality exists only for backwards compatibility
 
12462  * with old versions of Internet Explorer.
 
12465 // mixin to redefine some SVG methods to handle VML syntax which is similar but with some differences
 
12468         _initContainer: function () {
 
12469                 this._container = create$1('div', 'leaflet-vml-container');
 
12472         _update: function () {
 
12473                 if (this._map._animatingZoom) { return; }
 
12474                 Renderer.prototype._update.call(this);
 
12475                 this.fire('update');
 
12478         _initPath: function (layer) {
 
12479                 var container = layer._container = vmlCreate('shape');
 
12481                 addClass(container, 'leaflet-vml-shape ' + (this.options.className || ''));
 
12483                 container.coordsize = '1 1';
 
12485                 layer._path = vmlCreate('path');
 
12486                 container.appendChild(layer._path);
 
12488                 this._updateStyle(layer);
 
12489                 this._layers[stamp(layer)] = layer;
 
12492         _addPath: function (layer) {
 
12493                 var container = layer._container;
 
12494                 this._container.appendChild(container);
 
12496                 if (layer.options.interactive) {
 
12497                         layer.addInteractiveTarget(container);
 
12501         _removePath: function (layer) {
 
12502                 var container = layer._container;
 
12504                 layer.removeInteractiveTarget(container);
 
12505                 delete this._layers[stamp(layer)];
 
12508         _updateStyle: function (layer) {
 
12509                 var stroke = layer._stroke,
 
12510                     fill = layer._fill,
 
12511                     options = layer.options,
 
12512                     container = layer._container;
 
12514                 container.stroked = !!options.stroke;
 
12515                 container.filled = !!options.fill;
 
12517                 if (options.stroke) {
 
12519                                 stroke = layer._stroke = vmlCreate('stroke');
 
12521                         container.appendChild(stroke);
 
12522                         stroke.weight = options.weight + 'px';
 
12523                         stroke.color = options.color;
 
12524                         stroke.opacity = options.opacity;
 
12526                         if (options.dashArray) {
 
12527                                 stroke.dashStyle = isArray(options.dashArray) ?
 
12528                                     options.dashArray.join(' ') :
 
12529                                     options.dashArray.replace(/( *, *)/g, ' ');
 
12531                                 stroke.dashStyle = '';
 
12533                         stroke.endcap = options.lineCap.replace('butt', 'flat');
 
12534                         stroke.joinstyle = options.lineJoin;
 
12536                 } else if (stroke) {
 
12537                         container.removeChild(stroke);
 
12538                         layer._stroke = null;
 
12541                 if (options.fill) {
 
12543                                 fill = layer._fill = vmlCreate('fill');
 
12545                         container.appendChild(fill);
 
12546                         fill.color = options.fillColor || options.color;
 
12547                         fill.opacity = options.fillOpacity;
 
12550                         container.removeChild(fill);
 
12551                         layer._fill = null;
 
12555         _updateCircle: function (layer) {
 
12556                 var p = layer._point.round(),
 
12557                     r = Math.round(layer._radius),
 
12558                     r2 = Math.round(layer._radiusY || r);
 
12560                 this._setPath(layer, layer._empty() ? 'M0 0' :
 
12561                         'AL ' + p.x + ',' + p.y + ' ' + r + ',' + r2 + ' 0,' + (65535 * 360));
 
12564         _setPath: function (layer, path) {
 
12565                 layer._path.v = path;
 
12568         _bringToFront: function (layer) {
 
12569                 toFront(layer._container);
 
12572         _bringToBack: function (layer) {
 
12573                 toBack(layer._container);
 
12577 var create$2 = vml ? vmlCreate : svgCreate;
 
12581  * @inherits Renderer
 
12584  * Allows vector layers to be displayed with [SVG](https://developer.mozilla.org/docs/Web/SVG).
 
12585  * Inherits `Renderer`.
 
12587  * Due to [technical limitations](http://caniuse.com/#search=svg), SVG is not
 
12588  * available in all web browsers, notably Android 2.x and 3.x.
 
12590  * Although SVG is not available on IE7 and IE8, these browsers support
 
12591  * [VML](https://en.wikipedia.org/wiki/Vector_Markup_Language)
 
12592  * (a now deprecated technology), and the SVG renderer will fall back to VML in
 
12597  * Use SVG by default for all paths in the map:
 
12600  * var map = L.map('map', {
 
12601  *      renderer: L.svg()
 
12605  * Use a SVG renderer with extra padding for specific vector geometries:
 
12608  * var map = L.map('map');
 
12609  * var myRenderer = L.svg({ padding: 0.5 });
 
12610  * var line = L.polyline( coordinates, { renderer: myRenderer } );
 
12611  * var circle = L.circle( center, { renderer: myRenderer } );
 
12615 var SVG = Renderer.extend({
 
12617         getEvents: function () {
 
12618                 var events = Renderer.prototype.getEvents.call(this);
 
12619                 events.zoomstart = this._onZoomStart;
 
12623         _initContainer: function () {
 
12624                 this._container = create$2('svg');
 
12626                 // makes it possible to click through svg root; we'll reset it back in individual paths
 
12627                 this._container.setAttribute('pointer-events', 'none');
 
12629                 this._rootGroup = create$2('g');
 
12630                 this._container.appendChild(this._rootGroup);
 
12633         _destroyContainer: function () {
 
12634                 remove(this._container);
 
12635                 off(this._container);
 
12636                 delete this._container;
 
12637                 delete this._rootGroup;
 
12638                 delete this._svgSize;
 
12641         _onZoomStart: function () {
 
12642                 // Drag-then-pinch interactions might mess up the center and zoom.
 
12643                 // In this case, the easiest way to prevent this is re-do the renderer
 
12644                 //   bounds and padding when the zooming starts.
 
12648         _update: function () {
 
12649                 if (this._map._animatingZoom && this._bounds) { return; }
 
12651                 Renderer.prototype._update.call(this);
 
12653                 var b = this._bounds,
 
12654                     size = b.getSize(),
 
12655                     container = this._container;
 
12657                 // set size of svg-container if changed
 
12658                 if (!this._svgSize || !this._svgSize.equals(size)) {
 
12659                         this._svgSize = size;
 
12660                         container.setAttribute('width', size.x);
 
12661                         container.setAttribute('height', size.y);
 
12664                 // movement: update container viewBox so that we don't have to change coordinates of individual layers
 
12665                 setPosition(container, b.min);
 
12666                 container.setAttribute('viewBox', [b.min.x, b.min.y, size.x, size.y].join(' '));
 
12668                 this.fire('update');
 
12671         // methods below are called by vector layers implementations
 
12673         _initPath: function (layer) {
 
12674                 var path = layer._path = create$2('path');
 
12677                 // @option className: String = null
 
12678                 // Custom class name set on an element. Only for SVG renderer.
 
12679                 if (layer.options.className) {
 
12680                         addClass(path, layer.options.className);
 
12683                 if (layer.options.interactive) {
 
12684                         addClass(path, 'leaflet-interactive');
 
12687                 this._updateStyle(layer);
 
12688                 this._layers[stamp(layer)] = layer;
 
12691         _addPath: function (layer) {
 
12692                 if (!this._rootGroup) { this._initContainer(); }
 
12693                 this._rootGroup.appendChild(layer._path);
 
12694                 layer.addInteractiveTarget(layer._path);
 
12697         _removePath: function (layer) {
 
12698                 remove(layer._path);
 
12699                 layer.removeInteractiveTarget(layer._path);
 
12700                 delete this._layers[stamp(layer)];
 
12703         _updatePath: function (layer) {
 
12708         _updateStyle: function (layer) {
 
12709                 var path = layer._path,
 
12710                     options = layer.options;
 
12712                 if (!path) { return; }
 
12714                 if (options.stroke) {
 
12715                         path.setAttribute('stroke', options.color);
 
12716                         path.setAttribute('stroke-opacity', options.opacity);
 
12717                         path.setAttribute('stroke-width', options.weight);
 
12718                         path.setAttribute('stroke-linecap', options.lineCap);
 
12719                         path.setAttribute('stroke-linejoin', options.lineJoin);
 
12721                         if (options.dashArray) {
 
12722                                 path.setAttribute('stroke-dasharray', options.dashArray);
 
12724                                 path.removeAttribute('stroke-dasharray');
 
12727                         if (options.dashOffset) {
 
12728                                 path.setAttribute('stroke-dashoffset', options.dashOffset);
 
12730                                 path.removeAttribute('stroke-dashoffset');
 
12733                         path.setAttribute('stroke', 'none');
 
12736                 if (options.fill) {
 
12737                         path.setAttribute('fill', options.fillColor || options.color);
 
12738                         path.setAttribute('fill-opacity', options.fillOpacity);
 
12739                         path.setAttribute('fill-rule', options.fillRule || 'evenodd');
 
12741                         path.setAttribute('fill', 'none');
 
12745         _updatePoly: function (layer, closed) {
 
12746                 this._setPath(layer, pointsToPath(layer._parts, closed));
 
12749         _updateCircle: function (layer) {
 
12750                 var p = layer._point,
 
12751                     r = Math.max(Math.round(layer._radius), 1),
 
12752                     r2 = Math.max(Math.round(layer._radiusY), 1) || r,
 
12753                     arc = 'a' + r + ',' + r2 + ' 0 1,0 ';
 
12755                 // drawing a circle with two half-arcs
 
12756                 var d = layer._empty() ? 'M0 0' :
 
12757                         'M' + (p.x - r) + ',' + p.y +
 
12758                         arc + (r * 2) + ',0 ' +
 
12759                         arc + (-r * 2) + ',0 ';
 
12761                 this._setPath(layer, d);
 
12764         _setPath: function (layer, path) {
 
12765                 layer._path.setAttribute('d', path);
 
12768         // SVG does not have the concept of zIndex so we resort to changing the DOM order of elements
 
12769         _bringToFront: function (layer) {
 
12770                 toFront(layer._path);
 
12773         _bringToBack: function (layer) {
 
12774                 toBack(layer._path);
 
12779         SVG.include(vmlMixin);
 
12783 // @factory L.svg(options?: Renderer options)
 
12784 // Creates a SVG renderer with the given options.
 
12785 function svg$1(options) {
 
12786         return svg || vml ? new SVG(options) : null;
 
12790         // @namespace Map; @method getRenderer(layer: Path): Renderer
 
12791         // Returns the instance of `Renderer` that should be used to render the given
 
12792         // `Path`. It will ensure that the `renderer` options of the map and paths
 
12793         // are respected, and that the renderers do exist on the map.
 
12794         getRenderer: function (layer) {
 
12795                 // @namespace Path; @option renderer: Renderer
 
12796                 // Use this specific instance of `Renderer` for this path. Takes
 
12797                 // precedence over the map's [default renderer](#map-renderer).
 
12798                 var renderer = layer.options.renderer || this._getPaneRenderer(layer.options.pane) || this.options.renderer || this._renderer;
 
12801                         renderer = this._renderer = this._createRenderer();
 
12804                 if (!this.hasLayer(renderer)) {
 
12805                         this.addLayer(renderer);
 
12810         _getPaneRenderer: function (name) {
 
12811                 if (name === 'overlayPane' || name === undefined) {
 
12815                 var renderer = this._paneRenderers[name];
 
12816                 if (renderer === undefined) {
 
12817                         renderer = this._createRenderer({pane: name});
 
12818                         this._paneRenderers[name] = renderer;
 
12823         _createRenderer: function (options) {
 
12824                 // @namespace Map; @option preferCanvas: Boolean = false
 
12825                 // Whether `Path`s should be rendered on a `Canvas` renderer.
 
12826                 // By default, all `Path`s are rendered in a `SVG` renderer.
 
12827                 return (this.options.preferCanvas && canvas$1(options)) || svg$1(options);
 
12832  * L.Rectangle extends Polygon and creates a rectangle when passed a LatLngBounds object.
 
12838  * @inherits Polygon
 
12840  * A class for drawing rectangle overlays on a map. Extends `Polygon`.
 
12845  * // define rectangle geographical bounds
 
12846  * var bounds = [[54.559322, -5.767822], [56.1210604, -3.021240]];
 
12848  * // create an orange rectangle
 
12849  * L.rectangle(bounds, {color: "#ff7800", weight: 1}).addTo(map);
 
12851  * // zoom the map to the rectangle bounds
 
12852  * map.fitBounds(bounds);
 
12858 var Rectangle = Polygon.extend({
 
12859         initialize: function (latLngBounds, options) {
 
12860                 Polygon.prototype.initialize.call(this, this._boundsToLatLngs(latLngBounds), options);
 
12863         // @method setBounds(latLngBounds: LatLngBounds): this
 
12864         // Redraws the rectangle with the passed bounds.
 
12865         setBounds: function (latLngBounds) {
 
12866                 return this.setLatLngs(this._boundsToLatLngs(latLngBounds));
 
12869         _boundsToLatLngs: function (latLngBounds) {
 
12870                 latLngBounds = toLatLngBounds(latLngBounds);
 
12872                         latLngBounds.getSouthWest(),
 
12873                         latLngBounds.getNorthWest(),
 
12874                         latLngBounds.getNorthEast(),
 
12875                         latLngBounds.getSouthEast()
 
12881 // @factory L.rectangle(latLngBounds: LatLngBounds, options?: Polyline options)
 
12882 function rectangle(latLngBounds, options) {
 
12883         return new Rectangle(latLngBounds, options);
 
12886 SVG.create = create$2;
 
12887 SVG.pointsToPath = pointsToPath;
 
12889 GeoJSON.geometryToLayer = geometryToLayer;
 
12890 GeoJSON.coordsToLatLng = coordsToLatLng;
 
12891 GeoJSON.coordsToLatLngs = coordsToLatLngs;
 
12892 GeoJSON.latLngToCoords = latLngToCoords;
 
12893 GeoJSON.latLngsToCoords = latLngsToCoords;
 
12894 GeoJSON.getFeature = getFeature;
 
12895 GeoJSON.asFeature = asFeature;
 
12898  * L.Handler.BoxZoom is used to add shift-drag zoom interaction to the map
 
12899  * (zoom to a selected bounding box), enabled by default.
 
12903 // @section Interaction Options
 
12905         // @option boxZoom: Boolean = true
 
12906         // Whether the map can be zoomed to a rectangular area specified by
 
12907         // dragging the mouse while pressing the shift key.
 
12911 var BoxZoom = Handler.extend({
 
12912         initialize: function (map) {
 
12914                 this._container = map._container;
 
12915                 this._pane = map._panes.overlayPane;
 
12916                 this._resetStateTimeout = 0;
 
12917                 map.on('unload', this._destroy, this);
 
12920         addHooks: function () {
 
12921                 on(this._container, 'mousedown', this._onMouseDown, this);
 
12924         removeHooks: function () {
 
12925                 off(this._container, 'mousedown', this._onMouseDown, this);
 
12928         moved: function () {
 
12929                 return this._moved;
 
12932         _destroy: function () {
 
12933                 remove(this._pane);
 
12937         _resetState: function () {
 
12938                 this._resetStateTimeout = 0;
 
12939                 this._moved = false;
 
12942         _clearDeferredResetState: function () {
 
12943                 if (this._resetStateTimeout !== 0) {
 
12944                         clearTimeout(this._resetStateTimeout);
 
12945                         this._resetStateTimeout = 0;
 
12949         _onMouseDown: function (e) {
 
12950                 if (!e.shiftKey || ((e.which !== 1) && (e.button !== 1))) { return false; }
 
12952                 // Clear the deferred resetState if it hasn't executed yet, otherwise it
 
12953                 // will interrupt the interaction and orphan a box element in the container.
 
12954                 this._clearDeferredResetState();
 
12955                 this._resetState();
 
12957                 disableTextSelection();
 
12958                 disableImageDrag();
 
12960                 this._startPoint = this._map.mouseEventToContainerPoint(e);
 
12964                         mousemove: this._onMouseMove,
 
12965                         mouseup: this._onMouseUp,
 
12966                         keydown: this._onKeyDown
 
12970         _onMouseMove: function (e) {
 
12971                 if (!this._moved) {
 
12972                         this._moved = true;
 
12974                         this._box = create$1('div', 'leaflet-zoom-box', this._container);
 
12975                         addClass(this._container, 'leaflet-crosshair');
 
12977                         this._map.fire('boxzoomstart');
 
12980                 this._point = this._map.mouseEventToContainerPoint(e);
 
12982                 var bounds = new Bounds(this._point, this._startPoint),
 
12983                     size = bounds.getSize();
 
12985                 setPosition(this._box, bounds.min);
 
12987                 this._box.style.width  = size.x + 'px';
 
12988                 this._box.style.height = size.y + 'px';
 
12991         _finish: function () {
 
12994                         removeClass(this._container, 'leaflet-crosshair');
 
12997                 enableTextSelection();
 
13002                         mousemove: this._onMouseMove,
 
13003                         mouseup: this._onMouseUp,
 
13004                         keydown: this._onKeyDown
 
13008         _onMouseUp: function (e) {
 
13009                 if ((e.which !== 1) && (e.button !== 1)) { return; }
 
13013                 if (!this._moved) { return; }
 
13014                 // Postpone to next JS tick so internal click event handling
 
13015                 // still see it as "moved".
 
13016                 this._clearDeferredResetState();
 
13017                 this._resetStateTimeout = setTimeout(bind(this._resetState, this), 0);
 
13019                 var bounds = new LatLngBounds(
 
13020                         this._map.containerPointToLatLng(this._startPoint),
 
13021                         this._map.containerPointToLatLng(this._point));
 
13025                         .fire('boxzoomend', {boxZoomBounds: bounds});
 
13028         _onKeyDown: function (e) {
 
13029                 if (e.keyCode === 27) {
 
13035 // @section Handlers
 
13036 // @property boxZoom: Handler
 
13037 // Box (shift-drag with mouse) zoom handler.
 
13038 Map.addInitHook('addHandler', 'boxZoom', BoxZoom);
 
13041  * L.Handler.DoubleClickZoom is used to handle double-click zoom on the map, enabled by default.
 
13045 // @section Interaction Options
 
13048         // @option doubleClickZoom: Boolean|String = true
 
13049         // Whether the map can be zoomed in by double clicking on it and
 
13050         // zoomed out by double clicking while holding shift. If passed
 
13051         // `'center'`, double-click zoom will zoom to the center of the
 
13052         //  view regardless of where the mouse was.
 
13053         doubleClickZoom: true
 
13056 var DoubleClickZoom = Handler.extend({
 
13057         addHooks: function () {
 
13058                 this._map.on('dblclick', this._onDoubleClick, this);
 
13061         removeHooks: function () {
 
13062                 this._map.off('dblclick', this._onDoubleClick, this);
 
13065         _onDoubleClick: function (e) {
 
13066                 var map = this._map,
 
13067                     oldZoom = map.getZoom(),
 
13068                     delta = map.options.zoomDelta,
 
13069                     zoom = e.originalEvent.shiftKey ? oldZoom - delta : oldZoom + delta;
 
13071                 if (map.options.doubleClickZoom === 'center') {
 
13074                         map.setZoomAround(e.containerPoint, zoom);
 
13079 // @section Handlers
 
13081 // Map properties include interaction handlers that allow you to control
 
13082 // interaction behavior in runtime, enabling or disabling certain features such
 
13083 // as dragging or touch zoom (see `Handler` methods). For example:
 
13086 // map.doubleClickZoom.disable();
 
13089 // @property doubleClickZoom: Handler
 
13090 // Double click zoom handler.
 
13091 Map.addInitHook('addHandler', 'doubleClickZoom', DoubleClickZoom);
 
13094  * L.Handler.MapDrag is used to make the map draggable (with panning inertia), enabled by default.
 
13098 // @section Interaction Options
 
13100         // @option dragging: Boolean = true
 
13101         // Whether the map be draggable with mouse/touch or not.
 
13104         // @section Panning Inertia Options
 
13105         // @option inertia: Boolean = *
 
13106         // If enabled, panning of the map will have an inertia effect where
 
13107         // the map builds momentum while dragging and continues moving in
 
13108         // the same direction for some time. Feels especially nice on touch
 
13109         // devices. Enabled by default unless running on old Android devices.
 
13110         inertia: !android23,
 
13112         // @option inertiaDeceleration: Number = 3000
 
13113         // The rate with which the inertial movement slows down, in pixels/second².
 
13114         inertiaDeceleration: 3400, // px/s^2
 
13116         // @option inertiaMaxSpeed: Number = Infinity
 
13117         // Max speed of the inertial movement, in pixels/second.
 
13118         inertiaMaxSpeed: Infinity, // px/s
 
13120         // @option easeLinearity: Number = 0.2
 
13121         easeLinearity: 0.2,
 
13123         // TODO refactor, move to CRS
 
13124         // @option worldCopyJump: Boolean = false
 
13125         // With this option enabled, the map tracks when you pan to another "copy"
 
13126         // of the world and seamlessly jumps to the original one so that all overlays
 
13127         // like markers and vector layers are still visible.
 
13128         worldCopyJump: false,
 
13130         // @option maxBoundsViscosity: Number = 0.0
 
13131         // If `maxBounds` is set, this option will control how solid the bounds
 
13132         // are when dragging the map around. The default value of `0.0` allows the
 
13133         // user to drag outside the bounds at normal speed, higher values will
 
13134         // slow down map dragging outside bounds, and `1.0` makes the bounds fully
 
13135         // solid, preventing the user from dragging outside the bounds.
 
13136         maxBoundsViscosity: 0.0
 
13139 var Drag = Handler.extend({
 
13140         addHooks: function () {
 
13141                 if (!this._draggable) {
 
13142                         var map = this._map;
 
13144                         this._draggable = new Draggable(map._mapPane, map._container);
 
13146                         this._draggable.on({
 
13147                                 dragstart: this._onDragStart,
 
13148                                 drag: this._onDrag,
 
13149                                 dragend: this._onDragEnd
 
13152                         this._draggable.on('predrag', this._onPreDragLimit, this);
 
13153                         if (map.options.worldCopyJump) {
 
13154                                 this._draggable.on('predrag', this._onPreDragWrap, this);
 
13155                                 map.on('zoomend', this._onZoomEnd, this);
 
13157                                 map.whenReady(this._onZoomEnd, this);
 
13160                 addClass(this._map._container, 'leaflet-grab leaflet-touch-drag');
 
13161                 this._draggable.enable();
 
13162                 this._positions = [];
 
13166         removeHooks: function () {
 
13167                 removeClass(this._map._container, 'leaflet-grab');
 
13168                 removeClass(this._map._container, 'leaflet-touch-drag');
 
13169                 this._draggable.disable();
 
13172         moved: function () {
 
13173                 return this._draggable && this._draggable._moved;
 
13176         moving: function () {
 
13177                 return this._draggable && this._draggable._moving;
 
13180         _onDragStart: function () {
 
13181                 var map = this._map;
 
13184                 if (this._map.options.maxBounds && this._map.options.maxBoundsViscosity) {
 
13185                         var bounds = toLatLngBounds(this._map.options.maxBounds);
 
13187                         this._offsetLimit = toBounds(
 
13188                                 this._map.latLngToContainerPoint(bounds.getNorthWest()).multiplyBy(-1),
 
13189                                 this._map.latLngToContainerPoint(bounds.getSouthEast()).multiplyBy(-1)
 
13190                                         .add(this._map.getSize()));
 
13192                         this._viscosity = Math.min(1.0, Math.max(0.0, this._map.options.maxBoundsViscosity));
 
13194                         this._offsetLimit = null;
 
13199                     .fire('dragstart');
 
13201                 if (map.options.inertia) {
 
13202                         this._positions = [];
 
13207         _onDrag: function (e) {
 
13208                 if (this._map.options.inertia) {
 
13209                         var time = this._lastTime = +new Date(),
 
13210                             pos = this._lastPos = this._draggable._absPos || this._draggable._newPos;
 
13212                         this._positions.push(pos);
 
13213                         this._times.push(time);
 
13215                         this._prunePositions(time);
 
13223         _prunePositions: function (time) {
 
13224                 while (this._positions.length > 1 && time - this._times[0] > 50) {
 
13225                         this._positions.shift();
 
13226                         this._times.shift();
 
13230         _onZoomEnd: function () {
 
13231                 var pxCenter = this._map.getSize().divideBy(2),
 
13232                     pxWorldCenter = this._map.latLngToLayerPoint([0, 0]);
 
13234                 this._initialWorldOffset = pxWorldCenter.subtract(pxCenter).x;
 
13235                 this._worldWidth = this._map.getPixelWorldBounds().getSize().x;
 
13238         _viscousLimit: function (value, threshold) {
 
13239                 return value - (value - threshold) * this._viscosity;
 
13242         _onPreDragLimit: function () {
 
13243                 if (!this._viscosity || !this._offsetLimit) { return; }
 
13245                 var offset = this._draggable._newPos.subtract(this._draggable._startPos);
 
13247                 var limit = this._offsetLimit;
 
13248                 if (offset.x < limit.min.x) { offset.x = this._viscousLimit(offset.x, limit.min.x); }
 
13249                 if (offset.y < limit.min.y) { offset.y = this._viscousLimit(offset.y, limit.min.y); }
 
13250                 if (offset.x > limit.max.x) { offset.x = this._viscousLimit(offset.x, limit.max.x); }
 
13251                 if (offset.y > limit.max.y) { offset.y = this._viscousLimit(offset.y, limit.max.y); }
 
13253                 this._draggable._newPos = this._draggable._startPos.add(offset);
 
13256         _onPreDragWrap: function () {
 
13257                 // TODO refactor to be able to adjust map pane position after zoom
 
13258                 var worldWidth = this._worldWidth,
 
13259                     halfWidth = Math.round(worldWidth / 2),
 
13260                     dx = this._initialWorldOffset,
 
13261                     x = this._draggable._newPos.x,
 
13262                     newX1 = (x - halfWidth + dx) % worldWidth + halfWidth - dx,
 
13263                     newX2 = (x + halfWidth + dx) % worldWidth - halfWidth - dx,
 
13264                     newX = Math.abs(newX1 + dx) < Math.abs(newX2 + dx) ? newX1 : newX2;
 
13266                 this._draggable._absPos = this._draggable._newPos.clone();
 
13267                 this._draggable._newPos.x = newX;
 
13270         _onDragEnd: function (e) {
 
13271                 var map = this._map,
 
13272                     options = map.options,
 
13274                     noInertia = !options.inertia || this._times.length < 2;
 
13276                 map.fire('dragend', e);
 
13279                         map.fire('moveend');
 
13282                         this._prunePositions(+new Date());
 
13284                         var direction = this._lastPos.subtract(this._positions[0]),
 
13285                             duration = (this._lastTime - this._times[0]) / 1000,
 
13286                             ease = options.easeLinearity,
 
13288                             speedVector = direction.multiplyBy(ease / duration),
 
13289                             speed = speedVector.distanceTo([0, 0]),
 
13291                             limitedSpeed = Math.min(options.inertiaMaxSpeed, speed),
 
13292                             limitedSpeedVector = speedVector.multiplyBy(limitedSpeed / speed),
 
13294                             decelerationDuration = limitedSpeed / (options.inertiaDeceleration * ease),
 
13295                             offset = limitedSpeedVector.multiplyBy(-decelerationDuration / 2).round();
 
13297                         if (!offset.x && !offset.y) {
 
13298                                 map.fire('moveend');
 
13301                                 offset = map._limitOffset(offset, map.options.maxBounds);
 
13303                                 requestAnimFrame(function () {
 
13304                                         map.panBy(offset, {
 
13305                                                 duration: decelerationDuration,
 
13306                                                 easeLinearity: ease,
 
13316 // @section Handlers
 
13317 // @property dragging: Handler
 
13318 // Map dragging handler (by both mouse and touch).
 
13319 Map.addInitHook('addHandler', 'dragging', Drag);
 
13322  * L.Map.Keyboard is handling keyboard interaction with the map, enabled by default.
 
13326 // @section Keyboard Navigation Options
 
13328         // @option keyboard: Boolean = true
 
13329         // Makes the map focusable and allows users to navigate the map with keyboard
 
13330         // arrows and `+`/`-` keys.
 
13333         // @option keyboardPanDelta: Number = 80
 
13334         // Amount of pixels to pan when pressing an arrow key.
 
13335         keyboardPanDelta: 80
 
13338 var Keyboard = Handler.extend({
 
13345                 zoomIn:  [187, 107, 61, 171],
 
13346                 zoomOut: [189, 109, 54, 173]
 
13349         initialize: function (map) {
 
13352                 this._setPanDelta(map.options.keyboardPanDelta);
 
13353                 this._setZoomDelta(map.options.zoomDelta);
 
13356         addHooks: function () {
 
13357                 var container = this._map._container;
 
13359                 // make the container focusable by tabbing
 
13360                 if (container.tabIndex <= 0) {
 
13361                         container.tabIndex = '0';
 
13365                         focus: this._onFocus,
 
13366                         blur: this._onBlur,
 
13367                         mousedown: this._onMouseDown
 
13371                         focus: this._addHooks,
 
13372                         blur: this._removeHooks
 
13376         removeHooks: function () {
 
13377                 this._removeHooks();
 
13379                 off(this._map._container, {
 
13380                         focus: this._onFocus,
 
13381                         blur: this._onBlur,
 
13382                         mousedown: this._onMouseDown
 
13386                         focus: this._addHooks,
 
13387                         blur: this._removeHooks
 
13391         _onMouseDown: function () {
 
13392                 if (this._focused) { return; }
 
13394                 var body = document.body,
 
13395                     docEl = document.documentElement,
 
13396                     top = body.scrollTop || docEl.scrollTop,
 
13397                     left = body.scrollLeft || docEl.scrollLeft;
 
13399                 this._map._container.focus();
 
13401                 window.scrollTo(left, top);
 
13404         _onFocus: function () {
 
13405                 this._focused = true;
 
13406                 this._map.fire('focus');
 
13409         _onBlur: function () {
 
13410                 this._focused = false;
 
13411                 this._map.fire('blur');
 
13414         _setPanDelta: function (panDelta) {
 
13415                 var keys = this._panKeys = {},
 
13416                     codes = this.keyCodes,
 
13419                 for (i = 0, len = codes.left.length; i < len; i++) {
 
13420                         keys[codes.left[i]] = [-1 * panDelta, 0];
 
13422                 for (i = 0, len = codes.right.length; i < len; i++) {
 
13423                         keys[codes.right[i]] = [panDelta, 0];
 
13425                 for (i = 0, len = codes.down.length; i < len; i++) {
 
13426                         keys[codes.down[i]] = [0, panDelta];
 
13428                 for (i = 0, len = codes.up.length; i < len; i++) {
 
13429                         keys[codes.up[i]] = [0, -1 * panDelta];
 
13433         _setZoomDelta: function (zoomDelta) {
 
13434                 var keys = this._zoomKeys = {},
 
13435                     codes = this.keyCodes,
 
13438                 for (i = 0, len = codes.zoomIn.length; i < len; i++) {
 
13439                         keys[codes.zoomIn[i]] = zoomDelta;
 
13441                 for (i = 0, len = codes.zoomOut.length; i < len; i++) {
 
13442                         keys[codes.zoomOut[i]] = -zoomDelta;
 
13446         _addHooks: function () {
 
13447                 on(document, 'keydown', this._onKeyDown, this);
 
13450         _removeHooks: function () {
 
13451                 off(document, 'keydown', this._onKeyDown, this);
 
13454         _onKeyDown: function (e) {
 
13455                 if (e.altKey || e.ctrlKey || e.metaKey) { return; }
 
13457                 var key = e.keyCode,
 
13461                 if (key in this._panKeys) {
 
13462                         if (!map._panAnim || !map._panAnim._inProgress) {
 
13463                                 offset = this._panKeys[key];
 
13465                                         offset = toPoint(offset).multiplyBy(3);
 
13470                                 if (map.options.maxBounds) {
 
13471                                         map.panInsideBounds(map.options.maxBounds);
 
13474                 } else if (key in this._zoomKeys) {
 
13475                         map.setZoom(map.getZoom() + (e.shiftKey ? 3 : 1) * this._zoomKeys[key]);
 
13477                 } else if (key === 27 && map._popup && map._popup.options.closeOnEscapeKey) {
 
13488 // @section Handlers
 
13489 // @section Handlers
 
13490 // @property keyboard: Handler
 
13491 // Keyboard navigation handler.
 
13492 Map.addInitHook('addHandler', 'keyboard', Keyboard);
 
13495  * L.Handler.ScrollWheelZoom is used by L.Map to enable mouse scroll wheel zoom on the map.
 
13499 // @section Interaction Options
 
13501         // @section Mousewheel options
 
13502         // @option scrollWheelZoom: Boolean|String = true
 
13503         // Whether the map can be zoomed by using the mouse wheel. If passed `'center'`,
 
13504         // it will zoom to the center of the view regardless of where the mouse was.
 
13505         scrollWheelZoom: true,
 
13507         // @option wheelDebounceTime: Number = 40
 
13508         // Limits the rate at which a wheel can fire (in milliseconds). By default
 
13509         // user can't zoom via wheel more often than once per 40 ms.
 
13510         wheelDebounceTime: 40,
 
13512         // @option wheelPxPerZoomLevel: Number = 60
 
13513         // How many scroll pixels (as reported by [L.DomEvent.getWheelDelta](#domevent-getwheeldelta))
 
13514         // mean a change of one full zoom level. Smaller values will make wheel-zooming
 
13515         // faster (and vice versa).
 
13516         wheelPxPerZoomLevel: 60
 
13519 var ScrollWheelZoom = Handler.extend({
 
13520         addHooks: function () {
 
13521                 on(this._map._container, 'mousewheel', this._onWheelScroll, this);
 
13526         removeHooks: function () {
 
13527                 off(this._map._container, 'mousewheel', this._onWheelScroll, this);
 
13530         _onWheelScroll: function (e) {
 
13531                 var delta = getWheelDelta(e);
 
13533                 var debounce = this._map.options.wheelDebounceTime;
 
13535                 this._delta += delta;
 
13536                 this._lastMousePos = this._map.mouseEventToContainerPoint(e);
 
13538                 if (!this._startTime) {
 
13539                         this._startTime = +new Date();
 
13542                 var left = Math.max(debounce - (+new Date() - this._startTime), 0);
 
13544                 clearTimeout(this._timer);
 
13545                 this._timer = setTimeout(bind(this._performZoom, this), left);
 
13550         _performZoom: function () {
 
13551                 var map = this._map,
 
13552                     zoom = map.getZoom(),
 
13553                     snap = this._map.options.zoomSnap || 0;
 
13555                 map._stop(); // stop panning and fly animations if any
 
13557                 // map the delta with a sigmoid function to -4..4 range leaning on -1..1
 
13558                 var d2 = this._delta / (this._map.options.wheelPxPerZoomLevel * 4),
 
13559                     d3 = 4 * Math.log(2 / (1 + Math.exp(-Math.abs(d2)))) / Math.LN2,
 
13560                     d4 = snap ? Math.ceil(d3 / snap) * snap : d3,
 
13561                     delta = map._limitZoom(zoom + (this._delta > 0 ? d4 : -d4)) - zoom;
 
13564                 this._startTime = null;
 
13566                 if (!delta) { return; }
 
13568                 if (map.options.scrollWheelZoom === 'center') {
 
13569                         map.setZoom(zoom + delta);
 
13571                         map.setZoomAround(this._lastMousePos, zoom + delta);
 
13576 // @section Handlers
 
13577 // @property scrollWheelZoom: Handler
 
13578 // Scroll wheel zoom handler.
 
13579 Map.addInitHook('addHandler', 'scrollWheelZoom', ScrollWheelZoom);
 
13582  * L.Map.Tap is used to enable mobile hacks like quick taps and long hold.
 
13586 // @section Interaction Options
 
13588         // @section Touch interaction options
 
13589         // @option tap: Boolean = true
 
13590         // Enables mobile hacks for supporting instant taps (fixing 200ms click
 
13591         // delay on iOS/Android) and touch holds (fired as `contextmenu` events).
 
13594         // @option tapTolerance: Number = 15
 
13595         // The max number of pixels a user can shift his finger during touch
 
13596         // for it to be considered a valid tap.
 
13600 var Tap = Handler.extend({
 
13601         addHooks: function () {
 
13602                 on(this._map._container, 'touchstart', this._onDown, this);
 
13605         removeHooks: function () {
 
13606                 off(this._map._container, 'touchstart', this._onDown, this);
 
13609         _onDown: function (e) {
 
13610                 if (!e.touches) { return; }
 
13614                 this._fireClick = true;
 
13616                 // don't simulate click or track longpress if more than 1 touch
 
13617                 if (e.touches.length > 1) {
 
13618                         this._fireClick = false;
 
13619                         clearTimeout(this._holdTimeout);
 
13623                 var first = e.touches[0],
 
13626                 this._startPos = this._newPos = new Point(first.clientX, first.clientY);
 
13628                 // if touching a link, highlight it
 
13629                 if (el.tagName && el.tagName.toLowerCase() === 'a') {
 
13630                         addClass(el, 'leaflet-active');
 
13633                 // simulate long hold but setting a timeout
 
13634                 this._holdTimeout = setTimeout(bind(function () {
 
13635                         if (this._isTapValid()) {
 
13636                                 this._fireClick = false;
 
13638                                 this._simulateEvent('contextmenu', first);
 
13642                 this._simulateEvent('mousedown', first);
 
13645                         touchmove: this._onMove,
 
13646                         touchend: this._onUp
 
13650         _onUp: function (e) {
 
13651                 clearTimeout(this._holdTimeout);
 
13654                         touchmove: this._onMove,
 
13655                         touchend: this._onUp
 
13658                 if (this._fireClick && e && e.changedTouches) {
 
13660                         var first = e.changedTouches[0],
 
13663                         if (el && el.tagName && el.tagName.toLowerCase() === 'a') {
 
13664                                 removeClass(el, 'leaflet-active');
 
13667                         this._simulateEvent('mouseup', first);
 
13669                         // simulate click if the touch didn't move too much
 
13670                         if (this._isTapValid()) {
 
13671                                 this._simulateEvent('click', first);
 
13676         _isTapValid: function () {
 
13677                 return this._newPos.distanceTo(this._startPos) <= this._map.options.tapTolerance;
 
13680         _onMove: function (e) {
 
13681                 var first = e.touches[0];
 
13682                 this._newPos = new Point(first.clientX, first.clientY);
 
13683                 this._simulateEvent('mousemove', first);
 
13686         _simulateEvent: function (type, e) {
 
13687                 var simulatedEvent = document.createEvent('MouseEvents');
 
13689                 simulatedEvent._simulated = true;
 
13690                 e.target._simulatedClick = true;
 
13692                 simulatedEvent.initMouseEvent(
 
13693                         type, true, true, window, 1,
 
13694                         e.screenX, e.screenY,
 
13695                         e.clientX, e.clientY,
 
13696                         false, false, false, false, 0, null);
 
13698                 e.target.dispatchEvent(simulatedEvent);
 
13702 // @section Handlers
 
13703 // @property tap: Handler
 
13704 // Mobile touch hacks (quick tap and touch hold) handler.
 
13705 if (touch && !pointer) {
 
13706         Map.addInitHook('addHandler', 'tap', Tap);
 
13710  * L.Handler.TouchZoom is used by L.Map to add pinch zoom on supported mobile browsers.
 
13714 // @section Interaction Options
 
13716         // @section Touch interaction options
 
13717         // @option touchZoom: Boolean|String = *
 
13718         // Whether the map can be zoomed by touch-dragging with two fingers. If
 
13719         // passed `'center'`, it will zoom to the center of the view regardless of
 
13720         // where the touch events (fingers) were. Enabled for touch-capable web
 
13721         // browsers except for old Androids.
 
13722         touchZoom: touch && !android23,
 
13724         // @option bounceAtZoomLimits: Boolean = true
 
13725         // Set it to false if you don't want the map to zoom beyond min/max zoom
 
13726         // and then bounce back when pinch-zooming.
 
13727         bounceAtZoomLimits: true
 
13730 var TouchZoom = Handler.extend({
 
13731         addHooks: function () {
 
13732                 addClass(this._map._container, 'leaflet-touch-zoom');
 
13733                 on(this._map._container, 'touchstart', this._onTouchStart, this);
 
13736         removeHooks: function () {
 
13737                 removeClass(this._map._container, 'leaflet-touch-zoom');
 
13738                 off(this._map._container, 'touchstart', this._onTouchStart, this);
 
13741         _onTouchStart: function (e) {
 
13742                 var map = this._map;
 
13743                 if (!e.touches || e.touches.length !== 2 || map._animatingZoom || this._zooming) { return; }
 
13745                 var p1 = map.mouseEventToContainerPoint(e.touches[0]),
 
13746                     p2 = map.mouseEventToContainerPoint(e.touches[1]);
 
13748                 this._centerPoint = map.getSize()._divideBy(2);
 
13749                 this._startLatLng = map.containerPointToLatLng(this._centerPoint);
 
13750                 if (map.options.touchZoom !== 'center') {
 
13751                         this._pinchStartLatLng = map.containerPointToLatLng(p1.add(p2)._divideBy(2));
 
13754                 this._startDist = p1.distanceTo(p2);
 
13755                 this._startZoom = map.getZoom();
 
13757                 this._moved = false;
 
13758                 this._zooming = true;
 
13762                 on(document, 'touchmove', this._onTouchMove, this);
 
13763                 on(document, 'touchend', this._onTouchEnd, this);
 
13768         _onTouchMove: function (e) {
 
13769                 if (!e.touches || e.touches.length !== 2 || !this._zooming) { return; }
 
13771                 var map = this._map,
 
13772                     p1 = map.mouseEventToContainerPoint(e.touches[0]),
 
13773                     p2 = map.mouseEventToContainerPoint(e.touches[1]),
 
13774                     scale = p1.distanceTo(p2) / this._startDist;
 
13776                 this._zoom = map.getScaleZoom(scale, this._startZoom);
 
13778                 if (!map.options.bounceAtZoomLimits && (
 
13779                         (this._zoom < map.getMinZoom() && scale < 1) ||
 
13780                         (this._zoom > map.getMaxZoom() && scale > 1))) {
 
13781                         this._zoom = map._limitZoom(this._zoom);
 
13784                 if (map.options.touchZoom === 'center') {
 
13785                         this._center = this._startLatLng;
 
13786                         if (scale === 1) { return; }
 
13788                         // Get delta from pinch to center, so centerLatLng is delta applied to initial pinchLatLng
 
13789                         var delta = p1._add(p2)._divideBy(2)._subtract(this._centerPoint);
 
13790                         if (scale === 1 && delta.x === 0 && delta.y === 0) { return; }
 
13791                         this._center = map.unproject(map.project(this._pinchStartLatLng, this._zoom).subtract(delta), this._zoom);
 
13794                 if (!this._moved) {
 
13795                         map._moveStart(true, false);
 
13796                         this._moved = true;
 
13799                 cancelAnimFrame(this._animRequest);
 
13801                 var moveFn = bind(map._move, map, this._center, this._zoom, {pinch: true, round: false});
 
13802                 this._animRequest = requestAnimFrame(moveFn, this, true);
 
13807         _onTouchEnd: function () {
 
13808                 if (!this._moved || !this._zooming) {
 
13809                         this._zooming = false;
 
13813                 this._zooming = false;
 
13814                 cancelAnimFrame(this._animRequest);
 
13816                 off(document, 'touchmove', this._onTouchMove);
 
13817                 off(document, 'touchend', this._onTouchEnd);
 
13819                 // Pinch updates GridLayers' levels only when zoomSnap is off, so zoomSnap becomes noUpdate.
 
13820                 if (this._map.options.zoomAnimation) {
 
13821                         this._map._animateZoom(this._center, this._map._limitZoom(this._zoom), true, this._map.options.zoomSnap);
 
13823                         this._map._resetView(this._center, this._map._limitZoom(this._zoom));
 
13828 // @section Handlers
 
13829 // @property touchZoom: Handler
 
13830 // Touch zoom handler.
 
13831 Map.addInitHook('addHandler', 'touchZoom', TouchZoom);
 
13833 Map.BoxZoom = BoxZoom;
 
13834 Map.DoubleClickZoom = DoubleClickZoom;
 
13836 Map.Keyboard = Keyboard;
 
13837 Map.ScrollWheelZoom = ScrollWheelZoom;
 
13839 Map.TouchZoom = TouchZoom;
 
13841 Object.freeze = freeze;
 
13843 exports.version = version;
 
13844 exports.Control = Control;
 
13845 exports.control = control;
 
13846 exports.Browser = Browser;
 
13847 exports.Evented = Evented;
 
13848 exports.Mixin = Mixin;
 
13849 exports.Util = Util;
 
13850 exports.Class = Class;
 
13851 exports.Handler = Handler;
 
13852 exports.extend = extend;
 
13853 exports.bind = bind;
 
13854 exports.stamp = stamp;
 
13855 exports.setOptions = setOptions;
 
13856 exports.DomEvent = DomEvent;
 
13857 exports.DomUtil = DomUtil;
 
13858 exports.PosAnimation = PosAnimation;
 
13859 exports.Draggable = Draggable;
 
13860 exports.LineUtil = LineUtil;
 
13861 exports.PolyUtil = PolyUtil;
 
13862 exports.Point = Point;
 
13863 exports.point = toPoint;
 
13864 exports.Bounds = Bounds;
 
13865 exports.bounds = toBounds;
 
13866 exports.Transformation = Transformation;
 
13867 exports.transformation = toTransformation;
 
13868 exports.Projection = index;
 
13869 exports.LatLng = LatLng;
 
13870 exports.latLng = toLatLng;
 
13871 exports.LatLngBounds = LatLngBounds;
 
13872 exports.latLngBounds = toLatLngBounds;
 
13874 exports.GeoJSON = GeoJSON;
 
13875 exports.geoJSON = geoJSON;
 
13876 exports.geoJson = geoJson;
 
13877 exports.Layer = Layer;
 
13878 exports.LayerGroup = LayerGroup;
 
13879 exports.layerGroup = layerGroup;
 
13880 exports.FeatureGroup = FeatureGroup;
 
13881 exports.featureGroup = featureGroup;
 
13882 exports.ImageOverlay = ImageOverlay;
 
13883 exports.imageOverlay = imageOverlay;
 
13884 exports.VideoOverlay = VideoOverlay;
 
13885 exports.videoOverlay = videoOverlay;
 
13886 exports.DivOverlay = DivOverlay;
 
13887 exports.Popup = Popup;
 
13888 exports.popup = popup;
 
13889 exports.Tooltip = Tooltip;
 
13890 exports.tooltip = tooltip;
 
13891 exports.Icon = Icon;
 
13892 exports.icon = icon;
 
13893 exports.DivIcon = DivIcon;
 
13894 exports.divIcon = divIcon;
 
13895 exports.Marker = Marker;
 
13896 exports.marker = marker;
 
13897 exports.TileLayer = TileLayer;
 
13898 exports.tileLayer = tileLayer;
 
13899 exports.GridLayer = GridLayer;
 
13900 exports.gridLayer = gridLayer;
 
13902 exports.svg = svg$1;
 
13903 exports.Renderer = Renderer;
 
13904 exports.Canvas = Canvas;
 
13905 exports.canvas = canvas$1;
 
13906 exports.Path = Path;
 
13907 exports.CircleMarker = CircleMarker;
 
13908 exports.circleMarker = circleMarker;
 
13909 exports.Circle = Circle;
 
13910 exports.circle = circle;
 
13911 exports.Polyline = Polyline;
 
13912 exports.polyline = polyline;
 
13913 exports.Polygon = Polygon;
 
13914 exports.polygon = polygon;
 
13915 exports.Rectangle = Rectangle;
 
13916 exports.rectangle = rectangle;
 
13918 exports.map = createMap;
 
13920 var oldL = window.L;
 
13921 exports.noConflict = function() {
 
13926 // Always export us to window global (see #2364)
 
13927 window.L = exports;
 
13930 //# sourceMappingURL=leaflet-src.js.map